上线后,测试同学提了个 bug:他发现当他不分页,直接 ORDER BY points DESC 查看总榜单时,前 6 名的用户是 A, B, C, D, E, F。但是用上面的分页接口查出来的结果却是 A, B, C, D, G, H。
我人都麻了 😵。为什么加了个 LIMIT,排序结果就跟总排序不一致了?这俩用户 G 和 H 的积分跟 E 和 F 明明是一样的啊!
这两个场景让我陷入了沉思,同样的问题反复出现,绝对不是偶然。问题核心就是:
在排序字段有重复值的情况下,ORDER BY ... LIMIT ... 的分页查询结果,与不带 LIMIT 的总排序结果不一致,导致分页数据出现重复或错乱。
我是如何解决的 💡
在经历了初步的自我怀疑(“是不是我 SQL 写错了?”)和甩锅(“是不是数据库有 Bug?”)之后,我决定冷静下来,直面问题的根源。我开始怀疑,是不是 ORDER BY 和 LIMIT 一起用的时候,有什么我不知道的“潜规则”?
于是,我请出了我的终极武器——官方文档。功夫不负有心人,在 MySQL 的文档里,我找到了下面这段话,简直是醍醐灌顶!
If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.
One factor that affects the execution plan is LIMIT, so an ORDER BY query with and without LIMIT may return rows in different orders.