← tous les articles

Databases

SQL ou NoSQL ? Comment je vois ce choix

La question n'est pas quel database est le meilleur. C'est de savoir si je connais mes access patterns à l'avance, et si la donnée a des relations que je veux que le database lui-même protège.

SQL ou NoSQL ? Comment je vois ce choix

Pendant longtemps j'ai pensé que c'était une question d'échelle : SQL pour les apps normales, NoSQL une fois que ça devient gros. Ce n'est pas vraiment ça. Les deux projets où j'ai vraiment fait ce choix délibérément m'ont appris que c'est autre chose entièrement.

La question que je pose vraiment en premier

Est-ce que je sais comment cette donnée sera lue avant de la concevoir ?

Ça a l'air à l'envers, puisqu'en SQL vous concevez le schema autour de la donnée puis vous en faites des queries comme vous en avez besoin plus tard. Cette flexibilité est tout l'intérêt d'un database relationnel, et c'est pour ça que c'est le bon default quand je ne sais pas encore toutes les façons dont la donnée sera utilisée.

NoSQL inverse en gros ça. Vous concevez autour des queries que vous allez exécuter, et la forme du storage découle du pattern d'accès.

Là où ça a pris la décision pour moi

Le URL shortener que j'ai construit est l'exemple le plus clair. Il ne fait jamais qu'une seule chose : étant donné un short code, renvoyer l'URL d'origine. Un lookup, par une key, toujours. Pas de joins, pas de queries de reporting, pas de « montre-moi toutes les URLs créées mardi dernier groupées par domaine ».

C'est un access pattern que je connaissais complètement avant d'écrire une ligne de code, ce qui est exactement le cas où Cassandra fait sens. Il est construit pour les lookups par key et répartit la donnée entre les nodes à mesure qu'elle grossit.

Le payment orchestrator est allé dans l'autre sens, sur SQL, et la raison n'était pas non plus l'échelle. Un paiement a des attempts, et des fraud flags, et ces relations doivent être imposées. Un attempt qui référence un paiement qui n'existe pas est une donnée corrompue, et je veux que le database le rejette plutôt que de faire confiance à mon code pour ne jamais commettre cette erreur.

Les relations et les transactions sont la vraie ligne de partage

Cet exemple de paiement est la version générale. Quand la correction de ma donnée dépend de plusieurs lignes qui changent ensemble ou pas du tout, un database relationnel me donne cette garantie directement.

Le cas du transfert d'argent est l'évident, où débiter un compte et en créditer un autre doivent tous les deux réussir ou tous les deux échouer. Mais c'est plus courant que ça. Créer une commande et ses lignes, mettre à jour un paiement et enregistrer l'attempt qui l'a changé. Partout où « la moitié de ça s'est produite » est un état inacceptable, les transactions font un vrai travail pour moi.

Les foreign keys sont la même idée appliquée aux relations. Le database refuse de me laisser créer un orphelin, donc toute cette classe de bug n'atteint jamais la production peu importe ce que fait mon code applicatif.

Où je me tournerais vraiment vers NoSQL

Quelques cas où je pense que c'est clairement le bon choix, au-delà de celui du lookup par key :

Des données sans forme fixe, où différents records ont légitimement des champs différents et les forcer dans des colonnes signifie soit une table très large pleine de null soit un tas de tables presque vides jointes.

Un volume d'écriture très élevé où les écritures sont indépendantes les unes des autres, comme des event logs ou de la télémétrie, où rien n'a besoin d'être cohérent avec autre chose au moment où c'est écrit.

Le caching, qui est en fait une chose séparée mais qui appartient à la même famille. Redis devant le URL shortener n'est pas un choix de database, c'est une couche qui retient la donnée chaude que le vrai database possède aussi.

Ce que je me dirais il y a un an

Optez par défaut pour SQL. Pas parce que c'est meilleur, mais parce que c'est celui qui n'exige pas d'avoir raison sur vos access patterns à l'avance, et en tant que développeur junior sur un nouveau projet, ce n'est généralement pas mon cas.

Tournez-vous vers NoSQL quand vous pouvez nommer la raison précise. « Ça scale mieux » n'est pas une raison, puisqu'un database relationnel sur un server normal encaisse bien plus de trafic que la plupart des projets n'en voient jamais. « C'est un lookup par une key et j'ai besoin que ce soit réparti entre les nodes » est une raison. « Ces records ne partagent vraiment pas une forme » est une raison.

L'erreur que j'aurais faite sans construire les deux, c'est de supposer que le choix porte sur la taille. Il porte sur le fait de savoir si je connais les access patterns à l'avance, et si la donnée a des relations que je veux que le database lui-même protège.