這篇文章主要講解了“MySQL中SQL優(yōu)化、索引優(yōu)化、鎖機(jī)制、主從復(fù)制的方法”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“MySQL中SQL優(yōu)化、索引優(yōu)化、鎖機(jī)制、主從復(fù)制的方法”吧!
專注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)墨玉免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了近千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
myisam存儲:如果表對事務(wù)要求不高,同時(shí)是以查詢和添加為主的,我們考慮使用myisam存儲引擎,比如bbs 中的發(fā)帖表,回復(fù)表
需要定時(shí)進(jìn)行碎片整理(因?yàn)閯h除的數(shù)據(jù)還是存在):optimize table table_name;
InnoDB存儲:對事務(wù)要求高,保存的數(shù)據(jù)都是重要數(shù)據(jù),我們建議使用INN0DB,比如訂單表,賬號表.
面試問MyISAM和INNODB的區(qū)別:
1.事務(wù)安全
2.查詢和添加速度
3.支持全文索引
4.鎖機(jī)制
5.外鍵MyISAM不支持外鍵,INNODB 支持外鍵.
Mermory存儲:比如我們數(shù)據(jù)變化頻繁,不需要入庫,同時(shí)又頻繁的查詢和修改,我們考慮使用memory
查看mysql以提供什么存儲引擎:show engines;
查看mysql當(dāng)前默認(rèn)的存儲引擎:show variables like '%storage_engine%';
SQL性能下降原因:
1、查詢語句寫的爛
2、索引失效(數(shù)據(jù)變更)
3、關(guān)聯(lián)查詢太多join(設(shè)計(jì)缺陷或不得已的需求)
4、服務(wù)器調(diào)優(yōu)及各個參數(shù)設(shè)置(緩沖、線程數(shù)等)
通常SQL調(diào)優(yōu)過程:
觀察,至少跑1天,看看生產(chǎn)的慢SQL情況。
開啟慢查詢?nèi)罩?,設(shè)置闕值,比如超過5秒鐘的就是慢SQL,并將它抓取出來。
explain + 慢SQL分析。
show profile。
運(yùn)維經(jīng)理 or DBA,進(jìn)行SQL數(shù)據(jù)庫服務(wù)器的參數(shù)調(diào)優(yōu)。
總結(jié):
1、慢查詢的開啟并捕獲
2、explain + 慢SQL分析
3、show profile查詢SQL在Mysql服務(wù)器里面的執(zhí)行細(xì)節(jié)和生命周期情況
4、SQL數(shù)據(jù)庫服務(wù)器的參數(shù)調(diào)優(yōu)
手寫順序:
SELECT DISTINCT <select_list>FROM <left_table> <join_type>JOIN <right_table> on <join_codition> //join_codition:比如員工的部門ID和部門表的主鍵id相同WHERE <where_condition>GROUP BY <group_by_list>HAVING <having_condition>ORDER BY <order_by_condition>LIMIT <limit_number>
MySQL機(jī)讀順序:
1 FROM <left_table> 2 ON <join_condition> 3 <join_type> JOIN <right_table> 4 WHERE <where_condition> 5 GROUP BY <group_by_list> 6 HAVING <having_condition> 7 SELECT 8 DISTINCT <select_list> 9 ORDER BY <order_by_condition> 10 LIMIT <limit_number>
總結(jié):
運(yùn)行順序一上一下
創(chuàng)建表插入數(shù)據(jù)(左右主外鍵相連
):
CREATE TABLE tbl_dept( id INT(11) NOT NULL AUTO_INCREMENT, deptName VARCHAR(30) DEFAULT NULL, locAdd VARCHAR(40) DEFAULT NULL, PRIMARY KEY(id))ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;//設(shè)置存儲引擎,主鍵自動增長和默認(rèn)文本字符集CREATE TABLE tbl_emp ( id INT(11) NOT NULL AUTO_INCREMENT, NAME VARCHAR(20) DEFAULT NULL, deptId INT(11) DEFAULT NULL, PRIMARY KEY (id), KEY fk_dept_Id (deptId) #CONSTRAINT 'fk_dept_Id' foreign key ('deptId') references 'tbl_dept'('Id'))ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11);INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12);INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13);INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14);INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15);INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1);INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1);INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1);INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2);INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2);INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3);INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4);INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51);#查詢執(zhí)行后結(jié)果mysql> select * from tbl_dept;+----+----------+--------+| id | deptName | locAdd |+----+----------+--------+| 1 | RD | 11 || 2 | HR | 12 || 3 | MK | 13 || 4 | MIS | 14 || 5 | FD | 15 |+----+----------+--------+5 rows in set (0.00 sec)mysql> select * from tbl_emp;+----+------+--------+| id | NAME | deptId |+----+------+--------+| 1 | z3 | 1 || 2 | z4 | 1 || 3 | z5 | 1 || 4 | w5 | 2 || 5 | w6 | 2 || 6 | s7 | 3 || 7 | s8 | 4 || 8 | s9 | 51 |+----+------+--------+8 rows in set (0.00 sec)
1、inner join:只有 deptId 和 id 的共有部分
2、left join(全A):前七條共有數(shù)據(jù);第八條a表獨(dú)有數(shù)據(jù),b表補(bǔ)null
3、right join(全B):前七條共有數(shù)據(jù);第八條b表獨(dú)有數(shù)據(jù),a表補(bǔ)null
4、左join獨(dú)A:表A獨(dú)有部分
5、右join獨(dú)B:表B獨(dú)有部分
6、full join:MySQL不支持full join,用全a+全b,union去重中間部分
union關(guān)鍵字可以合并去重
7、A、B各自獨(dú)有集合
MySQL官方對索引的定義為:索引(Index)是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)(索引的本質(zhì)是數(shù)據(jù)結(jié)構(gòu),排序+查詢兩種功能)。
索引的目的在于提高查詢效率,可以類比字典。
如果要查“mysql”這個單詞,我們肯定需要定位到m字母,然后從下往下找到y(tǒng)字母,再找到剩下的sql。
如果沒有索引,那么你可能需要逐個逐個尋找,如果我想找到Java開頭的單詞呢?或者Oracle開頭的單詞呢?
是不是覺得如果沒有索引,這個事情根本無法完成?
索引可以理解為:排好序的快速查找數(shù)據(jù)結(jié)構(gòu)
下圖就是一種可能的索引方式示例:
假如:找4號這本書,掃碼得到對應(yīng)的編號為91,91比34大往右邊找,91比89大往右邊找,然后找到(比較三次后就可以找到,然后檢索出對應(yīng)的物理地址)
為了加快Col2的查找,可以維護(hù)一個右邊所示的二叉查找樹,每個節(jié)點(diǎn)分別包含索引鍵值和一個指向?qū)?yīng)數(shù)據(jù)記錄物理地址的指針,這樣就可以運(yùn)用二叉查找在一定的復(fù)雜度內(nèi)獲取到相應(yīng)數(shù)據(jù),從而快速的檢索出符合條件的記錄
結(jié)論:在數(shù)據(jù)之外,數(shù)據(jù)庫系統(tǒng)還維護(hù)著滿足特定查找算法的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用(指向)數(shù)據(jù),這樣就可以在這些數(shù)據(jù)結(jié)構(gòu)上實(shí)現(xiàn)高級查找算法。這種數(shù)據(jù)結(jié)構(gòu),就是索引
一般來說索引本身也很大,不可能全部存儲在內(nèi)存中,因此索引往往以索引文件的形式存儲的磁盤上。
我們平常所說的索引,如果沒有特別指明,都是指B樹(多路搜索樹,并不一定是二叉的)結(jié)構(gòu)組織的索引。其中聚集索引,次要索引,覆蓋索引,復(fù)合索引,前綴索引,唯一索引默認(rèn)都是使用B+樹索引,統(tǒng)稱索引。當(dāng)然,除了B+樹這種類型的索引之外,還有哈稀索引(hash index)等
優(yōu)勢:
類似大學(xué)圖書館建書目索引,提高數(shù)據(jù)檢索的效率,降低數(shù)據(jù)庫的IO成本。
通過索引列對數(shù)據(jù)進(jìn)行排序,降低數(shù)據(jù)排序的成本,降低了CPU的消耗。
劣勢:
實(shí)際上索引也是一張表,該表保存了主鍵與索引字段,并指向?qū)嶓w表的記錄,所以索引列也是要占用空間的(占空間)
雖然索引大大提高了查詢速度,同時(shí)卻會降低更新表的速度,如對表進(jìn)行INSERT、UPDATE和DELETE。因?yàn)楦卤頃r(shí),MySQL不僅要保存數(shù)據(jù),還要保存一下索引文件每次更新添加了索引列的字段,都會調(diào)整因?yàn)楦滤鶐淼逆I值變化后的索引信息。
索引只是提高效率的一個因素,如果你的MysQL有大數(shù)據(jù)量的表,就需要花時(shí)間研究建立最優(yōu)秀的索引,或優(yōu)化查詢
主鍵索引:索引值必須是唯一的,且不能為NULL
第一種:CREATE TABLE table_name(id int PRIMARY KEY aoto_increment,name varchar(10));
第二種: ALTER TABLE table_name ADD PRIMARY KEY (columnName);
普通索引:索引值可出現(xiàn)多次
第一種:CREATE INDEX index_name on table_name(columnName);
第二種:ALTER TABLE table_name ADD INDEX index_name (columnName);
全文索引:主要是針對文本的檢索,如:文章,全文索引只針對MyISAM引擎有效,并且只針對英文內(nèi)容生效
建表時(shí)創(chuàng)建
#建表CREATE TABLE articles( id INT UNSIGNED ATUO_INCREMENT NOT NULL PRIMARY KEY, title VARCHAR(200), body TEXT, FULLTEXT(title,body))engine=myisam charset utf8; #指定引擎#使用select * from articles where match(title,body) against('英文內(nèi)容'); #只針對英語內(nèi)容生效#說明#1、在mysql中fultext索引只針對 myisam 生效#2、mysq1自己提供的flltext只針對英文生效->sphinx (coreseek)技術(shù)處理中文工#3、使用方法是match(字段名...) against(‘關(guān)鍵字')#4、全文索引一個叫停止詞,因?yàn)樵谝粋€文本中創(chuàng)建索引是一個無窮大的數(shù),因此對一些常用詞和字符就不會創(chuàng)建,這些詞稱為停止詞
ALTER TABLE table_name ADD FULLTEXT index_name (columnName);
唯一索引:索引列的值必須唯一,但允許有空值NULL,并可以有多個。
第一種: CREATE UNIQUE INDEX index_name ON table_name(columnName);
第二種:ALTER TABLE table_name ADD UNIQUE INDEX index_name ON (columnName);
單值索引:即一個索引只包含單個列,一個表可以有多個單列索引。
第一種: CREATE INDEX index_name ON table_name(columnName);
第二種:ALTER TABLE table_name ADD INDEX index_name ON (columnName);
select * from user where name=''; //經(jīng)常查name字段,為其建索引create index idx_user_name on user(name);
復(fù)合索引:即一個索引包含多個列
第一種: CREATE INDEX index_name ON table_name(columnName1,columnName2...);
第二種:ALTER TABLE table_name ADD INDEX index_name ON (columnName1,columnName2...);
select * from user where name='' and email=''; //經(jīng)常查name和email字段,為其建索引create index idx_user_name on user(name, email);
查詢索引
第一種:SHOW INDEX FROM table_name;
第二種:SHOW KEYS FROM table_name;
刪除索引
第一種: DROP INDEX index_name ON table_name;
第二種:ALTER TABLE table_name DROP INDEX index_name;
刪除主鍵索引:ALTER TBALE table_name DROP PRIMARY KEY;
MySQL索引結(jié)構(gòu):
BTree索引
Hash索引
full-text全文索引
R-Tree索引
初始化介紹
一顆b+樹,淺藍(lán)色的塊我們稱之為一個磁盤塊,可以看到每個磁盤塊包含幾個數(shù)據(jù)項(xiàng)(深藍(lán)色所示)和指針(黃色所示),如磁盤塊1包含數(shù)據(jù)項(xiàng)17和35,包含指針P1、P2、P3,
P1表示小于17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大于35的磁盤塊。
真實(shí)的數(shù)據(jù)存在于葉子節(jié)點(diǎn):3、5、9、10、13、15、28、29、36、60、75、79、90、99。
非葉子節(jié)點(diǎn)只不存儲真實(shí)的數(shù)據(jù),只存儲指引搜索方向的數(shù)據(jù)項(xiàng),如17、35并不真實(shí)存在于數(shù)據(jù)表中。
查找過程
如果要查找數(shù)據(jù)項(xiàng)29,那么首先會把磁盤塊1由磁盤加載到內(nèi)存,此時(shí)發(fā)生一次IO。在內(nèi)存中用二分查找確定 29 在 17 和 35 之間,鎖定磁盤塊1的P2指針,內(nèi)存時(shí)間因?yàn)榉浅6蹋ㄏ啾却疟P的IO)可以忽略不計(jì),通過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內(nèi)存,發(fā)生第二次IO,29 在 26 和 30 之間,鎖定磁盤塊3的P2指針,通過指針加載磁盤塊8到內(nèi)存,發(fā)生第三次IO,同時(shí)內(nèi)存中做二分查找找到29,結(jié)束查詢,總計(jì)三次IO
真實(shí)的情況是,3層的b+樹可以表示上百萬的數(shù)據(jù),如果上百萬的數(shù)據(jù)查找只需要三次IO,性能提高將是巨大的,如果沒有索引,每個數(shù)據(jù)項(xiàng)都要發(fā)生一次IO,那么總共需要百萬次的IO,顯然成本非常非常高
主鍵自動建立唯一索引
頻繁作為查詢條件的字段應(yīng)該創(chuàng)建索引
查詢中與其它表關(guān)聯(lián)的字段,外鍵關(guān)系建立索引
單鍵/組合索引的選擇問題,who?(在高并發(fā)下傾向創(chuàng)建組合索引)
查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序速度
查詢中統(tǒng)計(jì)或者分組字段
Where條件里用不到的字段不創(chuàng)建索引
表記錄太少(300w以上建)
經(jīng)常增刪改的表(提高了查詢速度,同時(shí)卻會降低更新表的速度,如對表進(jìn)行INSERT、UPDATE和DELETE。因?yàn)楦卤頃r(shí),MySQL不僅要保存數(shù)據(jù),還要保存一下索引文件)
數(shù)據(jù)重復(fù)且分布平均的表字段,因此應(yīng)該只為最經(jīng)常查詢和最經(jīng)常排序的數(shù)據(jù)列建立索引。注意,如果某個數(shù)據(jù)列包含許多重復(fù)的內(nèi)容,為它建立索引就沒有太大的實(shí)際效果。(比如:國籍、性別)
假如一個表有10萬行記錄,有一個字段A只有T和F兩種值,且每個值的分布概率天約為50%,那么對這種表A字段建索引一般不會提高數(shù)據(jù)庫的查詢速度。
索引的選擇性是指索引列中不同值的數(shù)目與表中記錄數(shù)的比。如果一個表中有2000條記錄,表索引列有1980個不同的值,那么這個索引的選擇性就是1980/2000=0.99。一個索引的選擇性越接近于1,這個索引的效率就越高
MySQL Query Optimizer(查詢優(yōu)化器)[?kw??ri] [??pt?ma?z?]
Mysql中專門負(fù)責(zé)優(yōu)化SELECT語句的優(yōu)化器模塊,主要功能:通過計(jì)算分析系統(tǒng)中收集到的統(tǒng)計(jì)信息,為客戶端請求的Query提供他認(rèn)為最優(yōu)的執(zhí)行計(jì)劃(他認(rèn)為最優(yōu)的數(shù)據(jù)檢索方式,但不見得是DBA認(rèn)為是最優(yōu)的,這部分最耗費(fèi)時(shí)間)
當(dāng)客戶端向MySQL請求一條Query,命令解析器模塊完成請求分類,區(qū)別出是SELECT并轉(zhuǎn)發(fā)給MySQL Query Optimizer時(shí),MySQL Query Optimizer首先會對整條Query進(jìn)行優(yōu)化,處理掉一些常量表達(dá)式的預(yù)算直接換算成常量值。并對Query中的查詢條件進(jìn)行簡化和轉(zhuǎn)換,如去掉一些無用或顯而易見的條件、結(jié)構(gòu)調(diào)整等。然后分析Query 中的 Hint信息(如果有),看顯示Hint信息是否可以完全確定該Query的執(zhí)行計(jì)劃。如果沒有Hint 或Hint信息還不足以完全確定執(zhí)行計(jì)劃,則會讀取所涉及對象的統(tǒng)計(jì)信息,根據(jù)Query進(jìn)行寫相應(yīng)的計(jì)算分析,然后再得出最后的執(zhí)行計(jì)劃
MySQL常見瓶頸:
CPU:CPU在飽和的時(shí)候一般發(fā)生在數(shù)據(jù)裝入內(nèi)存或從磁盤上讀取數(shù)據(jù)時(shí)候
IO:磁盤I/O瓶頸發(fā)生在裝入數(shù)據(jù)遠(yuǎn)大于內(nèi)存容量的時(shí)候
服務(wù)器硬件的性能瓶頸:top,free,iostat和vmstat來查看系統(tǒng)的性能狀態(tài)
使用EXPLAIN關(guān)鍵字可以模擬優(yōu)化器執(zhí)行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的。分析你的查詢語句或是表結(jié)構(gòu)的性能瓶頸
官網(wǎng)地址
Explain的作用:
表的讀取順序
數(shù)據(jù)讀取操作的操作類型
哪些索引可以使用
哪些索引被實(shí)際使用
表之間的引用
每張表有多少行被優(yōu)化器查詢
使用Explain:
explain + sql語句
執(zhí)行計(jì)劃包含的信息(重點(diǎn)
) :| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
mysql> select * from tbl_emp; +----+------+--------+ | id | NAME | deptId | +----+------+--------+ | 1 | z3 | 1 | | 2 | z4 | 1 | | 3 | z5 | 1 | | 4 | w5 | 2 | | 5 | w6 | 2 | | 6 | s7 | 3 | | 7 | s8 | 4 | | 8 | s9 | 51 | +----+------+--------+ 8 rows in set (0.00 sec) mysql> explain select * from tbl_emp; +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ | 1 | SIMPLE | tbl_emp | NULL | ALL | NULL | NULL | NULL | NULL | 8 | 100.00 | NULL | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec)
執(zhí)行計(jì)劃包含的信息(重點(diǎn)
) :| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
面試重點(diǎn):id、type、key、rows、Extra
select查詢的序列號,包含一組數(shù)字,表示查詢中執(zhí)行select子句或操作表的順序
三種情況:
1、id相同,執(zhí)行順序由上至下(t1、t3、t2)
2、id不同,如果是子查詢,id的序號會遞增,id值越大優(yōu)先級越高,越先被執(zhí)行(t3、t1、t2)
3、id相同不同,同時(shí)存在。先走數(shù)字大的,數(shù)字相同的由上至下(t3、s1、t2)
查詢的類型,主要是用于區(qū)別普通查詢、聯(lián)合查詢、子查詢等的復(fù)雜查詢。
SIMPLE [?s?npl] :簡單的select查詢,查詢中不包含子查詢或者UNION
PRIMARY:查詢中若包含任何復(fù)雜的子部分,最外層查詢則被標(biāo)記為(最后加載的那個)
SUBQUERY [?kw??ri] :在SELECT或WHERE列表中包含了子查詢
DERIVED [d??ra?vd]:在FROM列表中包含的子查詢被標(biāo)記為DERIVED(衍生)MySQL會遞歸執(zhí)行這些子查詢,把結(jié)果放在臨時(shí)表里
UNION [?ju?ni?n]:若第二個SELECT出現(xiàn)在UNION之后,則被標(biāo)記為UNION;若UNION包含在FROM子句的子查詢中外層SELECT將被標(biāo)記為:DERIVED
UNION RESULT [r??z?lt] :從UNION表獲取結(jié)果的SELECT(兩個select語句用UNION合并)
顯示這一行的數(shù)據(jù)是關(guān)于哪張表的
顯示查詢使用了何種類型
訪問類型排列:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index >ALL
type常用八種類型:
結(jié)果值從最好到最壞依次是(重點(diǎn)):
:system > const > eq_ref > ref > range > index > ALL
一般來說,得保證查詢至少達(dá)到range級別,最好能達(dá)到ref
詳細(xì)說明
system:表只有一行記錄(等于系統(tǒng)表),這是const類型的特列,平時(shí)不會出現(xiàn),這個也可以忽略不計(jì)。
const:表示通過索引一次就找到了,const用于比較primary key或者unique索引。因?yàn)橹黄ヅ湟恍袛?shù)據(jù),所以很快如將主鍵置于where列表中,MySQL就能將該查詢轉(zhuǎn)換為一個常量。
eq_ref:唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描。
ref:非唯一性索引掃描,返回匹配某個單獨(dú)值的所有行,本質(zhì)上也是一種索引訪問,它返回所有匹配某個單獨(dú)值的行,然而,它可能會找到多個符合條件的行,所以他應(yīng)該屬于查找和掃描的混合體
range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引一般就是在你的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,將遍歷全表以找到匹配的行
工作案例:經(jīng)理這條SQL我跑了一下Explain分析,在系統(tǒng)上可能會有ALL全表掃描的情況,建議嘗試一下優(yōu)化。我把這條SQL改了改,我優(yōu)化后是這么寫,這個效果已經(jīng)從ALL變成了…
顯示可能應(yīng)用在這張表中的索引,一個或多個。查詢涉及到的字段火若存在索引,則該索引將被列出,但不一定被查詢實(shí)際使用(系統(tǒng)認(rèn)為理論上會使用某些索引)
實(shí)際使用的索引。如果為NULL,則沒有使用索引(要么沒建,要么建了失效)
查詢中若使用了覆蓋索引,則該索引僅出現(xiàn)在key列表中
覆蓋索引:建的索引字段和查詢的字段一致,如下圖
表示索引中使用的字節(jié)數(shù),可通過該列計(jì)算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好
key_len顯示的值為索引字段的最大可能長度,并非實(shí)際使用長度,即key_len是根據(jù)表定義計(jì)算而得,不是通過表內(nèi)檢索出的
顯示索引的哪一列被使用了,如果可能的話,是一個常數(shù)。哪些列或常量被用于查找索引列上的值。
根據(jù)表統(tǒng)計(jì)信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數(shù)(越小越好)
未建索引時(shí):
建索引后:掃描行數(shù)減少
包含不適合在其他列中顯示但十分重要的額外信息
信息種類:Using filesort 、Using temporary 、Using index 、Using where 、Using join buffer 、impossible where 、select tables optimized away 、distinct
Using filesort(需要優(yōu)化)
說明mysql會對數(shù)據(jù)使用一個外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。MySQL中無法利用索引完成的排序操作稱為"文件排序"
Using temporary(需要優(yōu)化)
使了用臨時(shí)表保存中間結(jié)果,MysQL在對查詢結(jié)果排序時(shí)使用臨時(shí)表。常見于排序order by和分組查詢group by
Using index(good)
表示相應(yīng)的select操作中使用了覆蓋索引(Covering Index),避免訪問了表的數(shù)據(jù)行,效率不錯!
情況一:
情況二:
覆蓋索引 / 索引覆蓋(Covering Index)。
理解方式一:就是select的數(shù)據(jù)列只用從索引中就能夠取得,不必讀取數(shù)據(jù)行,MySQL可以利用索引返回select列表中的字段,而不必根據(jù)索引再次讀取數(shù)據(jù)文件,換句話說查詢列要被所建的索引覆蓋。
理解方式二:索引是高效找到行的一個方法,但是一般數(shù)據(jù)庫也能使用索引找到一個列的數(shù)據(jù),因此它不必讀取整個行。畢竟索引葉子節(jié)點(diǎn)存儲了它們索引的數(shù)據(jù);當(dāng)能通過讀取索引就可以得到想要的數(shù)據(jù),那就不需要讀取行了。一個索引包含了(或覆蓋了)滿足查詢結(jié)果的數(shù)據(jù)就叫做覆蓋索引。
注意
:
如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select*
因?yàn)槿绻麑⑺凶侄我黄鹱鏊饕龝?dǎo)致索引文件過大,查詢性能下降
Using where:表明使用了where過濾。
Using join buffer:使用了連接緩存
impossible where:where子句的值總是false,不能用來獲取任何元組
select tables optimized away
在沒有GROUPBY子句的情況下,基于索引優(yōu)化MIN/MAX操作,或者對于MyISAM存儲引擎優(yōu)化COUNT(*)操作,不必等到執(zhí)行階段再進(jìn)行計(jì)算,查詢執(zhí)行計(jì)劃生成的階段即完成優(yōu)化。
distinct
優(yōu)化distinct操作,在找到第一匹配的元組后即停止找同樣值的動作。
寫出下圖的表的執(zhí)行順序
第一行(執(zhí)行順序4):id列為1,表示是union里的第一個select,select_type列的primary表示該查詢?yōu)橥鈱硬樵?,table列被標(biāo)記為,表示查詢結(jié)果來自一個衍生表,其中derived3中3代表該查詢衍生自第三個select查詢,即id為3的select?!緎elect d1.name… 】
第二行(執(zhí)行順序2):id為3,是整個查詢中第三個select的一部分。因查詢包含在from中,所以為derived?!緎elect id,namefrom t1 where other_column=’’】
第三行(執(zhí)行順序3):select列表中的子查詢select_type為subquery,為整個查詢中的第二個select?!緎elect id from t3】
第四行(執(zhí)行順序1):select_type為union,說明第四個select是union里的第二個select,最先執(zhí)行【select name,id from t2】
第五行(執(zhí)行順序5):代表從union的臨時(shí)表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結(jié)果進(jìn)行union操作。【兩個結(jié)果union操作】
建表:
CREATE TABLE IF NOT EXISTS article( id INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, author_id INT(10) UNSIGNED NOT NULL, category_id INT(10) UNSIGNED NOT NULL, views INT(10) UNSIGNED NOT NULL, comments INT(10) UNSIGNED NOT NULL, title VARCHAR(255) NOT NULL, content TEXT NOT NULL ); INSERT INTO article(author_id,category_id,views,comments,title,content) VALUES (1,1,1,1,'1','1'), (2,2,2,2,'2','2'), (1,1,3,3,'3','3'); //查詢 mysql> select * from article; +----+-----------+-------------+-------+----------+-------+---------+ | id | author_id | category_id | views | comments | title | content | +----+-----------+-------------+-------+----------+-------+---------+ | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | 2 | 2 | 2 | 2 | 2 | 2 | 2 | | 3 | 1 | 1 | 3 | 3 | 3 | 3 | +----+-----------+-------------+-------+----------+-------+---------+ 3 rows in set (0.00 sec)
案例
要求:查詢 category_id 為 1 且 comments 大于1 的情況下,views 最多的 article_id
//功能實(shí)現(xiàn) mysql> SELECT id, author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; +----+-----------+ | id | author_id | +----+-----------+ | 3 | 1 | +----+-----------+ 1 row in set (0.00 sec) //explain分析 mysql> explain SELECT id, author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ | 1 | SIMPLE | article | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ 1 row in set, 1 warning (0.00 sec)
結(jié)論
:很顯然,type是ALL,即最壞的情況。Extra里還出現(xiàn)了Using filesort,也是最壞的情況。優(yōu)化是必須的
開始優(yōu)化
新建索引(給WHERE語句后使用的字段添加索引)
創(chuàng)建方式:
create index idx_article_ccv on article(category_id,comments,views);
ALTER TABLE 'article' ADD INDEX idx_article_ccv ( 'category_id , 'comments', 'views' );
索引用處不大,刪除:DROP INDEX idx_article_ccv ON article;
結(jié)論:
type變成了range,這是可以忍受的。但是extra里使用Using filesort仍是無法接受的。
但是我們已經(jīng)建立了索引,為啥沒用呢?
這是因?yàn)榘凑誃Tree索引的工作原理,先排序category_id,如果遇到相同的category_id則再排序comments,如果遇到相同的comments 則再排序views。
當(dāng)comments字段在聯(lián)合索引里處于中間位置時(shí),因comments > 1條件是一個范圍值(所謂range),MySQL無法利用索引再對后面的views部分進(jìn)行檢索,即range類型查詢字段后面的索引無效
。
改進(jìn)
上次創(chuàng)建索引相比,這次不為comments字段創(chuàng)建索引
結(jié)論:type變?yōu)榱藃ef,ref 中是 const,Extra 中的 Using filesort也消失了,結(jié)果非常理想
建表:
CREATE TABLE IF NOT EXISTS class( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id) ); CREATE TABLE IF NOT EXISTS book( bookid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(bookid) ); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO class(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO book(card) VALUES(FLOOR(1+(RAND()*20))); //查詢 mysql> select * from class; +----+------+ | id | card | +----+------+ | 1 | 17 | | 2 | 2 | | 3 | 18 | | 4 | 4 | | 5 | 4 | | 6 | 8 | | 7 | 9 | | 8 | 1 | | 9 | 18 | | 10 | 6 | | 11 | 15 | | 12 | 15 | | 13 | 12 | | 14 | 15 | | 15 | 18 | | 16 | 2 | | 17 | 18 | | 18 | 5 | | 19 | 7 | | 20 | 1 | | 21 | 2 | +----+------+ 21 rows in set (0.00 sec) mysql> select * from book; +--------+------+ | bookid | card | +--------+------+ | 1 | 8 | | 2 | 14 | | 3 | 3 | | 4 | 16 | | 5 | 8 | | 6 | 12 | | 7 | 17 | | 8 | 8 | | 9 | 10 | | 10 | 3 | | 11 | 4 | | 12 | 12 | | 13 | 9 | | 14 | 7 | | 15 | 6 | | 16 | 8 | | 17 | 3 | | 18 | 11 | | 19 | 5 | | 20 | 11 | +--------+------+ 20 rows in set (0.00 sec)
開始Explain分析:type都是all,需要優(yōu)化(總有一個表來添加索引驅(qū)動)
左連接為左表加索引
刪除索引:drop index y on class;
左連接為右表添加索引
刪除索引:drop index Y on book;
案例:如果別人建的索引位置不對,只需要自己查詢時(shí)調(diào)整左右表的順序即可
結(jié)論:
第二行的type變?yōu)榱藃ef,rows也變少了,優(yōu)化比較明顯。這是由左連接特性決定的。LEFT JOIN條件用于確定如何從右表搜索行,左邊一定都有,所以右邊是我們的關(guān)鍵點(diǎn),一定需要在右表建立索引
(小表驅(qū)動大表)。
左連接,右表加索引
同理:右連接,左表加索引
建表:
CREATE TABLE IF NOT EXISTS phone( phoneid INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, card INT(10) UNSIGNED NOT NULL, PRIMARY KEY(phoneid) )ENGINE=INNODB; INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); INSERT INTO phone(card) VALUES(FLOOR(1+(RAND()*20))); //查詢 mysql> select * from phone; +---------+------+ | phoneid | card | +---------+------+ | 1 | 10 | | 2 | 13 | | 3 | 17 | | 4 | 5 | | 5 | 12 | | 6 | 7 | | 7 | 15 | | 8 | 17 | | 9 | 17 | | 10 | 14 | | 11 | 19 | | 12 | 13 | | 13 | 5 | | 14 | 8 | | 15 | 2 | | 16 | 8 | | 17 | 11 | | 18 | 14 | | 19 | 13 | | 20 | 5 | +---------+------+ 20 rows in set (0.00 sec)
用上一節(jié)兩個表,刪除他們的索引:
三表查詢語句應(yīng)為:SELECT * FROM class LEFT JOIN book ON class.card = book.card LEFT JOIN phone ON book.card = phone.card;
創(chuàng)建索引:
應(yīng)該為第一個LFET JOIN 的右表 book 建索引
alter table `book` add index Y(`card`);
應(yīng)該為第二個LFET JOIN 的右表 phone 建索引
alter table `phone` add index z(`card`);
Explain分析:
后2行的 type 都是ref且總 rows優(yōu)化很好,效果不錯。因此索引最好設(shè)置在需要經(jīng)常查詢的字段中
結(jié)論:
Join語句的優(yōu)化
盡可能減少Join語句中的NestedLoop的循環(huán)總次數(shù):“永遠(yuǎn)用小結(jié)果集驅(qū)動大的結(jié)果集
(比如:書的類型表驅(qū)動書的名稱表)”。
優(yōu)先優(yōu)化NestedLoop的內(nèi)層循環(huán),保證Join語句中被驅(qū)動表上Join條件字段已經(jīng)被索引。
當(dāng)無法保證被驅(qū)動表的Join條件字段被索引且內(nèi)存資源充足的前提下,不要太吝惜JoinBuffer的設(shè)置
建表:
CREATE TABLE staffs( id INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(24) NOT NULL DEFAULT'' COMMENT'姓名', `age` INT NOT NULL DEFAULT 0 COMMENT'年齡', `pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'職位', `add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入職時(shí)間' )CHARSET utf8 COMMENT'員工記錄表'; INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW()); INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW()); INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW()); ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`);
索引失效案例:
1、全值匹配我最愛
2、最佳左前綴法則(重要!)
:如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始并且不跳過復(fù)合索引中間列
。
中間列不能斷:
3、不在索引列上做任何操作(計(jì)算、函數(shù)、(自動or手動)類型轉(zhuǎn)換),會導(dǎo)致索引失效而轉(zhuǎn)向全表掃描。
4、存儲引擎不能使用索引中范圍條件右邊的列(范圍之后全失效,范圍列并不是做的查詢而是排序)。
5、盡量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select *。
6、mysql在使用不等于(!=或者<>)的時(shí)候無法使用索引會導(dǎo)致全表掃描。
7、is null, is not null 也無法使用索引。
8、like以通配符開頭(’%abc…’),mysql索引失效會變成全表掃描的操作(%寫在最右邊索引不會失效,或覆蓋索引)。問題:解決like '%字符串%'時(shí)索引不被使用的方法? 采用覆蓋索引的方法!
建表:
CREATE TABLE `tbl_user`( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `age`INT(11) DEFAULT NULL, `email` VARCHAR(20) DEFAULT NULL, PRIMARY KEY(`id`))ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('1aa1',21,'a@163.com');INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('2bb2',23,'b@163.com');INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('3cc3',24,'c@163.com');INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('4dd4',26,'d@163.com');//查詢mysql> select * from tbl_user;+----+------+------+-----------+| id | name | age | email |+----+------+------+-----------+| 1 | 1aa1 | 21 | a@163.com || 2 | 2bb2 | 23 | b@163.com || 3 | 3cc3 | 24 | c@163.com || 4 | 4dd4 | 26 | d@163.com |+----+------+------+-----------+4 rows in set (0.00 sec)
創(chuàng)建索引:
CREATE INDEX idx_user_nameAge ON tbl_user(NAME,age);
索引成功使用:
索引失效:總結(jié)
:%寫在最右邊,如果非要寫在最左邊,就使用覆蓋索引
9、字符串不加單引號索引失效。
Explain分析:
MySQL中SQL優(yōu)化、索引優(yōu)化、鎖機(jī)制、主從復(fù)制的方法
當(dāng)前URL:http://aaarwkj.com/article18/igsddp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、軟件開發(fā)、移動網(wǎng)站建設(shè)、微信小程序、網(wǎng)站制作、App設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)