目前发现,TiDB在如下两个场景中很容易导致表扫描(数据百万+),除了查询改写,还有哪些控制手段?
- 使用 or 条件:同表不同字段,改成 union 之后走索引,直接用则全表扫描
- IN(…),如果值很多,比如500+,则很容易走表扫描?
目前发现,TiDB在如下两个场景中很容易导致表扫描(数据百万+),除了查询改写,还有哪些控制手段?
来看看,是什么情况、!
加hint?
in内对象过多的,可以考虑构建临时表,然后两表关联。
主要就是看有没有不改查询能降低表扫描的方法
从 mysql 迁移过来,mysql那边表现正常,过来之后发现KV负载很重,分析慢查询,发现很多在mysql那边直接索引的,在tidb这边走表扫描,总结下来主要就是 OR 与 IN 这两类
查询改起来比较慢,所以想看有没有什么能够快速降低负载的方法
执行计划绑定了,如果sql不太多的话
举个例子
SELECT * FROM bbc
WHERE bbc.biz_type='ER'
AND bbc.biz_no='930'
;
SELECT * FROM bbc
WHERE bbc.biz_type='ER'
AND bbc.bid BASIC_INFO_NO='930'
;
-- 为什么是表扫描,而不是 index merge
SELECT * FROM bbc
WHERE
bbc.biz_type='ER'
AND
bbc.biz_no='930'
OR bbc.bid BASIC_INFO_NO='930'
)
表:`bbc`,记数率 44.6万
CREATE TABLE `bbc` (
`id` bigint NOT NULL AUTO_INCREMENT,
`biz_type` varchar(32) COLLATE utf8mb4_0900_ai_ci NOT NULL,
`biz_no` varchar(36) COLLATE utf8mb4_0900_ai_ci NOT NULL,
`bid_basic_info_no` varchar(50) COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_bid_change` (`bid_change_no`),
KEY `IX_biz_type_biz_no` (`biz_type`,`biz_no`),
KEY `IX_bid_no` (`bid_basic_info_no`),
KEY `IX_biz_no` (`biz_no`),
KEY `IX_biz_type_bid_basic_info_no` (`biz_type`, `bid_basic_info_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci AUTO_INCREMENT=592050 COMMENT='招标变更'
将 OR 拆成两个查询,都能走索引,而且返回的记录数都很少(实际执行也是如此),但写成OR条件之后,就变成表扫描了,絮棉按成本评估,应该是 index-merge 更优才对
in特别多的时候,我们都是改写成插入临时表,然后表关联
加分区,加索引
or 创建复合索引或联合索引
in的话,可以优化索引覆盖性
这种目前只能通过绑定处理,/+use_index_merge(bbc)/。
这只能是防守,而且是在出现问题可观察到的情况下,很疯
有一部分SQL,我们让开发改写成union了。
测试环境还可以调下这个参数试试。
52869 从 v8.1.0 版本开始引入
默认值:OFF
可选值:ON、OFF
如果查询有除了全表扫描以外的单索引扫描方式可以选择,优化器不会自动选择索引合并。详情请参考用 EXPLAIN 查看索引合并的 SQL 执行计划中的注意部分。
打开此开关后,这个限制会被解除。解除此限制能让优化器在更多查询中自动选择索引合并,但也有可能忽略其他更好的执行计划,因此建议在解除此限制前针对实际场景进行充分测试,确保不会带来性能回退。
这个具体应该如何理解?
但全表扫描的伤害感觉更大
一旦有单索引可以命中,并且效果不太好的话,导致的表扫描非常致命,而且这种情况的命中弯感觉更高
EXPLAIN FORMAT='verbose'
SELECT * FROM bbc1 bbc
WHERE bbc.biz_type='BASIC_INFO_ALTER'
AND( bbc.biz_no='422' OR bbc.bid_basic_info_no='422')
;
走表扫描
EXPLAIN FORMAT='verbose'
SELECT * FROM bbc1 bbc IGNORE INDEX(IX_biz_type_bid_basic_info_no,IX_biz_type_biz_no)
WHERE bbc.biz_type='BASIC_INFO_ALTER'
AND( bbc.biz_no='422' OR bbc.bid_basic_info_no='422')
走 index merge
EXPLAIN FORMAT='verbose'
SELECT * FROM bbc1 bbc USE INDEX(IX_biz_type_bid_basic_info_no)
WHERE bbc.biz_type='BASIC_INFO_ALTER'
AND( bbc.biz_no='422' OR bbc.bid_basic_info_no='422')
;
EXPLAIN FORMAT='verbose'
SELECT * FROM bbc1 bbc USE INDEX(IX_biz_type_biz_no)
WHERE bbc.biz_type='BASIC_INFO_ALTER'
AND( bbc.biz_no='422' OR bbc.bid_basic_info_no='422')
;
走单索引扫描