Favoriser l'immuabilité dans la conception de bases de données

26

Un des éléments de Java efficace de Joshua Bloch est la notion que les classes devraient permettre la mutation des instances aussi peu que possible, et de préférence pas du tout.

Souvent, les données d'un objet sont conservées dans une base de données sous une forme ou une autre. Cela m'a amené à réfléchir à l'idée d'immuabilité dans une base de données, en particulier pour les tables qui représentent une seule entité dans un système plus vaste.

Quelque chose que j'ai expérimenté récemment est l'idée d'essayer de minimiser les mises à jour que je fais pour les lignes de tableau représentant ces objets, et d'essayer d'effectuer des insertions à la place autant que possible.

Un exemple concret de quelque chose que j'expérimentais récemment. Si je sais que je pourrais ajouter un enregistrement avec des données supplémentaires plus tard, je créerai un autre tableau pour représenter cela, un peu comme les deux définitions de tableau suivantes:

create table myObj (id integer, ...other_data... not null);
create table myObjSuppliment (id integer, myObjId integer, ...more_data... not null);

Il est à espérer évident que ces noms ne sont pas textuellement, mais simplement pour démontrer l'idée.

Est-ce une approche raisonnable de la modélisation de la persistance des données? Vaut-il la peine d'essayer de limiter les mises à jour effectuées sur une table, en particulier pour remplir des valeurs nulles pour les données qui pourraient ne pas exister lors de la création de l'enregistrement? Y a-t-il des moments où une approche comme celle-ci pourrait causer de graves douleurs plus tard?

Ed Carrel
la source
7
J'ai l'impression que c'est une solution sans problème ... Vous devriez mettre à jour, plutôt que de créer des adaptations élaborées pour éviter la mise à jour.
Fosco
Je pense qu'il s'agissait plus d'avoir une idée intuitive d'une solution à l'esprit, et de vouloir la faire fonctionner par autant de personnes que possible, et dans le processus de réaliser que ce n'est peut-être pas la meilleure solution au problème que j'ai. Je peux ouvrir une autre question avec le problème, à condition que je ne la trouve pas ailleurs.
Ed Carrel
1
Il peut y avoir de bonnes raisons d'éviter les mises à jour dans les bases de données. Cependant, lorsque ces raisons se présentent, il s'agit davantage d'un problème d'optimisation et, en tant que tel, ne devrait pas être fait sans preuve qu'il y a un problème.
dietbuddha
6
Je pense qu'il y a un argument solide pour l'immuabilité dans les bases de données. Cela résout beaucoup de problèmes. Je pense que les commentaires négatifs ne sont pas venus de gens ouverts d'esprit. Les mises à jour sur place sont à l'origine de tant de problèmes. Je dirais que nous avons tout en arrière. Les mises à jour sur place sont la solution héritée d'un problème qui n'existe plus. Le stockage est bon marché. Pourquoi le faire? Combien de systèmes de base de données ont des journaux d'audit, des systèmes de gestion des versions, un besoin de réplication distribuée qui, comme nous le savons tous, nécessite la capacité de prendre en charge la latence pour l'évolutivité. L'immuabilité résout tout cela.
cirrus
@Fosco Certains systèmes sont absolument tenus de ne jamais supprimer les données (y compris en utilisant UPDATE). Comme les dossiers médicaux du médecin.
Izkata

Réponses:

25

Le but principal de l'immuabilité est de garantir qu'il n'y a aucun instant dans le temps lorsque les données en mémoire sont dans un état non valide. (L'autre est parce que les notations mathématiques sont principalement statiques, et que les choses immuables sont donc plus faciles à conceptualiser et à modéliser mathématiquement.) En mémoire, si un autre thread essaie de lire ou d'écrire des données pendant qu'il est utilisé, il pourrait finir par devenir corrompu, ou il pourrait lui-même être dans un état corrompu. Si vous avez plusieurs opérations d'affectation aux champs d'un objet, dans une application multithread, un autre thread peut essayer de travailler avec lui quelque part entre les deux - ce qui pourrait être mauvais.

L'immutabilité remédie à cela en écrivant d'abord toutes les modifications dans un nouvel emplacement en mémoire, puis en effectuant l'affectation finale en une seule étape de réécriture du pointeur sur l'objet pour pointer vers le nouvel objet - qui sur tous les CPU est un atomique opération.

Les bases de données font la même chose en utilisant les transactions atomiques : lorsque vous démarrez une transaction, elle écrit toutes les nouvelles mises à jour dans un nouvel emplacement sur le disque. Lorsque vous avez terminé la transaction, il modifie le pointeur sur le disque à l'endroit où se trouvent les nouvelles mises à jour - ce qu'il fait dans un court instant pendant lequel les autres processus ne peuvent pas y toucher.

C'est aussi exactement la même chose que votre idée de créer de nouvelles tables, sauf plus automatique et plus flexible.

Donc, pour répondre à votre question, oui, l'immuabilité est bonne dans les bases de données, mais non, vous n'avez pas besoin de créer des tableaux séparés juste à cette fin; vous pouvez simplement utiliser toutes les commandes de transaction atomique disponibles pour votre système de base de données.

Rei Miyasaka
la source
Merci d'avoir répondu. Cette perspective était exactement ce dont j'avais besoin pour réaliser que mon intuition essayait de confondre plusieurs idées différentes en un seul motif.
Ed Carrel du
8
Il y a un peu plus que l'atmosphère. L'argument que je vois le plus souvent en faveur de l'immuabilité dans un contexte de POO est que les objets immuables ne vous demandent de valider leur état qu'une seule fois, dans le constructeur. S'ils sont mutables, toutes les méthodes qui peuvent changer leur état doivent également vérifier que l'état résultant est toujours valide, ce qui peut ajouter une complexité importante à la classe. Cet argument s'applique également aux bases de données, mais est beaucoup plus faible, car les règles de validation db ont tendance à être déclaratives plutôt que procédurales, elles n'ont donc pas besoin d'être dupliquées pour chaque requête.
Dave Sherohman
24

Cela dépend des avantages que vous attendez de l'immuabilité. La réponse de Rei Miyasaka a abordé un (évitement d'états intermédiaires invalides), mais en voici un autre.

La mutation est parfois appelée mise à jour destructive : lorsque vous mutez un objet, l'ancien état est perdu (à moins que vous ne preniez des mesures supplémentaires pour le conserver explicitement d'une manière ou d'une autre). En revanche, avec des données immuables, il est trivial de représenter simultanément l'état avant et après une opération, ou de représenter plusieurs états successeurs. Imaginez que vous essayez d'implémenter une recherche en largeur en mutant un seul objet d'état.

Cela apparaît probablement dans le monde des bases de données le plus souvent sous forme de données temporelles . Disons que le mois dernier, vous étiez sur le plan Basic, mais le 16, vous êtes passé au plan Premium. Si nous remplaçons simplement un champ qui indique le plan sur lequel vous êtes, nous pourrions avoir des difficultés à bien facturer. Nous pourrions également manquer la possibilité d'analyser les tendances. (Hé, regardez ce qu'a fait cette campagne publicitaire locale!)

C'est ce qui me vient à l'esprit quand vous dites "immuabilité dans la conception de bases de données", de toute façon.

Ryan Culpepper
la source
2
Je ne suis pas d'accord avec votre troisième paragraphe. Si vous souhaitez avoir un historique (journal d'audit, journal des changements de plan, etc.), vous devez créer un tableau séparé pour cela. Dupliquer les 50 champs du Customertableau pour ne pas oublier que l'utilisateur a modifié le plan n'apporte rien sauf un énorme inconvénient de performances, des sélections plus lentes dans le temps, une exploration de données plus compliquée (par rapport aux journaux) et plus d'espace gaspillé.
Arseni Mourzenko
6
@MainMa: j'aurais peut-être dû dire plutôt "allez lire sur les bases de données temporelles". Mon exemple était destiné à esquisser ce que sont les données temporelles; Je ne prétends pas que c'est toujours la meilleure façon de représenter des données changeantes. D'un autre côté, bien que la prise en charge des données temporelles soit actuellement assez faible, je m'attends à ce que la tendance soit à la prise en compte des données temporelles dans la base de données elle-même, plutôt que de la reléguer à des représentations de "seconde classe" telles que les journaux des modifications.
Ryan Culpepper
Que se passe-t-il si nous conservons un historique des modifications dans une table d'audit (Spring Boot et Hibernate par exemple offe cette capacité)?
Mohammad Najar
14

Si vous êtes intéressé par les avantages que vous pouvez obtenir de l'immuabilité dans une base de données, ou au moins une base de données qui offre l'illusion de l'immuabilité, consultez Datomic.

Datomic est une base de données inventée par Rich Hickey en alliance avec Think Pertinence, il existe de nombreuses vidéos où elles expliquent l'architecture, les objectifs, le modèle de données. Recherchez infoq, l'un en particulier s'intitule Datomic, Database as a Value . Dans confreaks, vous pouvez trouver une keynote que Rich Hickey a donnée lors de la conférence euroclojure en 2012. confreaks.com/videos/2077-euroclojure2012-day-2-keynote-the-datomic-architecture-and-data-model

Il y a une discussion sur vimeo.com/53162418 qui est plus orientée vers le développement.

En voici un autre de stuart halloway à.pscdn.net/008/00102/videoplatform/kv/121105techconf_close.html

  • Datomic est une base de données de faits dans le temps, appelées datums, en 5 tuples [E, A, V, T, O]
    • E Identifiant d'entité
    • Un nom d'attribut dans l'entité (peut avoir des espaces de noms)
    • V Valeur de l'attribut
    • T ID de transaction, avec cela vous avez une notion de temps.
    • O Une opération d'assertion (valeur actuelle ou actuelle), de rejet (valeur passée);
  • Utilise son propre format de données, appelé EDN (Extensible Data Notation)
  • Les transactions sont ACID
  • Utilise le journal de données comme langage de requête, qui est déclaratif en tant que requêtes récursives SQL +. Les requêtes sont représentées avec des structures de données, et étendues avec votre langage jvm, vous n'avez pas besoin d'utiliser clojure.
  • La base de données est découplée en 3 services distincts (processus, machines):
    • Transaction
    • Espace de rangement
    • Moteur de requête.
  • Vous pouvez séparément, mettre à l'échelle chaque service.
  • Ce n'est pas open source, mais il existe une version gratuite (comme dans la bière) de Datomic.
  • Vous pouvez indiquer un schéma flexible.
    • l'ensemble d'attributs est ouvert
    • ajouter de nouveaux attributs à tout moment
    • aucune rigidité dans la définition ou la requête

Maintenant, puisque les informations sont stockées en tant que faits dans le temps:

  • tout ce que vous faites est d'ajouter des faits à la base de données, vous ne les supprimez jamais (sauf si la loi l'exige)
  • vous pouvez tout mettre en cache pour toujours. Query Engine, réside dans le serveur d'applications en tant que base de données en mémoire (pour les langues jvm, les langues non jvm ont accès via une API REST.)
  • vous pouvez interroger à partir du temps dans le passé.

La base de données est une valeur et un paramètre pour le moteur de requête, le QE gère la connexion et la mise en cache. Étant donné que vous pouvez voir la base de données comme une valeur et une structure de données immuable en mémoire, vous pouvez la fusionner avec une autre structure de données faite à partir de valeurs "dans le futur" et la transmettre au QE et à la requête avec des valeurs futures, sans changer la base de données réelle .

Il y a un projet open source de Rich Hickey, appelé codeq , vous pouvez le trouver dans github Datomic / codeq, qui étend le modèle git, et stocke les références aux objets git dans une base de données sans données, et faites des requêtes sur votre code, vous peut voir un exemple d'utilisation de datomic.

Vous pouvez considérer la datomique comme un NoSQL ACID, avec des datums vous pouvez modéliser des tableaux ou des documents ou des magasins Kv ou des graphiques.

kisai
la source
7

L'idée d'éviter les mises à jour et de préférer les insertions est l'une des idées derrière la construction de votre stockage de données en tant que source d'événements, une idée que vous trouverez souvent utilisée avec le CQRS. Dans un modèle de source d'événements, il n'y a pas de mise à jour: un agrégat est représenté comme la séquence de sa "transformation" (événements), et par conséquent le stockage est uniquement en ajout.
Ce site contient des discussions intéressantes sur le CQRS et la recherche d'événements, si vous êtes curieux à ce sujet!

Mathias
la source
Le CQRS et le sourcing d'événements sont de plus en plus en vogue ces derniers temps.
Gulshan
6

Cela a une relation très étroite avec ce que l'on appelle les «dimensions à évolution lente» dans le monde de l'entreposage de données et les tables «temporelles» ou «bi-temporelles» dans d'autres domaines.

La construction de base est:

  1. Utilisez toujours une clé de substitution générée comme clé primaire.
  2. L'identifiant unique de tout ce que vous décrivez devient la "clé logique".
  3. Chaque ligne doit avoir au moins un horodatage "ValidFrom" et éventuellement un horodatage "ValidTo" et encore plus éventuellement un indicateur "Latest Version".
  4. Lors de la "création" d'une entité logique, vous insérez une nouvelle ligne avec un "Valid From" de l'horodatage actuel. La valeur ValidTo facultative est définie sur "pour toujours" (9999-12-31 23:59:59) et la dernière version sur "True".
  5. Lors d'une mise à jour ultérieure de l'entité logique. Vous insérez au moins une nouvelle ligne comme ci-dessus. Vous devrez peut-être également régler le ValidTo de la version précédente sur "maintenant () - 1 seconde" et la dernière version sur "False"
    1. Lors de la suppression logique (cela ne fonctionne qu'avec l'horodatage ValidTo!), Vous définissez l'indicateur ValidTo de la ligne actuelle sur "now () -1 seconde".

Les avantages de ce schéma sont que vous pouvez recréer "l'état" de votre entité logique à tout moment, que vous avez un historique de votre entité dans le temps et que vous minimisez les conflits si votre "entité logique" est fortement utilisée.

Les inconvénients sont que vous stockez beaucoup plus de données et que vous devez conserver plus d'index (au moins sur Logical Key + ValidFrom + ValidTo). Un index sur Logical Key + Latest Version accélère considérablement la plupart des requêtes. Cela complique également votre SQL!

À vous de décider si cela vaut la peine, à moins que vous n'ayez vraiment besoin de conserver un historique et que vous ayez à recréer l'état de vos entités à un moment donné.

James Anderson
la source
1

Une autre raison possible d'avoir une base de données immuable serait de prendre en charge un meilleur traitement parallèle. Les mises à jour qui se produisent dans le désordre peuvent endommager les données de manière permanente, donc le verrouillage doit se produire pour éviter cela, détruisant les performances parallèles. Beaucoup d'insertions d'événements peuvent aller dans n'importe quel ordre, et l'état aura au moins finalement raison tant que tous les événements seront finalement traités. Cependant, c'est si difficile à travailler en pratique par rapport aux mises à jour de bases de données que vous auriez vraiment besoin de beaucoup de parallélisme pour envisager de faire les choses de cette façon - je ne le recommande pas.

psr
la source
0

Avertissement: je suis à peu près un nouveau dans DB: p

Cela dit, cette approche de la satellisation des données a un impact immédiat sur les performances:

  • Bon moins de trafic sur la table principale
  • Bonnes petites lignes sur la table principale
  • Mauvais nécessitant les données satellite signifie qu'une autre recherche est nécessaire
  • Mauvais plus d'espace occupé si tous les objets existent dans les deux tables

en fonction de vos besoins, vous pouvez vous en réjouir, ou non, mais c'est certainement un point à considérer.

Matthieu M.
la source
-1

Je ne vois pas comment votre schéma peut être appelé "immuable".

Que se passe-t-il lorsqu'une valeur stockée dans le tableau supplémentaire change? Il semble que vous auriez besoin d'effectuer une mise à jour sur cette table.

Pour qu'une base de données soit réellement immuable, elle devrait être gérée uniquement par "INSERTS". Pour cela, vous avez besoin d'une méthode d'identification de la ligne "actuelle". Cela finit presque toujours par être horriblement inefficace. Vous devez soit copier toutes les valeurs inchangées précédentes, soit reconstituer l'état actuel de plusieurs enregistrements lorsque vous interrogez. La sélection de la ligne actuelle nécessite généralement un SQL horriblement désordonné comme ( where updTime = (SELECT max(updTime) from myTab where id = ?).

Ce problème se présente souvent dans DataWarehousing où vous devez conserver un historique des données au fil du temps et pouvoir sélectionner l'état pour un moment donné. La solution est généralement des tableaux "dimensionnels". Cependant, alors qu'ils résolvent le problème DW "qui était le représentant des ventes en janvier dernier". Ils n'offrent aucun des avantages des classes immuables Javas.

Sur une note plus philosophique; des bases de données existent pour stocker "l'état" (votre solde bancaire, votre consommation d'électricité, vos points brownie sur StackOverflow etc. etc.) essayer de créer une base de données "sans état" semble un exercice plutôt inutile.

James Anderson
la source
Pour un seul enregistrement, ce WHERE id = {} ORDER BY updTime DESC LIMIT 1n'est généralement pas trop inefficace.
Izkata
@Izkata - essayez de mettre au milieu d'une jointure à trois tables :-)
James Anderson