-- 問題1 tablename使用主鍵索引反而比idx_ref_id慢的緣由
EXPLAIN SELECT SQL_NO_CACHE COUNT(id) FROM dbname.tbname FORCE INDEX (idx_ref_id)
EXPLAIN SELECT SQL_NO_CACHE COUNT(id) FROM dbname.tbname FORCE INDEX (PRIMARY)html
緣由:能夠看到走主鍵索引的時候效率比較差。那麼是爲何呢。
平時咱們檢索一列的時候,基本上等值或範圍查詢,那麼索引基數大的索引必然效率很高。
可是在作count(*)的時候並無檢索具體的一行或者一個範圍。那麼選擇基數小的索引對,count操做效率會更高。
在作count操做的時候,mysql會遍歷每一個葉子節點,因此基數越小,效率越高。
mysql非聚簇索引葉子節點保存的主鍵ID,因此須要檢索兩遍索引。可是這裏相對於遍歷主鍵索引。及時檢索兩遍索引效率也比單純的檢索主鍵索引快。
[主鍵索引太分散了]mysql
能夠參見http://www.2cto.com/database/201508/433975.htmlsql
-- 問題2 針對一個大表
SELECT SQL_NO_CACHE COUNT(id) FROM dbname.tbname 特別慢的處理辦法,
通常使用OPTIMIZE TABLE tablename
使用OPTIMIZE TABLE 對錶空間信息進行優化,而後執行COUNT效果很快提高緩存
實戰:MySQL Sending data致使查詢很慢的問題詳細分析
轉自http://blog.csdn.net/yunhua_lee/article/details/8573621工具
這兩天幫忙定位一個MySQL查詢很慢的問題,定位過程綜合各類方法、理論、工具,頗有表明性,分享給你們做爲新年禮物:)性能
【問題現象】測試
使用sphinx支持倒排索引,但sphinx從mysql查詢源數據的時候,查詢的記錄數才幾萬條,但查詢的速度很是慢,大概要4~5分鐘左右大數據
【處理過程】優化
1)explainspa
首先懷疑索引沒有建好,因而使用explain查看查詢計劃,結果以下:
從explain的結果來看,整個語句的索引設計是沒有問題的,除了第一個表由於業務須要進行整表掃描外,其它的表都是經過索引訪問
2)show processlist;
explain看不出問題,那到底慢在哪裏呢?
因而想到了使用 show processlist查看sql語句執行狀態,查詢結果以下:
發現很長一段時間,查詢都處在 「Sending data」狀態
查詢一下「Sending data」狀態的含義,原來這個狀態的名稱很具備誤導性,所謂的「Sending data」並非單純的發送數據,而是包括「收集 + 發送 數據」。
這裏的關鍵是爲何要收集數據,緣由在於:mysql使用「索引」完成查詢結束後,mysql獲得了一堆的行id,若是有的列並不在索引中,mysql須要從新到「數據行」上將須要返回的數據讀取出來返回個客戶端。
3)show profile
爲了進一步驗證查詢的時間分佈,因而使用了show profile命令來查看詳細的時間分佈
首先打開配置:set profiling=on;
執行完查詢後,使用show profiles查看query id;
使用show profile for query query_id查看詳細信息;
結果以下:
從結果能夠看出,Sending data的狀態執行了216s
4)排查對比
通過以上步驟,已經肯定查詢慢是由於大量的時間耗費在了Sending data狀態上,結合Sending data的定義,將目標聚焦在查詢語句的返回列上面
通過一 一排查,最後定爲到一個description的列上,這個列的設計爲:`description`varchar(8000) DEFAULT NULL COMMENT '遊戲描述',
因而採起了對比的方法,看看「不返回description的結果」如何。show profile的結果以下:
能夠看出,不返回description的時候,查詢時間只須要15s,返回的時候,須要216s,二者相差15倍
【原理研究】
至此問題已經明確,但原理上咱們還須要繼續探究。
這篇淘寶的文章很好的解釋了相關原理:innodb使用大字段text,blob的一些優化建議
這裏的關鍵信息是:當Innodb的存儲格式是 ROW_FORMAT=COMPACT
(or ROW_FORMAT=REDUNDANT
)的時候,Innodb只會存儲前768字節的長度,剩餘的數據存放到「溢出頁」中。
咱們使用show table status來查看錶的相關信息:
能夠看到,平均一行大約1.5K,也就說大約1/10行會使用「溢出存儲」,一旦採用了這種方式存儲,返回數據的時候原本是順序讀取的數據,就變成了隨機讀取了,因此致使性能急劇降低。
另外,在測試過程當中還發現,不管這條語句執行多少次,甚至將整個表select *幾回,語句的執行速度都沒有明顯變化。這個表的數據和索引加起來才150M左右,而整個Innodb buffer pool有5G,緩存整張表綽綽有餘,若是緩存了溢出頁,性能應該大幅提升纔對。
但實測結果卻並無提升,所以從這個測試能夠推論Innodb並無將溢出頁(overflow page)緩存到內存裏面。
這樣的設計也是符合邏輯的,由於overflow page原本就是存放大數據的,若是也放在緩存裏面,就會出現一次大數據列(blob、text、varchar)查詢,可能就將全部的緩存都更新了,這樣會致使其它普通的查詢性能急劇降低。
【解決方法】
找到了問題的根本緣由,解決方法也就不難了。有幾種方法:
1)查詢時去掉description的查詢,但這受限於業務的實現,可能須要業務作較大調整
2)表結構優化,將descripion拆分到另外的表,這個改動較大,須要已有業務配合修改,且若是業務仍是要繼續查詢這個description的信息,則優化後的性能也不會有很大提高。