缺陷的背后---LIMIT M,N 分頁查找

? ? 一、問題發現篇

? ? ? ? 最近組內做了一次典型缺陷分享時,翻閱2018年的缺陷,找到了一個讓我覺得“有料”的bug(別的同事測試發現的),先大致簡單的描述下這個問題:

  • ? 需要實現的功能:從一個DB庫同步某一段時間的數據到另一個DB庫(簡化后的需求)。
  • ? 問題描述:一次同步20w條符合記錄的數據,程序同步完成后,丟數據5條。
  • ? 問題定位:加載數據的sql,考慮到數據量大,使用了limit M,N的方法來分頁加載數據,大致如下:

    ? ? ? ? ? ? ? ? ? select ?* ?from test where Fmodify_time?>= '2018-12-12 05:00:00'and Fmodify_time <= '2018-12-12 05:01:00' limit 0,2; ? ? ??

? ? ? ? ? 開發哥哥定位問題產生的原因是因為20w的數據里,存在大量的Fmodify_time?值相同的記錄,由于未排序,導致這類數據服務器返回時會隨機選擇,從而導致分頁取出來的數據存在重復或者丟失的問題。然后解決的辦法就是,加上order by?Fmodify_time 就能滿足,因為Fmodify_time 是一個索引,索引排序是有序的。后面測試小哥用相同的數據驗證不丟數據,事情就告一段落了。

? ? ?

? ? ?二、問題驗證和解決篇

? ? ? ? ? 巧著,18年下半年花了2個月時間在學習mysql,在學習limit 分頁查找的官方文檔里有看到這么一段話:https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

    (1)如果在使用order by列中的多個行具有相同的值,則服務器可以按任何順序自由返回這些行,并且可能根據整體執行計劃的不同而不同。換句話說,這些行的排序順序相對于無序列是不確定的。(2)影響執行計劃的一個因素是 LIMIT,因此ORDER BY 使用和不使用查詢LIMIT可能會返回不同順序的行。如果確保使用和不使用相同的行順序很重要,請LIMIT在ORDER BY子句中包含其他列以使記錄具有確定性,
比如使用主鍵。

? ? ? ? ? 那這么說,之前開發說通過order by 索引能使得結果返回有序就是有問題的了,帶著這個疑問和官方文檔的demo,結合實際的這個缺陷,做了個小驗證。

? ? ? ? ? (1)建表如下:? ? 主鍵:id,索引:create_time

CREATE TABLE `test` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`),KEY `index_time` (`create_time`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1

? ? ? ? ? (2)插入數據如下:

?

? ? ? ? (3)模擬分頁查找,不使用order by。結果如下:

? ? ? ? ?測試結果:丟數據id=1的記錄,id=7的數據重復導致。看來這個問題是真的存在的。那按照開發哥哥的解決方法加上order by?create_time就沒問題了?

? ? ? (4)模擬分頁查找,使用order by +?create_time(索引),如下:

? ? ? ? ? ? 測試結果:丟數據id=7的記錄,id=3的數據重復導致。看來加了索引排序,還是有問題的,開發哥哥的“解決方法”實際是不能解決問題的。

? ? ? ? ? (5)模擬分頁查找,使用order by +?create_time(索引)+ id(主鍵),如下:

? ? ? ? ?測試結果:未丟數據。

? ? ? ? 分頁查找limit M,N 的基本原理,就是服務器查找M+N條,然后返回從M條開始的后N條,導致上面問題出現的本質原因,還是mysql服務器查詢后的數據排序不穩定導致。同小節的官方文檔有說明當LIMIT row_count 和ORDER BY一起使用的時候,mysql不會對整個結果集排序,而是只對row_count 行的數據進行排序然后就返回。

     If you combine LIMIT row_count with ORDER BY, MySQL stops sorting as soon as it has found the first row_count rows of the sorted result, rather than
sorting the entire result. If ordering is done by using an index, this is very fast. If a filesort must be done, all rows that match the query
without the LIMIT clause are selected, and most or all of them are sorted, before the firstrow_count are found. After the initial rows have been
found, MySQL does not sort any remainder of the result set.

? ? ? ? 三、未解之謎篇

? ? ? ? ?大致的原理已經理清了,但是還有幾個問題是沒想明白的:

? ? ? ? ?問題一: 如果構造測試數據的時,把id =1的記錄,時間2018-12-12 05:01:00?修改為2018-12-12 04:59:00,不加order by 都不會出現丟數據和重復數據的問題:

? ? ? ? ? ? ?這是為什么?數據的不同對結果是有影響的!!!那測試的時候數據要怎么構造呢?才能保證輸入的數據都是有代表的呢?這個現象背后的原理是什么呢?

??問題二:使用索引排序 order by+索引,是不穩定的?

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

?2019.01.28 問題跟進

問題二:使用索引排序 order by+索引,是不穩定的?

? ? ? ? ? ? ? 這個問題有“問題“,雖然order by 和 where 條件語句都是使用了index,但是并不確定所有的分頁查找的sql都是使用索引排序。比如:

? ? ? ??從第二個sql開始,執行計劃就已經開始出現Using where; Using filesort ,跟之前第一條sql的執行計劃是完全不一樣的,?第二條sql之后就不能利用索引來避免排序filesort并不意味著就是文件排序,其實也有可能是內存排序,這個主要由sort_buffer_size參數與結果集大小確定。MySQL內部實現排序主要有3種方式,常規排序,優化排序和優先隊列排序,主要涉及3種排序算法:快速排序、歸并排序和堆排序。

? ? ? ? 在學習其他文檔了解到,在5.5版本中沒有這個問題。產生這個現象的原因就是5.6針對limit M,N的語句采用了優先隊列,而優先隊列采用堆實現,使用的是堆排序算法,比如上述的例子order by create_time limit 4,2 limit 0,2 需要采用大小為2的大頂堆;limit 2,4需要采用大小為4的大頂堆。堆排序是非穩定的(對于相同的key值,無法保證排序后與排序前的位置一致),所以導致分頁重復的現象。而解決這個問題的有效方法就是可以在排序中加上唯一值,比如主鍵id。

? ? ? ?那為什么第一條執行sql的時候Extra =?Using index condition,不需要訪問表,直接拿數據呢? 后面sql執行全部都是Extra =?Using where; Using filesort ,估計就是跟sort_buffer_size參數有關

? ? ? ?學習參考文檔:《數據庫內核月報 - 2015 / 06》 鏈接:http://mysql.taobao.org/monthly/2015/06/04/

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?《MySQL排序原理》 https://blog.csdn.net/eagle89/article/details/81315981

?

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

?優化order by?方法:

?1 加大 max_length_for_sort_data 參數的設置
??在 MySQL 中,決定使用老式排序算法還是改進版排序算法是通過參數 max_length_for_ sort_data 來決定的。當所有返回字段的最大長度小于這個參數值時,MySQL 就會選擇改進后的排序算法,反之,則選擇老式的算法。所以,如果有充足的內存讓MySQL 存放須要返回的非排序字段,就可以加大這個參數的值來讓 MySQL 選擇使用改進版的排序算法。

2 去掉不必要的返回字段

??當內存不是很充裕時,不能簡單地通過強行加大上面的參數來強迫 MySQL 去使用改進版的排序算法,否則可能會造成 MySQL 不得不將數據分成很多段,然后進行排序,這樣可能會得不償失。此時就須要去掉不必要的返回字段,讓返回結果長度適應 max_length_for_sort_data 參數的限制。

3 增大 sort_buffer_size 參數設

? 這個值如果過小的話,再加上你一次返回的條數過多,那么很可能就會分很多次進行排序,然后最后將每次的排序結果再串聯起來,這樣就會更慢,增大 sort_buffer_size 并不是為了讓 MySQL選擇改進版的排序算法,而是為了讓MySQL盡量減少在排序過程中對須要排序的數據進行分段,因為分段會造成 MySQL 不得不使用臨時表來進行交換排序。
但是這個值不是越大越好:
1 Sort_Buffer_Size 是一個connection級參數,在每個connection第一次需要使用這個buffer的時候,一次性分配設置的內存。
2 Sort_Buffer_Size 并不是越大越好,由于是connection級的參數,過大的設置+高并發可能會耗盡系統內存資源。
3 據說Sort_Buffer_Size 超過2M的時候,就會使用mmap() 而不是 malloc() 來進行內存分配,導致效率降低。

?

?

??

轉載于:https://www.cnblogs.com/loleina/p/10319315.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450047.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450047.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450047.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android應用開發—Intent組件詳解

轉載自&#xff1a;Android中Intent組件詳解 Intent是不同組件之間相互通訊的紐帶&#xff0c;封裝了不同組件之間通訊的條件。 Intent本身是定義為一個類別(Class)&#xff0c;一個Intent對象表達一個目的(Goal)或期望(Expectation)&#xff0c;敘述其所期望的服務或動作、與…

angularjs 結構的兩種寫法(2)

app.js里面 route.js 本項目中的路由寫法,路由的意思是&#xff1a;對應的跳轉頁面路徑&#xff0c;比如此處當路由是member.user-statisttic時&#xff0c;是會跳轉到url&#xff1a;http://.../user-statisttic.html頁面&#xff0c;然后此頁面對應的ctrl會解析也頁面的參數。…

Linux指令小記(簡明實用)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1、ls指令,用于列出當前目錄的文件 通過添加-l參數可以使文件以詳情模式列出 通過添加-a參數可以將包含隱藏文件在內的全部文件列出。…

python之glob的用法

目錄 獲取特定擴展名的所有文件 獲取特定目錄下的所有文件 遞歸獲取所有文件 轉義特殊字符 iglob glob 是 Python 中用于文件模式匹配的一個模塊。它使用 Unix shell-style 的通配符來進行匹配&#xff0c;并返回所有匹配的文件路徑列表。 下面是一些 glob 的基本用法&am…

設置Eclipse中的字符集為UTF-8

Eclipse 修改字符集 默認情況下 Eclipse 字符集為 GBK&#xff0c;但現在很多項目采用的是 UTF-8&#xff0c;這是我們就需要設置我們的 Eclipse 開發環境字符集為 UTF-8&#xff0c; 設置步驟如下&#xff1a; 在菜單欄選擇 Window -> Preferences -> General -> Wor…

換位思考的最高境界是換待遇,所以,換位思考就是空話!!!

換位思考的最高境界是換待遇&#xff0c;所以&#xff0c;換位思考就是空話&#xff01;&#xff01;&#xff01; 換位思考是件說難也難&#xff0c;說容易也容易的事情。 如果你肯把你的工資待遇財富地位跟李彥宏互換一下&#xff0c;你就可以輕松理解他到底為什么非要在百度…

一個記錄最近搜索歷史的LRU實現

對于很多有搜索需求的功能&#xff0c;一般需要展示下最近n次的歷史搜索記錄&#xff0c;主要有以下幾個功能點&#xff1a; 最近搜索條目放在最前面&#xff0c;最早的搜索記錄放在最后。只記錄最近n條數據&#xff0c;如果超過n條搜索記錄&#xff0c;刪除搜索時間最久遠的記…

Python數據可視化2018:數據可視化庫為什么這么多?

本文最初發布于Anaconda開發者博客&#xff0c;經原作者授權由InfoQ中文站翻譯并分享。 在奧斯汀舉行的SciPy 2018年特別會議上&#xff0c;大量開源Python可視化工具的代表分享了他們對Python數據可視化未來的展望。我們看到了Matplotlib、Plotly、VisPy等許多庫的更新。我作為…

Oulipo (KMP出現次數)

The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter e. He was a member of the Oulipo group. A quote from the book: Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis su…

從夫妻吵架中看項目管理

從夫妻吵架中看項目管理&#xff08;很有意思的文章&#xff09; 首先要說明&#xff1a;和老婆吵架無論原因如何&#xff0c;無論結果如何你都是錯的&#xff0c;老婆永遠是對的。但是我不是神仙&#xff0c;偶爾也要吵架。但是如何讓吵架也發揮作用&#xff0c;增進夫妻感情&…

SpringMVC工作原理

大家好&#xff0c;我是IT修真院深圳分院第十一期學員&#xff0c;一枚正直純潔善良的JAVA程序員。 今天給大家分享一下&#xff0c;修真院官網JAVA任務二的一個知識點&#xff1a;SpringMVC工作原理 1、背景介紹 一&#xff1a;背景介紹 JavaWeb經歷的幾個變化&#xff1a; 1:…

Android應用開發—如何解決handler的警告:Handler Class Should be Static or Leaks Occur

轉自android handler的警告Handler Class Should be Static or Leaks Occur 在使用Handler更新UI的時候&#xff0c;我是這樣寫的&#xff1a; public class SampleActivity extends Activity {private final Handler mLeakyHandler new Handler() {Overridepublic void hand…

從遠程(包括ftp,http等協議)地址獲取文件流信息

URL url new URL("ftp://172.18.251.155:8010/recordsImg/2019-01-28/000008_1548649813267.jpg"); MultipartFile multipartFile new MockMultipartFile(fileName,fileName,"", url.openStream());轉載于:https://www.cnblogs.com/baihaojie/p/10331134…

shell 數組

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1&#xff09;定義數組&#xff1a; my_array(1 2 3 4) 也可這樣賦值&#xff1a;my_array[4]愛 讀取&#xff1a; echo ${my_array[2]…

nodejs 實現文件拷貝

通過4中不通的方式實現對一個文件的拷貝 方式一&#xff1a;readFile 異步一次性讀取文件再寫入 //異步形式讀取文件 function copyFile(url){const extName path.extname(url)const fileName path.basename(url)const dirName path.dirname(url)fs.readFile(url, (err, dat…

國家部委對4G調研:未定給中電信聯通發放牌照

一場有關4G牌照發放的論戰正在發酵&#xff0c;矛盾的核心在于&#xff0c;除了中移動外&#xff0c;政府是否也會向中電信和聯通發放TD-LTE(中國主導的4G標準)牌照 記者 王云輝 雍忠瑋 一場圍繞4G的新博弈已經白熱化。 “多個國家部委正在對4G展開全面調研&#xff0c;但最終如…

Luogu4735 最大異或和

題目藍鏈 Description 給你一個序列&#xff0c;你需要支持以下兩個操作&#xff1a; A x: 在序列尾部添加一個整數\(x\)&#xff0c;序列的長度增加\(1\)Q l r x: 詢問操作&#xff0c;你需要找到一個位置\(p \in [l, r]\)&#xff0c;使得&#xff1a;\(x \bigoplus a_p \big…

Spring-jdbc:JdbcTemplate使用簡介

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 為了使 JDBC 更加易于使用,Spring 在 JDBCAPI 上定義了一個抽象層, 以此建立一個JDBC存取框架. 作為 SpringJDBC 框架的核心, JDBC 模板…

Java多線程編程:變量共享分析(Thread)

Java多線程編程&#xff1a;變量共享分析&#xff08;Thread&#xff09; Java 創建線程的兩種方法 此處只簡單講下自己對java多線程變量共享的理解&#xff1a; 按照進程和多線程的原理&#xff0c;同一進程內的多個線程之間的地址空間是共享的&#xff08;除去ThreadLocal&a…