亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》
  • 首頁 > 
  • 教程 > 
  • 數據庫 > 
  • 如何解決千萬級數據表選錯索引導致的線上慢查詢事故

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

發布時間:2021-10-22 17:08:02 來源:億速云 閱讀:265 作者:iii 欄目:數據庫

這篇文章主要講解了“如何解決千萬級數據表選錯索引導致的線上慢查詢事故”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何解決千萬級數據表選錯索引導致的線上慢查詢事故”吧!

故障描述

在7月24日11點線上某數據庫突然收到大量告警,慢查詢數超標,并且引發了連接數暴增,導致數據庫響應緩慢,影響業務。看圖表慢查詢在高峰達到了每分鐘14w次,在平時正常情況下慢查詢數僅在兩位數以下,如下圖:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

趕緊查看慢SQL記錄,發現都是同一類語句導致的慢查詢(隱私數據例如表名,我已經隱去):

select   * from   sample_table where     1 = 1     and (city_id = 565)     and (type = 13) order by   id desc limit   0, 1

看起來語句很簡單,沒什么特別的。但是每個執行的查詢時間達到了驚人的44s。

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

簡直聳人聽聞,這已經不是“慢”能形容的了...

接下來查看表數據信息,如下圖:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

可以看到表數據量較大,預估行數在83683240,也就是8000w左右,「千萬數據量的表」。

大致情況就是這樣,下面進入排查問題的環節。

問題原因排查

首先當然要懷疑會不會該語句沒走索引,查看建表DML中的索引:

KEY `idx_1` (`city_id`,`type`,`rank`), KEY `idx_log_dt_city_id_rank` (`log_dt`,`city_id`,`rank`), KEY `idx_city_id_type` (`city_id`,`type`)

請忽略idx_1和idx_city_id_type兩個索引的重復,這都是歷史遺留問題了。

「可以看到是有idx_city_id_type和idx_1索引的」,我們的查詢條件是city_id和type,這兩個索引都是能走到的。

但是,我們的查詢條件真的只要考慮city_id和type嗎?(機智的小伙伴應該注意到問題所在了,先往下講,留給大家思考)

既然有索引,接下來就該看該語句實際有沒有走到索引了,MySQL提供了Explain可以分析SQL語句。Explain 用來分析 SELECT  查詢語句。

Explain比較重要的字段有:

  • select_type : 查詢類型,有簡單查詢、聯合查詢、子查詢等

  • key : 使用的索引

  • rows : 預計需要掃描的行數

更多詳細Explain介紹可以參考:MySQL 性能優化神器 Explain 使用分析

我們使用Explain分析該語句:

select * from sample_table where city_id = 565 and type = 13 order by id desc limit 0,1

得到結果:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

可以看出,雖然possiblekey有我們的索引,但是最后走了主鍵索引。而表是千萬級別,「并且該查詢條件最后實際是返回的空數據」,也就是MySQL在主鍵索引上實際檢索時間很長,導致了慢查詢。

我們可以使用force index(idx_city_id_type)讓該語句選擇我們設置的聯合索引:

select * from sample_table force index(idx_city_id_type)  where ( ( (1 = 1) and (city_id = 565) ) and (type = 13) ) order by id desc limit 0, 1

這次明顯執行的飛快,分析語句:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

實際執行時間0.00175714s,走了聯合索引后,不再是慢查詢了。

問題找到了,總結下來就是:「MySQL優化器認為在limit  1的情況下,走主鍵索引能夠更快的找到那一條數據,并且如果走聯合索引需要掃描索引后進行排序,而主鍵索引天生有序,所以優化器綜合考慮,走了主鍵索引。實際上,MySQL遍歷了8000w條數據也沒找到那個天選之人(符合條件的數據),所以浪費了很多時間。」

MySQL索引選擇原理

優化器索引選擇的準則

MySQL一條語句的執行流程大致如下圖,而「查詢優化器」則是選擇索引的地方:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

引用參考文獻一段解釋:

首先要知道,選擇索引是MySQL優化器的工作。

而優化器選擇索引的目的,是找到一個最優的執行方案,并用最小的代價去執行語句。在數據庫里面,掃描行數是影響執行代價的因素之一。掃描的行數越少,意味著訪問磁盤數據的次數越少,消耗的CPU資源越少。

「當然,掃描行數并不是唯一的判斷標準,優化器還會結合是否使用臨時表、是否排序等因素進行綜合判斷。」

總結下來,優化器選擇有許多考慮的因素:「掃描行數、是否使用臨時表、是否排序等等」

我們回頭看剛才的兩個explain截圖:

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

走了「主鍵索引」的查詢語句,rows預估行數1833,而強制走「聯合索引」行數是45640,并且Extra信息中,顯示需要Using  filesort進行額外的排序。所以在不加強制索引的情況下,「優化器選擇了主鍵索引,因為它覺得主鍵索引掃描行數少,而且不需要額外的排序操作,主鍵索引天生有序。」

rows是怎么預估出來的

同學們就要問了,為什么rows只有1833,明明實際掃描了整個主鍵索引啊,行數遠遠不止幾千行。實際上explain的rows是MySQL「預估」的行數,「是根據查詢條件、索引和limit綜合考慮出來的預估行數。」

MySQL是怎樣得到索引的基數的呢?這里,我給你簡單介紹一下MySQL采樣統計的方法。

為什么要采樣統計呢?因為把整張表取出來一行行統計,雖然可以得到精確的結果,但是代價太高了,所以只能選擇“采樣統計”。

采樣統計的時候,InnoDB默認會選擇N個數據頁,統計這些頁面上的不同值,得到一個平均值,然后乘以這個索引的頁面數,就得到了這個索引的基數。

而數據表是會持續更新的,索引統計信息也不會固定不變。所以,當變更的數據行數超過1/M的時候,會自動觸發重新做一次索引統計。

在MySQL中,有兩種存儲索引統計的方式,可以通過設置參數innodb_stats_persistent的值來選擇:

設置為on的時候,表示統計信息會持久化存儲。這時,默認的N是20,M是10。

設置為off的時候,表示統計信息只存儲在內存中。這時,默認的N是8,M是16。

由于是采樣統計,所以不管N是20還是8,這個基數都是很容易不準的。

我們可以使用analyze table  t命令,可以用來重新統計索引信息。但是這條命令生產環境需要聯系DBA,所以我就不做實驗了,大家可以自行實驗。

索引要考慮 order by 的字段

為什么這么說?因為如果我這個表中的索引是city_id,type和id的聯合索引,那優化器就會走這個聯合索引,因為索引已經做好了排序。

更改limit大小能解決問題?

把limit數量調大會影響預估行數rows,進而影響優化器索引的選擇嗎?

答案是會。

我們執行limit 10

select * from sample_table where city_id = 565 and type = 13 order by id desc limit 0,10

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

圖中rows變為了18211,增長了10倍。如果使用limit 100,會發生什么?

如何解決千萬級數據表選錯索引導致的線上慢查詢事故

優化器選擇了聯合索引。初步估計是rows還會翻倍,所以優化器放棄了主鍵索引。寧愿用聯合索引后排序,也不愿意用主鍵索引了。

為何突然出現異常慢查詢

問:這個查詢語句已經在線上穩定運行了非常長的時間,為何這次突然出現了慢查詢?

答:以前的語句查詢條件返回結果都不為空,limit1很快就能找到那條數據,返回結果。而這次代碼中查詢條件實際結果為空,導致了掃描了全部的主鍵索引。

解決方案

知道了MySQL為何選擇這個索引的原因后,我們就可以根據上面的思路來列舉出解決辦法了。

主要有兩個大方向:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2. 強制指定索引

  3. 干涉優化器選擇

強制選擇索引:force index

就像上面我最開始的操作那樣,我們直接使用force index,讓語句走我們想要走的索引。

select * from sample_table force index(idx_city_id_type)  where ( ( (1 = 1) and (city_id = 565) ) and (type = 13) ) order by id desc limit 0, 1

這樣做的優點是見效快,問題馬上就能解決。

缺點也很明顯:

高耦合,這種語句寫在代碼里,會變得難以維護,如果索引名變化了,或者沒有這個索引了,代碼就要反復修改。屬于硬編碼。

很多代碼用框架封裝了SQL,force index()并不容易加進去。

「我們換一種辦法,我們去引導優化器選擇聯合索引。」

干涉優化器選擇:增大limit

通過增大limit,我們可以讓預估掃描行數快速增加,比如改成下面的limit 0, 1000

SELECT * FROM sample_table where city_id = 565 and type = 13 order by id desc LIMIT 0,1000

這樣就會走上聯合索引,然后排序,但是這樣強行增長limit,其實總有種面向黑盒調參的感覺。我們還有更優美的解決方案嗎?

干涉優化器選擇:增加包含order by id字段的聯合索引

我們這句慢查詢使用的是order by  id,但是我們卻沒有在聯合索引中加入id字段,導致了優化器認為聯合索引后還要排序,干脆就不太想走這個聯合索引了。

我們可以新建city_id,type和id的聯合索引,來解決這個問題。

這樣也有一定的弊端,比如我這個表到了8000w數據,建立索引非常耗時,而且通常索引就有3.4個g,如果無限制的用索引解決問題,可能會帶來新的問題。表中的索引不宜過多。

干涉優化器選擇:寫成子查詢

還有什么辦法?我們可以用子查詢,在子查詢里先走city_id和type的聯合索引,得到結果集后在limit1選出第一條。

但是子查詢使用有風險,一版DBA也不建議使用子查詢,會建議大家在代碼邏輯中完成復雜的查詢。當然我們這句并不復雜啦~

Select * From sample_table Where id in (Select id From `newhome_db`.`af_hot_price_region` where (city_id = 565 and type = 13)) limit 0, 1

還有很多解決辦法...

SQL優化是個很大的工程,我們還有非常多的辦法能夠解決這句慢查詢問題,這里就不一一展開了。留給大家做為思考題了。

感謝各位的閱讀,以上就是“如何解決千萬級數據表選錯索引導致的線上慢查詢事故”的內容了,經過本文的學習后,相信大家對如何解決千萬級數據表選錯索引導致的線上慢查詢事故這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

封丘县| 中西区| 西丰县| 清镇市| 南丰县| 无极县| 麻栗坡县| 大冶市| 曲松县| 桐庐县| 德阳市| 长海县| 渝北区| 塘沽区| 通许县| 郁南县| 九龙县| 广汉市| 平乐县| 虞城县| 防城港市| 黎平县| 嘉祥县| 九台市| 遵化市| 大名县| 西充县| 象山县| 通渭县| 高雄市| 邓州市| 页游| 佛坪县| 襄城县| 同心县| 邵阳县| 延长县| 临沂市| 蓬溪县| 怀仁县| 汕尾市|