{"id":1497,"date":"2022-09-15T11:57:43","date_gmt":"2022-09-15T09:57:43","guid":{"rendered":"https:\/\/www.loicmathieu.fr\/wordpress\/?p=1497"},"modified":"2022-09-21T13:53:49","modified_gmt":"2022-09-21T11:53:49","slug":"apache-pinot-et-de-ses-differents-types-dindexes","status":"publish","type":"post","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/apache-pinot-et-de-ses-differents-types-dindexes\/","title":{"rendered":"Apache Pinot et de ses diff\u00e9rents types d&rsquo;indexes"},"content":{"rendered":"<p>Il y a quelques temps, j&rsquo;avais enfin pris le temps de tester Apache Pinot, vous pouvez trouver le r\u00e9cit de mes premi\u00e8res exp\u00e9rimentations <a href=\"https:\/\/www.loicmathieu.fr\/wordpress\/informatique\/jai-enfin-pris-le-temps-de-tester-apache-pinot\/\">ici<\/a>.<\/p>\n<p>Apache Pinot est un datastore OLAP (OnLine Analytical Processing) distribu\u00e9 et temps r\u00e9el, sp\u00e9cialement con\u00e7u pour fournir des analyses \u00e0 tr\u00e8s faible latence, m\u00eame \u00e0 un d\u00e9bit extr\u00eamement \u00e9lev\u00e9. Si vous ne le connaissez pas, commencez par lire <a href=\"https:\/\/www.loicmathieu.fr\/wordpress\/informatique\/jai-enfin-pris-le-temps-de-tester-apache-pinot\/\">mon article d&rsquo;introduction<\/a> avant celui-ci.<\/p>\n<p>Une des forces de Pinot est ses diff\u00e9rents types d&rsquo;indexes, ce sont ceux-ci que nous allons explorer dans cet article.<\/p>\n<h2>Le jeu de donn\u00e9es Chicago Crimes<\/h2>\n<p>Nous allons utiliser le jeu de donn\u00e9es Chicago Crimes de Google Big Query que nous allons exporter en CSV. Pour r\u00e9cup\u00e9rer ce jeu de donn\u00e9es, il faut aller dans l&rsquo;interface de BigQuery, puis rechercher le projet publique <code>bigquery-public-data<\/code>, puis la table <code>chicago_crime<\/code>; naviguer vers cette URL devrait faire la m\u00eame chose: <a href=\"https:\/\/console.cloud.google.com\/bigquery?p=bigquery-public-data&amp;d=chicago_crime\" target=\"_blank\" rel=\"noopener\"><a href=\"https:\/\/console.cloud.google.com\/bigquery?p=bigquery-public-data&amp;d=chicago_crime\">https:\/\/console.cloud.google.com\/bigquery?p=bigquery-public-data&amp;d=chicago_crime<\/a><\/a>.<\/p>\n<p>Vous devez ensuite copier cette table dans un ensemble de donn\u00e9es de v\u00f4tre projet GCP, puis l&rsquo;exporter en CSV.\nCela vous donnera 6 CSV d&rsquo;environ 250Mo chacun, donc 1,5Go de donn\u00e9es \u00e0 analyser.<\/p>\n<p>Ce jeu de donn\u00e9e contient les donn\u00e9es de crimes de la ville de Chicago sur plusieurs ann\u00e9es; sa description peut \u00eatre trouv\u00e9e ici : <a href=\"https:\/\/console.cloud.google.com\/marketplace\/details\/city-of-chicago-public-data\/chicago-crime?filter=solution-type:dataset\" target=\"_blank\" rel=\"noopener\"><a href=\"https:\/\/console.cloud.google.com\/marketplace\/details\/city-of-chicago-public-data\/chicago-crime?filter=solution-type:dataset\">https:\/\/console.cloud.google.com\/marketplace\/details\/city-of-chicago-public-data\/chicago-crime?filter=solution-type:dataset<\/a><\/a>.<\/p>\n<p>Une fois les CSV r\u00e9cup\u00e9r\u00e9, il va falloir d\u00e9finir un sch\u00e9ma et une table, puis cr\u00e9er un Job pour importer les donn\u00e9es.<\/p>\n<p>Voici le sch\u00e9ma que nous allons utiliser :<\/p>\n<pre>{\n  \"schemaName\": \"chicagoCrimes\",\n  \"dimensionFieldSpecs\": [\n    {\n      \"name\": \"unique_key\", \"dataType\": \"LONG\"\n    },\n    {\n      \"name\": \"case_number\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"block\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"iucr\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"primary_type\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"description\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"location_description\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\":\"district\", \"dataType\": \"INT\"\n    },\n    {\n      \"name\": \"ward\", \"dataType\": \"INT\"\n    },\n    {\n      \"name\": \"community_area\", \"dataType\": \"INT\"\n    },\n    {\n      \"name\": \"fbi_code\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"year\", \"dataType\": \"INT\"\n    },\n    {\n      \"name\": \"location\", \"dataType\": \"STRING\"\n    },\n    {\n      \"name\": \"arrest\", \"dataType\": \"BOOLEAN\"\n    },\n    {\n      \"name\": \"domestic\", \"dataType\": \"BOOLEAN\"\n    },\n    {\n      \"name\": \"beat\", \"dataType\": \"BOOLEAN\"\n    }\n  ],\n  \"metricFieldSpecs\": [\n    {\n      \"name\": \"x_coordinate\", \"dataType\": \"FLOAT\"\n    },\n    {\n      \"name\": \"y_coordinate\", \"dataType\": \"FLOAT\"\n    },\n    {\n      \"name\": \"latitude\", \"dataType\":\"FLOAT\"\n    },\n    {\n      \"name\": \"longitude\", \"dataType\": \"FLOAT\"\n    }\n  ],\n  \"dateTimeFieldSpecs\": [\n    {\n      \"name\": \"date\",\n      \"dataType\": \"STRING\",\n      \"format\": \"1:MILLISECONDS:SIMPLE_DATE_FORMAT:yyyy-MM-dd HH:mm:ss z\",\n      \"granularity\": \"1:SECONDS\"\n    },\n    {\n      \"name\": \"updated_on\",\n      \"dataType\": \"STRING\",\n      \"format\": \"1:MILLISECONDS:SIMPLE_DATE_FORMAT:yyyy-MM-dd HH:mm:ss z\",\n      \"granularity\": \"1:SECONDS\"\n    }\n  ]\n}\n<\/pre>\n<p>Et voici la table associ\u00e9e, celle-ci ne contient pour l&rsquo;instant aucun indexe :<\/p>\n<pre>{\n  \"tableName\": \"chicagoCrimes\",\n  \"segmentsConfig\" : {\n    \"replication\" : \"1\",\n    \"schemaName\" : \"chicagoCrimes\"\n  },\n  \"tableIndexConfig\" : {\n    \"invertedIndexColumns\" : [],\n    \"loadMode\"  : \"MMAP\"\n  },\n  \"tenants\" : {\n    \"broker\":\"DefaultTenant\",\n    \"server\":\"DefaultTenant\"\n  },\n  \"tableType\":\"OFFLINE\",\n  \"metadata\": {}\n}\n<\/pre>\n<p>Pour ing\u00e9rer les donn\u00e9es vous pouvez utiliser le job suivant :<\/p>\n<pre>\nexecutionFrameworkSpec:\n  name: 'standalone'\n  segmentGenerationJobRunnerClassName: 'org.apache.pinot.plugin.ingestion.batch.standalone.SegmentGenerationJobRunner'\n  segmentTarPushJobRunnerClassName: 'org.apache.pinot.plugin.ingestion.batch.standalone.SegmentTarPushJobRunner'\n  segmentUriPushJobRunnerClassName: 'org.apache.pinot.plugin.ingestion.batch.standalone.SegmentUriPushJobRunner'\njobType: SegmentCreationAndTarPush\ninputDirURI: '\/tmp\/pinot-quick-start'\nincludeFileNamePattern: 'glob:**\/*.csv'\noutputDirURI: '\/tmp\/pinot-quick-start\/segments\/'\noverwriteOutput: true\npinotFSSpecs:\n  - scheme: file\n    className: org.apache.pinot.spi.filesystem.LocalPinotFS\nrecordReaderSpec:\n  dataFormat: 'csv'\n  className: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReader'\n  configClassName: 'org.apache.pinot.plugin.inputformat.csv.CSVRecordReaderConfig'\ntableSpec:\n  tableName: 'chicagoCrimes'\n  schemaURI: 'http:\/\/pinot-controller:9000\/tables\/chicagoCrimes\/schema'\n  tableConfigURI: 'http:\/\/pinot-controller:9000\/tables\/chicagoCrimes'\npinotClusterSpecs:\n  - controllerURI: 'http:\/\/pinot-controller:9000'\n<\/pre>\n<p>Nous allons maintenant d\u00e9marrer un cluster Pinot, le plus simple est d&rsquo;utiliser le <a href=\"https:\/\/docs.pinot.apache.org\/basics\/getting-started\/running-pinot-in-docker#docker-compose\" target=\"_blank\" rel=\"noopener\">Docker Compose fournit dans la documentation de Pinot<\/a>.<\/p>\n<p>Pour d\u00e9marrer le cluster via Docker Compose, il faut lancer la commande <code>docker compose up<\/code>. Apr\u00e8s quelques minutes, vous aurez un cluster Pinot de d\u00e9marr\u00e9 dont l&rsquo;interface est accessible \u00e0 l&rsquo;URL <a href=\"http:\/\/localhost:9000\" target=\"_blank\" rel=\"noopener\"><a href=\"http:\/\/localhost:9000\">http:\/\/localhost:9000<\/a><\/a>.<\/p>\n<p>L&rsquo;image Pinot permet de lancer des jobs de cr\u00e9ation de table ou d&rsquo;ingestion de donn\u00e9es, les commandes suivantes partent du principe que les ressources n\u00e9cessaires sont dans un r\u00e9pertoire <code>~\/dev\/pinot\/crime<\/code> qui contient :<\/p>\n<ul><li>Le sch\u00e9ma de la table dans un fichier <code>chicagoCrimes-schema.json<\/code><\/li>\n\n<li>La configuration de la table dans un fichier <code>chicagoCrimes-table.json<\/code><\/li>\n\n<li>Les 6 CSV de donn\u00e9es dans un r\u00e9pertoire <code>data<\/code><\/li>\n<\/ul>\n<p>Pour cr\u00e9er la table vous pouvez utiliser la commande docker suivante :<\/p>\n<pre>docker run --rm -ti \\\n    --network=pinot_default \\\n    -v ~\/dev\/pinot\/crime:\/tmp\/pinot-quick-start \\\n    --name pinot-batch-table-creation \\\n    apachepinot\/pinot:0.10.0 AddTable \\\n    -schemaFile \/tmp\/pinot-quick-start\/chicagoCrimes-schema.json \\\n    -tableConfigFile \/tmp\/pinot-quick-start\/chicagoCrimes-table.json \\\n    -controllerHost manual-pinot-controller \\\n    -controllerPort 9000 -exec\n<\/pre>\n<p>Pour ing\u00e9rer les donn\u00e9es vous pouvez utiliser la commande docker suivante :<\/p>\n<pre>docker run --rm -ti \\\n    --network=pinot_default \\\n    -v ~\/dev\/pinot\/crime:\/tmp\/pinot-quick-start \\\n    --name pinot-data-ingestion-job \\\n    apachepinot\/pinot:0.10.0 LaunchDataIngestionJob \\\n    -jobSpecFile \/tmp\/pinot-quick-start\/job-ingest.yml\n<\/pre>\n<p>Apr\u00e8s ingestion, nous aurons 6452716 lignes dans notre table.<\/p>\n<h2>Les performances sans indexe<\/h2>\n<p>Pour tester les performances de Pinot sans aucun indexe, nous allons lancer quelques requ\u00eates depuis la console d\u2019administration de Pinot :<\/p>\n<pre>\nselect count(*) from chicagoCrimes\n\nselect year, count(*) from chicagoCrimes where arrest = true group by year\n\nselect year, count(*) from chicagoCrimes where primary_type='NARCOTICS' group by year\n\nselect year, count(*) from chicagoCrimes where x_coordinate&gt;1180000 group by year\n\nselect year, count(*) from chicagoCrimes where ward=45 group by year\n\nselect year, sum(community_area) from chicagoCrimes group by year\n<\/pre>\n<p>Constatation : les requ\u00eates s&rsquo;ex\u00e9cutent en quelques (dizaines de) millisecondes sur un jeu de donn\u00e9es de 1,5Go et 6,5 millions de lignes.<\/p>\n<p>Les segments prennent eux 476Mo sur disque.<\/p>\n<p>Le secret de cette bonne performance sans indexe est que chaque champ est stock\u00e9 dans un <strong>Forward Index<\/strong>, par d\u00e9faut de type <strong>dictionnaire<\/strong> pour les colonnes dimension sinon <strong>raw value<\/strong>.<\/p>\n<h3>Dictionary-encoded forward index with bit compression<\/h3>\n<p>Dans un forward index de type dictionnaire, un identifiant est attribu\u00e9 \u00e0 chaque valeur unique d&rsquo;une colonne, et un dictionnaire est construit pour associer l&rsquo;identifiant \u00e0 la valeur. Le forward index stocke les identifiants compress\u00e9s en bits. Si vous avez peu de valeurs uniques, le codage par dictionnaire peut am\u00e9liorer consid\u00e9rablement l&rsquo;efficacit\u00e9 de stockage de l&rsquo;indexe.<\/p>\n<p>Source : <a href=\"https:\/\/docs.pinot.apache.org\/basics\/indexing\/forward-index\" target=\"_blank\" rel=\"noopener\"><a href=\"https:\/\/docs.pinot.apache.org\/basics\/indexing\/forward-index\">https:\/\/docs.pinot.apache.org\/basics\/indexing\/forward-index<\/a><\/a>.<\/p>\n<h2>Les indexes de Pinot<\/h2>\n<h3>Inverted index<\/h3>\n<p>Dans un index invers\u00e9, une entr\u00e9e (un mapping) est cr\u00e9\u00e9e pour chaque valeur du champ. Cette entr\u00e9e va contenir la liste des documents qui contiennent cette valeur.<\/p>\n<p>Par exemple, pour les documents suivants :<\/p>\n<table><thead><tr><th>Document IDs<\/th>\n\n<th>primary_type<\/th>\n        <\/tr><\/thead><tbody><tr><td>1<\/td>\n\n<td>MURDER<\/td>\n        <\/tr><tr><td>2<\/td>\n\n<td>MURDER<\/td>\n        <\/tr><tr><td>3<\/td>\n\n<td>DRUGS<\/td>\n        <\/tr><\/tbody><\/table>\n<p>Vous obtiendrez l&rsquo;index invers\u00e9 suivant :<\/p>\n<table><thead><tr><th>primary_type<\/th>\n\n<th>Document IDs<\/th>\n        <\/tr><\/thead><tbody><tr><td>MURDER<\/td>\n\n<td>1, 2<\/td>\n        <\/tr><tr><td>DRUGS<\/td>\n\n<td>3<\/td>\n        <\/tr><\/tbody><\/table>\n<h3>Bloom Filter<\/h3>\n<p>Un Bloom Filter permet d&rsquo;exclure les segments qui ne contiennent aucun enregistrement correspondant \u00e0 un pr\u00e9dicat d&rsquo;\u00c9GALIT\u00c9.<\/p>\n<h3>Range index<\/h3>\n<p>Identique \u00e0 un index invers\u00e9, mais va cr\u00e9er une entr\u00e9e dans l&rsquo;index (un mapping) pour une plage (range) de valeur au lieu d&rsquo;en cr\u00e9er une pour chaque valeur.<\/p>\n<p>Permet d&rsquo;\u00e9conomiser de l&rsquo;espace pour les colonnes ayant beaucoup de valeurs distinctes.<\/p>\n<p>Pour les colonnes de types <code>TIMESTAMP<\/code>, un index d\u00e9di\u00e9 existe et sert le m\u00eame but : le <strong>Timestamp index<\/strong>.<\/p>\n<h3>Star-tree<\/h3>\n<blockquote>La structure de donn\u00e9es star-tree offre un compromis configurable entre le stockage et la latence et nous permet d&rsquo;atteindre une limite sup\u00e9rieure stricte pour les latences des requ\u00eates pour un cas d&rsquo;utilisation donn\u00e9.<\/blockquote>\n<p>Source : <a href=\"https:\/\/docs.pinot.apache.org\/basics\/indexing\/star-tree-index\" target=\"_blank\" rel=\"noopener\"><a href=\"https:\/\/docs.pinot.apache.org\/basics\/indexing\/star-tree-index\">https:\/\/docs.pinot.apache.org\/basics\/indexing\/star-tree-index<\/a><\/a>.<\/p>\n<p>Un index de type star-tree va pr\u00e9-calculer et stocker une ou plusieurs pr\u00e9-agr\u00e9gations.<\/p>\n<p>Il va utiliser une structure de donn\u00e9es en arbre pour pr\u00e9-calculer des sous-agr\u00e9gations en fonction d&rsquo;une dimension \u00e0 certains n\u0153uds de l&rsquo;arbre.<\/p>\n<p>Au requ\u00eatage, Pinot va ensuite s\u00e9lectionner les n\u0153uds prenant part \u00e0 la requ\u00eate et agr\u00e9ger les sous-agr\u00e9gation de ces n\u0153uds.<\/p>\n<img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/star-tree.png?resize=640%2C192&#038;ssl=1\" alt=\"\" width=\"640\" height=\"192\" class=\"alignnone size-full wp-image-1502\" srcset=\"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/star-tree.png?w=899&amp;ssl=1 899w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/star-tree.png?resize=300%2C90&amp;ssl=1 300w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/star-tree.png?resize=768%2C231&amp;ssl=1 768w, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/star-tree.png?resize=604%2C181&amp;ssl=1 604w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/>\n<p>Un index star-tree est une structure en arbre qui contient les types de n\u0153uds suivants :<\/p>\n<ul><li><strong>Root Node (Orange)<\/strong> : N\u0153ud racine unique, \u00e0 partir duquel le reste de l&rsquo;arbre peut \u00eatre parcouru.<\/li>\n\n<li><strong>Leaf Node (Blue)<\/strong> : Peut contenir au plus T enregistrements, o\u00f9 T est configurable.<\/li>\n\n<li><strong>Non-leaf Node (Green)<\/strong> : Les n\u0153uds avec plus de T enregistrements sont ensuite divis\u00e9s en n\u0153uds enfants.<\/li>\n\n<li><strong>Star-Node (Yellow)<\/strong> : Star-Node (n\u0153ud \u00e9toile), contient les enregistrements pr\u00e9-agr\u00e9g\u00e9s apr\u00e8s suppression de la dimension sur laquelle les donn\u00e9es ont \u00e9t\u00e9 fractionn\u00e9es pour ce niveau.<\/li>\n\n<li><strong>Dimensions Split Order ([D1, D2])<\/strong> : Liste ordonn\u00e9e de dimensions utilis\u00e9e pour d\u00e9terminer la dimension \u00e0 fractionner pour un niveau donn\u00e9 dans l&rsquo;arborescence.<\/li>\n<\/ul>\n<h2>Les r\u00e9sultats<\/h2>\n<p>Pour tester les diff\u00e9rents types d&rsquo;indexes, nous allons cr\u00e9er une table <code>chicagoCrimesWithIdx<\/code> avec un ensemble d&rsquo;indexe et ex\u00e9cuter les m\u00eames requ\u00eates sur la table avec indexes et celle sans pour comparer les performances.<\/p>\n<p>La d\u00e9finition de table suivante r\u00e9-utilise le sch\u00e9ma <code>chicagoCrimes<\/code> mais ajoute des indexes sur certains champs.<\/p>\n<pre>\n{\n  \"tableName\": \"chicagoCrimesWithIdx\",\n  \"segmentsConfig\" : {\n    \"replication\" : \"1\",\n    \"schemaName\" : \"chicagoCrimes\"\n  },\n  \"tableIndexConfig\" : {\n    \"invertedIndexColumns\" : [\"primary_type\"],\n    \"bloomFilterColumns\": [\"ward\"],\n    \"rangeIndexColumns\": [\"x_coordinate\"],\n    \"starTreeIndexConfigs\": [{\n      \"dimensionsSplitOrder\": [\"year\"],\n      \"skipStarNodeCreationForDimensions\": [],\n      \"functionColumnPairs\": [\"SUM__community_area\"],\n      \"maxLeafRecords\": 1\n    }],\n    \"loadMode\"  : \"MMAP\"\n  },\n  \"tenants\" : {\n    \"broker\":\"DefaultTenant\",\n    \"server\":\"DefaultTenant\"\n  },\n  \"tableType\":\"OFFLINE\",\n  \"metadata\": {}\n}\n<\/pre>\n<p>Vous pouvez cr\u00e9er la table via la ligne de commande Docker suivante :<\/p>\n<pre>\ndocker run --rm -ti \\\n    --network=pinot_default \\\n    -v ~\/dev\/pinot\/crime:\/tmp\/pinot-quick-start \\\n    --name pinot-batch-table-creation \\\n    apachepinot\/pinot:0.10.0 AddTable \\\n    -schemaFile \/tmp\/pinot-quick-start\/chicagoCrimes-schema.json \\\n    -tableConfigFile \/tmp\/pinot-quick-start\/chicagoCrimes-table-with-idx.json \\\n    -controllerHost manual-pinot-controller \\\n    -controllerPort 9000 -exec\n<\/pre>\n<p>Et charger les donn\u00e9es dans la table via la ligne de commande Docker suivante, le job <code>job-ingest-with-idx.yml<\/code> est identique au job <code>job-ingest.yml<\/code> sauf qu&rsquo;il utilise la nouvelle description de table :<\/p>\n<pre>\ndocker run --rm -ti \\\n    --network=pinot_default \\\n    -v ~\/dev\/pinot\/crime:\/tmp\/pinot-quick-start \\\n    --name pinot-data-ingestion-job \\\n    apachepinot\/pinot:0.10.0 LaunchDataIngestionJob \\\n    -jobSpecFile \/tmp\/pinot-quick-start\/job-ingest-with-idx.yml\n<\/pre>\n<h3>Inverted index<\/h3>\n<pre>\nselect year, count(*) from chicagoCrimes where primary_type='NARCOTICS' group by year\n<\/pre>\n<table class=\"small\"><tr><th>Index O\/N<\/th>\n\n<th>timeUsedMs<\/th>\n\n<th>numDocsScanned<\/th>\n\n<th>numEntriesScannedInFilter<\/th>\n\n<th>numEntriesScannedPostFilter<\/th>\n    <\/tr><tr><td>Sans indexe<\/td>\n\n<td>25ms<\/td>\n\n<td>636118<\/td>\n\n<td>6452716<\/td>\n\n<td>636118<\/td>\n    <\/tr><tr><td>Avec indexe<\/td>\n\n<td>11ms<\/td>\n\n<td>636118<\/td>\n\n<td>O<\/td>\n\n<td>636118<\/td>\n    <\/tr><\/table>\n<p>On voit ici l\u2019int\u00e9r\u00eat d&rsquo;un indexe invers\u00e9 : le filtre (la clause WHERE) n&rsquo;a scann\u00e9 aucune entr\u00e9e car il a utilis\u00e9 l&rsquo;indexe.\nLe temps d&rsquo;ex\u00e9cution de la requ\u00eate a \u00e9t\u00e9 grandement am\u00e9lior\u00e9 passant de 25ms \u00e0 11ms.<\/p>\n<h3>Range index<\/h3>\n<pre>\nselect year, count(*) from chicagoCrimes where x_coordinate&gt;1180000 group by year\n<\/pre>\n<table class=\"small\"><tr><th>Index O\/N<\/th>\n\n<th>timeUsedMs<\/th>\n\n<th>numDocsScanned<\/th>\n\n<th>numEntriesScannedInFilter<\/th>\n\n<th>numEntriesScannedPostFilter<\/th>\n    <\/tr><tr><td>Sans indexe<\/td>\n\n<td>29ms<\/td>\n\n<td>990885<\/td>\n\n<td>6452716<\/td>\n\n<td>990885<\/td>\n    <\/tr><tr><td>Avec indexe<\/td>\n\n<td>11ms<\/td>\n\n<td>990885<\/td>\n\n<td>641072<\/td>\n\n<td>990885<\/td>\n    <\/tr><\/table>\n<p>On voit ici l\u2019int\u00e9r\u00eat d&rsquo;un indexe de type range : le filtre (la clause WHERE) a scann\u00e9 beaucoup moins d&rsquo;entr\u00e9es car il a utilis\u00e9 l&rsquo;indexe m\u00eame si l&rsquo;index ayant une entr\u00e9e par range il a quand m\u00eame d\u00fb scanner une partie des entr\u00e9e (10% ici).\nLe temps d&rsquo;ex\u00e9cution de la requ\u00eate a \u00e9t\u00e9 grandement am\u00e9lior\u00e9 passant de 29ms \u00e0 11ms.<\/p>\n<h3>Bloom filter<\/h3>\n<pre>\nselect year, count(*) from chicagoCrimesWithIdx where ward=45 group by year\n<\/pre>\n<p>Un Bloom filter va filtrer les segments (segment pruning) \u00e0 traiter, bien qu&rsquo;ici nous ayons 6 segments, ceux-ci \u00e9tant cr\u00e9er sans aucune logique de filtre (segment technique), le Bloom filter n&rsquo;aura aucun effet.<\/p>\n<p>Pour pouvoir utiliser un Blomm filter sur le champs ward, il aurait fallut d\u00e9couper les segments sur la valeur de ce champs (un segment par plage de valeur par exemple).<\/p>\n<h3>Star-tree index<\/h3>\n<pre>\nselect year, sum(community_area) from chicagoCrimes group by year\n<\/pre>\n<table class=\"small\"><tr><th>Index O\/N<\/th>\n\n<th>timeUsedMs<\/th>\n\n<th>numDocsScanned<\/th>\n\n<th>numEntriesScannedInFilter<\/th>\n\n<th>numEntriesScannedPostFilter<\/th>\n    <\/tr><tr><td>Sans indexe<\/td>\n\n<td>31ms<\/td>\n\n<td>6452716<\/td>\n\n<td>0<\/td>\n\n<td>12905432<\/td>\n    <\/tr><tr><td>Avec indexe<\/td>\n\n<td>6ms<\/td>\n\n<td>132<\/td>\n\n<td>0<\/td>\n\n<td>264<\/td>\n    <\/tr><\/table>\n<p>Avec un index star-tree, des documents seront pr\u00e9-calcul\u00e9s avec des pr\u00e9-agr\u00e9gations.\nAu lieu de scanner les documents de la table, ce sont les documents de l&rsquo;indexes qui auront \u00e9t\u00e9 scann\u00e9s; d&rsquo;o\u00f9 le nombre de documents scann\u00e9 de 132.\nCette requ\u00eate n&rsquo;a utilis\u00e9 que les donn\u00e9es de l&rsquo;indexes et s&rsquo;est donc ex\u00e9cut\u00e9 tr\u00e8s rapidement.\nLe temps d&rsquo;ex\u00e9cution de la requ\u00eate a \u00e9t\u00e9 grandement am\u00e9lior\u00e9 passant de 31ms \u00e0 6ms.<\/p>\n<h2>Conclusion<\/h2>\n<p>Sans indexe, les performances d&rsquo;une requ\u00eate Pinot sont d\u00e9j\u00e0 tr\u00e8s bonnes gr\u00e2ce \u00e0 son stockage optimis\u00e9 des champs en forward index. L&rsquo;ajout d&rsquo;indexes sp\u00e9cifiques permet un gain de performance non n\u00e9gligeable m\u00eame pour des requ\u00eate balayant une grande partie des donn\u00e9es de la table. Attention, les temps de requ\u00eates donn\u00e9es dans cet articles sont en ex\u00e9cution locale sur un jeu de donn\u00e9es assez petit pour Pinot, ils ne peuvent donc \u00eatre extrapol\u00e9 \u00e0 un vrai jeu de donn\u00e9e sur un d\u00e9ploiement Pinot de production.<\/p>\n<p>L&rsquo;index de type star-tree permet de r\u00e9aliser des pr\u00e9-agr\u00e9gation sans n\u00e9cessiter un grand espace de stockage, les requ\u00eates l&rsquo;utilisant deviennent alors ultra-rapides car elles utilisent un petit nombre de documents pr\u00e9-construits au lieu de n\u00e9cessiter un scan total des segments. C&rsquo;est pour moi l&rsquo;index le plus int\u00e9ressant et novateur offert par Pinot.<\/p>","protected":false},"excerpt":{"rendered":"<p>Il y a quelques temps, j&rsquo;avais enfin pris le temps de tester Apache Pinot, vous pouvez trouver le r\u00e9cit de mes premi\u00e8res exp\u00e9rimentations ici. Apache Pinot est un datastore OLAP (OnLine Analytical Processing) distribu\u00e9 et temps r\u00e9el, sp\u00e9cialement con\u00e7u pour fournir des analyses \u00e0 tr\u00e8s faible latence, m\u00eame \u00e0 un d\u00e9bit extr\u00eamement \u00e9lev\u00e9. Si vous ne le connaissez pas, commencez par lire mon article d&rsquo;introduction avant celui-ci. Une des forces de Pinot est ses diff\u00e9rents types d&rsquo;indexes, ce sont ceux-ci&#8230;<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/apache-pinot-et-de-ses-differents-types-dindexes\/\">Lire la suite<span class=\"screen-reader-text\"> Lire la suite<\/span><\/a><\/p><\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[9],"tags":[203,204,202],"class_list":["post-1497","post","type-post","status-publish","format-standard","hentry","category-informatique","tag-database","tag-olap","tag-pinot"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1400,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/jai-enfin-pris-le-temps-de-tester-apache-pinot\/","url_meta":{"origin":1497,"position":0},"title":"J&rsquo;ai enfin pris le temps de tester Apache Pinot","author":"admin","date":"jeudi 20 janvier 2022","format":false,"excerpt":"Cela faisait tr\u00e8s longtemps que j'avais envie de tester Apache Pinot et j'ai enfin pris le temps de le faire ! Tout d'abord, une rapide description de Pinot Pinot est un datastore OLAP (OnLine Analytical Processing) distribu\u00e9e et temps r\u00e9el, sp\u00e9cialement con\u00e7u pour fournir des analyses \u00e0 tr\u00e8s faible latence,\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Pinot-architecture.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Pinot-architecture.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/Pinot-architecture.jpg?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":1508,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/4-ans-chez-zenika\/","url_meta":{"origin":1497,"position":1},"title":"4 ans chez Zenika","author":"admin","date":"mardi  6 septembre 2022","format":false,"excerpt":"Avec quelques jours de retard, la date anniversaire \u00e9tant le 3 septembre, voici le bilan de ma quatri\u00e8me ann\u00e9e chez Zenika. Pour ceux qui seraient int\u00e9ress\u00e9 par ce que j\u2019avais fait l\u2019ann\u00e9e pr\u00e9c\u00e9dente, c\u2019est ici : Ma troisi\u00e8me ann\u00e9e chez Zenika. Quelques chiffres : 11 articles sur mon blog perso,\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1674,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/devoxx-fr-2023-foundation-db-le-secret-le-mieux-garde-des-nouvelles-architectures-distribuees-par-pierre-zemb-et-steven-le-roux\/","url_meta":{"origin":1497,"position":2},"title":"Devoxx FR 2023 &#8211; FoundationDB : le secret le mieux gard\u00e9 des nouvelles architectures distribu\u00e9es ! par Pierre Zemb et Steven Le Roux","author":"admin","date":"lundi 17 avril 2023","format":false,"excerpt":"Il existe pr\u00e8s de 900 BDD (cf https:\/\/dbdb.io\/) et chacune a ses particularit\u00e9s : mod\u00e8le de requ\u00eatage, mod\u00e9lisation de la donn\u00e9e, moteur de stockage, .... Existe-t-il une de ces trois caract\u00e9ristiques que l'on peut mutualiser ? Oui, toutes les bases de donn\u00e9es stockent de la donn\u00e9e, on doit donc pouvoir\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":419,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/chti-jug-nosql\/","url_meta":{"origin":1497,"position":3},"title":"Ch&rsquo;ti JUG : NoSQL","author":"admin","date":"lundi 20 d\u00e9cembre 2010","format":false,"excerpt":"Le 2 d\u00e9cembre s'est tenu dans les locaux de l'IUT A de Lille une session du Ch'ti JUG sur les technologie NoSQL anim\u00e9 par Olivier Mallassi. L'intervenant a commenc\u00e9 la conf\u00e9rence par un bref historique de la mani\u00e8re dont les donn\u00e9es on \u00e9t\u00e9 stock\u00e9es dans le monde de l'informatique: Au\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":566,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/chti-jug-lili-et-cassandra\/","url_meta":{"origin":1497,"position":4},"title":"Ch&rsquo;ti JUG : Lili et Cassandra","author":"admin","date":"jeudi 15 d\u00e9cembre 2011","format":false,"excerpt":"Le 12 d\u00e9cembre s'est tenu dans les locaux de l'IUT A de Lille une session du Ch'ti JUG sur Lili et Cassandra deux outils autour des bases de donn\u00e9es NoSql. La pr\u00e9sentation de Lili a \u00e9t\u00e9 faite par Stevens Noel et celle sur Cassandra par J\u00e9r\u00e9my Sevellec. Ayant d\u00e9j\u00e0 \u00e9crit\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1611,"url":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/informatique\/introduction-a-kestra\/","url_meta":{"origin":1497,"position":5},"title":"Introduction \u00e0 Kestra","author":"admin","date":"lundi  6 mars 2023","format":false,"excerpt":"Kestra est un orchestrateur et scheduler de donn\u00e9e open source. Avec Kestra, les workflows de donn\u00e9es, appel\u00e9s flows, utilisent le format YAML et sont ex\u00e9cut\u00e9s par son moteur via un appel API, l'interface utilisateur, ou un trigger (webhook, schedule, SQL query, Pub\/Sub message, \u2026). Les notions importantes de Kestra sont\u2026","rel":"","context":"Dans &quot;informatique&quot;","block_context":{"text":"informatique","link":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/category\/informatique\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-01-1024x267.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-01-1024x267.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/loicmathieu.fr\/wordpress\/wp-content\/uploads\/kestra-01-1024x267.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/comments?post=1497"}],"version-history":[{"count":22,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1497\/revisions"}],"predecessor-version":[{"id":1530,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/posts\/1497\/revisions\/1530"}],"wp:attachment":[{"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/media?parent=1497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/categories?post=1497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.loicmathieu.fr\/wordpress\/fr\/wp-json\/wp\/v2\/tags?post=1497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}