轉載:https://blog.csdn.net/enmotech/article/details/88809822html
本文主要總結了慢查詢優化的過程當中經常使用的以及不合理的操做,適合有 MySQL 基礎的開發人員。python
索引相關mysql
索引基數算法
基數是數據列所包含的不一樣值的數量,例如,某個數據列包含值 一、三、七、四、七、3,那麼它的基數就是 4。spring
索引的基數相對於數據錶行數較高(也就是說,列中包含不少不一樣的值,重複的值不多)的時候,它的工做效果最好。sql
若是某數據列含有不少不一樣的年齡,索引會很快地分辨數據行;若是某個數據列用於記錄性別(只有「M」和「F」兩種值),那麼索引的用處就不大;若是值出現的概率幾乎相等,那麼不管搜索哪一個值均可能獲得一半的數據行。數據庫
在這些狀況下,最好根本不要使用索引,由於查詢優化器發現某個值出如今表的數據行中的百分比很高的時候,它通常會忽略索引,進行全表掃描。慣用的百分比界線是「30%」。緩存
索引失效緣由安全
索引失效的緣由有以下幾點:性能優化
對索引列運算,運算包括(+、-、*、/、!、<>、%、like'%_'(% 放在前面)。
類型錯誤,如字段類型爲 varchar,where 條件用 number。
對索引應用內部函數,這種狀況下應該要創建基於函數的索引。例如 select * from template t where ROUND (t.logicdb_id) = 1,此時應該建 ROUND (t.logicdb_id) 爲索引。
MySQL 8.0 開始支持函數索引,5.7 能夠經過虛擬列的方式來支持,以前只能新建一個 ROUND (t.logicdb_id) 列而後去維護。
若是條件有 or,即便其中有條件帶索引也不會使用(這也是爲何建議少使用 or 的緣由),若是想使用 or,又想索引有效,只能將 or 條件中的每一個列加上索引。
若是列類型是字符串,那必定要在條件中數據使用引號,不然不使用索引。
B-tree 索引 is null 不會走,is not null 會走,位圖索引 is null,is not null 都會走。
組合索引遵循最左原則。
索引的創建
索引的創建須要注意如下幾點:
最重要的確定是根據業務常常查詢的語句。
儘可能選擇區分度高的列做爲索引,區分度的公式是 COUNT(DISTINCT col) / COUNT(*),表示字段不重複的比率,比率越大咱們掃描的記錄數就越少。
若是業務中惟一特性最好創建惟一鍵,一方面能夠保證數據的正確性,另外一方面索引的效率能大大提升。
EXPLIAN中有用的信息
基本用法
EXPLIAN 基本用法以下:
desc 或者 explain 加上你的 SQL。
extended explain 加上你的 SQL,而後經過 show warnings 能夠查看實際執行的語句,這一點也是很是有用的,不少時候不一樣的寫法經 SQL 分析後,實際執行的代碼是同樣的。
提升性能的特性
EXPLIAN 提升性能的特性以下:
索引覆蓋(covering index):須要查詢的數據在索引上均可以查到不須要回表 EXTRA 列顯示 using index。
ICP特性(Index Condition Pushdown):原本 index 僅僅是 data access 的一種訪問模式,存數引擎經過索引回表獲取的數據會傳遞到 MySQL Server 層進行 where 條件過濾。
5.6 版本開始當 ICP 打開時,若是部分 where 條件能使用索引的字段,MySQL Server 會把這部分下推到引擎層,能夠利用 index 過濾的 where 條件在存儲引擎層進行數據過濾。
EXTRA 顯示 using index condition。須要瞭解 MySQL 的架構圖分爲 Server 和存儲引擎層。
索引合併(index merge):對多個索引分別進行條件掃描,而後將它們各自的結果進行合併(intersect/union)。
通常用 or 會用到,若是是 AND 條件,考慮創建複合索引。EXPLAIN 顯示的索引類型會顯示 index_merge,EXTRA 會顯示具體的合併算法和用到的索引。
Extra 字段
Extra 字段使用:
using filesort:說明 MySQL 會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。
MySQL 中沒法利用索引完成的排序操做稱爲「文件排序」,其實不必定是文件排序,內部使用的是快排。
using temporary:使用了臨時表保存中間結果,MySQL 在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by。
using index:表示相應的 SELECT 操做中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯。
impossible where:where 子句的值老是 false,不能用來獲取任何元組。
select tables optimized away:在沒有 group by 子句的狀況下基於索引優化 MIN/MAX 操做或者對於 MyISAM 存儲引擎優化 COUNT(*) 操做,沒必要等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。
distinct:優化 distinct 操做,在找到第一匹配的元組後即中止找一樣值的操做。
using filesort、using temporary 這兩項出現時須要注意下,這兩項是十分耗費性能的。
在使用 group by 的時候,雖然沒有使用 order by,若是沒有索引,是可能同時出現 using filesort,using temporary 的。
由於 group by 就是先排序在分組,若是沒有排序的須要,能夠加上一個 order by NULL 來避免排序,這樣 using filesort 就會去除,能提高一點性能。
type 字段
type 字段使用:
system:表只有一行記錄(等於系統表),這是 const 類型的特例,平時不會出現。
const:若是經過索引依次就找到了,const 用於比較主鍵索引或者 unique 索引。由於只能匹配一行數據,因此很快。若是將主鍵置於 where 列表中,MySQL 就能將該查詢轉換爲一個常量。
eq_ref:惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或惟一索引掃描。
ref:非惟一性索引掃描,返回匹配某個單獨值的全部行。本質上也是一種索引訪問,它返回全部匹配某個單獨值的行,然而它可能會找到多個符合條件的行,因此它應該屬於查找和掃描的混合體。
range:只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪一個索引,通常就是在你的 where 語句中出現 between、<、>、in 等的查詢。
這種範圍掃描索引比全表掃描要好,由於只須要開始於縮印的某一點,而結束於另外一點,不用掃描所有索引。
index:Full Index Scan ,index 與 ALL 的區別爲 index 類型只遍歷索引樹,這一般比 ALL 快,由於索引文件一般比數據文件小。
也就是說雖然 ALL 和 index 都是讀全表,但 index 是從索引中讀取的,而 ALL 是從硬盤讀取的。
all:Full Table Scan,遍歷全表得到匹配的行。
字段類型和編碼
MySQL 返回字符串長度
CHARACTER_LENGTH(同CHAR_LENGTH)方法返回的是字符數,LENGTH 函數返回的是字節數,一個漢字三個字節。
varchar 等字段創建索引長度計算語句
select count(distinct left(test,5))/count(*) from table;越趨近 1 越好。
MySQL 的 utf8
MySQL 的 utf8 最大是 3 個字節不支持 emoji 表情符號,必須只用 utf8mb4。須要在 MySQL 配置文件中配置客戶端字符集爲 utf8mb4。
JDBC 的鏈接串不支持配置 characterEncoding=utf8mb4,最好的辦法是在鏈接池中指定初始化 SQL。
例如:hikari 鏈接池,其餘鏈接池相似 spring . datasource . hikari . connection - init - sql =set names utf8mb4。不然須要每次執行 SQL 前都先執行 set names utf8mb4。
MySQL 排序規則
通常使用 _bin 和 _genera_ci:
utf8_genera_ci 不區分大小寫,ci 爲 case insensitive 的縮寫,即大小寫不敏感。
utf8_general_cs 區分大小寫,cs 爲 case sensitive 的縮寫,即大小寫敏感,可是目前 MySQL 版本中已經不支持相似於 ***_genera_cs 的排序規則,直接使用 utf8_bin 替代。
utf8_bin 將字符串中的每個字符用二進制數據存儲,區分大小寫。
那麼,一樣是區分大小寫,utf8_general_cs 和 utf8_bin 有什麼區別?
cs 爲 case sensitive 的縮寫,即大小寫敏感;bin 的意思是二進制,也就是二進制編碼比較。
utf8_general_cs 排序規則下,即使是區分了大小寫,可是某些西歐的字符和拉丁字符是不區分的,好比 ä=a,可是有時並不須要 ä=a,因此纔有 utf8_bin。
utf8_bin 的特色在於使用字符的二進制的編碼進行運算,任何不一樣的二進制編碼都是不一樣的,所以在 utf8_bin 排序規則下:ä<>a。
初始化命令
SQLyog 中初始鏈接指定編碼類型使用鏈接配置的初始化命令,以下圖:
SQL語句總結
經常使用但容易忘的
SQL 語句經常使用但容易忘的總結以下:
若是有主鍵或者惟一鍵衝突則不插入:insert ignore into。
若是有主鍵或者惟一鍵衝突則更新,注意這個會影響自增的增量:INSERT INTO room_remarks(room_id,room_remarks)VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks = "234"。
若是有就用新的替代,values 若是不包含自增列,自增列的值會變化:REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")。
備份表:CREATE TABLE user_info SELECT * FROM user_info。
複製表結構:CREATE TABLE user_v2 LIKE user。
從查詢語句中導入:INSERT INTO user_v2 SELECT * FROM user 或者 INSERT INTO user_v2(id,num) SELECT id,num FROM user。
連表更新:UPDATE user a, room b SET a.num=a.num+1 WHERE a.room_id=b.id。
連表刪除:DELETE user FROM user,black WHERE user.id=black.id。
鎖相關
鎖相關(做爲了解,不多用):
共享鎖:select id from tb_test where id = 1 lock in share mode。
排它鎖:select id from tb_test where id = 1 for update。
優化時用到
優化時用到:
強制使用某個索引:select * from table force index(idx_user) limit 2。
禁止使用某個索引:select * from table ignore index(idx_user) limit 2。
禁用緩存(在測試時去除緩存的影響):select SQL_NO_CACHE from table limit 2。
查看狀態
查看狀態:
查看字符集:SHOW VARIABLES LIKE 'character_set%'。
查看排序規則:SHOW VARIABLES LIKE 'collation%'。
SQL 編寫注意
SQL 編寫請注意:
where 語句的解析順序是從右到左,條件儘可能放 where 不要放 having。
採用延遲關聯(deferred join)技術優化超多分頁場景,好比 limit 10000,10,延遲關聯能夠避免回表。
distinct 語句很是損耗性能,能夠經過 group by 來優化。
連表儘可能不要超過三個表。
踩坑
踩坑總結以下:
若是有自增列,truncate 語句會把自增列的基數重置爲 0,有些場景用自增列做爲業務上的 ID 須要十分重視。
聚合函數會自動濾空,好比 a 列的類型是 int 且所有是 NULL,則 SUM(a) 返回的是 NULL 而不是 0。
MySQL 判斷 null 相等不能用 「a=null」,這個結果永遠爲 UnKnown,where 和 having 中,UnKnown 永遠被視爲 false,check 約束中,UnKnown 就會視爲 true 來處理。因此要用「a is null」處理。
千萬大表在線修改
MySQL 在表數據量很大的時候,若是修改表結構會致使鎖表,業務請求被阻塞。
MySQL 在 5.6 以後引入了在線更新,可是在某些狀況下仍是會鎖表,因此通常都採用 PT 工具( Percona Toolkit)。
如對錶添加索引:
pt-online-schema-change --user='root' --host='localhost' --ask-pass --alter "add index idx_user_id(room_id,create_time)"
D=fission_show_room_v2,t=room_favorite_info --execute
慢查詢日誌
有時候若是線上請求超時,應該去關注下慢查詢日誌,慢查詢的分析很簡單,先找到慢查詢日誌文件的位置,而後利用 mysqldumpslow 去分析。
查詢慢查詢日誌信息能夠直接經過執行 SQL 命令查看相關變量,經常使用的 SQL 以下:
-- 查看慢查詢配置
-- slow_query_log 慢查詢日誌是否開啓
-- slow_query_time 指定了慢查詢的閾值
-- long_query_time 指定了慢查詢的閾值
-- log_queries_not_using_indexes 是否記錄全部沒有利用索引的查詢
SHOW VARIABLES LIKE'%quer%';
--查看慢查詢是日誌仍是表的形式
SHOW VARIABLES LIKE'log_output'
-- 查看慢查詢的數量
SHOW GLOBAL STATUS LIKE'solw_queries';
mysqldumpslow 的工具十分簡單,我主要用到的參數以下:
-t:限制輸出的行數,我通常取前十條就夠了。
-s:根據什麼來排序默認是平均查詢時間 at,我還常常用到 c 查詢次數,由於查詢次數很頻繁可是時間不高也是有必要優化的,還有 t 查詢時間,查看那個語句特別卡。
-v:輸出詳細信息。
例子:mysqldumpslow -v -s t -t 10 mysql_slow.log.2018-11-20-0500。
查看SQL進程和殺死進程
若是你執行了一個 SQL 的操做,可是遲遲沒有返回,你能夠經過查詢進程列表看看它的實際執行情況。
若是該 SQL 十分耗時,爲了不影響線上能夠用 kill 命令殺死進程,經過查看進程列表也能直觀的看下當前 SQL 的執行狀態;若是當前數據庫負載很高,在進程列表可能會出現,大量的進程夯住,執行時間很長。
命令以下:
--查看進程列表
SHOW PROCESSLIST;
--殺死某個進程
kill 183665
若是你使用的 SQLyog,那麼也有圖形化的頁面,在菜單欄→工具→顯示→進程列表。
在進程列表頁面能夠右鍵殺死進程,以下所示:
一些數據庫性能的思考
在對公司慢查詢日誌作優化的時候,不少時候多是忘了建索引,像這種問題很容易解決,加個索引就好了。可是有幾種狀況就不是簡單加索引能解決了:
業務代碼循環讀數據庫
考慮這樣一個場景,獲取用戶粉絲列表信息,加入分頁是十個,其實像這樣的 SQL 是十分簡單的,經過連表查詢性能也很高。
可是有時候,不少開發採用了取出一串 ID,而後循環讀每一個 ID 的信息,這樣若是 ID 不少對數據庫的壓力是很大的,並且性能也很低。
統計 SQL
不少時候,業務上都會有排行榜這種,發現公司有不少地方直接採用數據庫作計算,在對一些大表作聚合運算的時候,常常超過五秒,這些 SQL 通常很長並且很難優化。
像這種場景,若是業務容許(好比一致性要求不高或者是隔一段時間才統計的),能夠專門在從庫裏面作統計。另外我建議仍是採用 Redis 緩存來處理這種業務。
超大分頁
在慢查詢日誌中發現了一些超大分頁的慢查詢如 Limit 40000,1000,由於 MySQL 的分頁是在 Server 層作的,能夠採用延遲關聯在減小回表。
可是看了相關的業務代碼正常的業務邏輯是不會出現這樣的請求的,因此頗有多是有惡意用戶在刷接口,最好在開發的時候也對接口加上校驗攔截這些惡意請求。
來源:https://www.cnblogs.com/chenfangzhi
資源下載
關注公衆號:數據和雲(OraNews)回覆關鍵字獲取
2018DTCC , 數據庫大會PPT
2018DTC,2018 DTC 大會 PPT
ENMOBK,《Oracle性能優化與診斷案例》
DBALIFE ,「DBA 的一天」海報
DBA04 ,DBA 手記4 電子書
122ARCH ,Oracle 12.2體系結構圖
2018OOW ,Oracle OpenWorld 資料
產品推薦雲和恩墨Bethune Pro企業版,集監控,巡檢,安全於一身,你的專屬數據庫實時監控和智能巡檢平臺,漂亮的不像實力派,你值得擁有!
雲和恩墨zData一體機現已發佈超融合版本和精簡版,支持各類簡化場景部署,零數據丟失備份一體機ZDBM也已發佈,歡迎關注。