Fonctionnalités de conception d'un modèle de données pour NoSQL

introduction

Fonctionnalités de conception d'un modèle de données pour NoSQL "Il faut courir aussi vite que possible pour rester en place,
et pour arriver quelque part, il faut courir au moins deux fois plus vite !
(c) Alice au pays des merveilles

Il y a quelque temps, on m'a demandé de donner une conférence analystes notre entreprise sur le thème de la conception de modèles de données, car assis longtemps sur des projets (parfois plusieurs années), nous perdons de vue ce qui se passe autour de nous dans le monde des technologies informatiques. Dans notre entreprise (il se trouve que) de nombreux projets n'utilisent pas de bases de données NoSQL (du moins pour l'instant), c'est pourquoi dans ma conférence, je leur ai prêté attention séparément en utilisant l'exemple de HBase et j'ai essayé d'orienter la présentation du matériel vers ceux-ci. qui ne les ont jamais utilisés ont fonctionné. En particulier, j'ai illustré certaines des fonctionnalités de la conception de modèles de données à l'aide d'un exemple que j'ai lu il y a plusieurs années. dans l'article « Introduction à HB ase Schema Design » par Amandeep Khurana. Lors de l'analyse d'exemples, j'ai comparé plusieurs options pour résoudre le même problème afin de mieux transmettre les idées principales au public.

Récemment, « n’ayant rien à faire », je me suis posé la question (le long week-end de mai en quarantaine y est particulièrement propice) : dans quelle mesure les calculs théoriques correspondront-ils à la pratique ? En fait, c’est ainsi qu’est née l’idée de cet article. Un développeur qui travaille avec NoSQL depuis plusieurs jours peut ne rien en apprendre de nouveau (et peut donc immédiatement sauter la moitié de l'article). Mais pour analystesPour ceux qui n'ont pas encore travaillé en étroite collaboration avec NoSQL, je pense que cela sera utile pour acquérir une compréhension de base des fonctionnalités de conception de modèles de données pour HBase.

Exemple d'analyse

À mon avis, avant de commencer à utiliser des bases de données NoSQL, vous devez bien réfléchir et peser le pour et le contre. Souvent, le problème peut très probablement être résolu à l’aide de SGBD relationnels traditionnels. Par conséquent, il est préférable de ne pas utiliser NoSQL sans raisons valables. Si vous décidez néanmoins d'utiliser une base de données NoSQL, vous devez tenir compte du fait que les approches de conception ici sont quelque peu différentes. En particulier, certains d'entre eux peuvent être inhabituels pour ceux qui n'ont auparavant traité que des SGBD relationnels (d'après mes observations). Ainsi, dans le monde « relationnel », nous commençons généralement par modéliser le domaine problématique, et ensuite seulement, si nécessaire, dénormalisons le modèle. En NoSQL, nous doit immédiatement prendre en compte les scénarios attendus pour travailler avec les données et dénormaliser initialement les données. En outre, il existe un certain nombre d'autres différences, qui seront discutées ci-dessous.

Considérons le problème « synthétique » suivant, avec lequel nous continuerons à travailler :

Il est nécessaire de concevoir une structure de stockage pour la liste des amis des utilisateurs d'un réseau social abstrait. Pour simplifier, nous supposerons que toutes nos connexions sont dirigées (comme sur Instagram, pas Linkedin). La structure doit vous permettre de :

  • Répondez à la question de savoir si l'utilisateur A lit l'utilisateur B (modèle de lecture)
  • Autoriser l'ajout/suppression de connexions en cas d'abonnement/désabonnement de l'utilisateur A de l'utilisateur B (modèle de modification de données)

Bien entendu, il existe de nombreuses options pour résoudre le problème. Dans une base de données relationnelle classique, on ferait très probablement simplement un tableau de relations (éventuellement typé si, par exemple, on a besoin de stocker un groupe d'utilisateurs : famille, travail, etc., qui inclut cet « ami »), et d'optimiser la vitesse d'accès ajouterait des index/partitionnement. Très probablement, la table finale ressemblerait à ceci :

user_id
id_ami

Vassia
Petya

Vassia
Olya

ci-après, pour plus de clarté et une meilleure compréhension, j'indiquerai les noms au lieu des identifiants

Dans le cas de HBase, nous savons que :

  • une recherche efficace qui n'aboutit pas à une analyse complète de la table est possible exclusivement par clé
    • en fait, c’est pourquoi écrire des requêtes SQL familières à beaucoup sur de telles bases de données est une mauvaise idée ; techniquement, bien sûr, vous pouvez envoyer une requête SQL avec des jointures et d'autres logiques à HBase à partir de la même Impala, mais quelle sera son efficacité...

Par conséquent, nous sommes obligés d’utiliser l’ID utilisateur comme clé. Et ma première réflexion sur le sujet « où et comment stocker les identifiants de mes amis ? peut-être une idée de les stocker dans des colonnes. Cette option la plus évidente et la plus « naïve » ressemblera à ceci (appelons-la Option 1 (par défaut)pour référence ultérieure) :

Clé de ligne
haut-parleurs

Vassia
1 : Petya
2 : Olia
3 : Dasha

Petya
1 : Macha
2 : Vassia

Ici, chaque ligne correspond à un utilisateur du réseau. Les colonnes ont des noms : 1, 2, ... - selon le nombre d'amis, et les identifiants des amis sont stockés dans les colonnes. Il est important de noter que chaque ligne aura un nombre différent de colonnes. Dans l'exemple de la figure ci-dessus, une ligne a trois colonnes (1, 2 et 3) et la seconde n'en a que deux (1 et 2) - ici nous avons nous-mêmes utilisé deux propriétés HBase que les bases de données relationnelles n'ont pas :

  • la possibilité de modifier dynamiquement la composition des colonnes (ajouter un ami -> ajouter une colonne, supprimer un ami -> supprimer une colonne)
  • différentes lignes peuvent avoir différentes compositions de colonnes

Vérifions que notre structure est conforme aux exigences de la tâche :

  • Lecture des données: pour comprendre si Vasya est abonnée à Olya, nous devrons soustraire toute la ligne par la clé RowKey = « Vasya » et trier les valeurs des colonnes jusqu'à ce que nous y « rencontrions » Olya. Ou parcourez les valeurs de toutes les colonnes, « ne rencontrez pas » Olya et renvoyez la réponse False ;
  • Modification des données : ajouter un ami: pour une tâche similaire, nous devons également soustraire toute la ligne en utilisant la clé RowKey = « Vasya » pour compter le nombre total de ses amis. Nous avons besoin de ce nombre total d'amis pour déterminer le numéro de la colonne dans laquelle nous devons noter l'ID du nouvel ami.
  • Modification des données : supprimer un ami:
    • Il faut soustraire toute la ligne par la clé RowKey = « Vasya » et trier les colonnes afin de trouver celle dans laquelle est enregistré l'ami à supprimer ;
    • Ensuite, après avoir supprimé un ami, nous devons « déplacer » toutes les données dans une seule colonne afin de ne pas avoir de « lacunes » dans leur numérotation.

Évaluons maintenant la productivité de ces algorithmes, qu'il nous faudra implémenter côté « application conditionnelle », en utilisant O-symbolisme. Notons n la taille de notre réseau social hypothétique. Le nombre maximum d'amis qu'un utilisateur peut avoir est alors (n-1). Nous pouvons encore négliger ce (-1) pour nos besoins, puisque dans le cadre de l'utilisation des symboles O, cela n'a pas d'importance.

  • Lecture des données: il faut soustraire toute la ligne et parcourir toutes ses colonnes dans la limite. Cela signifie que l'estimation supérieure des coûts sera d'environ O(n)
  • Modification des données : ajouter un ami: pour déterminer le nombre d'amis, vous devez parcourir toutes les colonnes de la ligne, puis insérer une nouvelle colonne => O(n)
  • Modification des données : supprimer un ami:
    • Semblable à l'ajout - vous devez parcourir toutes les colonnes dans la limite => O(n)
    • Après avoir supprimé les colonnes, nous devons les « déplacer ». Si vous implémentez cela «de front», alors dans la limite, vous aurez besoin de jusqu'à (n-1) opérations. Mais ici et plus loin dans la partie pratique, nous utiliserons une approche différente, qui mettra en œuvre un « pseudo-décalage » pour un nombre fixe d'opérations - c'est-à-dire qu'un temps constant y sera consacré, quel que soit n. Ce temps constant (O(2) pour être exact) peut être négligé par rapport à O(n). La démarche est illustrée dans la figure ci-dessous : on copie simplement les données de la « dernière » colonne vers celle dont on souhaite supprimer les données, puis on supprime la dernière colonne :
      Fonctionnalités de conception d'un modèle de données pour NoSQL

Au total, dans tous les scénarios, nous avons reçu une complexité informatique asymptotique de O(n).
Vous avez probablement déjà remarqué qu'il faut presque toujours lire la ligne entière de la base de données, et dans deux cas sur trois, simplement parcourir toutes les colonnes et calculer le nombre total d'amis. Par conséquent, comme tentative d'optimisation, vous pouvez ajouter une colonne « compte », qui stocke le nombre total d'amis de chaque utilisateur du réseau. Dans ce cas, nous ne pouvons pas lire la ligne entière pour calculer le nombre total d’amis, mais lire une seule colonne « compte ». L'essentiel est de ne pas oublier de mettre à jour le « compte » lors de la manipulation des données. Que. nous nous améliorons Option 2 (compte) :

Clé de ligne
haut-parleurs

Vassia
1 : Petya
2 : Olia
3 : Dasha
compte : 3

Petya
1 : Macha
2 : Vassia

compte : 2

Par rapport à la première option :

  • Lecture des données: pour obtenir une réponse à la question « Vasya lit-elle Olya ? rien n'a changé => O(n)
  • Modification des données : ajouter un ami: Nous avons simplifié l'insertion d'un nouvel ami, puisque désormais nous n'avons plus besoin de lire la ligne entière et de parcourir ses colonnes, mais nous ne pouvons obtenir que la valeur de la colonne « count », etc. déterminez immédiatement le numéro de colonne pour insérer un nouvel ami. Cela conduit à une réduction de la complexité informatique à O(1)
  • Modification des données : supprimer un ami: Lors de la suppression d'un ami, nous pouvons également utiliser cette colonne pour réduire le nombre d'opérations d'E/S lors du « décalage » des données d'une cellule vers la gauche. Mais la nécessité de parcourir les colonnes pour trouver celle qui doit être supprimée demeure, donc => ​​​​O(n)
  • D'un autre côté, désormais, lors de la mise à jour des données, nous devons mettre à jour la colonne « compte » à chaque fois, mais cela prend un temps constant, ce qui peut être négligé dans le cadre des symboles O.

En général, l’option 2 semble un peu plus optimale, mais elle s’apparente davantage à « une évolution plutôt qu’une révolution ». Pour faire une « révolution », nous aurons besoin Option 3 (col).
Renversons tout : nous attribuerons nom de la colonne ID utilisateur! Ce qui sera écrit dans la colonne elle-même n'a plus d'importance pour nous, que ce soit le chiffre 1 (en général, on peut y stocker des choses utiles, par exemple le groupe « famille/amis/etc. »). Cette approche peut surprendre le « profane » non préparé qui n'a aucune expérience préalable de travail avec des bases de données NoSQL, mais c'est précisément cette approche qui vous permet d'utiliser le potentiel de HBase dans cette tâche de manière beaucoup plus efficace :

Clé de ligne
haut-parleurs

Vassia
Petya : 1
Olia : 1
Dasha : 1

Petya
Macha : 1
Vassia : 1

Ici, nous obtenons plusieurs avantages à la fois. Pour les comprendre, analysons la nouvelle structure et estimons la complexité informatique :

  • Lecture des données: pour répondre à la question de savoir si Vasya est abonnée à Olya, il suffit de lire une colonne « Olya » : si elle est là, alors la réponse est Vrai, sinon – Faux => O(1)
  • Modification des données : ajouter un ami: Ajout d'un ami : ajoutez simplement une nouvelle colonne « Friend ID » => O(1)
  • Modification des données : supprimer un ami: supprimez simplement la colonne Friend ID => O(1)

Comme vous pouvez le constater, un avantage significatif de ce modèle de stockage est que dans tous les scénarios dont nous avons besoin, nous opérons avec une seule colonne, évitant ainsi de lire toute la ligne de la base de données et, en outre, d'énumérer toutes les colonnes de cette ligne. On pourrait s'arrêter là, mais...

Vous pouvez être perplexe et aller un peu plus loin dans la voie de l'optimisation des performances et de la réduction des opérations d'E/S lors de l'accès à la base de données. Et si nous stockions les informations complètes sur la relation directement dans la clé de ligne elle-même ? Autrement dit, rendre le composite de clé comme userID.friendID ? Dans ce cas, nous n’avons même pas besoin de lire les colonnes de la ligne (Option 4 (ligne)):

Clé de ligne
haut-parleurs

Vasya.Petya
Petya : 1

Vasya.Olya
Olia : 1

Vasya.Dasha
Dasha : 1

Petya.Masha
Macha : 1

Petya.Vasya
Vassia : 1

Évidemment, l’évaluation de tous les scénarios de manipulation de données dans une telle structure, comme dans la version précédente, sera O(1). La différence avec l'option 3 résidera uniquement dans l'efficacité des opérations d'E/S dans la base de données.

Eh bien, le dernier "arc". Il est facile de voir que dans l'option 4, la clé de ligne aura une longueur variable, ce qui peut éventuellement affecter les performances (on rappelle ici que HBase stocke les données sous la forme d'un ensemble d'octets et que les lignes des tableaux sont triées par clé). De plus, nous avons un séparateur qui devra peut-être être géré dans certains scénarios. Pour éliminer cette influence, vous pouvez utiliser les hachages de userID et friendID, et comme les deux hachages auront une longueur constante, vous pouvez simplement les concaténer, sans séparateur. Ensuite, les données du tableau ressembleront à ceci (Option 5 (hachage)):

Clé de ligne
haut-parleurs

dc084ef00e94aef49be885f9b01f51c01918fa783851db0dc1f72f83d33a5994
Petya : 1

dc084ef00e94aef49be885f9b01f51c0f06b7714b5ba522c3cf51328b66fe28a
Olia : 1

dc084ef00e94aef49be885f9b01f51c00d2c2e5d69df6b238754f650d56c896a
Dasha : 1

1918fa783851db0dc1f72f83d33a59949ee3309645bd2c0775899fca14f311e1
Macha : 1

1918fa783851db0dc1f72f83d33a5994dc084ef00e94aef49be885f9b01f51c0
Vassia : 1

De toute évidence, la complexité algorithmique du travail avec une telle structure dans les scénarios que nous envisageons sera la même que celle de l'option 4, c'est-à-dire O(1).
Au total, résumons toutes nos estimations de la complexité informatique dans un seul tableau :

Ajouter un ami
Vérifier un ami
Supprimer un ami

Option 1 (par défaut)
O (n)
O (n)
O (n)

Option 2 (compte)
O (1)
O (n)
O (n)

Option 3 (colonne)
O (1)
O (1)
O (1)

Option 4 (rangée)
O (1)
O (1)
O (1)

Option 5 (hachage)
O (1)
O (1)
O (1)

Comme vous pouvez le constater, les options 3 à 5 semblent être les plus préférables et garantissent théoriquement l'exécution de tous les scénarios de manipulation de données nécessaires en temps constant. Dans les conditions de notre tâche, il n'y a pas d'exigence explicite d'obtenir une liste de tous les amis de l'utilisateur, mais dans les activités réelles du projet, il serait bon pour nous, en bons analystes, « d'anticiper » qu'une telle tâche puisse survenir et "Étendez une paille." Par conséquent, mes sympathies sont du côté de l'option 3. Mais il est fort probable que dans un projet réel, cette demande aurait déjà pu être résolue par d'autres moyens, donc sans une vision générale de l'ensemble du problème, il vaut mieux ne pas faire conclusions finales.

Préparation de l'expérience

J'aimerais tester les arguments théoriques ci-dessus dans la pratique - c'était le but de l'idée née au cours de ce long week-end. Pour ce faire, il est nécessaire d'évaluer la vitesse de fonctionnement de notre « application conditionnelle » dans tous les scénarios d'utilisation de la base de données décrits, ainsi que l'augmentation de ce temps avec l'augmentation de la taille du réseau social (n). Le paramètre cible qui nous intéresse et que nous mesurerons au cours de l'expérimentation est le temps mis par « l'application conditionnelle » pour réaliser une « opération métier ». Par « transaction commerciale », nous entendons l’une des opérations suivantes :

  • Ajouter un nouvel ami
  • Vérifier si l'utilisateur A est un ami de l'utilisateur B
  • Supprimer un ami

Ainsi, compte tenu des exigences énoncées dans la déclaration initiale, le scénario de vérification se présente comme suit :

  • Enregistrement des données. Générer aléatoirement un réseau initial de taille n. Pour se rapprocher du « monde réel », le nombre d’amis de chaque utilisateur est également une variable aléatoire. Mesurez le temps pendant lequel notre « application conditionnelle » écrit toutes les données générées dans HBase. Divisez ensuite le temps obtenu par le nombre total d'amis ajoutés - c'est ainsi que nous obtenons le temps moyen pour une « opération commerciale »
  • Lecture des données. Pour chaque utilisateur, créez une liste de « personnalités » pour lesquelles vous devez obtenir une réponse, que l'utilisateur y soit abonné ou non. La longueur de la liste = approximativement le nombre d'amis de l'utilisateur, et pour la moitié des amis cochés, la réponse doit être « Oui », et pour l'autre moitié – « Non ». Le contrôle est effectué dans un ordre tel que les réponses « Oui » et « Non » alternent (c'est-à-dire que dans un cas sur deux, nous devrons parcourir toutes les colonnes de la ligne pour les options 1 et 2). La durée totale de dépistage est ensuite divisée par le nombre d’amis testés pour obtenir la durée moyenne de dépistage par sujet.
  • Suppression de données. Supprimez tous les amis de l'utilisateur. De plus, l’ordre de suppression est aléatoire (c’est-à-dire que nous « mélangeons » la liste originale utilisée pour enregistrer les données). La durée totale du contrôle est ensuite divisée par le nombre d'amis supprimés pour obtenir la durée moyenne par contrôle.

Les scénarios doivent être exécutés pour chacune des 5 options de modèle de données et pour différentes tailles de réseau social afin de voir comment le temps change à mesure qu'il grandit. En l’espace d’un n, les connexions au réseau et la liste des utilisateurs à vérifier doivent bien entendu être les mêmes pour les 5 options.
Pour une meilleure compréhension, vous trouverez ci-dessous un exemple de données générées pour n= 5. Le « générateur » écrit produit trois dictionnaires d'ID en sortie :

  • le premier est pour l'insertion
  • le deuxième est pour vérifier
  • troisième – pour suppression

{0: [1], 1: [4, 5, 3, 2, 1], 2: [1, 2], 3: [2, 4, 1, 5, 3], 4: [2, 1]} # всего 15 друзей

{0: [1, 10800], 1: [5, 10800, 2, 10801, 4, 10802], 2: [1, 10800], 3: [3, 10800, 1, 10801, 5, 10802], 4: [2, 10800]} # всего 18 проверяемых субъектов

{0: [1], 1: [1, 3, 2, 5, 4], 2: [1, 2], 3: [4, 1, 2, 3, 5], 4: [1, 2]} # всего 15 друзей

Comme vous pouvez le constater, tous les identifiants supérieurs à 10 000 dans le dictionnaire à vérifier sont précisément ceux qui donneront certainement la réponse Faux. L'insertion, la vérification et la suppression des « amis » s'effectuent exactement dans l'ordre spécifié dans le dictionnaire.

L'expérience a été réalisée sur un ordinateur portable exécutant Windows 10, où HBase s'exécutait dans un conteneur Docker et Python avec Jupyter Notebook s'exécutait dans l'autre. Docker s'est vu attribuer 2 cœurs de processeur et 2 Go de RAM. Toute la logique, comme l'émulation de « l'application conditionnelle » et le « piping » pour générer les données de test et mesurer le temps, a été écrite en Python. La bibliothèque a été utilisée pour travailler avec HBase base heureuse, pour calculer les hachages (MD5) pour l'option 5 - hashlib

Compte tenu de la puissance de calcul d'un ordinateur portable particulier, un lancement pour n = 10, 30,… a été sélectionné expérimentalement. 170 – lorsque la durée totale de fonctionnement du cycle de test complet (tous les scénarios pour toutes les options pour tous les n) était encore plus ou moins raisonnable et correspondait à un goûter (en moyenne 15 minutes).

Il convient ici de faire remarquer que dans cette expérience, nous n'évaluons pas principalement des chiffres de performance absolus. Même une comparaison relative de deux options différentes peut ne pas être entièrement correcte. Nous nous intéressons maintenant à la nature de l'évolution du temps en fonction de n, car compte tenu de la configuration ci-dessus du « banc d'essai », il est très difficile d'obtenir des estimations de temps « débarrassées » de l'influence de facteurs aléatoires et autres ( et une telle tâche n'a pas été définie).

Le résultat de l'expérience

Le premier test est de savoir comment évolue le temps passé à remplir la liste d’amis. Le résultat est dans le graphique ci-dessous.
Fonctionnalités de conception d'un modèle de données pour NoSQL
Les options 3 à 5, comme prévu, montrent un temps de « transaction commerciale » presque constant, qui ne dépend pas de la croissance de la taille du réseau et une différence de performances indiscernable.
L'option 2 affiche également des performances constantes, mais légèrement inférieures, presque exactement 2 fois par rapport aux options 3 à 5. Et cela ne peut que se réjouir, car cela est en corrélation avec la théorie - dans cette version, le nombre d'opérations d'E/S vers/depuis HBase est exactement 2 fois plus grand. Cela peut servir de preuve indirecte que notre banc de test offre en principe une bonne précision.
L'option 1 s'avère également, comme prévu, la plus lente et démontre une augmentation linéaire du temps passé à s'ajouter à la taille du réseau.
Examinons maintenant les résultats du deuxième test.
Fonctionnalités de conception d'un modèle de données pour NoSQL
Les options 3 à 5 se comportent à nouveau comme prévu : temps constant, quelle que soit la taille du réseau. Les options 1 et 2 démontrent une augmentation linéaire du temps à mesure que la taille du réseau augmente et des performances similaires. De plus, l'option 2 s'avère un peu plus lente - apparemment en raison de la nécessité de relire et de traiter la colonne supplémentaire « compte », qui devient plus visible à mesure que n grandit. Mais je m'abstiendrai néanmoins de tirer des conclusions, car la précision de cette comparaison est relativement faible. De plus, ces ratios (quelle option, 1 ou 2, est la plus rapide) ont changé d'une série à l'autre (tout en conservant la nature de la dépendance et en étant « au coude à coude »).

Eh bien, le dernier graphique est le résultat des tests de suppression.

Fonctionnalités de conception d'un modèle de données pour NoSQL

Encore une fois, pas de surprise ici. Les options 3 à 5 effectuent la suppression en temps constant.
De plus, il est intéressant de noter que les options 4 et 5, contrairement aux scénarios précédents, affichent des performances sensiblement légèrement inférieures à celles de l'option 3. Apparemment, l'opération de suppression de ligne est plus coûteuse que l'opération de suppression de colonne, ce qui est généralement logique.

Les options 1 et 2, comme prévu, démontrent une augmentation linéaire du temps. Dans le même temps, l'option 2 est systématiquement plus lente que l'option 1 - en raison de l'opération d'E/S supplémentaire pour « maintenir » la colonne de comptage.

Conclusions générales de l'expérience :

  • Les options 3 à 5 démontrent une plus grande efficacité car elles tirent parti de HBase ; De plus, leurs performances diffèrent les unes des autres d'une constante et ne dépendent pas de la taille du réseau.
  • La différence entre les options 4 et 5 n’a pas été enregistrée. Mais cela ne signifie pas que l’option 5 ne doit pas être utilisée. Il est probable que le scénario expérimental utilisé, compte tenu des performances du banc d’essai, n’ait pas permis de le détecter.
  • La nature de l'augmentation du temps nécessaire pour effectuer des « opérations commerciales » avec des données a généralement confirmé les calculs théoriques obtenus précédemment pour toutes les options.

Le final

Les expériences grossières réalisées ne doivent pas être considérées comme une vérité absolue. De nombreux facteurs n'ont pas été pris en compte et ont faussé les résultats (ces fluctuations sont particulièrement visibles dans les graphiques avec une petite taille de réseau). Par exemple, la vitesse d'économie utilisée par happybase, le volume et la méthode de mise en œuvre de la logique que j'ai écrite en Python (je ne peux pas prétendre que le code a été écrit de manière optimale et a utilisé efficacement les capacités de tous les composants), peut-être les fonctionnalités de mise en cache HBase, l'activité en arrière-plan de Windows 10 sur mon ordinateur portable, etc. De manière générale, on peut supposer que tous les calculs théoriques ont démontré expérimentalement leur validité. Eh bien, ou du moins, il n’était pas possible de les réfuter avec une telle « attaque frontale ».

En conclusion, des recommandations pour tous ceux qui commencent tout juste à concevoir des modèles de données dans HBase : résumez votre expérience précédente de travail avec des bases de données relationnelles et rappelez-vous les « commandements » :

  • Lors de la conception, nous partons de la tâche et des modèles de manipulation des données, et non du modèle de domaine
  • Accès efficace (sans analyse complète de la table) – uniquement par clé
  • Dénormalisation
  • Différentes lignes peuvent contenir différentes colonnes
  • Composition dynamique des intervenants

Source: habr.com

Ajouter un commentaire