为什么这个简单的查询在 MySQL 8.0.25 这么慢?

2022-09-15 10:56:43 +08:00
 yiplee

有一张简单的表 t ,总共 11 个字段 ,关键字段如下:

  1. id int64 自增主键
  2. a char(36) NOT NULL
  3. b char(36) NOT NULL
  4. c char(36) NULL

除主键索引之外,还有两个索引:

  1. UNIQUE INDEX (a)
  2. INDEX (b)

现在有一个简单的查询

SELECT * FROM t WHERE b = ? AND c IS NOT NULL AND id > ? LIMIT 32 ORDER BY id ;

在表有大几千万条数据,并且 WHERE 匹配到的行数很多的时候,同样的数据在 MySQL 5.7.x 版本查询非常快,1s 以内,但是在另外一台 MySQL 8.0.25 上就要二十几秒,可能是什么原因呢?

6795 次点击
所在节点    MySQL
58 条回复
thinkershare
2022-09-15 12:35:14 +08:00
@yiplee 具体的还是要走分析器看看. 另外确定一点, 你的插入是否高频? 你的插入是否会导致大规模索引重建?
另外, 你确认 2 个表的存储引擎是一致的吗? 我在上千万的的 MySQL 上执行复杂查询, 就会比较慢, 感觉几百万-2000W 基本上查询还好, 后面就会越来越慢. 另外 2 台机器的磁盘 I/O 性能一样吗? 影响数据库性能的因素实在太多了.
yiplee
2022-09-15 12:36:42 +08:00
@wanguorui123 #2
@xuanbg #5
@sulinwork #11
@circle33 #17
@rrfeng #18

因为 8.x 那台控制权不在我手里,刚拿到 explain 的结果 😂

```json
[
{
"id": 1,
"select_type": "SIMPLE",
"table": "outputs",
"partitions": null,
"type": "range",
"possible_keys": "b",
"key": "b",
"key_len": 152,
"ref": null,
"rows": 1519240,
"filtered": 10,
"Extra": "Using index condition; Using where"
}
]
```
yiplee
2022-09-15 12:37:24 +08:00
@djoiwhud #20 是我手打的,顺序打错了
yiplee
2022-09-15 12:39:33 +08:00
@thinkershare #21 高频插入导致读很慢这个是有可能的,我之前没考虑到。打算从业务层面规避下,处理进度落后到一定值就先不要插入新数据了。
pengtdyd
2022-09-15 12:49:11 +08:00
有没有考虑过是硬件的问题
iseki
2022-09-15 13:07:16 +08:00
b=?命中的数据太多?
yiplee
2022-09-15 13:19:14 +08:00
@iseki 的确命中很多,但是索引是有序的带 limit 的情况下扫描前几行不就 OK 了吗
1018ji
2022-09-15 13:29:27 +08:00
我感觉走 a 更快
1018ji
2022-09-15 13:30:45 +08:00
@1018ji 错了,自增主键的索引,不知道有没
zznext
2022-09-15 13:49:38 +08:00
先锁表试试;
YIsion
2022-09-15 14:03:33 +08:00
盲猜 id> ? 这个条件的问题。刚优化了一个类似的,我们业务系统的加这个条件虽然走索引,但会扫 40w 条数据。不加这个条件扫描 2w 条数据
justfindu
2022-09-15 14:04:25 +08:00
试试去掉 order by, 也可以试试 select * from idin(select id.....order by id limit 32)
yangxx
2022-09-15 14:10:35 +08:00
有一种可能,5.7 走的是主键索引,条件里还有一个 id>?,这个值在 b 列过滤出来的行可能比较靠后。b 列过滤出来 100 多万行,数据量不少
yiplee
2022-09-15 14:13:55 +08:00
@YIsion #31 啊还会这样啊,但是 id > ? 这个条件必须得留着
yiplee
2022-09-15 14:15:08 +08:00
@justfindu #32 先走索引覆盖取出 id ,再 join 原表,是个不错的思路。但是我这个查询条件里面有 c ,会导致无法触发索引覆盖 😂
yiplee
2022-09-15 14:16:58 +08:00
@yangxx #33 我了解到 MySQL 的二级索引在尾部是包含主键的,也就是 INDEX( b ) = INDEX( b ,id ),b = ? AND id > ? 应该都用到的索引才对吧!?
5boy
2022-09-15 14:17:36 +08:00
force index (b),数据库执行时可能会用错索引
rrfeng
2022-09-15 14:17:59 +08:00
"rows": 1519240, 然后需要按 id 重新排序

主要原因还是 b 命中太多了
次要原因是需要 id 排序,在 b 索引下 id 有序性无法保证,除非你创建 b+id 联合索引
yiplee
2022-09-15 14:24:01 +08:00
@rrfeng #38 突然意识到我对 innodb 的二级索引理解错了,我一直以为 INDEX( b ) = INDEX( b ,id ),实际上 id 是保存在 INDEX( b ) 的叶子节点上值,并不是有序的,所以无法用于 ORDER BY ,不知道这次理解对没 😂
buster
2022-09-15 17:08:32 +08:00
看了各位大佬的分析,我觉得应该跟 C isnotnull 有关,这里可以试一下在 8 的版本下,把这个条件去掉看下执行速度的变化。

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

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

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

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

© 2021 V2EX