Dans une table distribuée PolarDB-X, les requêtes sans clé de partition déclenchent une analyse complète sur tous les nœuds de données. Les performances se limitent alors à celles d'un seul nœud et la mise à l'échelle linéaire devient impossible. Un index secondaire global (GSI) élimine ces analyses complètes en permettant de définir un schéma de partitionnement indépendant pour un index. Ainsi, toute colonne peut servir de chemin de requête efficace.
Fonctionnement des GSI
PolarDB-X stocke les données des tables sur plusieurs nœuds de données, partitionnées selon une clé spécifique. Une requête incluant la clé de partition est routée directement vers la partition concernée. À l'inverse, une requête ne l'incluant pas se propage à toutes les partitions.
Si un système comporte N nœuds de données, une requête par clé de partition génère une charge moyenne de 1/N sur chaque nœud. Une analyse complète des partitions impose une charge de 1 sur chaque nœud. Par conséquent, le plafond de performance chute à celui d'un nœud unique et le système perd sa capacité de mise à l'échelle linéaire.
Le GSI résout ce problème en agissant comme une table partitionnée spéciale qui duplique certaines colonnes de la table principale. Son partitionnement horizontal repose sur une clé définie par l'utilisateur, différente de celle de la table principale. Lorsqu'une requête correspond à la clé de partition du GSI, PolarDB-X la dirige vers la partition GSI appropriée, récupère les lignes correspondantes, puis extrait les colonnes supplémentaires depuis la table principale. Cela évite l'analyse complète des partitions.
PolarDB-X utilise des transactions distribuées pour maintenir une forte cohérence des données entre la table principale et ses GSI.
Les GSI prennent également en charge :
DDL en ligne : créez, modifiez et supprimez des GSI sans verrouiller la table.
Colonnes de couverture personnalisées : stockez des colonnes supplémentaires dans le GSI afin de réduire ou d'éliminer les recherches dans la table principale.
Index invisibles : testez l'impact d'un GSI sur les plans de requête sans l'exposer à l'optimiseur de requêtes, ce qui permet de valider ses performances avant son activation en production.
Types de GSI
GSI standard
Un GSI standard partitionne les données d'index selon une colonne autre que la clé de partition de la table principale. Lorsqu'une requête correspond à la clé de partition du GSI, PolarDB-X route cette requête vers la partition concernée et interroge la table principale pour obtenir les colonnes restantes.
Example: La table user est partitionnée par user_id. Pour permettre des requêtes efficaces par name, créez un GSI sur name.
CREATE TABLE user(
user_id bigint,
name varchar(10),
addr varchar(30),
GLOBAL INDEX `g_i_name` (name) PARTITION BY HASH(name),
PRIMARY KEY(user_id)
) PARTITION BY KEY(user_id);
Avec g_i_name en place, une requête filtrant par name est dirigée vers la partition correspondante de l'index au lieu d'analyser toutes les partitions de user.
UGSI
Un index secondaire global unique (UGSI) possède toutes les propriétés d'un GSI standard et applique en outre une contrainte UNIQUE globale sur l'ensemble de la table distribuée.
Exemple : La table user2 est partitionnée par user_id. Pour garantir que phone soit globalement unique sur toutes les partitions, créez un UGSI sur phone.
CREATE TABLE user2(
user_id bigint,
phone varchar(20),
addr varchar(30),
UNIQUE GLOBAL INDEX `g_i_phone`(phone) PARTITION BY HASH(phone),
PRIMARY KEY(user_id)
) PARTITION BY KEY(user_id);
GSI clusterisé
Par défaut, un GSI clusterisé stocke toutes les colonnes de la table principale, et non seulement la clé d'index et les colonnes de couverture. Comme chaque colonne est présente dans l'index, les requêtes n'interrogent jamais la table principale, ce qui élimine totalement la surcharge liée aux recherches.
En contrepartie, un GSI clusterisé occupe le même espace disque que la table principale.
Exemple : La table order_tbl est partitionnée par order_id. Pour permettre des requêtes rapides par user_id sans aucune recherche dans la table principale, créez un GSI clusterisé sur user_id.
CREATE TABLE order_tbl(
order_id bigint,
user_id bigint,
addr varchar(30),
info text,
create_time datetime,
CLUSTERED INDEX `cg_i_user`(user_id) PARTITION BY HASH(user_id),
PRIMARY KEY(order_id)
) PARTITION BY KEY(order_id);
Lors d'une requête par user_id, PolarDB-X route la requête vers la partition correspondante de cg_i_user et lit toutes les données nécessaires directement depuis l'index.
Cas d'usage recommandés
| Scénario | Type recommandé |
|---|---|
| Requête par une colonne hors clé de partition ; la recherche dans la table principale est acceptable | GSI |
| Requête par une colonne hors clé de partition ; la colonne doit être globalement unique | UGSI |
| Requête par une colonne hors clé de partition ; l'élimination de la surcharge de recherche est prioritaire | GSI clusterisé |
Performances
Les GSI améliorent les performances en lecture mais dégradent celles en écriture. Les benchmarks Sysbench suivants illustrent cet impact à grande échelle.
Performances en lecture
| Table | Threads | select_random_ranges QPS | Avg latency (ms) | 95th percentile (ms) | select_random_points QPS | Avg latency (ms) | 95th percentile (ms) |
|---|---|---|---|---|---|---|---|
| Partitioned table | 128 | 2,769.17 | 46.21 | 99.33 | 5,226.99 | 24.48 | 42.61 |
| 256 | 3,415.64 | 144.97 | 144.97 | 5,476.76 | 46.73 | 82.96 | |
| 512 | 3,272.46 | 156.31 | 257.95 | 5,290.67 | 96.72 | 179.94 | |
| 1024 | 2,453.16 | 416.12 | 539.71 | 5,165.31 | 198.07 | 404.61 | |
| Partitioned table + GSI | 128 | 9,662.11 | 13.24 | 25.28 | 22,584.89 | 5.66 | 9.73 |
| 256 | 10,431.73 | 24.52 | 51.02 | 25,558.26 | 10.01 | 17.95 | |
| 512 | 15,634.51 | 32.72 | 73.13 | 27,116.56 | 18.86 | 39.65 | |
| 1024 | 22,948.76 | 44.53 | 108.68 | 32,509.87 | 31.43 | 73.13 |
Avec un GSI sur la colonne d'index :
QPS pour les requêtes par plage : 3 415,64 → 22 948,76 — 571% improvement
QPS pour les requêtes ponctuelles : 5 476,76 → 32 509,87 — 493% improvement
Performances en écriture
| Table | Threads | Write-only QPS | Avg latency (ms) | 95th percentile (ms) | Read-write QPS | Avg latency (ms) | 95th percentile (ms) |
|---|---|---|---|---|---|---|---|
| Partitioned table | 128 | 86,548.12 | 8.87 | 10.27 | 113,655.28 | 22.52 | 26.2 |
| 256 | 115,774.71 | 13.26 | 19.29 | 149,677.52 | 34.19 | 44.17 | |
| 512 | 143,928.94 | 20.51 | 34.95 | 14,555.16 | 70.28 | 112.67 | |
| 1024 | 153,501.7 | 39.53 | 70.55 | 132,150.69 | 131.58 | 287.38 | |
| Partitioned table + GSI | 128 | 52,069.22 | 14.25 | 18.28 | 90,074.59 | 28.41 | 33.72 |
| 256 | 66,250.79 | 23.17 | 32.53 | 114,420.32 | 44.73 | 57.87 | |
| 512 | 75,700.74 | 39.1 | 59.99 | 111,093.61 | 92.09 | 142.39 | |
| 1024 | 76,557.94 | 80.14 | 134.9 | 101,828.32 | 182.51 | 350.33 |
Avec un GSI sur la colonne d'index :
QPS en écriture seule : 153 501,7 → 76 557,94 — 50% reduction
QPS mixte lecture-écriture : 149 677,52 → 114 420,32 — 23% reduction