Champs logiques dans les bases de données, existe-t-il un antidote?


Souvent, les tables contiennent un grand nombre de champs logiques, il n’existe aucun moyen de les indexer tous et l’efficacitĂ© de cette indexation est faible. NĂ©anmoins, pour travailler avec des expressions logiques arbitraires en SQL, un mĂ©canisme d'indexation multidimensionnelle convient, qui sera discutĂ© sous le chat.

En SQL, les champs logiques sont utilisés principalement dans deux cas. Tout d'abord, lorsque vous avez vraiment besoin d'un attribut binaire, par exemple, 'acheter / vendre' dans la table de transaction. Ces attributs changent rarement au fil du temps.

DeuxiÚmement, pour enregistrer l'état de la machine d'état qui décrit l'enregistrement. Il est entendu qu'un objet logique correspondant à un enregistrement de table passe par une série d'états, dont le nombre et les transitions entre eux sont déterminés par la logique appliquée. Un exemple simple est la technique de «suppression en douceur», lorsqu'un enregistrement n'est pas physiquement détruit, mais uniquement marqué comme supprimé.

Si la machine est complexe, il peut y avoir une bonne quantitĂ© de tels champs, dans l'un de nos tableaux, il y a 58 (+14 obsolĂštes) de tels champs (y compris les ensembles d'indicateurs) et ce n'est pas quelque chose qui sort de l'ordinaire. Cela n'Ă©tait pas prĂ©vu Ă  l'origine, mais au fur et Ă  mesure que le produit se dĂ©veloppe et que les exigences externes changent, les machines correspondantes se dĂ©veloppent, les dĂ©veloppeurs vont et viennent, les analystes changent ... Ă  un moment donnĂ©, il peut ĂȘtre plus sĂ»r d'obtenir un nouveau drapeau, plutĂŽt que de comprendre toutes les subtilitĂ©s. De plus, les donnĂ©es historiques se sont accumulĂ©es et leurs conditions doivent rester adĂ©quates.

hors sujet
À certains Ă©gards, cela est similaire Ă  un processus Ă©volutif lorsqu'une masse d'informations / mĂ©canismes sont stockĂ©s dans le gĂ©nome qui, Ă  premiĂšre vue, ne sont pas du tout nĂ©cessaires, mais il est impossible de s'en dĂ©barrasser. D'un autre cĂŽtĂ©, il vaut la peine de respecter ces mĂ©canismes, car ce sont eux qui ont permis aux prĂ©dĂ©cesseurs Ă©volutionnaires de survivre (y compris pendant les grandes extinctions ) et de gagner la course Ă©volutionnaire. Encore une fois, qui sait oĂč l'Ă©volution nous mĂšnera et ce qui s'avĂ©rera utile Ă  l'avenir.

Mettre un drapeau signifie non seulement ajouter un champ du type correspondant, mais aussi le prendre en compte dans le fonctionnement de l'automate, quels Ă©tats il affecte, Ă  quelles transitions il participe. En pratique, cela ressemble Ă  ceci:

  • un processus ou une sĂ©rie de processus, appelons-les «écrivains», crĂ©ons de nouveaux enregistrements dans l'Ă©tat initial (Ă©ventuellement dans l'un des Ă©tats initiaux)
  • un certain nombre de processus, appelons-les "lecteurs", de temps en temps ils lisent des objets qui sont dans les Ă©tats dont ils ont besoin
  • un certain nombre de processus, appelons-les «gestionnaires», surveillons des Ă©tats spĂ©cifiques et, en fonction de la logique appliquĂ©e, modifions ces Ă©tats. C'est-Ă -dire faire fonctionner une machine d'Ă©tat.

Pour sélectionner des enregistrements dans un état particulier, il est rare que le filtrage par l'un des champs booléens soit suffisant. Il s'agit généralement d'une expression entiÚre, parfois non triviale. Il semblerait que vous ayez besoin d'indexer ces champs et le processeur SQL le découvrira. Mais pas si simple.

PremiÚrement, il peut y avoir de nombreux champs booléens; les indexer tous serait trop inutile.

DeuxiĂšmement, il peut s’avĂ©rer inutile car la sĂ©lectivitĂ© pour chacun des champs sera faible et la probabilitĂ© conjointe n'est pas couverte par les statistiques du processeur SQL.

Supposons que dans la table T1, il existe deux champs boolĂ©ens: F1 et F2 et la requĂȘte

select F1, F2, count(1) from T1 group by F1, F2 

donne
F1F2COUNT
fauxfaux499
fauxvrai1
vraifaux1
vraivrai499

C'est-Ă -dire bien que, selon F1 et F2, vrai et faux soient Ă©galement probables, la combinaison (vrai, faux) ne tombe qu'une fois sur mille. Par consĂ©quent, si nous indexons F1 et F2 sĂ©parĂ©ment et les forçons Ă  ĂȘtre utilisĂ©s dans la requĂȘte , le processeur SQL devrait lire la moitiĂ© des deux indices et croiser les rĂ©sultats. Il peut ĂȘtre moins coĂ»teux de lire l'intĂ©gralitĂ© du tableau et de calculer l'expression pour chaque ligne.

Et mĂȘme si vous collectez des statistiques sur les demandes traitĂ©es, cela ne sera pas trĂšs utile. les statistiques spĂ©cifiques aux domaines responsables de l'Ă©tat de la machine flottent beaucoup. En effet, Ă  tout moment un "handler" peut venir et transfĂ©rer la moitiĂ© des lignes de l'Ă©tat S1 Ă  S2.

Pour travailler avec de telles expressions, un indice multidimensionnel se suggÚre, dont l'algorithme a été présenté précédemment et s'est révélé assez bon.

Mais vous devez d'abord comprendre comment une expression logique arbitraire se transformera en requĂȘte (s) pour l'index.

Forme normale disjonctive


Une requĂȘte unique vers un index multidimensionnel est un rectangle multidimensionnel qui limite l'espace de requĂȘte. Si le champ participe Ă  la demande, une restriction est dĂ©finie pour celui-ci. Sinon, le rectangle dans cette coordonnĂ©e n'est limitĂ© que par la largeur de cette coordonnĂ©e. Les coordonnĂ©es logiques ont une capacitĂ© de 1.

Une requĂȘte de recherche dans un tel index est une chaĂźne de & (conjonction), par exemple, l'expression: v1 & v2 & v3 & (! V4), Ă©quivalente Ă  v1: [1,1], v2: [1,1], v3: [1, 1], v4: [0,0]. Et tous les autres champs ont une plage: [0,1].

Compte tenu de cela, notre regard se tourne immĂ©diatement vers le DNF - l'une des formes canoniques d'expressions logiques. On soutient que toute expression peut ĂȘtre reprĂ©sentĂ©e comme une disjonction de conjonctions littĂ©raires. Un littĂ©ral dĂ©signe ici un champ logique ou sa nĂ©gation.

En d'autres termes, grĂące Ă  de simples manipulations, toute expression logique peut ĂȘtre reprĂ©sentĂ©e comme une disjonction de plusieurs requĂȘtes vers un index logique multidimensionnel.

Il y a un MAIS. Une telle transformation peut dans certains cas conduire Ă  une augmentation exponentielle de la taille de l'expression. Par exemple, la conversion de



conduit à une expression de 2 ** n termes. Dans de tels cas, le développeur de l'application doit réfléchir à la signification physique de ce qu'il fait et, de la part du processeur SQL, vous pouvez toujours refuser d'utiliser l'index logique si le nombre de conjonctions dépasse les limites raisonnables.

Algorithme d'indexation multidimensionnelle


Pour l'indexation multidimensionnelle, les propriétés d'une courbe de numérotation auto-similaire basée sur des simplexes hyper-cubiques avec le cÎté 2. Il s'est avéré que deux versions de ces courbes sont d'une importance pratique - la courbe Z et la courbe Hilbert.

image
Figure 1 Courbe Z bidimensionnelle, 3 et 6 itérations

image
Figure 2 Courbe de Hilbert bidimensionnelle, 3 et 6 itérations

  • Un simplexe Ă  N dimensions avec le cĂŽtĂ© 2 a 2 ** n sommets et (2 ** n-1) transitions entre eux.
  • Une itĂ©ration Ă©lĂ©mentaire d'un simplex transforme chaque sommet d'un simplex en un simplex de niveau infĂ©rieur.
  • AprĂšs avoir fait le nombre d'itĂ©rations nĂ©cessaire, nous pouvons construire un rĂ©seau hyper-cubique de n'importe quelle taille.
  • De plus, chaque nƓud de ce rĂ©seau aura son propre numĂ©ro unique - le chemin empruntĂ© le long de la courbe de numĂ©rotation depuis son dĂ©but. De plus, chaque nƓud de ce rĂ©seau a une valeur dans chacune des coordonnĂ©es. En fait, la courbe de numĂ©rotation traduit le point multidimensionnel en une valeur unidimensionnelle adaptĂ©e Ă  l'indexation avec un arbre B rĂ©gulier .
  • Tous les nƓuds situĂ©s Ă  l'intĂ©rieur d'un simplex de n'importe quel niveau se trouvent dans le mĂȘme intervalle et cet intervalle ne coupe aucun simplex du mĂȘme niveau.
  • Par consĂ©quent, tout rectangle de recherche (case) peut ĂȘtre divisĂ© en un petit nombre de sous-requĂȘtes hyper-cubiques, dans chacune desquelles l'index peut ĂȘtre lu par une recherche / traversĂ©e.
  • À cela, nous ajoutons la magie du travail de bas niveau avec l'arbre B afin de ne pas faire de requĂȘtes inutiles et ... l'algorithme est prĂȘt.

Voici comment cela fonctionne dans la pratique:


Figure 3 Exemple de recherche dans un index bidimensionnel (courbe Z)

La figure 3 montre le partitionnement de l'Ă©tendue de recherche d'origine en sous-requĂȘtes et les points trouvĂ©s. Un indice bidimensionnel a Ă©tĂ© utilisĂ©, construit sur un ensemble alĂ©atoire et uniformĂ©ment rĂ©parti de 100 000 000 de points [1 000 000, 1 000 000].

Index multidimensionnel logique


Puisque nous parlons d'indexation multidimensionnelle, il est temps de penser Ă  combien elle peut ĂȘtre multidimensionnelle? Y a-t-il des limites objectives?

Bien sĂ»r, parce que l'arbre B a une organisation de page et pour ĂȘtre un arbre, au moins deux Ă©lĂ©ments doivent ĂȘtre garantis pour tenir sur la page. Si vous prenez la page pour 8K, le stockage d'un Ă©lĂ©ment ne peut pas dĂ©passer 4K. En 4K, sans compression, environ 1000 valeurs 32 bits conviennent. C'est beaucoup, au-delĂ  des limites de toute application raisonnable, on peut dire que les limites physiques ne sont pratiquement pas disponibles.

Il y a un autre cĂŽtĂ©, chaque dimension supplĂ©mentaire n'est en aucun cas libre, elle prend de l'espace disque et ralentit le travail. Du point de vue de la «signification physique», les champs qui changent en mĂȘme temps doivent ĂȘtre inclus dans le mĂȘme index et leur recherche va Ă©galement de pair. Il est inutile d'indexer tout de suite.

Les champs logiques sont diffĂ©rents. Comme nous l'avons vu, des dizaines de champs logiques peuvent ĂȘtre impliquĂ©s dans les mĂȘmes mĂ©canismes. Et les coĂ»ts de stockage / lecture sont assez faibles. Il y a une tentation de collecter tous les champs logiques dans un index et de voir ce qui se passe.

Certes, il y a des nuances:

  • Jusqu'Ă  prĂ©sent, dans la valeur indexĂ©e, les chiffres de coordonnĂ©es diffĂ©rentes Ă©taient mĂ©langĂ©s, dans les chiffres les moins significatifs de la clĂ© Ă©taient les bits les moins significatifs des coordonnĂ©es ... Par consĂ©quent, l'ordre des champs lors de l'indexation n'avait pas d'importance.
  • Maintenant, un bit est dĂ©pensĂ© pour stocker la valeur d'un champ logique. C'est-Ă -dire certains champs logiques iront Ă  la fin de la clĂ©, et certains au dĂ©but. Cela signifie que le filtrage par une partie des champs sera trĂšs efficace, et par certains, il sera trĂšs inefficace. En fait, si nous effectuons une recherche dans l'ordre le plus bas, nous devrons lire l'index entier pour obtenir une rĂ©ponse. Mais cela (le plus probable) est mieux que de lire le tableau entier pour rĂ©pondre Ă  la mĂȘme question.
  • Il y a un problĂšme de choix - tous les champs logiques sont Ă©gaux, mais certains seront plus Ă©gaux que d'autres. De considĂ©rations gĂ©nĂ©rales, il est nĂ©cessaire d'examiner les distorsions des statistiques, plus le rapport vrai / faux pour un domaine particulier est Ă©levĂ©, plus la dĂ©charge dans laquelle sa valeur sera ancienne.
  • Le partitionnement par type de courbe de numĂ©rotation disparaĂźt, si auparavant il fallait choisir entre la courbe Z et la courbe de Hilbert, il n'y a pas de diffĂ©rence pratique sur les donnĂ©es mono-bit.
  • NULLs. Étant donnĂ© que NULL n'est pas une valeur inconnue, mais l'absence de toute valeur, ces enregistrements ne doivent pas ĂȘtre inclus dans l'index. Dans les indices unidimensionnels, c'est ce qui se passe. Mais dans notre cas, il peut s'avĂ©rer que certains des champs logiques contiennent des valeurs, d'autres non. En consĂ©quence, nous ne pouvons pas mettre cela dans l'indice car l'algorithme de recherche ne sait pas travailler avec la logique ternaire. Et donc, de tels enregistrements devraient ĂȘtre impossibles Ă  insĂ©rer dans la table (s'il y a un index multidimensionnel, pas nĂ©cessairement logique, soit dit en passant)

Il est prĂ©vu qu'un index logique multidimensionnel peut dans certains cas ne pas fonctionner trĂšs efficacement. À strictement parler, tout index peut fonctionner de maniĂšre inefficace si trop de donnĂ©es tombent dans la zone de recherche. Mais pour un index multidimensionnel logique, cela est aggravĂ© par la dĂ©pendance de l'ordre des champs dĂ©crit ci-dessus, lorsque, pour un petit rĂ©sultat, vous devez lire l'index entier. Dans la mesure oĂč il s'agit d'un problĂšme dans la pratique, seule l'expĂ©rience peut le montrer.

Expérience numérique


Construire un index:

  • l'index sera de 128 bits, c'est-Ă -dire construit sur 128 champs logiques
  • et contiendra 2 ** 30 Ă©lĂ©ments
  • la valeur de l'Ă©lĂ©ment d'index sera un nombre de 0 Ă  2 ** 30
  • la clĂ© de l'Ă©lĂ©ment d'index sera le mĂȘme nombre dĂ©calĂ© de 48 bits vers la gauche, c'est-Ă -dire les champs logiques 48 Ă  78 seront remplis avec les chiffres du nombre dans le mĂȘme ordre
  • en consĂ©quence, nous obtenons 30 champs logiques significatifs au milieu de la clĂ©, les bits restants seront remplis 0
  • Chacun des champs boolĂ©ens a des statistiques Ă©gales vrai / faux
  • Tous sont statistiquement indĂ©pendants.

Recherche:

  • Chaque expĂ©rience correspond Ă  la sĂ©lection de plusieurs champs logiques consĂ©cutifs et Ă  leur affectation de valeurs de recherche. Non pas parce que l'algorithme ne peut rechercher que dans des bandes, mais parce qu'il est possible de visualiser plus clairement les rĂ©sultats de l'expĂ©rience, nous n'avons que deux dimensions - la largeur de la bande et sa position
  • Un total de 24 sĂ©ries d'expĂ©riences. Dans chaque sĂ©rie, nous chercherons des valeurs oĂč la bande de champs logiques de largeur correspondante N (de 1 Ă  24 bits) prend la valeur true.
  • Dans chaque sĂ©rie, il y aura une sous-sĂ©rie d'expĂ©riences dans laquelle une bande de champs logiques d'une largeur sĂ©lectionnĂ©e est situĂ©e avec diffĂ©rents dĂ©calages S depuis le dĂ©but de la bande en 30 champs logiques significatifs. ExpĂ©riences totales (30-N) dans la sous-sĂ©rie.
  • Dans chaque expĂ©rience, une recherche est effectuĂ©e pour tous les Ă©lĂ©ments de l'indice qui satisfont Ă  la condition, c'est-Ă -dire les champs avec des nombres dans l'intervalle [48 + S, 48 + S + N -1] seront recherchĂ©s dans l'intervalle [1,1], le reste dans l'intervalle [0,1]
  • La recherche se fait Ă  partir d'un dĂ©marrage Ă  froid
  • Le rĂ©sultat est le nombre de pages de disque lues, y compris la mise en cache (4096 pages cache)
  • Le contrĂŽle du bon fonctionnement se fait de deux maniĂšres - le nombre d'Ă©lĂ©ments trouvĂ©s doit ĂȘtre Ă©gal Ă  2 ** (30-N) et dans les valeurs trouvĂ©es vous pouvez vĂ©rifier les chiffres correspondants

Alors


Figure 4 Résultats, le nombre de pages lues dans différentes séries

Par Y - le nombre de pages lues est reporté.
Au X - passage des bandes de la catégorie des plus jeunes (48) aux seniors. Des rayures de différentes largeurs sont signées et marquées de différentes couleurs.


Figure 5 Les mĂȘmes donnĂ©es que la figure 4, une autre vue

X - décalage de bande
Y - bande passante

À noter:

  • bien que cela ne soit pas directement visible dans les images, l'index fonctionne correctement, il est visible Ă  la fois dans le nombre d'Ă©lĂ©ments trouvĂ©s et dans le contenu des Ă©lĂ©ments eux-mĂȘmes
  • toutes les bandes d'une largeur ne dĂ©passant pas 10 avec un dĂ©calage de 0 nĂ©cessitent une lecture continue de l'index
  • les bandes d'une largeur de 1 Ă  18 avec une augmentation du dĂ©calage atteignent l'asymptote 2 ** (- N) de la taille de l'index entier, ce qui est logique
  • pour les bandes plus larges de l'asymptote - la hauteur de l'arbre, il ne peut pas y avoir moins de lectures
  • un peu plus de 1000 Ă©lĂ©ments sont placĂ©s sur la page de feuille d'index, cela peut ĂȘtre vu dans une bande de largeur 10, qui lors du dĂ©calage 0 ne nĂ©cessite plus de lire l'index entier, certaines pages peuvent ĂȘtre sautĂ©es
  • le filtrage de bas niveau fonctionne Ă©tonnamment bien. ConsidĂ©rez une bande d'une largeur de 10. Une option idĂ©ale pour une recherche est avec un dĂ©calage de 20 (un total de 30 champs significatifs), quand il n'y a aucun champ non dĂ©fini dans le prĂ©fixe du tout, les donnĂ©es peuvent ĂȘtre trouvĂ©es avec un seul faisceau. Dans cette situation, environ 1/1000 de l'index est lu pendant la recherche - 779 pages.
    Le cas intermédiaire est un décalage de 10, nous avons un préfixe et un suffixe de 10 champs inconnus. Le nombre de pages est de 2484, seulement trois fois pire que dans le cas idéal.
    Et mĂȘme dans le pire des cas, avec un dĂ©calage de 0 (un prĂ©fixe de 20 champs inconnus), vous pouvez ignorer certaines pages.

Dans l'ensemble, l'algorithme d'indexation multidimensionnelle peut ĂȘtre reconnu comme efficace mĂȘme dans un cas aussi absurde. Mais l'option la plus infructueuse du point de vue de l'index logique est considĂ©rĂ©e - des Ă©tats Ă©quiprobables dans tous les champs logiques indĂ©pendants.

Expérience sur des données réelles


Tableau des métiers , total 278 479 918 lignes, données d'une des boucles de test.
Les rĂ©sultats de certaines requĂȘtes dans le tableau ci-dessous:

NDemandeLe nombre de lignes en conséquenceLire les pages
1IsProcessed == 0 && NullStatus == 06,2739
2IsProcessed == 0 && NullStatus == 0 && IsCoverage == 06,2739
3IsCoverage == 1 && QF_ICEBERG == 11 388 128386
4PutStatus == 1 && PayStatus == 061 788 37616,486
5IsProcessed == 1 && NullStatus == 0 &&
QF_CURR_PFI == 0 && QF_TERMINATION == 0
278 473 64574 285
6IsProcessed == 1 && PutStatus == 0 &&
IsCoverage == 1
1 650 240447
7QF_UNK3 == 0 && QF_UNK4 == 023 39219

La lecture / le traitement d'une seule page prend en moyenne 0,8 ms.

Il n'est pas nĂ©cessaire de dĂ©crire la signification de requĂȘtes spĂ©cifiques, elles ne sont lĂ  que pour dĂ©montrer l'opĂ©rabilitĂ©. Ce qui, soit dit en passant, est confirmĂ©.

Mais avant que cette technique puisse ĂȘtre d'une utilitĂ© pratique, il reste beaucoup Ă  faire. Donc, pour continuer.

Source: https://habr.com/ru/post/fr483056/


All Articles