数据库调优
數據庫調優其實一般情況都是我們的SQL調優,SQL的調優就可以解決大部分問題了,當然也不排除SQL執行環節的調優。
一般在開發涉及SQL的業務都會去本地環境跑一遍SQL,用explain去看一下執行計劃,看看分析的結果是否符合自己的預期,用沒用到相關的索引,然后再去線上環境跑一下看看執行時間(這里只有查詢語句,修改語句也無法在線上執行)
排除緩存干擾
因為在MySQL8.0之前我們的數據庫是存在緩存這樣的情況的,我之前就被坑過,因為存在緩存,我發現我sql怎么執行都是很快,當然第一次其實不快但是我沒注意到,以至于上線后因為緩存經常失效,導致rt(Response time)時高時低。
后面就發現了是緩存的問題,我們在執行SQL的時候,記得加上SQL NoCache去跑SQL,這樣跑出來的時間就是真實的查詢時間了。
我說一下為什么緩存會失效,而且是經常失效。
如果我們當前的MySQL版本支持緩存而且我們又開啟了緩存,那每次請求的查詢語句和結果都會以key-value的形式緩存在內存中的,一個請求會先去看緩存是否存在,不存在才會走解析器。
緩存失效比較頻繁的原因就是,只要我們一對表進行更新,那這個表所有的緩存都會被清空,其實我們很少存在不更新的表。
大家如果是8.0以上的版本就不用擔心這個問題,如果是8.0之下的版本,記得排除緩存的干擾。
Explain
explain你記得哪些字段,分別有什么含義?
我的總行數大概有10W行,但是我去用explain去分析sql的時候,就會發現只得到了9.4W,為啥行數只是個近視值呢?
MySQL中數據的單位都是頁,MySQL又采用了采樣統計的方法,采樣統計的時候,InnoDB默認會選擇N個數據頁,統計這些頁面上的不同值,得到一個平均值,然后乘以這個索引的頁面數,就得到了這個索引的基數。
我們數據是一直在變的,所以索引的統計信息也是會變的,會根據一個閾值,重新做統計。
至于MySQL索引可能走錯也很好理解,如果走A索引要掃描100行,B索引只要20行,但是他可能選擇走A索引,你可能會想MySQL是不是有病啊,其實不是的。
一般走錯都是因為優化器在選擇的時候發現,走A索引沒有額外的代價,比如走B索引并不能直接拿到我們的值,還需要回到主鍵索引才可以拿到,多了一次回表的過程,這個也是會被優化器考慮進去的。
他發現走A索引不需要回表,沒有額外的開銷,所有他選錯了。
如果是上面的統計信息錯了,那簡單,我們用analyze table tablename 就可以重新統計索引信息了,所以在實踐中,如果你發現explain的結果預估的rows值跟實際情況差距比較大,可以采用這個方法來處理。
還有一個方法就是force index強制走正確的索引,或者優化SQL,最后實在不行,可以新建索引,或者刪掉錯誤的索引。
最左匹配原則
大家在寫sql的時候,最好能利用到現有的SQL最大化利用,像上面的場景,如果利用一個模糊查詢 itemname like ’中國%‘,這樣還是能利用到這個索引的,而且如果有這樣的聯合索引,大家也沒必要去新建一個商品名稱單獨的索引了。
很多時候我們索引可能沒建對,那你調整一下順序,可能就可以優化到整個SQL了。
很長的字段,想做索引我們怎么去優化他呢?
因為存在一個磁盤占用的問題,索引選取的越長,占用的磁盤空間就越大,相同的數據頁能放下的索引值就越少,搜索的效率也就會越低。
把字段hash為另外一個字段存起來,每次校驗hash就好了,hash的索引也不大。
我們都知道只要區分度過高,都可以,那我們可以采用倒序,或者刪減字符串這樣的情況去建立我們自己的區分度,不過大家需要注意的是,調用函數也是一次開銷喲,這點當時沒注意。
就比如本來是www.aa@qq,com 其實前面的www.基本上是沒任何區分度的,所有人的郵箱都是這么開頭的,你一搜一大堆出來,放在索引還浪費內存,你可以substring()函數截取掉前面的,然后建立索引。
我們所有人的身份證都是區域開頭的,同區域的人很多,那怎么做良好的區分呢?REVERSE()函數翻轉一下,區分度可能就高了。
這些操作都用到了函數,我就說一下函數的坑。
條件字段函數操作
日常開發過程中,大家經常對很多字段進行函數操作,如果對日期字段操作,浮點字符操作等等,大家需要注意的是,如果對字段做了函數計算,就用不上索引了,這是MySQL的規定。
對索引字段做函數操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜索功能。
需要注意的是,優化器并不是要放棄使用這個索引。
這個時候大家可以用一些取巧的方法,比如 select * from tradelog where id + 1 = 10000 就走不上索引,select * from tradelog where id = 9999就可以。
隱式類型轉換
select * from t where id = 1
如果id是字符類型的,1是數字類型的,你用explain會發現走了全表掃描,根本用不上索引,為啥呢?
因為MySQL底層會對你的比較進行轉換,相當于加了 CAST( id AS signed int) 這樣的一個函數,上面說過函數會導致走不上索引。
隱式字符編碼轉換
還是一樣的問題,如果兩個表的字符集不一樣,一個是utf8mb4,一個是utf8,因為utf8mb4是utf8的超集,所以一旦兩個字符比較,就會轉換為utf8mb4再比較。
轉換的過程相當于加了CONVERT(id USING utf8mb4)函數,那又回到上面的問題了,用到函數就用不上索引了。
還有大家一會可能會遇到mysql突然卡頓的情況,那可能是MySQLflush了。
flush
redo log大家都知道,也就是我們對數據庫操作的日志,他是在內存中的,每次操作一旦寫了redo log就會立馬返回結果,但是這個redo log總會找個時間去更新到磁盤,這個操作就是flush。
在更新之前,當內存數據頁跟磁盤數據頁內容不一致的時候,我們稱這個內存頁為“臟頁”。
內存數據寫入到磁盤后,內存和磁盤上的數據頁的內容就一致了,稱為“干凈頁“。
?
總結
- 上一篇: 工作中用到的设计模式?
- 下一篇: RocketMQ 消息持久化机制