做爲程序員,常常寫 SQL 語句是正常不過了。然而,編寫一些 SQL 語句,總會出現一些奇怪的問題。程序員
最近在項目中遇到一個很神奇的問題,MySQL 使用 order by 進行排序並進行分頁的時候,會出現部分數據丟失和重複。具體看下面這三張圖算法
其中,sql
仔細看我用紅色標記出來的,能夠發現,分類11 的數據在分頁後查詢不出來,而分類18 則出現了兩次。很明顯的發現,當進行數據分頁時,部分數據出現了丟失和重複。數據庫
在 MySQL 關係型數據庫中,每每會存在多種排序算法。經過 MySQL 的源碼和官方文檔介紹能夠得知,它的排序規律能夠總結以下:ide
根據上面的總結,當 order by limit 分頁出現數據丟失和重複。而 order by 的 sort 字段沒有使用索引(正常狀況下,排序的字段也不會使用索引),若是使用了索引,則會進行索引排序。優化
所以能夠得出,上面的圖二和圖三的 SQL 語句使用了堆排序。由於 sort 字段沒有索引,因此沒走索引排序;而且使用了 limit。致使最終使用了堆排序。.net
若是瞭解算法的你,應該知道堆排序是不穩定的。這種不穩定性,指的就是屢次排序後,各個數的相對位置發生了變化。code
可是,不是全部的 MySQL 版本都是這樣。從 MySQL 5.6 版本開始,優化器在使用 order by limit 時,作了上面的優化,致使排序字段沒有使用索引時,使用堆排序。排序
經過上面的分析,有兩種解決方案能夠解決此問題。索引
在圖2、圖三中,增長主鍵 category_id 字段排序後,就不會出現數據丟失和重複了。
若是查詢數據進行排序和分頁時,若是排序字段沒有使用索引,必定要添加一個有索引的字段,好比主鍵 ID,保證順序穩定。不然,查詢的數據會致使數據丟失和重複。
理解此問題出現的緣由後,趕忙去看看你的項目中有沒有這種狀況吧!要否則出問題就很差辦了!
最後,附上新建表和表相關數據的 SQL 語句:
DROP TABLE IF EXISTS `sys_category`; CREATE TABLE `sys_category` ( `category_id` bigint NOT NULL AUTO_INCREMENT, `category_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '分類名稱', `sort` int DEFAULT NULL COMMENT '分類排序', PRIMARY KEY (`category_id`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8; INSERT INTO `sys_category` VALUES (1,'分類1',1),(2,'分類2',2),(3,'分類3',20),(4,'分類4',21),(5,'分類5',22),(6,'分類6',23),(7,'分類7',0),(8,'分類8',0),(9,'分類9',0),(10,'分類10',0),(11,'分類11',0),(12,'分類12',0),(13,'分類13',0),(14,'分類14',0),(15,'分類15',0),(16,'分類16',0),(17,'分類17',0),(18,'分類18',0);