SQL 执行计划异常

【 TiDB 使用环境】生产环境
【 TiDB 版本】TiDB-v6.1.3
遇到的问题:集群突然变慢,登录进去发现有个sql 在大量执行,TIKV-CPU 被拉满


表结构如下:

 CREATE TABLE `tbl_tel_call_log` (
  `record_id` varchar(100) NOT NULL DEFAULT '' COMMENT '话单id',
  `ext_data` text NOT NULL COMMENT '扩展字段',
  `source` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '来源',
  .........
  `called_name` varchar(40) NOT NULL DEFAULT '' COMMENT '被叫方的名称',
  `call_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '呼叫类型,1-呼出,2-呼入',
  `call_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '对应状态,1-成功调用下游,2-通话中,3-通话成功结束,4-通话失败(响铃未接),5-通话失败(未响铃)',
  `duration` int(10) NOT NULL DEFAULT '0' COMMENT '通话时长(单位:s)',
  `agent_ring_time` int(10) NOT NULL DEFAULT '0' COMMENT '座席响铃时间',
  .......
  `record_file` varchar(500) NOT NULL DEFAULT '' COMMENT '通话录音文件cos对象名',
  `display_number` varchar(20) NOT NULL DEFAULT '' COMMENT '被叫方的可展示号码',
  `encrypt_number` varchar(100) NOT NULL DEFAULT '' COMMENT '被叫方脱敏号码',
  `create_name` varchar(40) NOT NULL DEFAULT '' COMMENT '创建人--邮箱前缀',
  `create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
  `update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
  `last_modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '数据表最后更新时间',
  `device_env` tinyint(4) NOT NULL DEFAULT '0' COMMENT '设备刷机状态',
  `device_uniq_id` varchar(40) NOT NULL DEFAULT '' COMMENT '设备内编码',
  PRIMARY KEY (`record_id`) /*T![clustered_index] NONCLUSTERED */,
  KEY `idx_call_name` (`call_name`),
  KEY `idx_record_id` (`record_id`),
  KEY `idx_called_uid` (`called_uid`),
  KEY `idx_call_status` (`call_status`),
  KEY `idx_encrypt_time` (`encrypt_number`,`create_time`),
  KEY `idx_outcall_record_id` (`outcall_record_id`),
  KEY `idx_call_id` (`call_id`),
  KEY `idx_create_time_role` (`create_time`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T! SHARD_ROW_ID_BITS=4 */

变慢的sql 如下:

SELECT count(1) FROM `tbl_tel_call_log`  WHERE `encrypt_number` = '0415df6acde779843ac1b2172db07bbdb5c486662d002ffb' AND  create_time >= 1680105600 and call_status in (3,5,31);

出问题时执行计划在dashboard 上看,使用了错误的索引KEY idx_call_status (call_status)

出问题执行这个sql 的执行计划如下:


使用的正确的索引: KEY idx_encrypt_time (encrypt_number,create_time)

问题1: 这个sql 已经运行了好久了,为什么突然还行计划会变成使用call_status 这个索引

问题2:我在本地复现的时候,使用了force index 也不走正确的索引是为什么 ?

当我调整了 where 条件中 call_status 的位置, 执行计划就正常了。这是什么原因 ?

stats:pseudo
做个表分析

这个问题大概率是表的健康度或统计信息方面的异常情况,检查一下表的健康度,同时查看执行计划。

通常对常用的TiDB表,最佳实践是手动定期analyze 它,以让统计信息最新最准确

即使是pseudo ,但force index 应该会强制走索引才对,实际却没有,这一点确实不好理解

看别的大佬怎么解释

force index实际不是一般理解的强制索引,而是建议索引,如果tidb的cbo很坚定的认为应该走a索引,你force index 并不能强制走到b索引上

原来如此

看下cost 呢? explain format=‘verbose’ select * from ****是不是优化器认为走call_status的代价比 encrypt_number ,create_time 代价小?