<select resultMap="App" parameterClass="java.lang.Long"> select <include refid="fileds"/> from table_a where id = #id# and type = 2 and state != 0 </select>
排查問題
那麼到底MySQL在沒有指定排序欄位以及排序順序時是按照什麼規則排序呢?最早猜測是按照主鍵排序,即在table_a表中按照id欄位進行降序排列。那麼到底是否正確呢?為了驗證猜測,在本地透過Navicat8對這個猜測進行了驗證:SELECT * FROM test_a查詢結果:
SELECT * FROM tbl -- this will do a "table scan". If the table has never had any DELETEs/REPLACEs/UPDATEs, the records will happen to be in the insertion order, hence what you observed. If you had done the same statement with an InnoDB table, they would have been delivered in PRIMARY KEY order, not INSERT order. Again, this is an artifact of the underlying implementation, not something to depend on.大概意思是說在使用預設的儲存引擎(MyISAM)進行全表查詢時,會進行表掃描,如果表沒有進行過刪除、替換以及更新操作,那麼查詢結果將按照插入順序進行排序,如果在使用InnoDB作為儲存引擎執行相同的操作時,查詢結果將按照主鍵進行排序,而不是按照插入順序進行排序的。
今天在review程式碼檢視SQL語句時突然對沒有指定排序方式的SQL語句返回怎樣的排序結構感興趣!在某些場景中返回結果中的元素到底是按照什麼順序以什麼欄位排序的將直接影響到整個業務是否正確。例如myBatis中語句如下:
<select resultMap="App" parameterClass="java.lang.Long"> select <include refid="fileds"/> from table_a where id = #id# and type = 2 and state != 0 </select>
排查問題
那麼到底MySQL在沒有指定排序欄位以及排序順序時是按照什麼規則排序呢?最早猜測是按照主鍵排序,即在table_a表中按照id欄位進行降序排列。那麼到底是否正確呢?為了驗證猜測,在本地透過Navicat8對這個猜測進行了驗證:SELECT * FROM test_a查詢結果:
發現查詢結果並沒有按照猜測的方式進行排序,並且查詢結果沒有任何規律可循。這樣看來如果按照上述的方式處理業務是很有可能出現問題的。仔細想了一下,這個問題應該和資料庫儲存引擎有關係,然後再網上查閱了一下這個問題,發現各種說法都有,但是初步驗證了這個問題確實和資料庫儲存引擎有關係,不同的資料庫引擎對預設排序的處理是不一致的,但是都沒有統一的答案,因此決定測試一下常用的兩種資料庫引擎:InnoDB以及MyISAM,簡單介紹一下兩個資料庫引擎:
簡單點說就是InnoDB的優點是支援事務,支援非鎖定讀,行鎖設計,以及全文索引,MyISAM的優點是操作速度快,不支援事務以及行鎖,是MySQL預設的儲存引擎。
在本地建立兩個表a,b,兩個表唯一的區別在於a使用MyISAM作為儲存引擎,b使用InnoDB作為儲存引擎,然後兩個表均忘表中插入100條資料。在插入資料後對量表分別進行無排序的全表查詢結果如下:
(表a:MyISAM)(表b:InnoDB)
從查詢結果上來看兩個表是否都按照id進行升序排列,與table_a表查詢結果順序完全不一致,檢視一下開發環境的table_a表的儲存引擎為MyISAM,與表a一致,但是查詢結果卻沒有任何相似的地方,是什麼影響了table_a表的查詢結果呢?難道是各種增刪改查操作?因為兩個相同引擎的表都進行無排序的全表查詢唯一區別是表a沒有進行過任何的更新刪除操作,表table_a進行過大量的增刪改查操作,於是對錶a以及表b均模擬進行大量的增刪改查操作,然後再次進行查詢:
(表a:MyISAM)(表b:InnoDB)
發現表a查詢結果出現了無序情況,表象與表table_a一致,而表b查詢結果仍然按照id進行升序排列。
因此初步判定在MySQL中,如果使用MyISAM作為儲存引擎,那麼在進行預設排序的查詢時,如果表沒有進行過任何的更新刪除等操作,那麼查詢結果將按照出入順序進行升序排列,但是如果對錶進行了更新刪除等操作後,查詢結果將是無序的;如果使用InnoDB作為儲存引擎,那麼在存在主鍵的情況下,查詢結果將按照主鍵進行排序。因此在編寫預設排序的SQL語句的時候,一定要注意使用的儲存引擎,不同的儲存引擎可能產生的查詢結果完全不一致。
猜想原因:應該和兩種儲存引擎採用的儲存結構有關係,在查閱資料以後發現InnoDB採用的是聚簇索引表,而MyISAM採用的是無序的堆表結構。最後在MySQL的官方網頁上發現了對該問題的一個描述:
SELECT * FROM tbl -- this will do a "table scan". If the table has never had any DELETEs/REPLACEs/UPDATEs, the records will happen to be in the insertion order, hence what you observed. If you had done the same statement with an InnoDB table, they would have been delivered in PRIMARY KEY order, not INSERT order. Again, this is an artifact of the underlying implementation, not something to depend on.大概意思是說在使用預設的儲存引擎(MyISAM)進行全表查詢時,會進行表掃描,如果表沒有進行過刪除、替換以及更新操作,那麼查詢結果將按照插入順序進行排序,如果在使用InnoDB作為儲存引擎執行相同的操作時,查詢結果將按照主鍵進行排序,而不是按照插入順序進行排序的。