MySQL GROUP BY 隐式排序

前言

在MySQL的老版本(MySQL5.7及以前)中, GROUP BY xxx 等同于 GROUP BY xxx ORDER BY xxx,在 GROUP BY 的同时,会自带排序的效果。包括也可以手动排序 GROUP BY xxx DESC,等同于 GROUP BY xxx ORDER BY xxx DESC

MySQL8.0 版本删除了隐式排序,包括显示排序也报错。在升级到MySQL8.0之后,由于缺少了隐式排序,可能会造成查询结果返回的顺序不稳定,造成结果不满足预期。

问题现象

  1. aliyun PolarDB MySQL 版 8.0.1.1.40版本,在DMS查询时,返回的顺序稳定
  2. 在程序JDBC查询时(连接的集群地址),返回的结果多次查询顺序不一致。

由于我们使用的是PolarDB 8.0+,首先开始怀疑是不是aliyun做了什么操作,查到他们的更新日志:https://help.aliyun.com/zh/polardb/polardb-for-mysql/polardb-for-mysql-8-0-1,发现他们在8.0版本中,将隐式排序的功能又加了回来,那么更进一步证明问题出现在数据库层面了。

有了上面的分析和更新日志,我们又进行了接下来的几次测试,进一步缩小问题范围,定位问题的根本原因。

  1. 通过MySQL命令行,连接到集群地址,返回的结果多次查询顺序不一致。
  2. 通过MySQL命令行,连接到主节点地址,返回的顺序稳定
  3. 通过MySQL命令行,连接到只读从节点地址,返回的结果多次查询顺序不一致。

通过咨询aliyun工程师,了解到DMS默认是直连主库的,结合上述三个执行结果来看,似乎问题出现在了从库,当请求从从库执行来看,顺序总是不一致。

有了上述的结论,通过进一步咨询aliyun的工程师,最终定位到问题是因为其中有台从库开启了并行查询,导致了返回顺序会存在多次查询不一致的问题。


总结

虽然最终定位到了原因是并行查询导致的问题,但是我还是认为,既然aliyun的版本公告中已经说明了加回了 GROUP BY 隐式排序功能,那么不管是并行查询还是什么原因,就算是在 MySQL Server层进行汇总排序,也需要保证最终结果和预期一致,否则会造成使用方的预期不满足。
其次,对于隐式排序肯定不能进行依赖,还是回归到程序上,不论什么情况肯定不能依赖于约定俗成的一些功能,应该强依赖自己写下的指令,比如手动指定 ORDER BY 比事后去分析还是好很多。但整个过程还是学习到不少东西,还是有一定收获的。

扩展

旧版本为什么会有排序?

因为要对数据进行分组的话,本身也会对分组前的数据进行排序,降低数据复杂度,所以原始数据本身就已经有序了。
其次,如果是 GROUP BY 的一个索引字段,那么索引字段本身是 B+ Trree,本身也是有序的,且平时 GROUP BY的字段大概率是有索引的(否则效率太低了),所以绝大部分情况下,旧版本的 GROUP BY 就为我们保留了排序。

为什么要删除隐式排序?

会造成预期外的结果,比如没有明确指定