[{"data":1,"prerenderedAt":1599},["ShallowReactive",2],{"authors":3,"article-2022-04-07-creer-un-index-b-tree-compose-efficace":331},[4,23,35,48,61,73,85,98,111,124,136,148,161,173,185,197,209,221,233,245,258,270,282,295,307,319],{"id":5,"title":6,"body":7,"description":11,"extension":14,"meta":15,"name":16,"navigation":17,"path":18,"readingTime":19,"seo":20,"stem":21,"__hash__":22},"authors\u002Fauthors\u002Falexandre-guillon.md","Software Engineer",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"md",{},"Alexandre Guillon",true,"\u002Fauthors\u002Falexandre-guillon",1,{"title":6,"description":11},"authors\u002Falexandre-guillon","4tf48mjyjFNqItOHaulICbrjeCyMag1o6801uHeTz98",{"id":24,"title":6,"body":25,"description":11,"extension":14,"meta":29,"name":30,"navigation":17,"path":31,"readingTime":19,"seo":32,"stem":33,"__hash__":34},"authors\u002Fauthors\u002Falexis-ablain.md",{"type":8,"value":26,"toc":27},[],{"title":11,"searchDepth":12,"depth":12,"links":28},[],{},"Alexis Ablain","\u002Fauthors\u002Falexis-ablain",{"title":6,"description":11},"authors\u002Falexis-ablain","_SIAtB7f-39e5t3GiJof81NP47s6MGo2n4gaHkTy1uQ",{"id":36,"title":37,"body":38,"description":11,"extension":14,"meta":42,"name":43,"navigation":17,"path":44,"readingTime":19,"seo":45,"stem":46,"__hash__":47},"authors\u002Fauthors\u002Faxel-shaita.md","Engineering Manager",{"type":8,"value":39,"toc":40},[],{"title":11,"searchDepth":12,"depth":12,"links":41},[],{},"Axel Shaïta","\u002Fauthors\u002Faxel-shaita",{"title":37,"description":11},"authors\u002Faxel-shaita","fK0argUhsBkWLjpTAhY13oYLVzQthcEYkCEdtHWmIgE",{"id":49,"title":50,"body":51,"description":11,"extension":14,"meta":55,"name":56,"navigation":17,"path":57,"readingTime":19,"seo":58,"stem":59,"__hash__":60},"authors\u002Fauthors\u002Fbaptiste-faure.md","Head of Talent Acquisition",{"type":8,"value":52,"toc":53},[],{"title":11,"searchDepth":12,"depth":12,"links":54},[],{},"Baptiste Faure","\u002Fauthors\u002Fbaptiste-faure",{"title":50,"description":11},"authors\u002Fbaptiste-faure","ELisToYtcgHmgdVWZkCclTPV6exZtfyXqhpx1jjbJHs",{"id":62,"title":6,"body":63,"description":11,"extension":14,"meta":67,"name":68,"navigation":17,"path":69,"readingTime":19,"seo":70,"stem":71,"__hash__":72},"authors\u002Fauthors\u002Fbenjamin-bouillot.md",{"type":8,"value":64,"toc":65},[],{"title":11,"searchDepth":12,"depth":12,"links":66},[],{},"Benjamin Bouillot","\u002Fauthors\u002Fbenjamin-bouillot",{"title":6,"description":11},"authors\u002Fbenjamin-bouillot","tbhCFZyfTt7ZM5b5YgqQ2nhgnSTl8BweaQQryc87fHo",{"id":74,"title":37,"body":75,"description":11,"extension":14,"meta":79,"name":80,"navigation":17,"path":81,"readingTime":19,"seo":82,"stem":83,"__hash__":84},"authors\u002Fauthors\u002Fcedric-nicoloso.md",{"type":8,"value":76,"toc":77},[],{"title":11,"searchDepth":12,"depth":12,"links":78},[],{},"Cédric Nicoloso","\u002Fauthors\u002Fcedric-nicoloso",{"title":37,"description":11},"authors\u002Fcedric-nicoloso","ibSoh4VZYiWYTuLOnZTedaAfcnvet1Q9H7ogW0LgorY",{"id":86,"title":87,"body":88,"description":11,"extension":14,"meta":92,"name":93,"navigation":17,"path":94,"readingTime":19,"seo":95,"stem":96,"__hash__":97},"authors\u002Fauthors\u002Fdavid-touzet.md","Staff Engineer",{"type":8,"value":89,"toc":90},[],{"title":11,"searchDepth":12,"depth":12,"links":91},[],{},"David Touzet","\u002Fauthors\u002Fdavid-touzet",{"title":87,"description":11},"authors\u002Fdavid-touzet","dHWwnQxb1Ubt-WwXWEODGEo9AFoq1cJUhfg3kdnYSBM",{"id":99,"title":100,"body":101,"description":11,"extension":14,"meta":105,"name":106,"navigation":17,"path":107,"readingTime":19,"seo":108,"stem":109,"__hash__":110},"authors\u002Fauthors\u002Feloise-chizat.md","Data Engineer",{"type":8,"value":102,"toc":103},[],{"title":11,"searchDepth":12,"depth":12,"links":104},[],{},"Eloïse Chizat","\u002Fauthors\u002Feloise-chizat",{"title":100,"description":11},"authors\u002Feloise-chizat","Utd72Vm9qT4hh2ZbFi6a2_nXw5Wb494Ed_HL1ra5yw8",{"id":112,"title":113,"body":114,"description":11,"extension":14,"meta":118,"name":119,"navigation":17,"path":120,"readingTime":19,"seo":121,"stem":122,"__hash__":123},"authors\u002Fauthors\u002Femmanuel-auclair.md","Staff engineer",{"type":8,"value":115,"toc":116},[],{"title":11,"searchDepth":12,"depth":12,"links":117},[],{},"Emmanuel Auclair","\u002Fauthors\u002Femmanuel-auclair",{"title":113,"description":11},"authors\u002Femmanuel-auclair","MtsA8THNLEn0dTtYEIQaGwDuf7MjQL55IOeei5gugEg",{"id":125,"title":6,"body":126,"description":11,"extension":14,"meta":130,"name":131,"navigation":17,"path":132,"readingTime":19,"seo":133,"stem":134,"__hash__":135},"authors\u002Fauthors\u002Fhoreb-parraud.md",{"type":8,"value":127,"toc":128},[],{"title":11,"searchDepth":12,"depth":12,"links":129},[],{},"Horeb Parraud","\u002Fauthors\u002Fhoreb-parraud",{"title":6,"description":11},"authors\u002Fhoreb-parraud","ajjsnUX4ohZI-ghMdbb92q_taWDkKXVZSLZXoAeLQtg",{"id":137,"title":37,"body":138,"description":11,"extension":14,"meta":142,"name":143,"navigation":17,"path":144,"readingTime":19,"seo":145,"stem":146,"__hash__":147},"authors\u002Fauthors\u002Fhugo-contreras.md",{"type":8,"value":139,"toc":140},[],{"title":11,"searchDepth":12,"depth":12,"links":141},[],{},"Hugo Contreras","\u002Fauthors\u002Fhugo-contreras",{"title":37,"description":11},"authors\u002Fhugo-contreras","2nc3VMu9ASq9Z6Pwx2-7-Ye991Pww4p-UEDBQFfjF-Q",{"id":149,"title":150,"body":151,"description":11,"extension":14,"meta":155,"name":156,"navigation":17,"path":157,"readingTime":19,"seo":158,"stem":159,"__hash__":160},"authors\u002Fauthors\u002Fjulien-tassin.md","Head of Engineering",{"type":8,"value":152,"toc":153},[],{"title":11,"searchDepth":12,"depth":12,"links":154},[],{},"Julien Tassin","\u002Fauthors\u002Fjulien-tassin",{"title":150,"description":11},"authors\u002Fjulien-tassin","iUIHI7SITje38Jh9X9uvYs4-VsHx4eCdt6hAlyLFG_o",{"id":162,"title":6,"body":163,"description":11,"extension":14,"meta":167,"name":168,"navigation":17,"path":169,"readingTime":19,"seo":170,"stem":171,"__hash__":172},"authors\u002Fauthors\u002Flaurent-renard.md",{"type":8,"value":164,"toc":165},[],{"title":11,"searchDepth":12,"depth":12,"links":166},[],{},"Laurent Renard","\u002Fauthors\u002Flaurent-renard",{"title":6,"description":11},"authors\u002Flaurent-renard","5BP7Ed-pt1SQHjh0UJ1XUrlLTcdlFaDoKBCP4deHq8A",{"id":174,"title":6,"body":175,"description":11,"extension":14,"meta":179,"name":180,"navigation":17,"path":181,"readingTime":19,"seo":182,"stem":183,"__hash__":184},"authors\u002Fauthors\u002Fleo-martin.md",{"type":8,"value":176,"toc":177},[],{"title":11,"searchDepth":12,"depth":12,"links":178},[],{},"Léo Martin","\u002Fauthors\u002Fleo-martin",{"title":6,"description":11},"authors\u002Fleo-martin","eYxCHkRgbGDV7shKdTA9s7Tu0zGV4yDGFoKR5MHQntY",{"id":186,"title":6,"body":187,"description":11,"extension":14,"meta":191,"name":192,"navigation":17,"path":193,"readingTime":19,"seo":194,"stem":195,"__hash__":196},"authors\u002Fauthors\u002Floic-bousquet.md",{"type":8,"value":188,"toc":189},[],{"title":11,"searchDepth":12,"depth":12,"links":190},[],{},"Loïc Bousquet","\u002Fauthors\u002Floic-bousquet",{"title":6,"description":11},"authors\u002Floic-bousquet","ko12qZwiGL8XNjAoy9oWypPkIjr29Pbq7vhdtgldqeQ",{"id":198,"title":6,"body":199,"description":11,"extension":14,"meta":203,"name":204,"navigation":17,"path":205,"readingTime":19,"seo":206,"stem":207,"__hash__":208},"authors\u002Fauthors\u002Floic-poullain.md",{"type":8,"value":200,"toc":201},[],{"title":11,"searchDepth":12,"depth":12,"links":202},[],{},"Loïc Poullain","\u002Fauthors\u002Floic-poullain",{"title":6,"description":11},"authors\u002Floic-poullain","oRIyJhFRTqxy5dLCYQ2OnYZ1DB-gLDUM-85vTSYuTF0",{"id":210,"title":100,"body":211,"description":11,"extension":14,"meta":215,"name":216,"navigation":17,"path":217,"readingTime":19,"seo":218,"stem":219,"__hash__":220},"authors\u002Fauthors\u002Fmaud-lelu.md",{"type":8,"value":212,"toc":213},[],{"title":11,"searchDepth":12,"depth":12,"links":214},[],{},"Maud Lélu","\u002Fauthors\u002Fmaud-lelu",{"title":100,"description":11},"authors\u002Fmaud-lelu","MMbsCKuE41OMHusrl12FIEsI-Trx7l8Nn_ANhvj2_y4",{"id":222,"title":37,"body":223,"description":11,"extension":14,"meta":227,"name":228,"navigation":17,"path":229,"readingTime":19,"seo":230,"stem":231,"__hash__":232},"authors\u002Fauthors\u002Fnicolas-poirier.md",{"type":8,"value":224,"toc":225},[],{"title":11,"searchDepth":12,"depth":12,"links":226},[],{},"Nicolas Poirier","\u002Fauthors\u002Fnicolas-poirier",{"title":37,"description":11},"authors\u002Fnicolas-poirier","dXrJkYo8az4SN_D23aYc3fQ7z8s1dR2a0lt1ogjAjJs",{"id":234,"title":37,"body":235,"description":11,"extension":14,"meta":239,"name":240,"navigation":17,"path":241,"readingTime":19,"seo":242,"stem":243,"__hash__":244},"authors\u002Fauthors\u002Fraphael-sauget.md",{"type":8,"value":236,"toc":237},[],{"title":11,"searchDepth":12,"depth":12,"links":238},[],{},"Raphaël Sauget","\u002Fauthors\u002Fraphael-sauget",{"title":37,"description":11},"authors\u002Fraphael-sauget","Uri9bcq0QDuxRA0PbBoNtu7p_5L3dALu4kzcXVW0xyM",{"id":246,"title":247,"body":248,"description":11,"extension":14,"meta":252,"name":253,"navigation":17,"path":254,"readingTime":19,"seo":255,"stem":256,"__hash__":257},"authors\u002Fauthors\u002Fromain-koenig.md","Co-funder & Head of innovation",{"type":8,"value":249,"toc":250},[],{"title":11,"searchDepth":12,"depth":12,"links":251},[],{},"Romain Koenig","\u002Fauthors\u002Fromain-koenig",{"title":247,"description":11},"authors\u002Fromain-koenig","uyS8--eG2_ezyqRABcJnMJmQKKuSArhPWd14aUvFeEw",{"id":259,"title":37,"body":260,"description":11,"extension":14,"meta":264,"name":265,"navigation":17,"path":266,"readingTime":19,"seo":267,"stem":268,"__hash__":269},"authors\u002Fauthors\u002Fromaric-juniet.md",{"type":8,"value":261,"toc":262},[],{"title":11,"searchDepth":12,"depth":12,"links":263},[],{},"Romaric Juniet","\u002Fauthors\u002Fromaric-juniet",{"title":37,"description":11},"authors\u002Fromaric-juniet","4Zb2artgT-eo-PHLXi3xi4d5t7s6PfhUxeSfXIikSUY",{"id":271,"title":6,"body":272,"description":11,"extension":14,"meta":276,"name":277,"navigation":17,"path":278,"readingTime":19,"seo":279,"stem":280,"__hash__":281},"authors\u002Fauthors\u002Fstanyslas-bres.md",{"type":8,"value":273,"toc":274},[],{"title":11,"searchDepth":12,"depth":12,"links":275},[],{},"Stanyslas Bres","\u002Fauthors\u002Fstanyslas-bres",{"title":6,"description":11},"authors\u002Fstanyslas-bres","Xa0SahETuiN4q1jrmR2ych3moAqcZ2LbU7vSfEt2RuU",{"id":283,"title":284,"body":285,"description":11,"extension":14,"meta":289,"name":290,"navigation":17,"path":291,"readingTime":19,"seo":292,"stem":293,"__hash__":294},"authors\u002Fauthors\u002Ftalent-acquisition.md","Talent Acquisition",{"type":8,"value":286,"toc":287},[],{"title":11,"searchDepth":12,"depth":12,"links":288},[],{},"Équipe Talent Acquisition","\u002Fauthors\u002Ftalent-acquisition",{"description":11},"authors\u002Ftalent-acquisition","doDfE76txftQ4wIiKjJoDmSpyzSKk0tzlgVAp6-opAY",{"id":296,"title":6,"body":297,"description":11,"extension":14,"meta":301,"name":302,"navigation":17,"path":303,"readingTime":19,"seo":304,"stem":305,"__hash__":306},"authors\u002Fauthors\u002Fvictor-borg.md",{"type":8,"value":298,"toc":299},[],{"title":11,"searchDepth":12,"depth":12,"links":300},[],{},"Victor Borg","\u002Fauthors\u002Fvictor-borg",{"title":6,"description":11},"authors\u002Fvictor-borg","-Za-JweoiP6hyclue_WkxMXdRUDTczPGlJf6AZckjUc",{"id":308,"title":6,"body":309,"description":11,"extension":14,"meta":313,"name":314,"navigation":17,"path":315,"readingTime":19,"seo":316,"stem":317,"__hash__":318},"authors\u002Fauthors\u002Fvirgil-roger.md",{"type":8,"value":310,"toc":311},[],{"title":11,"searchDepth":12,"depth":12,"links":312},[],{},"Virgil Roger","\u002Fauthors\u002Fvirgil-roger",{"title":6,"description":11},"authors\u002Fvirgil-roger","DfVFe5j0bCgXeEr381ZYOM5DP4m-pWb93J9-m_muKJ0",{"id":320,"title":6,"body":321,"description":11,"extension":14,"meta":325,"name":326,"navigation":17,"path":327,"readingTime":19,"seo":328,"stem":329,"__hash__":330},"authors\u002Fauthors\u002Fyukan-zhao.md",{"type":8,"value":322,"toc":323},[],{"title":11,"searchDepth":12,"depth":12,"links":324},[],{},"Yukan Zhao","\u002Fauthors\u002Fyukan-zhao",{"title":6,"description":11},"authors\u002Fyukan-zhao","LRPHugtAJnWHsmHxy9_SR5Zas_C5p-GR_uHEs1Fhk_E",{"id":332,"title":333,"author":334,"body":335,"date":1588,"description":1589,"extension":14,"lang":1590,"meta":1591,"navigation":17,"path":1592,"published":17,"readingTime":1593,"seo":1594,"stem":1595,"tags":1596,"__hash__":1598},"articles\u002Farticles\u002F2022-04-07-creer-un-index-b-tree-compose-efficace.md","Créer un index B-tree composé efficace,","julien-tassin",{"type":8,"value":336,"toc":1578},[337,342,349,352,355,358,363,366,371,374,377,380,383,436,439,444,451,456,459,462,468,479,484,487,491,494,499,502,505,591,594,613,730,734,740,745,748,783,786,789,794,797,800,803,806,809,814,817,868,879,882,888,892,895,900,903,906,971,977,980,983,989,1002,1008,1015,1019,1022,1026,1029,1078,1081,1148,1154,1158,1161,1193,1271,1274,1280,1283,1288,1291,1294,1300,1304,1307,1357,1370,1459,1462,1465,1469,1472,1489,1492,1497,1501,1504,1507,1544,1547,1551,1560,1567,1574],[338,339,341],"h2",{"id":340},"créer-un-index-b-tree-composé-efficace-un-défi-plein-de-pièges","Créer un index B-tree composé efficace, un défi plein de pièges",[343,344,345],"p",{},[346,347],"img",{"alt":11,"src":348},"\u002Fimages\u002FUntitled-4.png",[343,350,351],{},"Nous avons tous eu à indexer nos bases de données à un moment donné.",[343,353,354],{},"Nous avons tous cherché à indexer plusieurs champs avec des index composés à un moment donné.",[343,356,357],{},"Et pourtant nos index composés peuvent avoir fait pire que mieux ou ne pas être à la hauteur. Nous\nallons ici avec un véritable use case voir la différence entre les bons et les mauvais index\ncomposés.",[359,360,362],"h3",{"id":361},"rappel-du-b-tree-et-idées-reçues","Rappel du B-tree et idées reçues",[343,364,365],{},"Etant donné l’étendu de la littérature sur les B-tree, nous ne nous étendrons pas trop dessus. Pour\nmémoire, le fonctionnement du B-tree est le suivant",[343,367,368],{},[346,369],{"alt":11,"src":370},"\u002Fimages\u002FCapture-d%C3%A9cran-2021-12-24-%C3%A0-15.35.35.png",[343,372,373],{},"Pour lire la valeur 7, on va traverser l’arbre en partant du nœud racine en bleu, traverser la\nbranche jaune à gauche et lire les valeurs en vert. La, l’index va nous donner directement l’adresse\nde l’enregistrement voulu.",[343,375,376],{},"Dans le cas d’un index composé, l’index sera une succession d’index simples.",[343,378,379],{},"Exemple de schéma si on créé un index B-tree sur les colonnes “A” et “B” (dans cet ordre).",[343,381,382],{},"Par exemple avec la requête SQL",[384,385,389],"pre",{"className":386,"code":387,"language":388,"meta":11,"style":11},"language-sql shiki shiki-themes github-light github-dark","CREATE INDEX \"composed_index_name\" ON \"my_table\" (\"A\", \"B\");\n","sql",[390,391,392],"code",{"__ignoreMap":11},[393,394,396,400,403,407,411,414,417,421,424,427,430,433],"span",{"class":395,"line":19},"line",[393,397,399],{"class":398},"szBVR","CREATE",[393,401,402],{"class":398}," INDEX",[393,404,406],{"class":405},"sVt8B"," \"",[393,408,410],{"class":409},"sScJk","composed_index_name",[393,412,413],{"class":405},"\" ",[393,415,416],{"class":398},"ON",[393,418,420],{"class":419},"sZZnC"," \"my_table\"",[393,422,423],{"class":405}," (",[393,425,426],{"class":419},"\"A\"",[393,428,429],{"class":405},", ",[393,431,432],{"class":419},"\"B\"",[393,434,435],{"class":405},");\n",[343,437,438],{},"On va créer l’arbre suivant",[343,440,441],{},[346,442],{"alt":11,"src":443},"\u002Fimages\u002FUntitled-5.png",[343,445,446,447,450],{},"Si maintenant, je veux la valeur ",[390,448,449],{},"A: 1, B: 5"," voici le parcours (en rouge) qui va être effectué.",[343,452,453],{},[346,454],{"alt":11,"src":455},"\u002Fimages\u002FUntitled-6.png",[343,457,458],{},"Si on suit le trajet en rouge lors d’une requête : aucun doute c’est vraiment bien optimisé.",[343,460,461],{},"De plus, en observant l’arbre, on voit apparaître un pattern assez connu et souvent documenté.",[463,464,465],"blockquote",{},[343,466,467],{},"Un index sur A et B permet aussi d’indexer des queries sur A uniquement",[343,469,470,471,474,475,478],{},"Si, je reprend, l’exemple, si je veux tous les enregistrements ayant pour valeur de ",[390,472,473],{},"A"," à ",[390,476,477],{},"1"," il\nsuffit de parcourir l’arbre en rouge ci-dessous.",[343,480,481],{},[346,482],{"alt":11,"src":483},"\u002Fimages\u002FUntitled-7.png",[343,485,486],{},"Relativement peu de flèches rouges, ça semble être effectivement une bonne astuce à première vue.",[359,488,490],{"id":489},"le-piège-du-composed-index","Le piège du composed index",[343,492,493],{},"Nous allons maintenant nous intéresser à l’index composé et essayer de voir comment il peut devenir\nun piège.",[495,496,498],"h4",{"id":497},"les-données-théoriques-de-nos-index","Les données théoriques de nos index",[343,500,501],{},"Pour ce faire, nous allons nous intéresser à de la donnée plus proche de la réalité que notre\nexemple théorique. On va stocker en base de données des exécutions de tâches s’exécutant trois fois\ntous les dix jours et échouant une fois sur trois.",[343,503,504],{},"Ce qui nous donne les données suivantes :",[506,507,508,522],"table",{},[509,510,511],"thead",{},[512,513,514,519],"tr",{},[515,516,518],"th",{"align":517},"left","date",[515,520,521],{"align":517},"status",[523,524,525,534,542,549,556,563,570,577,584],"tbody",{},[512,526,527,531],{},[528,529,530],"td",{"align":517},"2022-01-01",[528,532,533],{"align":517},"success",[512,535,536,539],{},[528,537,538],{"align":517},"2022-01-05",[528,540,541],{"align":517},"error",[512,543,544,547],{},[528,545,546],{"align":517},"2022-01-10",[528,548,533],{"align":517},[512,550,551,554],{},[528,552,553],{"align":517},"2022-01-11",[528,555,533],{"align":517},[512,557,558,561],{},[528,559,560],{"align":517},"2022-01-15",[528,562,541],{"align":517},[512,564,565,568],{},[528,566,567],{"align":517},"2022-01-20",[528,569,533],{"align":517},[512,571,572,575],{},[528,573,574],{"align":517},"2022-01-21",[528,576,533],{"align":517},[512,578,579,582],{},[528,580,581],{"align":517},"2022-01-25",[528,583,541],{"align":517},[512,585,586,589],{},[528,587,588],{"align":517},"2022-01-30",[528,590,533],{"align":517},[343,592,593],{},"Si nous avons les deux requêtes suivantes à indexer :",[595,596,597,601],"ul",{},[598,599,600],"li",{},"Récupérer un run pour une date donnée",[598,602,603,604,608,609,612],{},"Un compte des runs en échec entre deux dates. L’exemple s’appliquerait aussi si nous voulons\n",[605,606,607],"strong",{},"tous"," les runs en échecs (sans compte), mais la récupération des runs serait coûteuse en temps,\nle ",[390,610,611],{},"count"," nous permet de mesurer uniquement le temps de parcours de l’index en excluant le fetch\nde données.",[384,614,616],{"className":386,"code":615,"language":388,"meta":11,"style":11},"# Requête 1\nSELECT * from runs WHERE date = \u003Cdate>\n\n#Requête 2\nSELECT count(*) from runs WHERE date > \u003Cdate_start> AND date \u003C \u003Cdate_end> AND status = 'error'\n",[390,617,618,627,653,659,668],{"__ignoreMap":11},[393,619,620,623],{"class":395,"line":19},[393,621,622],{"class":405},"# Requête ",[393,624,626],{"class":625},"sj4cs","1\n",[393,628,629,632,635,638,641,644,647,650],{"class":395,"line":12},[393,630,631],{"class":398},"SELECT",[393,633,634],{"class":398}," *",[393,636,637],{"class":398}," from",[393,639,640],{"class":405}," runs ",[393,642,643],{"class":398},"WHERE",[393,645,646],{"class":398}," date",[393,648,649],{"class":398}," =",[393,651,652],{"class":398}," \u003Cdate>\n",[393,654,656],{"class":395,"line":655},3,[393,657,658],{"emptyLinePlaceholder":17},"\n",[393,660,662,665],{"class":395,"line":661},4,[393,663,664],{"class":405},"#Requête ",[393,666,667],{"class":625},"2\n",[393,669,671,673,676,679,682,685,688,690,692,694,697,700,703,706,709,711,713,715,718,720,722,725,727],{"class":395,"line":670},5,[393,672,631],{"class":398},[393,674,675],{"class":625}," count",[393,677,678],{"class":405},"(",[393,680,681],{"class":398},"*",[393,683,684],{"class":405},") ",[393,686,687],{"class":398},"from",[393,689,640],{"class":405},[393,691,643],{"class":398},[393,693,646],{"class":398},[393,695,696],{"class":398}," >",[393,698,699],{"class":398}," \u003C",[393,701,702],{"class":405},"date_start",[393,704,705],{"class":398},">",[393,707,708],{"class":398}," AND",[393,710,646],{"class":398},[393,712,699],{"class":398},[393,714,699],{"class":398},[393,716,717],{"class":405},"date_end",[393,719,705],{"class":398},[393,721,708],{"class":398},[393,723,724],{"class":398}," status",[393,726,649],{"class":398},[393,728,729],{"class":419}," 'error'\n",[495,731,733],{"id":732},"premier-index","Premier index",[343,735,736,737],{},"En bon développeur devant une requête SQL lente, nous allons penser ",[605,738,739],{},"index.",[343,741,742],{},[346,743],{"alt":11,"src":744},"\u002Fimages\u002FUntitled-8.png",[343,746,747],{},"A partir de l’idée reçue vue au paragraphe précédent, nous allons préférer l’index suivant :",[384,749,751],{"className":386,"code":750,"language":388,"meta":11,"style":11},"CREATE INDEX \"date_status\" ON \"runs\" (\"date\", \"status\");\n",[390,752,753],{"__ignoreMap":11},[393,754,755,757,759,761,764,766,768,771,773,776,778,781],{"class":395,"line":19},[393,756,399],{"class":398},[393,758,402],{"class":398},[393,760,406],{"class":405},[393,762,763],{"class":409},"date_status",[393,765,413],{"class":405},[393,767,416],{"class":398},[393,769,770],{"class":419}," \"runs\"",[393,772,423],{"class":405},[393,774,775],{"class":419},"\"date\"",[393,777,429],{"class":405},[393,779,780],{"class":419},"\"status\"",[393,782,435],{"class":405},[343,784,785],{},"Cet index, semble couvrir les deux queries avec un seul index.",[343,787,788],{},"Intéressons nous maintenant à l’arbre que cet index va générer.",[343,790,791],{},[346,792],{"alt":11,"src":793},"\u002Fimages\u002FUntitled-9.png",[343,795,796],{},"Par rapport à ce à quoi nous nous attendions, celui-ci semble étrange.",[343,798,799],{},"En effet, nous n’avons qu’un enregistrement par bloc de feuilles (en vert). Et il y a beaucoup de\nflèches.",[343,801,802],{},"Nous allons maintenant exécuter nos deux requêtes :",[495,804,600],{"id":805},"récupérer-un-run-pour-une-date-donnée",[343,807,808],{},"Si je veux récupérer le run du 21\u002F01\u002F2022, je vais exécuter le trajet suivant (en rouge).",[343,810,811],{},[346,812],{"alt":11,"src":813},"\u002Fimages\u002FUntitled-10.png",[343,815,816],{},"Le trajet semble plutôt bien optimisé. Peu de flèches rouges empruntées. Vérifions ça par la\npratique. Nous allons utiliser la base de données témoin contenant 10 millions de runs avec un run\nsur 100 en erreur.",[506,818,819,832],{},[509,820,821],{},[512,822,823,826,829],{},[515,824,825],{"align":517},"Scénario",[515,827,828],{"align":517},"Temps d’exécution",[515,830,831],{"align":517},"Taille d’index",[523,833,834,845,858],{},[512,835,836,839,842],{},[528,837,838],{"align":517},"Sans index",[528,840,841],{"align":517},"+\u002F- 2s",[528,843,844],{"align":517},"0Mb",[512,846,847,852,855],{},[528,848,849,850],{"align":517},"index ",[390,851,763],{},[528,853,854],{"align":517},"+\u002F- 2ms",[528,856,857],{"align":517},"160Mb",[512,859,860,863,865],{},[528,861,862],{"align":517},"index date",[528,864,854],{"align":517},[528,866,867],{"align":517},"120Mb",[343,869,870,871,873,874,878],{},"On remarque que le temps d’exécution est très proche avec l’index ",[390,872,763],{}," qu’avec l’index\n",[875,876,877],"em",{},"date uniquement",".",[343,880,881],{},"Le contrat est donc clairement rempli avec cet index sur cette requête.",[343,883,884,885,887],{},"Seul bémol, la taille de l’index est bien supérieure à celle de l’index ",[875,886,877],{}," on\ncomprend aisément pourquoi en revenant à l’arbre. La surabondance de blocs et de flèches se paye sur\nla taille de l’index.",[495,889,891],{"id":890},"tous-les-runs-en-échec-entre-deux-dates","Tous les runs en échec entre deux dates",[343,893,894],{},"Les choses vont se compliquer dans ce cas. Dans notre exemple théorique, si je demande une plage de\ndate équivalente à la moitié de l’amplitude de la base, on se retrouve avec le parcours suivant.",[343,896,897],{},[346,898],{"alt":11,"src":899},"\u002Fimages\u002FUntitled-11.png",[343,901,902],{},"Pour finalement récupérer un seul enregistrement, nous avons un parcours extrêmement complexe et\ncoûteux.",[343,904,905],{},"Si on valide par la pratique, en reprenant notre jeu de données de test, on va demander une plage de\ndate correspondant à environ 500 000 runs (sur 10 millions de runs dans la table).",[506,907,908,923],{},[509,909,910],{},[512,911,912,914,917,920],{},[515,913,825],{"align":517},[515,915,916],{"align":517},"Temps d’exécution requête date",[515,918,919],{"align":517},"Temps d’exécution requête runs en error entre deux dates",[515,921,922],{"align":517},"Taille des index",[523,924,925,935,947,958],{},[512,926,927,929,931,933],{},[528,928,838],{"align":517},[528,930,841],{"align":517},[528,932,841],{"align":517},[528,934,844],{"align":517},[512,936,937,940,942,945],{},[528,938,939],{"align":517},"index date_status",[528,941,854],{"align":517},[528,943,944],{"align":517},"+\u002F- 310ms",[528,946,857],{"align":517},[512,948,949,951,953,956],{},[528,950,862],{"align":517},[528,952,854],{"align":517},[528,954,955],{"align":517},"+\u002F- 300ms",[528,957,867],{"align":517},[512,959,960,963,965,968],{},[528,961,962],{"align":517},"index status",[528,964,841],{"align":517},[528,966,967],{"align":517},"+\u002F- 100ms",[528,969,970],{"align":517},"45Mb",[343,972,973,974],{},"On observe effectivement que notre index composé ne fait pas mieux qu’un index uniquement sur le\nchamp ",[875,975,976],{},"date.",[343,978,979],{},"Il est important de noter que plus la fenêtre temporelle de requête est large, pire vont être les\nperformances de cet index.",[343,981,982],{},"A l’inverse, plus la fenêtre est étroite meilleur sera l’index.",[343,984,985,986],{},"C’est donc un véritable camouflet pour le postulat de départ : ",[875,987,988],{},"un index composé sur A-B permet de\nrequêter sur A de la même façon que un index sur A",[343,990,991,992,994,995,998,999,1001],{},"On observe même une valeur étrange, la requête est plus rapide avec l’index ",[875,993,521],{}," que\n",[875,996,997],{},"date_status."," Pourquoi ? Tout s’explique avec les cardinalités. Nous avons 10 millions de runs dont\n1% en erreur. Soit 100 000 runs alors que dans le parcours via ",[875,1000,763],{}," nous allons au final\n**parcourir 500 000 runs.",[343,1003,1004,1005],{},"Ce qui nous amène a une première grande idée générale, ",[605,1006,1007],{},"un index composé efficace est le plus\nsouvent un index réfléchi pour une et une seule requête.",[343,1009,1010,1011,1014],{},"Autre première observation, ",[605,1012,1013],{},"un index composé occupe environ le même espace que les index uniques\nqui le compose."," Ca semble logique si on repense à la structure de l’arbre.",[359,1016,1018],{"id":1017},"comment-exploiter-au-mieux-le-composed-index","Comment exploiter au mieux le composed index",[343,1020,1021],{},"A partir de là, nous allons réfléchir aux solutions d’indexation qui s’offrent à nous et essayer\nd’en tirer quelques règles.",[495,1023,1025],{"id":1024},"indexer-chaque-champ","Indexer chaque champ",[343,1027,1028],{},"Contre toute attente, cette solution, sans aucune subtilité est souvent une bonne approche.",[384,1030,1032],{"className":386,"code":1031,"language":388,"meta":11,"style":11},"CREATE INDEX \"date\" ON \"runs\" (\"date\");\nCREATE INDEX \"status\" ON \"runs\" (\"status\");\n",[390,1033,1034,1056],{"__ignoreMap":11},[393,1035,1036,1038,1040,1042,1044,1046,1048,1050,1052,1054],{"class":395,"line":19},[393,1037,399],{"class":398},[393,1039,402],{"class":398},[393,1041,406],{"class":405},[393,1043,518],{"class":409},[393,1045,413],{"class":405},[393,1047,416],{"class":398},[393,1049,770],{"class":419},[393,1051,423],{"class":405},[393,1053,775],{"class":419},[393,1055,435],{"class":405},[393,1057,1058,1060,1062,1064,1066,1068,1070,1072,1074,1076],{"class":395,"line":12},[393,1059,399],{"class":398},[393,1061,402],{"class":398},[393,1063,406],{"class":405},[393,1065,521],{"class":409},[393,1067,413],{"class":405},[393,1069,416],{"class":398},[393,1071,770],{"class":419},[393,1073,423],{"class":405},[393,1075,780],{"class":419},[393,1077,435],{"class":405},[343,1079,1080],{},"SI nous reprenons l’exemple précédent, voici le résultat du bench.",[506,1082,1083,1095],{},[509,1084,1085],{},[512,1086,1087,1089,1091,1093],{},[515,1088,825],{"align":517},[515,1090,916],{"align":517},[515,1092,919],{"align":517},[515,1094,922],{"align":517},[523,1096,1097,1107,1117,1127,1137],{},[512,1098,1099,1101,1103,1105],{},[528,1100,838],{"align":517},[528,1102,841],{"align":517},[528,1104,841],{"align":517},[528,1106,844],{"align":517},[512,1108,1109,1111,1113,1115],{},[528,1110,939],{"align":517},[528,1112,854],{"align":517},[528,1114,944],{"align":517},[528,1116,857],{"align":517},[512,1118,1119,1121,1123,1125],{},[528,1120,862],{"align":517},[528,1122,854],{"align":517},[528,1124,955],{"align":517},[528,1126,867],{"align":517},[512,1128,1129,1131,1133,1135],{},[528,1130,962],{"align":517},[528,1132,841],{"align":517},[528,1134,967],{"align":517},[528,1136,970],{"align":517},[512,1138,1139,1142,1144,1146],{},[528,1140,1141],{"align":517},"index date + index status",[528,1143,854],{"align":517},[528,1145,955],{"align":517},[528,1147,857],{"align":517},[343,1149,1150,1151,1153],{},"C’est encore un coup d’épée dans l’eau, on ne fait pas mieux que avec ",[875,1152,877],{}," et la taille\ndes index a augmenté.",[495,1155,1157],{"id":1156},"inverser-lindex-composé","Inverser l’index composé",[343,1159,1160],{},"Plutôt que d’avoir un index sur date puis status, on va inverser et le créer sur status puis date.",[384,1162,1164],{"className":386,"code":1163,"language":388,"meta":11,"style":11},"CREATE INDEX \"status_date\" ON \"runs\" (\"status\", \"date\");\n",[390,1165,1166],{"__ignoreMap":11},[393,1167,1168,1170,1172,1174,1177,1179,1181,1183,1185,1187,1189,1191],{"class":395,"line":19},[393,1169,399],{"class":398},[393,1171,402],{"class":398},[393,1173,406],{"class":405},[393,1175,1176],{"class":409},"status_date",[393,1178,413],{"class":405},[393,1180,416],{"class":398},[393,1182,770],{"class":419},[393,1184,423],{"class":405},[393,1186,780],{"class":419},[393,1188,429],{"class":405},[393,1190,775],{"class":419},[393,1192,435],{"class":405},[506,1194,1195,1207],{},[509,1196,1197],{},[512,1198,1199,1201,1203,1205],{},[515,1200,825],{"align":517},[515,1202,916],{"align":517},[515,1204,919],{"align":517},[515,1206,922],{"align":517},[523,1208,1209,1219,1229,1239,1249,1259],{},[512,1210,1211,1213,1215,1217],{},[528,1212,838],{"align":517},[528,1214,841],{"align":517},[528,1216,841],{"align":517},[528,1218,844],{"align":517},[512,1220,1221,1223,1225,1227],{},[528,1222,939],{"align":517},[528,1224,854],{"align":517},[528,1226,944],{"align":517},[528,1228,857],{"align":517},[512,1230,1231,1233,1235,1237],{},[528,1232,862],{"align":517},[528,1234,854],{"align":517},[528,1236,955],{"align":517},[528,1238,867],{"align":517},[512,1240,1241,1243,1245,1247],{},[528,1242,962],{"align":517},[528,1244,841],{"align":517},[528,1246,967],{"align":517},[528,1248,970],{"align":517},[512,1250,1251,1253,1255,1257],{},[528,1252,1141],{"align":517},[528,1254,854],{"align":517},[528,1256,955],{"align":517},[528,1258,857],{"align":517},[512,1260,1261,1264,1266,1269],{},[528,1262,1263],{"align":517},"index status_date",[528,1265,841],{"align":517},[528,1267,1268],{"align":517},"+\u002F- 3ms",[528,1270,867],{"align":517},[343,1272,1273],{},"Ca y est nous avons enfin trouvé un index qui fonctionne pour la deuxième requête. Mais nous y\nperdons sur la première requête.",[343,1275,1276,1277,878],{},"C’est bien la preuve que ",[605,1278,1279],{},"l’ordre des colonnes d’un index composé est très important",[343,1281,1282],{},"Si nous reprenons notre arbre, voici l’explication.",[343,1284,1285],{},[346,1286],{"alt":11,"src":1287},"\u002Fimages\u002FUntitled-12.png",[343,1289,1290],{},"Visuellement on voit tout de suite pourquoi l’index marche bien.",[343,1292,1293],{},"Très peu de traits rouges, le chemin vers les feuilles est très direct.",[343,1295,1296,1297],{},"Nous pouvons en tirer une nouvelle bonne pratique ",[605,1298,1299],{},"un index composé efficace commence par les\ncolonnes dont les valeurs ont les plus faibles cardinalités : le moins de valeurs possibles.",[495,1301,1303],{"id":1302},"utiliser-un-index-partiel","Utiliser un index partiel",[343,1305,1306],{},"Certains SGDB comme MongoDB et PostgreSQL permettent de spécifier des conditions sur un index. Ca\ndevient particulièrement intéressant à combiner avec un index composé qui sera tunné précisément\npour une requête. Ca permettra de diminuer l’empreinte de l’index sur l’espace de stockage.",[384,1308,1310],{"className":386,"code":1309,"language":388,"meta":11,"style":11},"CREATE INDEX \"partial_status_date\"\nON \"runs\" (\"status\", \"date\")\nWHERE status = \"error\";\n",[390,1311,1312,1326,1343],{"__ignoreMap":11},[393,1313,1314,1316,1318,1320,1323],{"class":395,"line":19},[393,1315,399],{"class":398},[393,1317,402],{"class":398},[393,1319,406],{"class":405},[393,1321,1322],{"class":409},"partial_status_date",[393,1324,1325],{"class":405},"\"\n",[393,1327,1328,1330,1332,1334,1336,1338,1340],{"class":395,"line":12},[393,1329,416],{"class":398},[393,1331,770],{"class":419},[393,1333,423],{"class":405},[393,1335,780],{"class":419},[393,1337,429],{"class":405},[393,1339,775],{"class":419},[393,1341,1342],{"class":405},")\n",[393,1344,1345,1347,1349,1351,1354],{"class":395,"line":655},[393,1346,643],{"class":398},[393,1348,724],{"class":398},[393,1350,649],{"class":398},[393,1352,1353],{"class":419}," \"error\"",[393,1355,1356],{"class":405},";\n",[343,1358,1359,1360,1363,1364,1367,1368,878],{},"Cet index va indexer runs de la même façon que l’index composé sur ",[390,1361,1362],{},"“status”, “date”"," mais\n",[605,1365,1366],{},"uniquement"," pour les enregistrements qui matchent la condition du ",[390,1369,643],{},[506,1371,1372,1384],{},[509,1373,1374],{},[512,1375,1376,1378,1380,1382],{},[515,1377,825],{"align":517},[515,1379,916],{"align":517},[515,1381,919],{"align":517},[515,1383,922],{"align":517},[523,1385,1386,1396,1406,1416,1426,1436,1446],{},[512,1387,1388,1390,1392,1394],{},[528,1389,838],{"align":517},[528,1391,841],{"align":517},[528,1393,841],{"align":517},[528,1395,844],{"align":517},[512,1397,1398,1400,1402,1404],{},[528,1399,939],{"align":517},[528,1401,854],{"align":517},[528,1403,944],{"align":517},[528,1405,857],{"align":517},[512,1407,1408,1410,1412,1414],{},[528,1409,862],{"align":517},[528,1411,854],{"align":517},[528,1413,955],{"align":517},[528,1415,867],{"align":517},[512,1417,1418,1420,1422,1424],{},[528,1419,962],{"align":517},[528,1421,841],{"align":517},[528,1423,967],{"align":517},[528,1425,970],{"align":517},[512,1427,1428,1430,1432,1434],{},[528,1429,1141],{"align":517},[528,1431,854],{"align":517},[528,1433,955],{"align":517},[528,1435,857],{"align":517},[512,1437,1438,1440,1442,1444],{},[528,1439,1263],{"align":517},[528,1441,841],{"align":517},[528,1443,1268],{"align":517},[528,1445,867],{"align":517},[512,1447,1448,1451,1453,1456],{},[528,1449,1450],{"align":517},"index partial_status_date",[528,1452,841],{"align":517},[528,1454,1455],{"align":517},"+\u002F- 6ms",[528,1457,1458],{"align":517},"1Mb",[343,1460,1461],{},"C’est une vraie bonne nouvelle !",[343,1463,1464],{},"Pour 1Mb de stockage, nous avons un index qui va adresser très correctement la requête voulue.",[359,1466,1468],{"id":1467},"en-conclusion-les-bonnes-pratiques-de-la-création-dindex-composé","En conclusion, les bonnes pratiques de la création d’index composé",[343,1470,1471],{},"Les leçons que nous avons tirées de notre exercice sont :",[595,1473,1474,1477,1480,1483,1486],{},[598,1475,1476],{},"l’ordre des colonnes d’un index composé est important",[598,1478,1479],{},"un index composé vraiment efficace est souvent un index réfléchi pour une et une seule requête",[598,1481,1482],{},"un bon index composé occupe environ le même espace que les index uniques qui le compose",[598,1484,1485],{},"Dans le doute, on peut appliquer la règle suivante : un index composé efficace commence par les\ncolonnes dont les valeurs ont les plus faibles cardinalités",[598,1487,1488],{},"Les index partiels sont souvent un bon compromis, pour une taille qui peut être très réduite ils\npermettent de multiplier les index.",[343,1490,1491],{},"Maintenant j’espère que les index composés vous semblent beaucoup moins magiques.",[343,1493,1494],{},[346,1495],{"alt":11,"src":1496},"\u002Fimages\u002FUntitled-13.png",[338,1498,1500],{"id":1499},"la-stratégie-de-benchmark","La stratégie de benchmark",[343,1502,1503],{},"Pour valider nos futures hypothèses nous allons utiliser une table témoin contenant 10 millions de\nruns.",[343,1505,1506],{},"Chaque enregistrement a le format suivant :",[506,1508,1509,1522],{},[509,1510,1511],{},[512,1512,1513,1516,1519],{},[515,1514,1515],{"align":517},"Colonne",[515,1517,1518],{"align":517},"Type",[515,1520,1521],{"align":517},"valeur",[523,1523,1524,1534],{},[512,1525,1526,1528,1531],{},[528,1527,518],{"align":517},[528,1529,1530],{"align":517},"Date",[528,1532,1533],{"align":517},"date de type auto increment",[512,1535,1536,1538,1541],{},[528,1537,521],{"align":517},[528,1539,1540],{"align":517},"Text",[528,1542,1543],{"align":517},"un enregistrement sur 100 en échec (⚠️ dans les graphes on a un échec sur 3 pour que ce soit plus compréhensible)",[343,1545,1546],{},"Les benchmarks ont été fait sur mongodb, mais peuvent s’appliquer à PostgreSQL aussi.",[338,1548,1550],{"id":1549},"ressources","Ressources",[343,1552,1553,1554],{},"Repository github contenant le benchmark :\n",[1555,1556,1557],"a",{"href":1557,"rel":1558},"https:\u002F\u002Fgithub.com\u002Fjtassin\u002Fblog-post-composed-index-1",[1559],"nofollow",[343,1561,1562],{},[1555,1563,1566],{"href":1564,"rel":1565},"https:\u002F\u002Fwww.postgresql.org\u002Fdocs\u002Fcurrent\u002Findexes-partial.html",[1559],"Partial index sur postgres",[343,1568,1569],{},[1555,1570,1573],{"href":1571,"rel":1572},"https:\u002F\u002Fdocs.mongodb.com\u002Fmanual\u002Fcore\u002Findex-partial\u002F",[1559],"Partial index sur mongodb",[1575,1576,1577],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":11,"searchDepth":12,"depth":12,"links":1579},[1580,1586,1587],{"id":340,"depth":12,"text":341,"children":1581},[1582,1583,1584,1585],{"id":361,"depth":655,"text":362},{"id":489,"depth":655,"text":490},{"id":1017,"depth":655,"text":1018},{"id":1467,"depth":655,"text":1468},{"id":1499,"depth":12,"text":1500},{"id":1549,"depth":12,"text":1550},"2022-04-07","Un défi plein de pièges. Nous avons tous eu à indexer nos bases de données à un moment donné.","fr",{},"\u002Farticles\u002F2022-04-07-creer-un-index-b-tree-compose-efficace",12,{"title":333,"description":1589},"articles\u002F2022-04-07-creer-un-index-b-tree-compose-efficace",[1597],"Tech","R2dOKVakMtK5BegC0Rvf0dE7PY0L5JdkHBkhZIVGcTI",1778159243959]