MySQL基本知識點梳理和查詢優化

目錄

1、索引相關mysql

2、EXPLIAN中有用的信息redis

3、字段類型和編碼算法

4、SQL語句總結spring

5、踩坑sql

6、千萬大表在線修改數據庫

7、慢查詢日誌緩存

8、查看sql進程和殺死進程架構

9、一些數據庫性能的思考併發

本文主要是總結了工做中一些經常使用的操做,以及不合理的操做,在對慢查詢進行優化時收集的一些有用的資料和信息,本文適合有mysql基礎的開發人員。分佈式

1、索引相關

一、索引基數:基數是數據列所包含的不一樣值的數量。例如,某個數據列包含值一、三、七、四、七、3,那麼它的基數就是4。索引的基數相對於數據錶行數較高(也就是說,列中包含不少不一樣的值,重複的值不多)的時候,它的工做效果最好。若是某數據列含有不少不一樣的年齡,索引會很快地分辨數據行。若是某個數據列用於記錄性別(只有"M"和"F"兩種值),那麼索引的用處就不大。若是值出現的概率幾乎相等,那麼不管搜索哪一個值均可能獲得一半的數據行。在這些狀況下,最好根本不要使用索引,由於查詢優化器發現某個值出如今表的數據行中的百分比很高的時候,它通常會忽略索引,進行全表掃描。慣用的百分比界線是"30%"。

二、索引失效緣由:

1.對索引列運算,運算包括(+、-、*、/、!、<>、%、like'%_'(%放在前面)

2.類型錯誤,如字段類型爲varchar,where條件用number。

3.對索引應用內部函數,這種狀況下應該創建基於函數的索引

如select * from template t where ROUND(t.logicdb_id) = 1

此時應該建ROUND(t.logicdb_id)爲索引,mysql8.0開始支持函數索引,5.7能夠經過虛擬列的方式來支持,以前只能新建一個ROUND(t.logicdb_id)列而後去維護

4.若是條件有or,即便其中有條件帶索引也不會使用(這也是爲何建議少使用or的緣由),若是想使用or,又想索引有效,只能將or條件中的每一個列加上索引

5.若是列類型是字符串,那必定要在條件中數據使用引號,不然不使用索引;

6.B-tree索引 is null不會走,is not null會走,位圖索引 is null,is not null 都會走

7.組合索引遵循最左原則

索引的創建

一、最重要的確定是根據業務常常查詢的語句

二、儘可能選擇區分度高的列做爲索引,區分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重複的比率,比率越大咱們掃描的記錄數就越少

三、若是業務中惟一特性最好創建惟一鍵,一方面能夠保證數據的正確性,另外一方面索引的效率能大大提升

2、EXPLIAN中有用的信息

基本用法

一、desc 或者 explain 加上你的sql

二、extended explain加上你的sql,而後經過show warnings能夠查看實際執行的語句,這一點也是很是有用的,不少時候不一樣的寫法通過sql分析以後實際執行的代碼是同樣的

提升性能的特性

一、索引覆蓋(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字段

一、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字段

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,遍歷全表得到匹配的行

3、字段類型和編碼

一、mysql返回字符串長度:CHARACTER_LENGTH方法(CHAR_LENGTH同樣的)返回的是字符數,LENGTH函數返回的是字節數,一個漢字三個字節

二、varvhar等字段創建索引長度計算語句:select count(distinct left(test,5))/count(*) from table; 越趨近1越好

三、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。

四、msyql排序規則(通常使用_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

五、sql yog中初始鏈接指定編碼類型使用鏈接配置的初始化命令

4、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編寫注意

一、where語句的解析順序是從右到左,條件儘可能放where不要放having

二、採用延遲關聯(deferred join)技術優化超多分頁場景,好比limit 10000,10,延遲關聯能夠避免回表

三、distinct語句很是損耗性能,能夠經過group by來優化

四、連表儘可能不要超過三個表

五、想學習Java工程化、分佈式架構、高併發、高性能、深刻淺出、微服務架構、Spring,MyBatis,Netty源碼分析等技術能夠加羣:479499375,羣裏有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們,歡迎進羣一塊兒深刻交流學習。

5、踩坑

一、若是有自增列,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」處理

6、千萬大表在線修改

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 

7、慢查詢日誌

有時候若是線上請求超時,應該去關注下慢查詢日誌,慢查詢的分析很簡單,先找到慢查詢日誌文件的位置,而後利用mysqldumpslow去分析。查詢慢查詢日誌信息能夠直接經過執行sql命令查看相關變量,經常使用的sql以下:

-- 查看慢查詢配置 
-- slow_query_log  慢查詢日誌是否開啓 
-- slow_query_log_file 的值是記錄的慢查詢日誌到文件中 
-- long_query_time 指定了慢查詢的閾值 
-- log_queries_not_using_indexes 是否記錄全部沒有利用索引的查詢 
SHOW VARIABLES LIKE '%quer%'; 
 
-- 查看慢查詢是日誌仍是表的形式 
SHOW VARIABLES LIKE 'log_output' 
 
-- 查看慢查詢的數量 

mysqldumpslow的工具十分簡單,我主要用到的是參數以下:

  • -t:限制輸出的行數,我通常取前十條就夠了
  • -s:根據什麼來排序默認是平均查詢時間at,我還常常用到c查詢次數,由於查詢次數很頻繁可是時間不高也是有必要優化的,還有t查詢時間,查看那個語句特別卡。
  • -v:輸出詳細信息

例子:mysqldumpslow -v -s t -t 10 mysql_slow.log.2018-11-20-0500

8、查看sql進程和殺死進程

若是你執行了一個sql的操做,可是遲遲沒有返回,你能夠經過查詢進程列表看看他的實際執行情況,若是該sql十分耗時,爲了不影響線上能夠用kill命令殺死進程,經過查看進程列表也能直觀的看下當前sql的執行狀態,若是當前數據庫負載很高,在進程列表可能會出現,大量的進程夯住,執行時間很長。命令以下:

--查看進程列表
SHOW PROCESSLIST;
--殺死某個進程
kill 183665 

若是你使用的sqlyog,那麼也有圖形化的頁面,在菜單欄-工具-顯示-進程列表。在進程列表頁面能夠右鍵殺死進程。以下所示:

9、一些數據庫性能的思考

在對公司慢查詢日誌作優化的時候,不少時候多是忘了建索引,像這種問題很容易解決,加個索引就好了。可是有兩種狀況就不是簡單能加索引能解決了:

一、業務代碼循環讀數據庫: 考慮這樣一個場景,獲取用戶粉絲列表信息 加入分頁是十個 其實像這樣的sql是十分簡單的,經過連表查詢性能也很高,可是有時候,不少開發採用了取出一串id,而後循環讀每一個id的信息,這樣若是id不少對數據庫的壓力是很大的,並且性能也很低

二、統計sql:不少時候,業務上都會有排行榜這種,發現公司有不少地方直接採用數據庫作計算,在對一些大表的作聚合運算的時候,常常超過五秒,這些sql通常很長並且很難優化, 像這種場景,若是業務容許(好比一致性要求不高或者是隔一段時間才統計的),能夠專門在從庫裏面作統計。另外我建議仍是採用redis緩存來處理這種業務

三、超大分頁:在慢查詢日誌中發現了一些超大分頁的慢查詢如limit 40000,1000,由於mysql的分頁是在server層作的,能夠採用延遲關聯在減小回表。可是看了相關的業務代碼正常的業務邏輯是不會出現這樣的請求的,因此頗有多是有惡意用戶在刷接口,因此最好在開發的時候也對接口加上校驗攔截這些惡意請求。

文章出處:https://mp.weixin.qq.com/s/dhsk9Zsw3Ul574SFpI_W5w

相關文章
相關標籤/搜索