本文为您介绍PolarDB-X的自动FORCE INDEX机制。
背景信息
当一条查询SQL(逻辑SQL)发往PolarDB-X时,PolarDB-X的计算节点的优化器会将其分成可下推和不可下推的两部分,可下推的部分也被称为物理SQL。大多数情况下,物理SQL会直接发送给存储节点,由PolarDB-X的存储节点基于代价选择合适的局部索引。
由于代价模型缺陷、统计信息不准确等问题,基于代价选择索引的模式可能会出现索引选错。因此在某些确定性场景下,计算节点会基于规则选择合适的局部索引,使用FORCE INDEX指定索引将其添加到物理SQL中,干预存储节点的索引选择,减少错误发生的概率。
支持版本
对于5.4.18-17181576之前的实例,无自动FORCE INDEX机制。
对于初次购买版本在5.4.18-17181576之前并升级到5.4.18-17181576及以上的实例,默认关闭自动FORCE INDEX机制。
对于新购实例
计算节点组件版本低于5.4.18,无自动FORCE INDEX机制。
计算节点组件版本为5.4.18,小版本低于17181576,无自动FORCE INDEX机制;小版本不低于17181576,默认开启自动FORCE INDEX机制。
计算节点组件版本为5.4.19,发布日期早于2024-07-10之前,无自动FORCE INDEX机制;发布日期不早于2024-07-10,默认开启自动FORCE INDEX机制。
计算节点组件版本高于5.4.19,默认开启自动FORCE INDEX机制。
注意事项
对于只涉及单表、广播表的查询,不会触发自动FORCE INDEX。
对于下推到存储节点的多表关联物理SQL,不会触发自动FORCE INDEX。
对于下推到单分片的查询,不会触发自动FORCE INDEX。
局部索引选择(local index selection)的优先级从高到低,如下所示:
手动FORCE INDEX。
自动FORCE INDEX。
XPlan索引选择。
存储节点索引选择。
原理
主键索引和唯一索引
主键索引和唯一索引自动FORCE INDEX的条件如下:
收集等值条件的列,收集到的列都是表中的原始列。
主键索引或唯一索引被收集到的列覆盖。
普通二级索引
普通二级索引自动FORCE INDEX的条件如下:
剔除不能利用索引的条件后,剩余条件仅包含等值条件。
收集等值条件与ORDER BY的列,收集到的列都是表中的原始列。
收集到的列被索引前缀覆盖。
示例
示例表example
,包含唯一索引UK
与联合索引LK
,示例如下:
CREATE TABLE `example` (
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
`c` int(11) NOT NULL,
`d` int(11) NOT NULL,
UNIQUE KEY `UK` (`a`, `b`),
KEY `LK` (`b`, `c`, `d`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4
PARTITION BY KEY(`a`)
PARTITIONS 4
场景一:
收集到的列为(a,b),c
,可以覆盖索引UK
,会自动执行FORCE INDEX(UK)
,示例如下:
select * from example where (a,b) in ((1,2),((3,4))) and c = 1 order by d
mysql> explain select * from example where (a,b) in ((1,2),((3,4))) and c = 1 order by d;
+------------------------+
| LOGICAL EXECUTIONPLAN
+------------------------+
| MergeSort(sort="d ASC")
| LogicalView(tables="example[p1,p4]", shardCount=2, sql="SELECT `a`, `b`, `c`, `d` FROM `example` AS `example` FORCE INDEX(UK) WHERE ((((`a`, `b`)) IN(?)) AND (`c` = ?)) ORDER BY `d`", pruningInfo="all size:2*2(part), pruning size:2") |
| HitCache:false
| Source:PLAN_CACHE
| TemplateId: 3148a498
+------------------------+
场景二:
收集到的列为c
,不可以覆盖索引UK
,不会自动执行FORCE INDEX()
,示例如下:
select * from example where (a+1,b) in ((1,2),((3,4))) and c = 1 order by d
mysql> explain select * from example where (a+1,b) in ((1,2),((3,4))) and c = 1 order by d;
+------------------------+
| LOGICAL EXECUTIONPLAN
+------------------------+
| MergeSort(sort="d ASC")
| LogicalView(tables="example[p1,p2,p3,p4]", shardCount=4, sql="SELECT `a`, `b`, `c`, `d` FROM `example` AS `example` WHERE (((((`a` + ?), `b`)) IN(?)) AND (`c` = ?)) ORDER BY `d`") |
| HitCache:false
| Source:PLAN_CACHE
| TemplateId: 5ad46a6e
+------------------------+
场景三:
收集到的列为b,c
,可以被索引LK
前缀匹配,会自动执行FORCE INDEX(LK)
,示例如下:
select * from example where (b) in (1,2) and a > b order by c
mysql> explain select * from example where (b) in (1,2) and a > b order by c;
+------------------------+
| LOGICAL EXECUTIONPLAN
+------------------------+
| MergeSort(sort="c ASC")
| LogicalView(tables="example[p1,p2,p3,p4]", shardCount=4, sql="SELECT `a`, `b`, `c`, `d` FROM `example` AS `example` FORCE INDEX(LK) WHERE ((`a` > `b`) AND (`b` IN(?))) ORDER BY `c`") |
| HitCache:false
| Source:PLAN_CACHE
| TemplateId: 3a4124bd
+------------------------+
场景四:
剔除不能利用索引的条件后,剩余条件包含非等值条件c>1
,不会自动执行FORCE INDEX
,示例如下:
select * from example where (b) in (1,2) and c > 1 order by d
mysql> explain select * from example where (b) in (1,2) and c > 1 order by d;
+------------------------+
| LOGICAL EXECUTIONPLAN
+------------------------+
| MergeSort(sort="d ASC")
| LogicalView(tables="example[p1,p2,p3,p4]", shardCount=4, sql="SELECT `a`, `b`, `c`, `d` FROM `example` AS `example` WHERE ((`b` IN(?)) AND (`c` > ?)) ORDER BY `d`") |
| HitCache:false
| Source:PLAN_CACHE
| TemplateId: f01b447f
+------------------------+