1.什么是索引
索引是一種數據結構,會對添加索引的字段的值進行排序存放,提高查詢效率;一張表中可以添加多個索引;innodb存儲引擎默認使用的是b+tree索引結構,也支持哈希、全文索引。
2.索引的優缺點
2.1索引的優點
①提高數據庫查詢效率
②減少鎖等待和死鎖的產生(行鎖是基於索引創建的)
③減少主從復制從庫的延遲時間(sql thread回放sql時會應用索引)
2.2索引的缺點
①索引維護成本高(可通過insert buffer,change buffer提升DML語句效率)
②占用更多的存儲空間(磁盤和內存)
③索引過多會造成優化器負擔
3.b+tree數據結構
3.1 b+tree數據結構概括:b+tree是平衡多叉樹的數據結構,是基於頁進行管理數據;

3.2b+tree高度:2 (一般為2-4層);
影響索引樹高度因素:
。索引長度
。記錄數
索引樹高度不同消耗時間不同:
如sata磁盤(ssd固態硬盤同樣計算):300iops,0.0033 /io
2層:0.0033*2 單位是秒
3層:0.0033*3 單位是秒
3.3非葉子節點
保存鍵值(添加索引的字段的值)和指針
3.4指針
指針與數據頁是一種映射的關系,通過指針就可以找到對應的數據頁
3.5葉子節點
用於保存數據,保存所有記錄的值,並經過排序
3.6雙向指針(雙向鏈表)
用於保存相鄰頁的指針,提升范圍查詢效率
4.b+tree管理
4.1 b+tree插入操作:(頁旋轉操作)
情況一:b+tree插入數據時,葉子節點沒有滿
直接插入到對應的數據頁
情況二:b+tree插入數據時,葉子節點已滿(產生頁分裂 split)
先取出中間值,存放到上一層非葉子節點中;
情況三:b+tree插入數據時,葉子節點和上一層的非葉子節點都已滿(產生兩次頁分裂操作)
4.2 b+tree刪除操作
當葉子節點小於填充因子50%,就會發生頁合並操作
5.相輔相成的聚集索引和輔助索引
5.1 b+tree索引:索引的本質就是b+tree在數據庫中的實現
5.2 索引的分類:從物理存儲角度分類
聚集索引和輔助索引
5.3 聚集索引的選擇:
①優先選擇顯示創建的主鍵索引來作為聚集索引
②如沒有主鍵索引就會選擇第一個創建的非空的唯一索引作為聚集索引
③系統自動創建一個實例級rowid作為聚集索引(6字節)
5.4 聚集索引的特點:
①葉子節點存放的是整行數據
②一張表只能有一個聚集索引,因為實際的數據頁只能按一顆b+tree進行排序
③聚集索引的順序決定表數據行的物理順序
5.5 聚集索引的優勢:
①不用回表查詢就可以直接找到數據,節省更多的查詢時間
②范圍查詢性能得到提升 where 4<o_orderkey<10
③排序性能提升
5.6 輔助索引:內部也是b+tree
①輔助索引存放的是鍵值和主鍵值
②每張表中可以存放多個輔助索引
6.覆蓋索引與回表查詢
6.1 回表查詢
指回到聚集索引構建的b+tree去搜索的過程,就稱為回表;回表查詢要多經歷幾次io,消耗時間更多,性能相對較差
6.2 覆蓋索引
在一個查詢語句中,使用到的b+tree上面的數據就覆蓋我要查詢需求,稱為覆蓋索引;可以減少對b+tree的搜索次數(減少io的消耗,不用回表查詢)
7.創建高性能的主鍵索引
7.1 主鍵索引創建的原則
①使用自增列作為主鍵 id int/bigint auto_increment primary key;
②主鍵與業務不相關,不受業務變化影響
③主鍵盡量不要修改、刪除
7.2 主鍵索引的特點
①值不能為空,也不能重復
②一張表只能有一個主鍵
③創建輔助索引時,會隱式的將主鍵值保存,(name,pk)5.7自動識別里面的主鍵
where name=? and pk=?
where name=? order by pk
7.3 為什么建議使用自增列作為主鍵
①讀;顯示創建的主鍵會被作為聚集索引,在數據頁上存整行數據,無論讀記錄任何的列,我們都不用回表查詢,直接在主鍵構建的b+tree就可以找到。
②寫;寫性能非常高,順序獲取頁;離散獲取頁;insert buffer , change buffer
③節省更多的內存
8.唯一索引與普通索引的性能差距
8.1 唯一索引特點:
- 值不能重復,可以為空
- 一張表可以創建多個唯一索引
- 如果表中已有數據,添加唯一索引時,該字段的值,不能重復,如果有重復的,就會報錯
select count(b) from t;
select count(distinct b) from t;
8.2 普通索引特點
- 值可以重復,可以為空
- 一張表可以創建多個普通索引
8.3 唯一索引與普通索引的性能差距
- 讀性能差距:
唯一索引:由於唯一性約束,查找到第一個滿足條件的記錄后,就會停止繼續匹配
普通索引:值不是唯一,可能會有重復值,需要繼續查找
總結:在讀性能上,唯一索引的性能高於普通索引,性能差距非常小
- 寫性能差距:
唯一索引:在進行寫操作時要判斷這個操作是否違反了唯一性約束。這個判斷必須是在將頁加載到內存后,才能進行判斷,無法使用change buffer
普通索引:在進行寫操作時,如果數據頁不在內存中,會將寫操作放到change buffer
總結:普通索引的寫性能高於唯一索引
9.前綴索引帶來的性能影響
9.1 前綴索引作用:
- 索引長度影響b+tree高度,索引長度越短越好
- 節省磁盤空和內存空間
- 建前綴索引時,如果合理的定義前綴索引的長度,會對查詢性能帶來好的影響
9.2 前綴索引長度創建不合理會帶來哪些不好的影響?
- 不合理的長度,會帶來更多回表查詢
總結:建前綴索引時,最重要的就是指定合理的長度
9.3 合理長度判斷:
select count(distinct a) from t;去除重復的值,總共有多少條記錄
select count(distinct left(a, 3)) from t;
80%-90%就是合理的
9.4 前綴索引缺點:
- 無法使用覆蓋索引
- 無法進行order by和group by,會產生額外排序和產生臨時表
10.生產中索引的管理
①建表時創建索引
主鍵索引
create table t1(id int auto_increment primary key);
create table t2(
id int auto_increment,
primary key(id)
);
唯一索引
create table t1(name varchar(10) not null unique);
create table t2(
name varchar(10) not null,
unique key i_name(name)
);
前綴索引
create table t1(
name varchar(10) not null,
key i_name(name(5))
);
聯合索引
create table t2(
name varchar(10) not null,
o_date datetime,
key i_name_date(name,o_date)
);
普通索引
create table t1(
name varchar(10) not null,
key i_name(name)
);
②建表后創建索引
create table t1(
id int ,
name varchar(10) not null,
o_date datetime,
title varchar(30) not null
);
建表后創建索引
主鍵索引
alter table t1 add primary key(id);
唯一索引
alter table t1 add unique index i_name(name);
前綴索引
alter table t1 add index i_title(title(10));
聯合索引
alter table t1 add index i_name_date(name, o_date);
普通索引
alter table t1 add index i_o_date(o_date);
刪除索引:
alter table t1 drop index 索引名稱
查看索引:
show create table t1;
show index from t1;
11.SQL語句無法使用索引的情況
①where條件:
列進行計算:
explain select * from orders where o_custkey=o_custkey+1;
列使用函數:
explain select * from orders where o_custkey=ceil(o_custkey);
列進行隱式轉換:
explain select * from emp where ename=007;
②聯合索引:用到范圍查詢,只能用到部分索引
③聯表查詢:
關聯條件字符集不同,不走索引
關聯條件的列類型不同,不走索引
④其他情況:
。select * from emp;
。查詢結果集大於數據量的30%,不走索引
explain select * from emp where empno > 7000;
。索引本身失效
。like '%s'
explain select * from emp where ename like '%s';
。not in(111,9999) 普通索引,如果是主鍵索引,會被優化為范圍查詢,可以利用索引
explain select * from emp where empno not in(111, 9999);
。!=
explain select * from emp where empno != 9999;