一.索引作用
提供了類似于書中目錄的作用陨仅,目的是為了優(yōu)化查詢
二.索引的種類
B樹索引??Hash索引? R樹索引? Full text? ?GIS
三.B樹基于不同的查找算法分類介紹
B-tree
B+tree? ? 在范圍查詢方面提供了更好的性能 (> < >= <=)
B*tree?
四.在功能上的分類
4.1輔助索引(s)怎么構(gòu)建B樹結(jié)構(gòu)的星爪?
1) 索引是基于表中放前,列(索引建)的值生成的B樹結(jié)構(gòu)
2)首先提取此列的所有值蛾狗,進行自動排序
3)將排好序的值括细,均勻分布到索引樹的葉子節(jié)點中(16k)
4)然后生成此索引的鍵值所對應(yīng)的后端數(shù)據(jù)頁的指針
5)生成枝節(jié)點和根節(jié)點氯质,根據(jù)數(shù)據(jù)量級和索引鍵長度螟加,生成合適的索引樹高度
問題: 基于索引鍵做where查詢,對于id列是順序IO,但是對于其他列的查詢,可能是隨機IO.
4.2聚集索引(C)
4.2.1前提
1)表中設(shè)置了主鍵,主鍵列就會自動被作為聚集索引
2)如果沒有主鍵景东,會選擇唯一鍵作為聚集索引
3)聚集索引必須在建表時才有意義砂轻,一般是表的無關(guān)列(ID)
4)聚集索引在葉子節(jié)點上是整行的數(shù)據(jù)(輔助索引僅是提取列的值進行排序)
4.2.2輔助索引(s)怎么構(gòu)建B樹結(jié)構(gòu)的
1)在建表時,設(shè)置了主鍵列(ID)
2)在將來錄入數(shù)據(jù)時耐薯,就會按照ID列的順序存儲到磁盤上(又稱之為聚集索引組織表)
3)將排好序的整行數(shù)據(jù)舔清,生成葉子節(jié)點,可以理解為曲初,磁盤的數(shù)據(jù)頁就是葉子節(jié)點
(因為聚集索引是在創(chuàng)建表的時候就將每一行的內(nèi)容進行了排序生成了葉子節(jié)點体谒。因此在將來存儲的時候就會按照這個順序進行存儲。而輔助索引只是某一列的值進行排列臼婆。)
五.輔助索引細分
1.普通的單列輔助索引
2.覆蓋索引(聯(lián)合索引):多個列作為索引條件,生成索引樹,理論上設(shè)計的好的,可以減少大量的回表查詢
3.唯一索引 : 索引列的值都是唯一的
六.索引樹的高度受什么影響
1.數(shù)據(jù)量級抒痒,解決方法:分表,分庫颁褂,分布式
2.索引列值過長故响,解決方法:前綴索引
3.數(shù)據(jù)類型
變長長度的字符串,使用了char颁独,解決方案:邊長字符串使用varchar
enum類型的使用?('山東','河北','黑龍江','吉林','遼寧','陜西'......) 1? 2? 3
七.索引的基本管理
7.1索引的建立前
db01 [world]>desc city;
+-------------+----------+------+-----+---------+----------------+
| Field? ? ?? | Type? ?? | Null | Key | Default | Extra? ? ? ? ? |
+-------------+----------+------+-----+---------+----------------+
Field :列名字
key? :有沒有索引,索引類型
? PRI: 主鍵索引
? UNI: 唯一索引
? MUL: 輔助索引(單列,聯(lián)和,前綴)
7.1.單列普通輔助索引
7.1.1創(chuàng)建索引
alter table 表 add index? 索引名(列名)??
例:? alter table city add index idx_name(name);
show index from city;(查看表的索引名)
注:同一個表中彩届,索引名不能同名。同時在生產(chǎn)操作中誓酒,不建議在一個列上建多個索引
7.1.2刪除索引:
alter table 表名 drop index 索引名
例:alter table city drop index idx_name1;
7.2覆蓋索引(聯(lián)合索引)
alter table? 表名 add? index? 索引名(列名樟蠕,列名)
例:alter table city add index idx_co_po(countrycode,population);
7.3前綴索引
alter table? 表名 add index? 索引名(列名(前幾個字符))
例:alter table city add index idx_di(district(5));
7.4 唯一索引
db01 [world]>alter table city add unique index idx_uni1(name);
ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1'
統(tǒng)計city表中,以省的名字為分組靠柑,統(tǒng)計組的個數(shù)
select district,count(id) from city group by district;
需求: 找到world下,city表中 name列有重復(fù)值的行,最后刪掉重復(fù)的行
db01 [world]>select name,count(id) as cid from city group by name? having cid>1 order by cid desc;
db01 [world]>select * from city where name='suzhou';
===============================================
八. 執(zhí)行計劃獲取及分析
8.0 介紹
(1)
獲取到的是優(yōu)化器選擇完成的,他認為代價最小的執(zhí)行計劃.
作用: 語句執(zhí)行前,先看執(zhí)行計劃信息,可以有效的防止性能較差的語句帶來的性能問題.
如果業(yè)務(wù)中出現(xiàn)了慢語句寨辩,我們也需要借助此命令進行語句的評估,分析優(yōu)化方案歼冰。
(2) select 獲取數(shù)據(jù)的方法
1. 全表掃描(應(yīng)當(dāng)盡量避免,因為性能低)
2. 索引掃描
3. 獲取不到數(shù)據(jù)
8.1 執(zhí)行計劃獲取
獲取優(yōu)化器選擇后的執(zhí)行計劃
8.2 執(zhí)行計劃分析
8.2.0 重點關(guān)注的信息
table: city? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ---->查詢操作的表? ? **
possible_keys: CountryCode,idx_co_po? ? ? ---->可能會走的索引? **
key: CountryCode ? ---->真正走的索引? ? ***
type: ref ? ---->索引類型? ? ? ? *****
Extra: Using index condition? ? ? ? ? ? ? ---->額外信息? ? ? ? *****
8.2.1 type詳解
從左到右性能依次變好.
ALL,index,range,ref,eq_ref,system(const),NULL(*****)
(1) ALL: 代表的是全表掃描
desc select * from city;
desc select * from city where name like '%C%';
desc select * from city where name != 'CHN';
desc select * from city where countrycode not in ('CHN','USA');
注意:生產(chǎn)中幾乎是沒有這種需求的靡狞。盡量避免
(2) index: 全索引掃描
需要掃描整個索引樹,獲取到想要數(shù)據(jù),比ALL性能好,順序IO,可以減少回表查詢
db01 [world]>desc select name from city;
(3) range : 索引范圍查詢
>? <? >= <=
in
or
like 'CH%'
between and
db01 [world]>desc select * from city where id<10;
db01 [world]>desc select * from city where countrycode like 'CH%';
B+樹額外優(yōu)化:
> < >= <=
between and
like 'CH%'
in or無法享受B+樹的額外優(yōu)化,可以用union all來替代
例:查看中國和美國的城市信息
SELECT * FROM city WHERE countrycode='CHN' OR countrycode='USA';
或者:
SELECT * FROM city WHERE countrycode='CHN'
UNION ALL
SELECT * FROM city WHERE countrycode='USA'
(4) ref: 輔助索引的等值查詢
db01 [world]>desc select * from city where countrycode = 'CHN';
+----+-------------+-------+------------+------+-----------------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys? ? ? ?? | key? ? ? ?? | key_len | ref?? | rows | filtered | Extra |
+----+-------------+-------+------------+------+-----------------------+-------------+---------+-------+------+----------+-------+
|? 1 | SIMPLE? ? ? | city? | NULL? ? ?? | ref? | CountryCode,idx_co_po | CountryCode | 3? ? ?? | const |? 363 |?? 100.00 | NULL? |
+----+-------------+-------+------------+------+-----------------------+-------------+---------+-------+------+----------+-------+
db01 [world]>desc select * from city where countrycode in ('CHN','USA');
db01 [world]>desc select * from city where countrycode='CHN'
? ? -> union all
? ? -> select * from city where countrycode='USA';
(5) eq_ref :多表連接的表,On的條件是主鍵或唯一鍵
desc select city.name,country.name
?from city?
join country?
on city.countrycode=country.code
?where city.population<100\G
(6) system 或 const :主鍵或唯一鍵的等值查詢
db01 [world]>desc select * from city where id=10;
(7) NULL , 索引中掃描不到這個數(shù)據(jù)
db01 [world]>desc select * from city where id=5000;
結(jié)論:在索引掃描類型方面,至少保證在range以上級別隔嫡。
============================
8.2.2 其他字段解釋
Extra:
? Using filesort(關(guān)注)
意思是在查詢過程中又一次啟用了排序的功能*因為索引在構(gòu)造B+樹的時候本身就進行了一次排列甸怕,數(shù)據(jù)已經(jīng)是有序的了甘穿。如果出現(xiàn)?Using filesort?則說名 索引是有問題的。?
**** 統(tǒng)一優(yōu)化方法 把該列與前面where 條件的列進行一個聯(lián)合索引蕾各。
desc select * from city where countrycode='CHN' order by population desc limit 10;
+----+-------------+-------+------+---------------+-------------+---------+-------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key? ? ? ?? | key_len | ref?? | rows | Extra? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+----------------------------------------------------+
|? 1 | SIMPLE? ? ? | city? | ref? | CountryCode?? | CountryCode | 3? ? ?? | const |? 363 | Using index condition; Using where; Using filesort |
+----+-------------+-------+------+---------------+-------------+---------+-------+------+--------------------------
db01 [world]>alter table city add index idx_po(countrycode,population);
db01 [world]>desc select * from city where countrycode='CHN' order by population limit 10;
解決思路:
索引可以減少排序,可以很大程度減少CPU時間
輔助索引 應(yīng)用順序(優(yōu)化器選擇的)
如果查詢條件:符合覆蓋索引的順序時,優(yōu)先選擇覆蓋索引
不符合順序,優(yōu)先會走where條件的索引
優(yōu)化方法,將where列和order列建立聯(lián)合索引
alter table city add index idx_co_po(countrycode,population);
6.2.3 explain(desc)使用場景(面試題)
SQL語句有問題扒磁,
題目意思:? 我們公司業(yè)務(wù)慢,請你從數(shù)據(jù)庫的角度分析原因
1.mysql出現(xiàn)性能問題,我總結(jié)有兩種情況:
(1)應(yīng)急性的慢:突然夯住
應(yīng)急情況:數(shù)據(jù)庫hang(卡了,資源耗盡)
處理過程:
1.show full? processlist;?? 獲取到導(dǎo)致數(shù)據(jù)庫hang的語句
額外關(guān)注一個time 庆揪,語句執(zhí)行時間式曲。通常有問題的SQL語句執(zhí)行時間都會很長
2. explain 分析SQL的執(zhí)行計劃,有沒有走索引,索引的類型情況
3. 建索引,改語句
(2)一段時間慢(持續(xù)性的):
(1)記錄慢日志slowlog,分析slowlog
(2)explain 分析SQL的執(zhí)行計劃,有沒有走索引,索引的類型情況
?(3)建索引,改語句
九. 索引效果壓力測試
===========壓力測試===========
1、模擬數(shù)據(jù)庫數(shù)據(jù)
drop database if exists oldboy;
create database oldboy charset utf8mb4 collate utf8mb4_bin;
use oldboy;
create table t_100w (id int,num int,k1 char(2),k2 char(4),dt timestamp);
delimiter //
create? procedure rand_data(in num int)
begin
declare str char(62) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
declare str2 char(2);
declare str4 char(4);
declare i int default 0;
while i<num do
set str2=concat(substring(str,1+floor(rand()*61),1),substring(str,1+floor(rand()*61),1));
set str4=concat(substring(str,1+floor(rand()*61),2),substring(str,1+floor(rand()*61),2));
set i=i+1;
insert into t_100w values (i,floor(rand()*num),str2,str4,now());
end while;
end;
//
delimiter ;
插入100w條數(shù)據(jù):
call rand_data(1000000);
commit;
2缸榛、檢查數(shù)據(jù)可用性
mysql -uroot -p
select count(*) from oldboy.?t_100w;
3吝羞、在沒有優(yōu)化之前我們使用mysqlslap來進行壓力測試
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='oldboy' \
--query="select * from oldboy.t1 where stuname='alexsb_100'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose
==========================================================
壓力測試工具:
tpcc
sysbench
=================================================
十. 索引應(yīng)用規(guī)范
業(yè)務(wù):
1.產(chǎn)品的功能
2.用戶的行為
"熱"查詢語句
"熱"數(shù)據(jù)
10.1? 建立索引的原則(DBA運維規(guī)范)
10.1.0 說明
為了使索引的使用效率更高,在創(chuàng)建索引時内颗,必須考慮在哪些字段上創(chuàng)建索引和創(chuàng)建什么類型的索引钧排。那么索引設(shè)計原則又是怎樣的?
10.1.1 (必須的) 建表時一定要有主鍵,一般是個無關(guān)列
10.1.2 選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄均澳。
例如恨溜,學(xué)生表中學(xué)號是具有唯一性的字段。為該字段建立唯一性索引可以很快的確定某個學(xué)生的信息找前。
如果使用姓名的話糟袁,可能存在同名現(xiàn)象,從而降低查詢速度躺盛。
優(yōu)化方案:
(1) 如果非得使用重復(fù)值較多的列作為查詢條件(例如:男女),可以將表邏輯拆分
(2) 可以將此列和其他的查詢類,做聯(lián)和索引
select count(*) from world.city;
select count(distinct countrycode) from world.city;
select count(distinct countrycode,population ) from world.city;
10.1.3(必須的) 為經(jīng)常需要where 项戴、ORDER BY、GROUP BY,join on等操作的字段槽惫,
排序操作會浪費很多時間周叮。
where? A B C? ? ? ----》 A? B? C
in?
如果where A? ?group by B? order by C
聯(lián)合索引需要的順序為 A,B,C
如果為其建立索引界斜,優(yōu)化查詢
注:如果經(jīng)常作為條件的列仿耽,重復(fù)值特別多,可以建立聯(lián)合索引各薇。
10.1.4 盡量使用前綴來索引
如果索引字段的值很長项贺,最好使用值的前綴來索引。
------------------------以上的是重點關(guān)注的得糜,以下是能保證則保證的--------------------
10.1.5 限制索引的數(shù)目
索引的數(shù)目不是越多越好敬扛。
可能會產(chǎn)生的問題:
(1) 每個索引都需要占用磁盤空間,索引越多朝抖,需要的磁盤空間就越大啥箭。
(2) 修改表時,對索引的重構(gòu)和更新很麻煩治宣。越多的索引急侥,會使更新表變得很浪費時間砌滞。
(3) 優(yōu)化器的負擔(dān)會很重,有可能會影響到優(yōu)化器的選擇.
percona-toolkit中有個工具,專門分析索引是否有用
10.1.6 刪除不再使用或者很少使用的索引(percona toolkit)
表中的數(shù)據(jù)被大量更新,或者數(shù)據(jù)的使用方式被改變后坏怪,原有的一些索引可能不再需要贝润。數(shù)據(jù)庫管理
員應(yīng)當(dāng)定期找出這些索引,將它們刪除铝宵,從而減少索引對更新操作的影響打掘。
10.1.7 大表加索引,要在業(yè)務(wù)不繁忙期間操作
10.1.8 盡量少在經(jīng)常更新值的列上建索引
10.1.9 建索引原則
(1) 必須要有主鍵,如果沒有可以做為主鍵條件的列,創(chuàng)建無關(guān)列
(2) 經(jīng)常做為where條件列? order by? group by?? join on, distinct 的條件(業(yè)務(wù):產(chǎn)品功能+用戶行為)
(3) 最好使用唯一值多的列作為索引,如果索引列重復(fù)值較多,可以考慮使用聯(lián)合索引
(4) 列值長度較長的索引列,我們建議使用前綴索引.
(5) 降低索引條目,一方面不要創(chuàng)建沒用索引,不常使用的索引清理,percona toolkit(xxxxx)
(6) 索引維護要避開業(yè)務(wù)繁忙期
===============================================
10.2 不走索引的情況(開發(fā)規(guī)范)
10.2.1 沒有查詢條件,或者查詢條件沒有建立索引
select * from tab;? ? ? ?全表掃描鹏秋。
select? * from tab where 1=1;
在業(yè)務(wù)數(shù)據(jù)庫中尊蚁,特別是數(shù)據(jù)量比較大的表。
是沒有全表掃描這種需求侣夷。
1横朋、對用戶查看是非常痛苦的。
2百拓、對服務(wù)器來講毀滅性的琴锭。
(1)select * from tab;
SQL改寫成以下語句:
select? * from? tab? order by? price? limit 10 ;? ?? 需要在price列上建立索引
(2)
select? * from? tab where name='zhangsan'? ? ? ? ? name列沒有索引
改:
1、換成有索引的列作為查詢條件
2衙传、將name列建立索引
10.2.2 查詢結(jié)果集是原表中的大部分?jǐn)?shù)據(jù)决帖,應(yīng)該是25%以上。
查詢的結(jié)果集粪牲,超過了總數(shù)行數(shù)25%古瓤,優(yōu)化器覺得就沒有必要走索引了。
假如:tab表 id腺阳,name? ? id:1-100w? 落君,id列有(輔助)索引
select * from tab? where id>500000;
如果業(yè)務(wù)允許,可以使用limit控制亭引。
怎么改寫 绎速?
結(jié)合業(yè)務(wù)判斷,有沒有更好的方式焙蚓。如果沒有更好的改寫方案
盡量不要在mysql存放這個數(shù)據(jù)了纹冤。放到redis里面。
10.2.3? 索引本身失效购公,統(tǒng)計數(shù)據(jù)不真實
索引有自我維護的能力萌京。
對于表內(nèi)容變化比較頻繁的情況下,有可能會出現(xiàn)索引失效宏浩。
一般是刪除重建
10.2.4 查詢條件使用函數(shù)在索引列上知残,或者對索引列進行運算,運算包括(+比庄,-求妹,*乏盐,/,! 等)
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
算術(shù)運算
函數(shù)運算
子查詢
10.2.5? 隱式轉(zhuǎn)換導(dǎo)致索引失效.這一點應(yīng)當(dāng)引起重視.也是開發(fā)中經(jīng)常會犯的錯誤.
這樣會導(dǎo)致索引失效. 錯誤的例子:
mysql> alter table tab add index inx_tel(telnum);
Query OK, 0 rows affected (0.03 sec)
Records: 0? Duplicates: 0? Warnings: 0
mysql>
mysql> desc tab;
+--------+-------------+------+-----+---------+-------+
| Field? | Type? ? ? ? | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id? ?? | int(11)? ?? | YES? |? ?? | NULL? ? |? ? ?? |
| name?? | varchar(20) | YES? |? ?? | NULL? ? |? ? ?? |
| telnum | varchar(20) | YES? | MUL | NULL? ? |? ? ?? |
+--------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
mysql> select * from tab where telnum='1333333';
+------+------+---------+
| id?? | name | telnum? |
+------+------+---------+
|? ? 1 | a? ? | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> select * from tab where telnum=1333333;
+------+------+---------+
| id?? | name | telnum? |
+------+------+---------+
|? ? 1 | a? ? | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain? select * from tab where telnum='1333333';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key? ?? | key_len | ref?? | rows | Extra? ? ? ? ? ? ? ?? |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
|? 1 | SIMPLE? ? ? | tab?? | ref? | inx_tel? ? ?? | inx_tel | 63? ? ? | const |? ? 1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql> explain? select * from tab where telnum=1333333;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key? | key_len | ref? | rows | Extra? ? ?? |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|? 1 | SIMPLE? ? ? | tab?? | ALL? | inx_tel? ? ?? | NULL | NULL? ? | NULL |? ? 2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain? select * from tab where telnum=1555555;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key? | key_len | ref? | rows | Extra? ? ?? |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|? 1 | SIMPLE? ? ? | tab?? | ALL? | inx_tel? ? ?? | NULL | NULL? ? | NULL |? ? 2 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain? select * from tab where telnum='1555555';
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key? ?? | key_len | ref?? | rows | Extra? ? ? ? ? ? ? ?? |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
|? 1 | SIMPLE? ? ? | tab?? | ref? | inx_tel? ? ?? | inx_tel | 63? ? ? | const |? ? 1 | Using index condition |
+----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
1 row in set (0.00 sec)
mysql>
---------------------------------------
10.2.6? <>? 制恍,not in 不走索引(輔助索引)
EXPLAIN? SELECT * FROM teltab WHERE telnum?? <> '110';
EXPLAIN? SELECT * FROM teltab WHERE telnum? NOT IN ('110','119');
------------
mysql> select * from tab where telnum <> '1555555';
+------+------+---------+
| id?? | name | telnum? |
+------+------+---------+
|? ? 1 | a? ? | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
mysql> explain select * from tab where telnum <> '1555555';
-----
單獨的>,<,in 有可能走父能,也有可能不走,和結(jié)果集有關(guān)净神,盡量結(jié)合業(yè)務(wù)添加limit
or或in? 盡量改成union
EXPLAIN? SELECT * FROM teltab WHERE telnum?? IN ('110','119');
改寫成:
EXPLAIN SELECT * FROM teltab WHERE telnum='110'
UNION ALL
SELECT * FROM teltab WHERE telnum='119'
-----------------------------------
10.2.7? like "%_" 百分號在最前面不走
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'?? 走range索引掃描
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'? 不走索引
%linux%類的搜索需求何吝,可以使用elasticsearch+mongodb 專門做搜索服務(wù)的數(shù)據(jù)庫產(chǎn)品
作者:wwwoldguocom
鏈接:http://www.reibang.com/p/3621e36cf0af
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處强挫。