开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共700多人左右 1 + 2)。
最近我们在使用MYSQL 8 的情况下(8.025) 在数据库运行中出现一个问题 参数prefer_order_index 的问题, 熟悉 MYSQL的同学看到这个参数估计都是恨的牙根痒痒,这个参数的想法是好的,仅仅是限于想法是好的。
数据库中的优化器的工作就是选择更好的执行的方式来将SQL语句给出最好的方案来执行,那么评估一个SQL的好坏的方式就是代价cost 最小或者尽可能的小, cost 小了,则我们可以认为是 IOPS, CPU 的消耗都会减少,这就是我们希望看到的。
那么怎么能这样,回答可能是扫描的行数要少,那么用这个思路我们串一下。
MYSQL的优化器判断语句执行路径---- 评判执行计划的COST---- 扫描的行数少----成本低--- 推出执行计划
所以这期就的说说在这样的优化思路下的 prefer_order_index 的参数在MYSQL 8 默认开启后的问题。
这个参数如果大家细心的情况下,在我们的MYSQL 5.7 也有这个参数,而且这个参数是关闭的见上图。先说说这个参数的历史,这个参数是在 MYSQL5.7.33 版本上产生的,目的就是为了要提高整体的SQL 在运行ORDER BY LIMIT GROUP BY 时,可以选择有序索引提高数据排序的的速度和 提高语句执行的效率。但是基于一贯的风格,这个参数虽然在MYSQL 5.7.33 中推出但默认是关闭的所以当时的使用这个版本的应用并未受到影响,而这个参数发布的版本也是MYSQL5.7 最靠后的版本,所以这边版本本身就使用的人少。
而后到了MYSQL8.X 这个参数得到了进化,除了对ORDER BY 和 GROUP BY 进行优化的后,还加入了 limit 的考虑的元素, 并且这个一开始的设定就是参数开启的状态,这个参数实际上在 8.021 的这个版本上重新启用了,并且一开始默认是开启的,这样一搞,就出现很多的SQL 运行时的故障。
我们看下面这个例子
一个1000万的表,然后我们有相关的主键和索引,我们对这个表发起下面的查询方式。
在打开这个参数后,用下面的语句来进行运算,则
mysql> set optimizer_switch = "prefer_ordering_index=on";
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user where age >= 20 and age < 50 order by id limit 10;
mysql> set optimizer_switch = "prefer_ordering_index=off";
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t_user where age >= 20 and age < 50 order by id limit 10;
这里明显的结果是打开这个参数后,运行的结果会很快给出,但是关闭后,性能会较低。
所以在这样的情况下,可以证明打开对于数据查询的性能是有提高的,但是如果我们针对一些特殊的场景来看,这个参数打开后,会导致同一个SQL 部分SQL 稍许变动后,导致的执行计划不稳定的情况。
mysql> set optimizer_switch = "prefer_ordering_index=on";
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql>
mysql> explain select * from t_user where age > 20 and age < 22 order by id limit 10,20;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t_user | NULL | index | idx_age | PRIMARY | 8 | NULL | 604 | 4.96 | Using where |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> explain select * from t_user where age > 20 and age < 22 order by id limit 10000,10010;
+----+-------------+--------+------------+-------+---------------+---------+---------+------+-------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+-------+----------+---------------------------------------+
| 1 | SIMPLE | t_user | NULL | range | idx_age | idx_age | 2 | NULL | 49484 | 100.00 | Using index condition; Using filesort |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+-------+----------+---------------------------------------+
1 row in set, 1 warning (0.00 sec)
而在MYSQL 曾经提交的BUG 中也有这个部分的身影,所以 prefer_order_index 的参数是一个双刃剑,如果你打开了这个参数,那么如果你发现一些语句突发性的变慢,不要慌张,先看看是不是这个参数打开导致的问题,尤其是 order by limit 的SQL。
如果你想更清楚问题可以查找下面的两个BUG OF MYSQL
Bug#74602: Optimizer prefers wrong index because of low limit
Bug#78612: Optimizer chooses wrong index for ORDER BY