关于索引下推,请教下大佬们

76 天前
 q1450718943

表结构如下

CREATE TABLE `user_cp` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `cp_id` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'cpId 下划线分割',
  `intimacy` int(11) NOT NULL COMMENT '加了 add_exp 后的当前等级下的经验',
   `attenuation_utime` int(11) NOT NULL DEFAULT '0' COMMENT '扣减时间',
  `utime` int(11) NOT NULL COMMENT '更新时间',
  `ctime` int(11) NOT NULL COMMENT '时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `cp_id_unique` (`cp_id`),
  KEY `idx_attenuationutime_utime_intimacy_cpid` (`attenuation_utime`,`utime`,`intimacy`,`cp_id`)
) ENGINE=InnoDB AUTO_INCREMENT=31988443 DEFAULT CHARSET=utf8

sql 语句

explain SELECT cp_id
FROM user_cp
WHERE utime < 1707912100
 AND attenuation_utime < 1708430500
 AND intimacy > 0

extra: Using where; Using index

MySQL 版本:5.7.32

按照我的理解,这个 SQL 应该是符合索引下推的条件的,但是实际并没有使用,有大佬可以解答一下吗?

1462 次点击
所在节点    MySQL
10 条回复
bigfei
76 天前
以下 GPT4 回答:

MySQL 的索引下推优化( Index Condition Pushdown, ICP )是 MySQL 5.6 版本引入的一种查询优化方式。它允许 MySQL 在索引扫描时就进行部分 WHERE 条件的过滤,而不是在全部数据检索后再进行过滤,从而提高查询效率。

然而,ICP 并不是对所有查询都有效,它对某些查询的优化效果更为明显,例如当数据表的行数非常大,且需要过滤的数据占比较小的情况下。

你的查询语句的 EXPLAIN 结果显示"Using where; Using index",这表明 MySQL 在进行索引扫描时,已经使用了你定义的复合索引`idx_attenuationutime_utime_intimacy_cpid`,并且在扫描索引时就已经应用了 WHERE 条件进行了部分过滤。

但是,这并不意味着 MySQL 一定使用了 ICP 。ICP 的使用是由优化器决定的,优化器会根据表的统计信息以及查询条件来决定是否使用 ICP 。在某些情况下,即使查询符合 ICP 的使用条件,优化器也可能决定不使用 ICP ,例如当表的行数较小,或者需要过滤的数据占比较大的情况下。

在你的情况下,如果你确定你的查询符合 ICP 的使用条件,但 EXPLAIN 的结果显示没有使用 ICP ,那么可能的原因包括但不限于以下几点:

1. 表的统计信息不准确,导致优化器做出了错误的决策。你可以尝试使用`ANALYZE TABLE`语句更新表的统计信息,然后再查看 EXPLAIN 的结果。

2. MySQL 的配置参数没有正确设置。你可以检查`optimizer_switch`参数的设置,确保`index_condition_pushdown`选项被设置为了`on`。

3. 查询的过滤条件不满足 ICP 的使用条件。ICP 只对某些类型的查询有效,例如比较运算符(如<、>、=等)和某些函数(如 IS NULL 和 IS NOT NULL )。如果查询的过滤条件包含其他类型的运算或函数,那么 ICP 可能不会被使用。

总的来说,ICP 的使用是由 MySQL 的优化器决定的,它会根据多种因素来决定是否使用 ICP 。如果你想了解更多关于 ICP 的信息,你可以查阅 MySQL 的官方文档。
lbprivateacc
76 天前
看了下官方文档,貌似要回表查的时候才会使用
cxshun
76 天前
你这里的查询语句直接使用了覆盖索引,Using Index ,有可能优化器觉得用了覆盖索引已经是比较优化的结果了,所以就不会使用索引下推。
按覆盖索引的作用来说,它是为了减少回表的次数,而覆盖索引了,实际上根本就不需要回表,所以就不会使用了。
按官方文档是这样的:
> ICP can reduce the number of times the storage engine must access the base table and the number of times the MySQL server must access the storage engine.

综上:我的理解是因为这里的覆盖索引,所以导致不会用索引下推来做优化。
rockyliang
76 天前
试了一下,确实如上面回答说的,要回表的时候才会使用索引下推,将 SELECT cp_id 改为 SELECT * 就会使用索引下推
elza
76 天前
你这个使用的是覆盖索引
q1450718943
76 天前
@lbprivateacc
@cxshun
@elza
@rockyliang
是的,SELECT * 的时候才会使用索引下推,感谢回答。
q1450718943
76 天前
@cxshun 本地测试了一下,虽然 extra 没有输出 using index condition ,但是 key len 是 115 。可惜线上 key len 是 4 ,rows 也很大,主要是这个问题。
cxshun
73 天前
@q1450718943 感觉有点奇怪,你的 key len 最大也只是 11+11+11+25 = 58 ,这个 115 是不是命中了其他的 key 。
你可以提供 explain 的大概信息么?
q1450718943
72 天前
@cxshun
id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra
1,SIMPLE,user_cp,,index,idx_attenuationutime_utime_intimacy_cpid,idx_attenuationutime_utime_intimacy_cpid,115,,1,100,Using where; Using index

应该是 4 + 4 + 4 + 25*4+2+1 ,按字节算的。
cxshun
72 天前
@q1450718943 对,不好意思,一下子弄混了,是按字节来算的。
key_len 是 115 应该是表示它使用了完整的索引,Using index 表示它使用了覆盖索引,因为这里用不了最左匹配,就相当于只能全索引扫描了,所以这里看起来没啥毛病。

线上的 key len 是 4 ,这个我暂时解释不了,看看有没有大佬能解释一下哈。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1017698

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX