前言

这个问题的话不仔细看容易产生误区,在我们对MySQL常识性认识的前提下,我们肯定认为SQL一样时,返回的结果一定是一样的,当然我曾经也是这么认为的,注意这里说的SQL一样时指SQL的字符串一模一样, 一个空格不多一个空格不少! 这里先说一下结论,当SQL一毛一样的时候,排序字段一致时,返回的结果顺序不一定一样,这个问题的产生是在我重构公司系统的权限时产生的,由于业务比较复杂,而且业务线跨度比较大,在对SQL数据处理的时候分外注意,在SQL更改前后做数据对比的时候,发现返回的数据结果集不一致,当时以为是重构代码出现了问题,然后断点调试到SQL的时候,纵向对比之前的SQL执行的结果发现,同样的SQL执行返回的数据顺序不一致,但是这个不一致是偶现的,有时候跑10个请求SQL执行的结果都是一样的,当时就有点怀疑人生了。然后接着执行,又偶然出现顺序不一致的情况,这时联想到MyBatis的缓存,于是把SQL单独拿出来到MySQL上执行,发现这种问题是存在的,而且是随机的。背后原理如下!

MySQL乱序问题

在我们写排序的时候很多情况下是通过创建时间来降序、或者升序的方式排列,但是创建时间这玩意很容易重复,一旦排序字段重复那么就会出现多次查询返回的结果顺序不一致的情况,尤其是在分页的场景下很容易导致数据上下页重复数据问题,那么解决这种问题我们可以在根据创建时间排序后再根据主键进行排序一下,那么由于主键具有唯一性,不可能出现重复,所以查询结果集也就可以固定固定下来

原因
首先这种乱序问题是MySQL故意设计的,如果没有指定ORDER BY语句,则SQL Server(或任何RDBMS)不保证以特定顺序返回结果。 有些人认为,如果没有指定order by子句,行总是以聚簇索引顺序或物理磁盘顺序返回。 然而,这是不正确的,因为在查询处理期间可以改变行顺序的许多因素,例如并行的HASH连接是更改行顺序的操作符的一个很好的例子。如果指定ORDER BY语句,SQL Server将对行进行排序,并按请求的顺序返回。 但是,如果该顺序不是确定性的,即可能有重复的值,则在每个具有相同值的组中,由于与上述相同的原因,该顺序是“随机的”

深入探索

虽然上面提到乱序是MySQL故意设计的,但是为什么有时候一连执行好几遍返回的结果又都是一样的呢?这是由于MySQL缓存的问题。这里在文章开头有提到一模一样, 一个空格不多一个空格不少! 的SQL,这样的SQL查询会被MySQL缓存起来!(注意,只有SQL字符串一模一样)所以这也就能解释为什么有时候一连执行好几遍SQL返回的结果顺序有是一样的问题了。

查看查询缓存情况:

mysql> show variables like '%query_cache%'; 
(query_cache_type 为 ON 表示已经开启)
+------------------------------+----------+
| Variable_name | Value |
+------------------------------+----------+
| have_query_cache | YES |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 20971520 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
+------------------------------+----------+

这里各个属性从字面意思也好理解,我这就不过多解释了,query_cache_type代表的是缓存是否开启!