Mysql基礎(chǔ)相關(guān)

本文主要總結(jié)了工作中一些常用的操作及不合理的操作,在對慢查詢進(jìn)行優(yōu)化時(shí)收集的一些有用的資料和信息,本文適合有MySQL基礎(chǔ)的開發(fā)人員。

一、索引相關(guān)

1、索引基數(shù)

基數(shù)是數(shù)據(jù)列所包含的不同值的數(shù)量,例如,某個(gè)數(shù)據(jù)列包含值1、3、7、4、7、3,那么它的基數(shù)就是4。

索引的基數(shù)相對于數(shù)據(jù)表行數(shù)較高(也就是說,列中包含很多不同的值,重復(fù)的值很少)的時(shí)候,它的工作效果最好。

如果某數(shù)據(jù)列含有很多不同的年齡,索引會(huì)很快地分辨數(shù)據(jù)行;如果某個(gè)數(shù)據(jù)列用于記錄性別(只有“M”和“F”兩種值),那么索引的用處就不大;如果值出現(xiàn)的幾率幾乎相等,那么無論搜索哪個(gè)值都可能得到一半的數(shù)據(jù)行。

在這些情況下,最好根本不要使用索引,因?yàn)椴樵儍?yōu)化器發(fā)現(xiàn)某個(gè)值出現(xiàn)在表的數(shù)據(jù)行中的百分比很高的時(shí)候,它一般會(huì)忽略索引,進(jìn)行全表掃描。慣用的百分比界線是“30%”。

2、索引失效原因

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

類型錯(cuò)誤,如字段類型為varchar,where條件用number;

對索引應(yīng)用內(nèi)部函數(shù),這種情況下應(yīng)該要建立基于函數(shù)的索引。例如 select * from template t where ROUND (t.logicdb_id) = 1,此時(shí)應(yīng)該建ROUND (t.logicdb_id)為索引,MySQL8.0開始支持函數(shù)索引,5.7可以通過虛擬列的方式來支持,之前只能新建一個(gè)ROUND (t.logicdb_id)列然后去維護(hù);

如果條件有or,即使其中有條件帶索引也不會(huì)使用(這也是為什么建議少使用or的原因),如果想使用or,又想索引有效,只能將or條件中的每個(gè)列加上索引;

如果列類型是字符串,那一定要在條件中數(shù)據(jù)使用引號,否則不使用索引;

B-tree索引 is 不會(huì)走,is not 會(huì)走,位圖索引 is ,is not 都會(huì)走;

組合索引遵循最左原則。

3、索引的建立

最重要的肯定是根據(jù)業(yè)務(wù)經(jīng)常查詢的語句;

盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是 COUNT(DISTINCT col) / COUNT(*),表示字段不重復(fù)的比率,比率越大我們掃描的記錄數(shù)就越少;

如果業(yè)務(wù)中唯一特性最好建立唯一鍵,一方面可以保證數(shù)據(jù)的正確性,另一方面索引的效率能大大提高。

二、EXPLIAN中有用的信息

1、基本用法

desc或者explain加上你的SQL;

extended explain加上你的SQL,然后通過show warnings可以查看實(shí)際執(zhí)行的語句,這一點(diǎn)也是非常有用的,很多時(shí)候不同的寫法經(jīng)SQL分析后,實(shí)際執(zhí)行的代碼是一樣的。

2、提高性能的特性

索引覆蓋(covering index):需要查詢的數(shù)據(jù)在索引上都可以查到不需要回表 EXTRA列顯示using index;

ICP特性(Index Condition Pushdown):本來index僅僅是data access的一種訪問模式,存數(shù)引擎通過索引回表獲取的數(shù)據(jù)會(huì)傳遞到MySQL Server層進(jìn)行where條件過濾。5.6版本開始當(dāng)ICP打開時(shí),如果部分where條件能使用索引的字段,MySQL Server會(huì)把這部分下推到引擎層,可以利用index過濾的where條件在存儲(chǔ)引擎層進(jìn)行數(shù)據(jù)過濾。EXTRA顯示using index condition。需要了解MySQL的架構(gòu)圖分為Server和存儲(chǔ)引擎層;

索引合并(index merge):對多個(gè)索引分別進(jìn)行條件掃描,然后將它們各自的結(jié)果進(jìn)行合并(intersect/union)。一般用OR會(huì)用到,如果是AND條件,考慮建立復(fù)合索引。EXPLAIN顯示的索引類型會(huì)顯示index_merge,EXTRA會(huì)顯示具體的合并算法和用到的索引。

3、extra字段

using filesort:說明MySQL會(huì)對數(shù)據(jù)使用一個(gè)外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。MySQL中無法利用索引完成的排序操作稱為“文件排序”,其實(shí)不一定是文件排序,內(nèi)部使用的是快排;

using temporary:使用了臨時(shí)表保存中間結(jié)果,MySQL在對查詢結(jié)果排序時(shí)使用臨時(shí)表。常見于排序order by和分組查詢group by;

using index:表示相應(yīng)的SELECT操作中使用了覆蓋索引(Covering Index),避免訪問了表的數(shù)據(jù)行,效率不錯(cuò);

impossible where:WHERE子句的值總是false,不能用來獲取任何元組;

select tables optimized away:在沒有GROUP BY子句的情況下基于索引優(yōu)化MIN/MAX操作或者對于MyISAM存儲(chǔ)引擎優(yōu)化COUNT(*)操作,不必等到執(zhí)行階段再進(jìn)行計(jì)算,查詢執(zhí)行計(jì)劃生成的階段即完成優(yōu)化;

distinct:優(yōu)化distinct操作,在找到第一匹配的元組后即停止找同樣值的操作。

using filesort、using temporary這兩項(xiàng)出現(xiàn)時(shí)需要注意下,這兩項(xiàng)是十分耗費(fèi)性能的,在使用group by的時(shí)候,雖然沒有使用order by,如果沒有索引,是可能同時(shí)出現(xiàn)using filesort,using temporary的,因?yàn)間roup by就是先排序在分組,如果沒有排序的需要,可以加上一個(gè)order by 來避免排序,這樣using filesort就會(huì)去除,能提升一點(diǎn)性能。

4、type字段

system:表只有一行記錄(等于系統(tǒng)表),這是const類型的特例,平時(shí)不會(huì)出現(xiàn);

const:如果通過索引依次就找到了,const用于比較主鍵索引或者unique索引。因?yàn)橹荒芷ヅ湟恍袛?shù)據(jù),所以很快。如果將主鍵置于where列表中,MySQL就能將該查詢轉(zhuǎn)換為一個(gè)常量;

eq_ref:唯一性索引掃描,對于每個(gè)索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描;

ref:非唯一性索引掃描,返回匹配某個(gè)單獨(dú)值的所有行。本質(zhì)上也是一種索引訪問,它返回所有匹配某個(gè)單獨(dú)值的行,然而它可能會(huì)找到多個(gè)符合條件的行,所以它應(yīng)該屬于查找和掃描的混合體;

range:只檢索給定范圍的行,使用一個(gè)索引來選擇行。key列顯示使用了哪個(gè)索引,一般就是在你的where語句中出現(xiàn)between、<、>、in等的查詢,這種范圍掃描索引比全表掃描要好,因?yàn)橹恍枰_始于縮印的某一點(diǎn),而結(jié)束于另一點(diǎn),不用掃描全部索引;

index:Full Index Scan ,index與ALL的區(qū)別為index類型只遍歷索引樹,這通常比ALL快,因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小,也就是說雖然ALL和index都是讀全表,但index是從索引中讀取的,而ALL是從硬盤讀取的;

all:Full Table Scan,遍歷全表獲得匹配的行。

參考地址:

https://blog.csdn.net/DrDanger/article/details/79092808

三、字段類型和編碼

1)MySQL返回字符串長度

CHARACTER_LENGTH(同CHAR_LENGTH)方法返回的是字符數(shù),LENGTH函數(shù)返回的是字節(jié)數(shù),一個(gè)漢字三個(gè)字節(jié)。

2)varvhar等字段建立索引長度計(jì)算語句

select count(distinct left(test,5))/count(*) from table;越趨近1越好

3)MySQL的utf8

MySQL的utf8最大是3個(gè)字節(jié)不支持emoji表情符號,必須只用utf8mb4。需要在MySQL配置文件中配置客戶端字符集為utf8mb4。

JDBC的連接串不支持配置characterEncoding=utf8mb4,最好的辦法是在連接池中指定初始化SQL,例如:hikari連接池,其他連接池類似spring . datasource . hikari . connection - init - sql =set names utf8mb4。否則需要每次執(zhí)行SQL前都先執(zhí)行set names utf8mb4。

4)MySQL排序規(guī)則(一般使用_bin和_genera_ci)

utf8_genera_ci不區(qū)分大小寫,ci為case insensitive的縮寫,即大小寫不敏感;

utf8_general_cs區(qū)分大小寫,cs為case sensitive的縮寫,即大小寫敏感,但是目前MySQL版本中已經(jīng)不支持類似于***_genera_cs的排序規(guī)則,直接使用utf8_bin替代;

utf8_bin將字符串中的每一個(gè)字符用二進(jìn)制數(shù)據(jù)存儲(chǔ),區(qū)分大小寫。

那么,同樣是區(qū)分大小寫,utf8_general_cs和utf8_bin有什么區(qū)別?

cs為case sensitive的縮寫,即大小寫敏感;bin的意思是二進(jìn)制,也就是二進(jìn)制編碼比較;

utf8_general_cs排序規(guī)則下,即便是區(qū)分了大小寫,但是某些西歐的字符和拉丁字符是不區(qū)分的,比如?=a,但是有時(shí)并不需要?=a,所以才有utf8_bin;

utf8_bin的特點(diǎn)在于使用字符的二進(jìn)制的編碼進(jìn)行運(yùn)算,任何不同的二進(jìn)制編碼都是不同的,因此在utf8_bin排序規(guī)則下:?<>a。

5)sql yog中初始連接指定編碼類型使用連接配置的初始化命令

四、SQL語句總結(jié)

1、常用但容易忘的

如果有主鍵或者唯一鍵沖突則不插入:insert ignore into

如果有主鍵或者唯一鍵沖突則更新,注意這個(gè)會(huì)影響自增的增量:INSERT INTO room_remarks(room_id,room_remarks)VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks = "234"

如果有就用新的替代,values如果不包含自增列,自增列的值會(huì)變化:REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")

備份表:CREATE TABLE user_info SELECT * FROM user_info

復(fù)制表結(jié)構(gòu):CREATE TABLE user_v2 LIKE user

從查詢語句中導(dǎo)入: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

2、鎖相關(guān)(作為了解,很少用)

共享鎖:select id from tb_test where id = 1 lock in share mode;

排它鎖:select id from tb_test where id = 1 for update

3、優(yōu)化時(shí)用到

強(qiáng)制使用某個(gè)索引:select * from table force index(idx_user) limit 2;

禁止使用某個(gè)索引:select * from table ignore index(idx_user) limit 2;

禁用緩存(在測試時(shí)去除緩存的影響):select SQL_NO_CACHE from table limit 2;

4、查看狀態(tài)

查看字符集:SHOW VARIABLES LIKE 'character_set%';

查看排序規(guī)則:SHOW VARIABLES LIKE 'collation%';

5、SQL編寫注意

where語句的解析順序是從右到左,條件盡量放where不要放having;

采用延遲關(guān)聯(lián)(deferred join)技術(shù)優(yōu)化超多分頁場景,比如limit 10000,10,延遲關(guān)聯(lián)可以避免回表;

distinct語句非常損耗性能,可以通過group by來優(yōu)化;

連表盡量不要超過三個(gè)表。

五、踩坑

如果有自增列,truncate語句會(huì)把自增列的基數(shù)重置為0,有些場景用自增列作為業(yè)務(wù)上的ID需要十分重視;

聚合函數(shù)會(huì)自動(dòng)濾空,比如a列的類型是int且全部是,則SUM(a)返回的是而不是0;

MySQL判斷相等不能用“a=”,這個(gè)結(jié)果永遠(yuǎn)為UnKnown,where和having中,UnKnown永遠(yuǎn)被視為false,check約束中,UnKnown就會(huì)視為true來處理。所以要用“a is ”處理。

六、千萬大表在線修改

MySQL在表數(shù)據(jù)量很大的時(shí)候,如果修改表結(jié)構(gòu)會(huì)導(dǎo)致鎖表,業(yè)務(wù)請求被阻塞。MySQL在5.6之后引入了在線更新,但是在某些情況下還是會(huì)鎖表,所以一般都采用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

七、慢查詢?nèi)罩?br>

有時(shí)候如果線上請求超時(shí),應(yīng)該去關(guān)注下慢查詢?nèi)罩荆樵兊姆治龊芎唵危日业铰樵內(nèi)罩疚募奈恢茫缓罄胢ysqldumpslow去分析。查詢慢查詢?nèi)罩拘畔⒖梢灾苯油ㄟ^執(zhí)行SQL命令查看相關(guān)變量,常用的SQL如下:

mysqldumpslow的工具十分簡單,我主要用到的是參數(shù)如下:

-t:限制輸出的行數(shù),我一般取前十條就夠了;

-s:根據(jù)什么來排序默認(rèn)是平均查詢時(shí)間at,我還經(jīng)常用到c查詢次數(shù),因?yàn)椴樵兇螖?shù)很頻繁但是時(shí)間不高也是有必要優(yōu)化的,還有t查詢時(shí)間,查看那個(gè)語句特別卡;

-v:輸出詳細(xì)信息。

例子:mysqldumpslow -v -s t -t 10

mysql_slow.log.2018-11-20-0500

八、查看SQL進(jìn)程和殺死進(jìn)程

如果你執(zhí)行了一個(gè)SQL的操作,但是遲遲沒有返回,你可以通過查詢進(jìn)程列表看看它的實(shí)際執(zhí)行狀況,如果該SQL十分耗時(shí),為了避免影響線上可以用kill命令殺死進(jìn)程,通過查看進(jìn)程列表也能直觀的看下當(dāng)前SQL的執(zhí)行狀態(tài);如果當(dāng)前數(shù)據(jù)庫負(fù)載很高,在進(jìn)程列表可能會(huì)出現(xiàn),大量的進(jìn)程夯住,執(zhí)行時(shí)間很長。

命令如下:

--查看進(jìn)程列表

SHOW PROCESSLIST;

--殺死某個(gè)進(jìn)程

kill 183665

如果你使用的SQLyog,那么也有圖形化的頁面,在菜單欄-工具-顯示-進(jìn)程列表。在進(jìn)程列表頁面可以右鍵殺死進(jìn)程。如下所示:

九、一些數(shù)據(jù)庫性能的思考

在對公司慢查詢?nèi)罩咀鰞?yōu)化的時(shí)候,很多時(shí)候可能是忘了建索引,像這種問題很容易解決,加個(gè)索引就行了。但是有幾種情況就不是簡單加索引能解決了:

1、業(yè)務(wù)代碼循環(huán)讀數(shù)據(jù)庫

考慮這樣一個(gè)場景,獲取用戶粉絲列表信息,加入分頁是十個(gè),其實(shí)像這樣的SQL是十分簡單的,通過連表查詢性能也很高。但是有時(shí)候,很多開發(fā)采用了取出一串ID,然后循環(huán)讀每個(gè)ID的信息,這樣如果ID很多對數(shù)據(jù)庫的壓力是很大的,而且性能也很低。

2、統(tǒng)計(jì)SQL

很多時(shí)候,業(yè)務(wù)上都會(huì)有排行榜這種,發(fā)現(xiàn)公司有很多地方直接采用數(shù)據(jù)庫做計(jì)算,在對一些大表的做聚合運(yùn)算的時(shí)候,經(jīng)常超過五秒,這些SQL一般很長而且很難優(yōu)化。像這種場景,如果業(yè)務(wù)允許(比如一致性要求不高或者是隔一段時(shí)間才統(tǒng)計(jì)的),可以專門在從庫里面做統(tǒng)計(jì)。另外我建議還是采用Redis緩存來處理這種業(yè)務(wù)。

3、超大分頁

在慢查詢?nèi)罩局邪l(fā)現(xiàn)了一些超大分頁的慢查詢?nèi)鏻imit 40000,1000,因?yàn)镸ySQL的分頁是在server層做的,可以采用延遲關(guān)聯(lián)在減少回表。但是看了相關(guān)的業(yè)務(wù)代碼正常的業(yè)務(wù)邏輯是不會(huì)出現(xiàn)這樣的請求的,所以很有可能是有惡意用戶在刷接口,最好在開發(fā)的時(shí)候也對接口加上校驗(yàn)攔截這些惡意請求。

這篇文章就總結(jié)到這里,希望能夠?qū)Υ蠹矣兴鶐椭”疚闹饕偨Y(jié)了工作中一些常用的操作及不合理的操作,在對慢查詢進(jìn)行優(yōu)化時(shí)收集的一些有用的資料和信息,本文適合有MySQL基礎(chǔ)的開發(fā)人員。


一、索引相關(guān)


1、索引基數(shù)


基數(shù)是數(shù)據(jù)列所包含的不同值的數(shù)量,例如,某個(gè)數(shù)據(jù)列包含值1、3、7、4、7、3,那么它的基數(shù)就是4。


索引的基數(shù)相對于數(shù)據(jù)表行數(shù)較高(也就是說,列中包含很多不同的值,重復(fù)的值很少)的時(shí)候,它的工作效果最好。


如果某數(shù)據(jù)列含有很多不同的年齡,索引會(huì)很快地分辨數(shù)據(jù)行;如果某個(gè)數(shù)據(jù)列用于記錄性別(只有“M”和“F”兩種值),那么索引的用處就不大;如果值出現(xiàn)的幾率幾乎相等,那么無論搜索哪個(gè)值都可能得到一半的數(shù)據(jù)行。


在這些情況下,最好根本不要使用索引,因?yàn)椴樵儍?yōu)化器發(fā)現(xiàn)某個(gè)值出現(xiàn)在表的數(shù)據(jù)行中的百分比很高的時(shí)候,它一般會(huì)忽略索引,進(jìn)行全表掃描。慣用的百分比界線是“30%”。


2、索引失效原因


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


類型錯(cuò)誤,如字段類型為varchar,where條件用number;


對索引應(yīng)用內(nèi)部函數(shù),這種情況下應(yīng)該要建立基于函數(shù)的索引。例如 select * from template t where ROUND (t.logicdb_id) = 1,此時(shí)應(yīng)該建ROUND (t.logicdb_id)為索引,MySQL8.0開始支持函數(shù)索引,5.7可以通過虛擬列的方式來支持,之前只能新建一個(gè)ROUND (t.logicdb_id)列然后去維護(hù);


如果條件有or,即使其中有條件帶索引也不會(huì)使用(這也是為什么建議少使用or的原因),如果想使用or,又想索引有效,只能將or條件中的每個(gè)列加上索引;


如果列類型是字符串,那一定要在條件中數(shù)據(jù)使用引號,否則不使用索引;


B-tree索引 is 不會(huì)走,is not 會(huì)走,位圖索引 is ,is not 都會(huì)走;


組合索引遵循最左原則。


3、索引的建立


最重要的肯定是根據(jù)業(yè)務(wù)經(jīng)常查詢的語句;


盡量選擇區(qū)分度高的列作為索引,區(qū)分度的公式是 COUNT(DISTINCT col) / COUNT(*),表示字段不重復(fù)的比率,比率越大我們掃描的記錄數(shù)就越少;


如果業(yè)務(wù)中唯一特性最好建立唯一鍵,一方面可以保證數(shù)據(jù)的正確性,另一方面索引的效率能大大提高。


二、EXPLIAN中有用的信息


1、基本用法


desc或者explain加上你的SQL;


extended explain加上你的SQL,然后通過show warnings可以查看實(shí)際執(zhí)行的語句,這一點(diǎn)也是非常有用的,很多時(shí)候不同的寫法經(jīng)SQL分析后,實(shí)際執(zhí)行的代碼是一樣的。


2、提高性能的特性


索引覆蓋(covering index):需要查詢的數(shù)據(jù)在索引上都可以查到不需要回表 EXTRA列顯示using index;


ICP特性(Index Condition Pushdown):本來index僅僅是data access的一種訪問模式,存數(shù)引擎通過索引回表獲取的數(shù)據(jù)會(huì)傳遞到MySQL Server層進(jìn)行where條件過濾。5.6版本開始當(dāng)ICP打開時(shí),如果部分where條件能使用索引的字段,MySQL Server會(huì)把這部分下推到引擎層,可以利用index過濾的where條件在存儲(chǔ)引擎層進(jìn)行數(shù)據(jù)過濾。EXTRA顯示using index condition。需要了解MySQL的架構(gòu)圖分為Server和存儲(chǔ)引擎層;


索引合并(index merge):對多個(gè)索引分別進(jìn)行條件掃描,然后將它們各自的結(jié)果進(jìn)行合并(intersect/union)。一般用OR會(huì)用到,如果是AND條件,考慮建立復(fù)合索引。EXPLAIN顯示的索引類型會(huì)顯示index_merge,EXTRA會(huì)顯示具體的合并算法和用到的索引。


3、extra字段


using filesort:說明MySQL會(huì)對數(shù)據(jù)使用一個(gè)外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。MySQL中無法利用索引完成的排序操作稱為“文件排序”,其實(shí)不一定是文件排序,內(nèi)部使用的是快排;


using temporary:使用了臨時(shí)表保存中間結(jié)果,MySQL在對查詢結(jié)果排序時(shí)使用臨時(shí)表。常見于排序order by和分組查詢group by;


using index:表示相應(yīng)的SELECT操作中使用了覆蓋索引(Covering Index),避免訪問了表的數(shù)據(jù)行,效率不錯(cuò);


impossible where:WHERE子句的值總是false,不能用來獲取任何元組;


select tables optimized away:在沒有GROUP BY子句的情況下基于索引優(yōu)化MIN/MAX操作或者對于MyISAM存儲(chǔ)引擎優(yōu)化COUNT(*)操作,不必等到執(zhí)行階段再進(jìn)行計(jì)算,查詢執(zhí)行計(jì)劃生成的階段即完成優(yōu)化;


distinct:優(yōu)化distinct操作,在找到第一匹配的元組后即停止找同樣值的操作。


using filesort、using temporary這兩項(xiàng)出現(xiàn)時(shí)需要注意下,這兩項(xiàng)是十分耗費(fèi)性能的,在使用group by的時(shí)候,雖然沒有使用order by,如果沒有索引,是可能同時(shí)出現(xiàn)using filesort,using temporary的,因?yàn)間roup by就是先排序在分組,如果沒有排序的需要,可以加上一個(gè)order by 來避免排序,這樣using filesort就會(huì)去除,能提升一點(diǎn)性能。


4、type字段


system:表只有一行記錄(等于系統(tǒng)表),這是const類型的特例,平時(shí)不會(huì)出現(xiàn);


const:如果通過索引依次就找到了,const用于比較主鍵索引或者unique索引。因?yàn)橹荒芷ヅ湟恍袛?shù)據(jù),所以很快。如果將主鍵置于where列表中,MySQL就能將該查詢轉(zhuǎn)換為一個(gè)常量;


eq_ref:唯一性索引掃描,對于每個(gè)索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描;


ref:非唯一性索引掃描,返回匹配某個(gè)單獨(dú)值的所有行。本質(zhì)上也是一種索引訪問,它返回所有匹配某個(gè)單獨(dú)值的行,然而它可能會(huì)找到多個(gè)符合條件的行,所以它應(yīng)該屬于查找和掃描的混合體;


range:只檢索給定范圍的行,使用一個(gè)索引來選擇行。key列顯示使用了哪個(gè)索引,一般就是在你的where語句中出現(xiàn)between、<、>、in等的查詢,這種范圍掃描索引比全表掃描要好,因?yàn)橹恍枰_始于縮印的某一點(diǎn),而結(jié)束于另一點(diǎn),不用掃描全部索引;


index:Full Index Scan ,index與ALL的區(qū)別為index類型只遍歷索引樹,這通常比ALL快,因?yàn)樗饕募ǔ1葦?shù)據(jù)文件小,也就是說雖然ALL和index都是讀全表,但index是從索引中讀取的,而ALL是從硬盤讀取的;


all:Full Table Scan,遍歷全表獲得匹配的行。


參考地址:


https://blog.csdn.net/DrDanger/article/details/79092808


三、字段類型和編碼


1)MySQL返回字符串長度


CHARACTER_LENGTH(同CHAR_LENGTH)方法返回的是字符數(shù),LENGTH函數(shù)返回的是字節(jié)數(shù),一個(gè)漢字三個(gè)字節(jié)。


2)varvhar等字段建立索引長度計(jì)算語句


select count(distinct left(test,5))/count(*) from table;越趨近1越好


3)MySQL的utf8


MySQL的utf8最大是3個(gè)字節(jié)不支持emoji表情符號,必須只用utf8mb4。需要在MySQL配置文件中配置客戶端字符集為utf8mb4。


JDBC的連接串不支持配置characterEncoding=utf8mb4,最好的辦法是在連接池中指定初始化SQL,例如:hikari連接池,其他連接池類似spring . datasource . hikari . connection - init - sql =set names utf8mb4。否則需要每次執(zhí)行SQL前都先執(zhí)行set names utf8mb4。


4)MySQL排序規(guī)則(一般使用_bin和_genera_ci)


utf8_genera_ci不區(qū)分大小寫,ci為case insensitive的縮寫,即大小寫不敏感;


utf8_general_cs區(qū)分大小寫,cs為case sensitive的縮寫,即大小寫敏感,但是目前MySQL版本中已經(jīng)不支持類似于***_genera_cs的排序規(guī)則,直接使用utf8_bin替代;


utf8_bin將字符串中的每一個(gè)字符用二進(jìn)制數(shù)據(jù)存儲(chǔ),區(qū)分大小寫。


那么,同樣是區(qū)分大小寫,utf8_general_cs和utf8_bin有什么區(qū)別?


cs為case sensitive的縮寫,即大小寫敏感;bin的意思是二進(jìn)制,也就是二進(jìn)制編碼比較;


utf8_general_cs排序規(guī)則下,即便是區(qū)分了大小寫,但是某些西歐的字符和拉丁字符是不區(qū)分的,比如?=a,但是有時(shí)并不需要?=a,所以才有utf8_bin;


utf8_bin的特點(diǎn)在于使用字符的二進(jìn)制的編碼進(jìn)行運(yùn)算,任何不同的二進(jìn)制編碼都是不同的,因此在utf8_bin排序規(guī)則下:?<>a。


5)sql yog中初始連接指定編碼類型使用連接配置的初始化命令




四、SQL語句總結(jié)


1、常用但容易忘的


如果有主鍵或者唯一鍵沖突則不插入:insert ignore into


如果有主鍵或者唯一鍵沖突則更新,注意這個(gè)會(huì)影響自增的增量:INSERT INTO room_remarks(room_id,room_remarks)VALUE(1,"sdf") ON DUPLICATE KEY UPDATE room_remarks = "234"


如果有就用新的替代,values如果不包含自增列,自增列的值會(huì)變化:REPLACE INTO room_remarks(room_id,room_remarks) VALUE(1,"sdf")


備份表:CREATE TABLE user_info SELECT * FROM user_info


復(fù)制表結(jié)構(gòu):CREATE TABLE user_v2 LIKE user


從查詢語句中導(dǎo)入: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


2、鎖相關(guān)(作為了解,很少用)


共享鎖:select id from tb_test where id = 1 lock in share mode;


排它鎖:select id from tb_test where id = 1 for update


3、優(yōu)化時(shí)用到


強(qiáng)制使用某個(gè)索引:select * from table force index(idx_user) limit 2;


禁止使用某個(gè)索引:select * from table ignore index(idx_user) limit 2;


禁用緩存(在測試時(shí)去除緩存的影響):select SQL_NO_CACHE from table limit 2;


4、查看狀態(tài)


查看字符集:SHOW VARIABLES LIKE 'character_set%';


查看排序規(guī)則:SHOW VARIABLES LIKE 'collation%';


5、SQL編寫注意


where語句的解析順序是從右到左,條件盡量放where不要放having;


采用延遲關(guān)聯(lián)(deferred join)技術(shù)優(yōu)化超多分頁場景,比如limit 10000,10,延遲關(guān)聯(lián)可以避免回表;


distinct語句非常損耗性能,可以通過group by來優(yōu)化;


連表盡量不要超過三個(gè)表。


五、踩坑


如果有自增列,truncate語句會(huì)把自增列的基數(shù)重置為0,有些場景用自增列作為業(yè)務(wù)上的ID需要十分重視;


聚合函數(shù)會(huì)自動(dòng)濾空,比如a列的類型是int且全部是,則SUM(a)返回的是而不是0;


MySQL判斷相等不能用“a=”,這個(gè)結(jié)果永遠(yuǎn)為UnKnown,where和having中,UnKnown永遠(yuǎn)被視為false,check約束中,UnKnown就會(huì)視為true來處理。所以要用“a is ”處理。


六、千萬大表在線修改


MySQL在表數(shù)據(jù)量很大的時(shí)候,如果修改表結(jié)構(gòu)會(huì)導(dǎo)致鎖表,業(yè)務(wù)請求被阻塞。MySQL在5.6之后引入了在線更新,但是在某些情況下還是會(huì)鎖表,所以一般都采用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


七、慢查詢?nèi)罩?/p>


有時(shí)候如果線上請求超時(shí),應(yīng)該去關(guān)注下慢查詢?nèi)罩荆樵兊姆治龊芎唵危日业铰樵內(nèi)罩疚募奈恢茫缓罄胢ysqldumpslow去分析。查詢慢查詢?nèi)罩拘畔⒖梢灾苯油ㄟ^執(zhí)行SQL命令查看相關(guān)變量,常用的SQL如下:




mysqldumpslow的工具十分簡單,我主要用到的是參數(shù)如下:


-t:限制輸出的行數(shù),我一般取前十條就夠了;


-s:根據(jù)什么來排序默認(rèn)是平均查詢時(shí)間at,我還經(jīng)常用到c查詢次數(shù),因?yàn)椴樵兇螖?shù)很頻繁但是時(shí)間不高也是有必要優(yōu)化的,還有t查詢時(shí)間,查看那個(gè)語句特別卡;


-v:輸出詳細(xì)信息。


例子:mysqldumpslow -v -s t -t 10

mysql_slow.log.2018-11-20-0500


八、查看SQL進(jìn)程和殺死進(jìn)程


如果你執(zhí)行了一個(gè)SQL的操作,但是遲遲沒有返回,你可以通過查詢進(jìn)程列表看看它的實(shí)際執(zhí)行狀況,如果該SQL十分耗時(shí),為了避免影響線上可以用kill命令殺死進(jìn)程,通過查看進(jìn)程列表也能直觀的看下當(dāng)前SQL的執(zhí)行狀態(tài);如果當(dāng)前數(shù)據(jù)庫負(fù)載很高,在進(jìn)程列表可能會(huì)出現(xiàn),大量的進(jìn)程夯住,執(zhí)行時(shí)間很長。


命令如下:


--查看進(jìn)程列表


SHOW PROCESSLIST;


--殺死某個(gè)進(jìn)程


kill 183665


如果你使用的SQLyog,那么也有圖形化的頁面,在菜單欄-工具-顯示-進(jìn)程列表。在進(jìn)程列表頁面可以右鍵殺死進(jìn)程。如下所示:






九、一些數(shù)據(jù)庫性能的思考


在對公司慢查詢?nèi)罩咀鰞?yōu)化的時(shí)候,很多時(shí)候可能是忘了建索引,像這種問題很容易解決,加個(gè)索引就行了。但是有幾種情況就不是簡單加索引能解決了:


1、業(yè)務(wù)代碼循環(huán)讀數(shù)據(jù)庫


考慮這樣一個(gè)場景,獲取用戶粉絲列表信息,加入分頁是十個(gè),其實(shí)像這樣的SQL是十分簡單的,通過連表查詢性能也很高。但是有時(shí)候,很多開發(fā)采用了取出一串ID,然后循環(huán)讀每個(gè)ID的信息,這樣如果ID很多對數(shù)據(jù)庫的壓力是很大的,而且性能也很低。


2、統(tǒng)計(jì)SQL


很多時(shí)候,業(yè)務(wù)上都會(huì)有排行榜這種,發(fā)現(xiàn)公司有很多地方直接采用數(shù)據(jù)庫做計(jì)算,在對一些大表的做聚合運(yùn)算的時(shí)候,經(jīng)常超過五秒,這些SQL一般很長而且很難優(yōu)化。像這種場景,如果業(yè)務(wù)允許(比如一致性要求不高或者是隔一段時(shí)間才統(tǒng)計(jì)的),可以專門在從庫里面做統(tǒng)計(jì)。另外我建議還是采用Redis緩存來處理這種業(yè)務(wù)。


3、超大分頁


在慢查詢?nèi)罩局邪l(fā)現(xiàn)了一些超大分頁的慢查詢?nèi)鏻imit 40000,1000,因?yàn)镸ySQL的分頁是在server層做的,可以采用延遲關(guān)聯(lián)在減少回表。但是看了相關(guān)的業(yè)務(wù)代碼正常的業(yè)務(wù)邏輯是不會(huì)出現(xiàn)這樣的請求的,所以很有可能是有惡意用戶在刷接口,最好在開發(fā)的時(shí)候也對接口加上校驗(yàn)攔截這些惡意請求。


這篇文章就總結(jié)到這里,希望能夠?qū)Υ蠹矣兴鶐椭?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容