1.1 索引的介紹 索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定信息。如果想按特定職員的姓來查找他或她,則與在表中搜索所有的行相比,索引有助於更快地獲取信息。 索引的一個主要目的就是加快檢索表中數據的方法,亦即能協助信息搜索者儘快的找到符合限制條件的記錄ID ...
1.1 索引的介紹
索引是對資料庫表中一列或多列的值進行排序的一種結構,使用索引可快速訪問資料庫表中的特定信息。如果想按特定職員的姓來查找他或她,則與在表中搜索所有的行相比,索引有助於更快地獲取信息。
索引的一個主要目的就是加快檢索表中數據的方法,亦即能協助信息搜索者儘快的找到符合限制條件的記錄ID的輔助數據結構。
1.1.1 唯一索引
唯一索引是不允許其中任何兩行具有相同索引值的索引。當現有數據中存在重覆的鍵值時,大多數資料庫不允許將新創建的唯一索引與表一起保存。資料庫還可能防止添加將在表中創建重覆鍵值的新數據。
例如,如果在employee表中職員的姓(lname)上創建了唯一索引,則任何兩個員工都不能同姓。
1.1.2 主鍵索引
資料庫表經常有一列或多列組合,其值唯一標識表中的每一行。該列稱為表的主鍵。在資料庫關係圖中為表定義主鍵將自動創建主鍵索引,主鍵索引是唯一索引的特定類型。
該索引要求主鍵中的每個值都唯一。當在查詢中使用主鍵索引時,它還允許對數據的快速訪問。
1.1.3 聚集索引
在聚集索引中,表中行的物理順序與鍵值的邏輯(索引)順序相同。一個表只能包含一個聚集索引。如果某索引不是聚集索引,則表中行的物理順序與鍵值的邏輯順序不匹配。與非聚集索引相比,聚集索引通常提供更快的數據訪問速度。
聚集索引和非聚集索引的區別,如字典預設按字母順序排序,讀者如知道某個字的讀音可根據字母順序快速定位。因此聚集索引和表的內容是在一起的。如讀者需查詢某個生僻字,則需按字典前面的索引,舉例按偏旁進行定位,找到該字對應的頁數,再打開對應頁數找到該字。
這種通過兩個地方而查詢到某個字的方式就如非聚集索引。
1.1.4 索引列
可以基於資料庫表中的單列或多列創建索引。多列索引可以區分其中一列可能有相同值的行。如果經常同時搜索兩列或多列或按兩列或多列排序時,索引也很有幫助。
例如,如果經常在同一查詢中為姓和名兩列設置判據,那麼在這兩列上創建多列索引將很有意義。
檢查查詢的WHERE和JOIN子句。在任一子句中包括的每一列都是索引可以選擇的對象。對新索引進行試驗以檢查它對運行查詢性能的影響。考慮已在表上創建的索引數量。最好避免在單個表上有很多索引。
檢查已在表上創建的索引的定義。最好避免包含共用列的重疊索引。
檢查某列中唯一數據值的數量,並將該數量與表中的行數進行比較。比較的結果就是該列的可選擇性,這有助於確定該列是否適合建立索引,如果適合,確定索引的類型。
1.1.5 B樹演算法
B樹的搜索,從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那麼就命中;否則,如果查詢關鍵字比結點關鍵字小,就進入左邊;如果比結點關鍵字大,就進入右邊;如果左邊或右邊的指針為空,則報告找不到相應的關鍵。
如果B樹的所有非葉子結點的左右子樹的結點數目均保持差不多(平衡),那麼B樹的搜索性能逼近二分查找;但它比連續記憶體空間的二分查找的優點是,改變B樹結構(插入與刪除結點)不需要移動大段的記憶體數據,甚至通常是常數開銷。
1.1.6 B+樹演算法
B+樹是B-樹的變體,也是一種多路搜索樹:
1.其定義基本與B-樹同,除了: 2.非葉子結點的子樹指針與關鍵字個數相同; 3.非葉子結點的子樹指針P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間); 5.為所有葉子結點增加一個鏈指針; 6.所有關鍵字都在葉子結點出現; 如:(M=3)
B+的搜索與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹可以在非葉子結點命中),其性能也等價於在關鍵字全集做一次二分查找;
B+的特性:
1.所有關鍵字都出現在葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字恰好是有序的; 2.不可能在非葉子結點命中; 3.非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是存儲(關鍵字)數據的數據層; 4.更適合文件索引系統;
1.1.7 HASH:HASH演算法
哈希索引只有Memory, NDB兩種引擎支持,Memory引擎預設支持哈希索引,如果多個hash值相同,出現哈希碰撞,那麼索引以鏈表方式存儲。
但是,Memory引擎表只對能夠適合機器的記憶體切實有限的數據集。
要使InnoDB或MyISAM支持哈希索引,可以通過偽哈希索引來實現,叫自適應哈希索引。
主要通過增加一個欄位,存儲hash值,將hash值建立索引,在插入和更新的時候,建立觸發器,自動添加計算後的hash到表裡。
1.1.8 其他的索引
FULLTEXT:全文索引
RTREE:R樹索引
1.2 MySQL索引管理
索引建立在表的列上(欄位)的。
在where後面的列建立索引才會加快查詢速度。
pages<---索引(屬性)<----查數據。
添加索引的方法:
alter table test add index index_name(name); create index index_name on test(name);
語法格式:
alter table 表 add index 索引名稱(name);
1.2.1 創建普通索引
創建普通索引方法一:
mysql> ALTER TABLE PLAYERS ADD INDEX name_idx(NAME); mysql> desc PLAYERS; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | NAME | char(15) | NO | MUL | NULL | |
創建普通索引方法二:
mysql> ALTER TABLE PLAYERS ADD INDEX name_idx(NAME); mysql> desc PLAYERS; +------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +------------+-------------+------+-----+---------+-------+ | NAME | char(15) | NO | MUL | NULL | |
1.2.2 刪除索引
alter table PLAYERS delete INDEX name_idx;
mysql> show index from PLAYERS\G *************************** 1. row *************************** Table: PLAYERS Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: PLAYERNO Collation: A Cardinality: 14 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment:
1.3 MySQL中的約束索引
主鍵索引
只能有一個主鍵。
主鍵索引:列的內容是唯一值,例如學號.
表創建的時候至少要有一個主鍵索引,最好和業務無關。
普通索引
加快查詢速度,工作中優化資料庫的關鍵。
在合適的列上建立索引,讓數據查詢更高效。
create index index_name on test(name); alter table test add index index_name(name);
用了索引,查一堆內容。
在where條件關鍵字後面的列建立索引才會加快查詢速度.
select id,name from test where state=1 order by id group by name;
唯一索引
內容唯一,但不是主鍵。
create unique index index_name on test(name);
1.3.1 創建主鍵索引
建立表時
CREATE TABLE `test` ( `id` int(4) NOT NULL AUTO_INCREMENT, `name` char(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
建立表後增加
CREATE TABLE `test` ( `id` int(4) NOT NULL, `name` char(20) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
增加自增主鍵
alter table test change id id int(4) primary key not null auto_increment;
1.3.2 使用欄位首碼創建索引及聯合索引
首碼索引:根據欄位的前N個字元建立索引
create index index_name on test(name(8));
聯合索引:多個欄位建立一個索引。
where a女生 and b身高165 and c身材好 index(a,b,c)
特點:首碼生效特性。
a,ab,abc 可以走索引。 b ac bc c 不走索引(5.6之後 ac 可以走主鍵索引)。
原則:把最常用來作為條件查詢的列放在前面。
示例:
創建表
create table people (id int not null auto_increment ,name char(20),sr(20),sex int ,age int, primary key (id));
創建聯合索引
mysql> alter table people add key name_sex_idx(name,sex) -> ; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0
查看索引的類型
mysql> desc people; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | char(20) | YES | MUL | NULL | | | sex | int(11) | YES | | NULL | | | age | int(11) | YES | | NULL | | +-------+----------+------+-----+---------+----------------+
建立唯一鍵索引
mysql> alter table people add unique key age_uidx(age); Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0
查看數據表
mysql> desc people; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | char(20) | YES | MUL | NULL | | | sex | int(11) | YES | | NULL | | | age | int(11) | YES | UNI | NULL | | +-------+----------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
聯合主鍵是聯合索引的特殊形式
PRIMARY KEY (`Host`,`User`) alter table test add sex char(4) not null; create index ind_name_sex on test(name,sex);
首碼加聯合索引
create index index_name on test(name(8),sex(2));
1.4 SQL語句優化
1.4.1 企業SQL優化思路
1、把一個大的不使用索引的SQL語句按照功能進行拆分
2、長的SQL語句無法使用索引,能不能變成2條短的SQL語句讓它分別使用上索引。
3、對SQL語句功能的拆分和修改
4、減少“爛”SQL由運維(DBA)和開發交流(確認),共同確定如何改,最終由DBA執行
5、制定開發流程
1.4.2 不適合走索引的場景
1、唯一值少的列上不適合建立索引或者建立索引效率低。例如:性別列
2、小表可以不建立索引,100條記錄。
3、對於數據倉庫,大量全表掃描的情況,建索引反而會慢
1.4.3 查看表的唯一值數量
select count(distinct user) from mysql.user; select count(distinct user,host) from mysql.user;
1.4.4 建立索引流程
1、找到慢SQL。
show processlist;
記錄慢查詢日誌。
2、explain select句,條件列多。
3、查看表的唯一值數量:
select count(distinct user) from mysql.user; select count(distinct user,host) from mysql.user;
條件列多。可以考慮建立聯合索引。
4、建立索引(流量低谷)
force index
5、拆開語句(和開發)。
6、like '%%'不用mysql
7、進行判斷重覆的行數
查看行數:
mysql> select count(*) from city; +----------+ | count(*) | +----------+ | 4079 | +----------+ 1 row in set (0.00 sec)
查看去重後的行數:
mysql> select count(distinct countrycode) from city; +-----------------------------+ | count(distinct countrycode) | +-----------------------------+ | 232 | +-----------------------------+ 1 row in set (0.00 sec)
1.5 用expain查看SQL的執行計劃
在工作中,我們用於捕捉性能問題最常用的就是打開慢查詢,定位執行效率差的SQL,那麼當我們定位到一個SQL以後還不算完事,我們還需要知道該SQL的執行計劃,比如是全表掃描,還是索引掃描,這些都需要通過EXPLAIN去完成。
EXPLAIN命令是查看優化器如何決定執行查詢的主要方法。可以幫助我們深入瞭解MySQL的基於開銷的優化器,還可以獲得很多可能被優化器考慮到的訪問策略的細節,以及當運行SQL語句時哪種策略預計會被優化器採用。
需要註意的是,生成的QEP並不確定,它可能會根據很多因素髮生改變。MySQL不會將一個QEP和某個給定查詢綁定,QEP將由SQL語句每次執行時的實際情況確定,即便使用存儲過程也是如此。儘管在存儲過程中SQL語句都是預先解析過的,但QEP仍然會在每次調用存儲過程的時候才被確定。
1.5.1 查看 select 語句的執行過程
mysql> explain select id,name from test where name='clsn'; +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ | 1 | SIMPLE | test | ref | name_idx | name_idx | 24 | const | 1 | Using where | +----+-------------+-------+------+---------------+----------+---------+-------+------+-------------+ 1 row in set (0.00 sec)
SQL_NO_CACHE的作用是禁止緩存查詢結果。
使用where條件查找
mysql> explain select user,host from mysql.user where user='root' and host='127.0.0.1'; +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ | 1 | SIMPLE | user | const | PRIMARY | PRIMARY | 228 | const,const | 1 | Using index | +----+-------------+-------+-------+---------------+---------+---------+-------------+------+-------------+ 1 row in set (0.00 sec)
1.5.2 通過執行計劃可以知道什麼?
mysql> explain select d1.age, t2.id from (select age,name from t1 where id in (1,2))d1, t2 where d1.age=t2.age group by d1.age, t2.id order by t2.id; +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | Using temporary; Using filesort | | 1 | PRIMARY | t2 | ref | age | age | 5 | d1.age | 1 | Using where; Using index | | 2 | DERIVED | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ 3 rows in set (0.00 sec)
1.5.3 MySQL執行計劃調用方式
1.EXPLAIN SELECT …… 2.EXPLAIN EXTENDED SELECT …… 將執行計劃"反編譯"成SELECT語句,運行SHOW WARNINGS 可得到被MySQL優化器優化後的查詢語句 3.EXPLAIN PARTITIONS SELECT …… 用於分區表的EXPLAIN生成QEP的信息
1.5.4 執行計劃包含的信息
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1.5.5 id
包含一組數字,表示查詢中執行select子句或操作表的順序
【示例一】id相同,執行順序由上至下
mysql> explain select t2.* from t1, t2, t3 where t1.id=t2.id and t1.id=t3.id and t1.name=''; +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | 1 | SIMPLE | t1 | ref | PRIMARY,name | name | 63 | const | 1 | Using where; Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ 3 rows in set (0.00 sec)
【示例二】如果是子查詢,id的序號會遞增,id值越大優先順序越高,越先被執行
mysql> explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name='')); +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table | | 3 | SUBQUERY | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ 3 rows in set (0.00 sec)
【示例三】id如果相同,可以認為是一組,從上往下順序執行;在所有組中,id值越大,優先順序越高,越先執行
mysql> explain select t2.* from (select t3.id from t3 where t3.name='')s1, t2 where s1.id=t2.id; +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | t2 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | DERIVED | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ 3 rows in set (0.00 sec)
1.5.6 select_type
示查詢中每個select子句的類型(簡單OR複雜) a. SIMPLE:查詢中不包含子查詢或者UNION b. 查詢中若包含任何複雜的子部分,最外層查詢則被標記為:PRIMARY c. 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為:SUBQUERY d. 在FROM列表中包含的子查詢被標記為:DERIVED(衍生)用來表示包含在from子句中的子查詢的select,mysql會遞歸執行並將結果放到一個臨時表中。伺服器內部稱為"派生表",因為該臨時表是從子查詢中派生出來的 e. 若第二個SELECT出現在UNION之後,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED f. 從UNION表獲取結果的SELECT被標記為:UNION RESULT
說明:
SUBQUERY和UNION還可以被標記為DEPENDENT和UNCACHEABLE。
DEPENDENT意味著select依賴於外層查詢中發現的數據。
UNCACHEABLE意味著select中的某些 特性阻止結果被緩存於一個item_cache中。
【示例】
mysql> explain select d1.name, ( select id from t3) d2 from (select id,name from t1 where name='')d1 union (select name,id from t2); +----+