MySQL/MariaDB數據庫的索引工做原理和優化html
做者:尹正傑 mysql
版權聲明:原創做品,謝絕轉載!不然將追究法律責任。sql
實際工做中索引這個技術是影響服務器性能一個很是重要的指標,所以咱們得花時間去了解一下索引的相關特性。數據庫
一.索引概述緩存
1>.什麼是索引性能優化
索引是特殊數據結構,定義在查找時做爲查找條件的字段,在MySQL又稱爲鍵key,索引經過存儲引擎實現。
不是說有了索引性能就必定能提示,有了索引我們還得會利用索引,用正確的方法使用索引,使用不當反而會下降服務器性能。
2>.索引的優勢服務器
索引能夠下降服務須要掃描的數據量,減小了IO次數
索引能夠幫助服務器避免排序和使用臨時表
索引能夠幫助將隨機I/O轉爲順序I/O
3>.索引的缺點數據結構
佔用額外空間,影響插入速度
二.索引類型app
B+ TREE、HASH、R TREE 聚簇(集)索引、非聚簇索引:
數據和索引是否存儲在一塊兒 主鍵索引、二級(輔助)索引 稠密索引、稀疏索引:
是否索引了每個數據項 簡單索引、組合索引: 左前綴索引:取前面的字符作索引 覆蓋索引:從索引中便可取出要查詢的數據,性能高
1>.二叉樹ide
(1)每一個結點最多2棵子樹 二叉樹不存在度數大於2的結點。 (2)它是有序樹,左子樹,右子樹是順序的,不能交換次序 (3)即便某個節點只有一棵子樹,也要肯定它是左子樹仍是右子樹 (4)二叉樹的物種基本形態 1)空二叉樹 2)只有一個根結點 3)根結點只有左子樹 4)根結點只有右子樹 5)根結點有左子樹和右子樹 博主推薦閱讀: https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91 https://www.cnblogs.com/yinzhengjie/p/10960896.html
2>.紅黑樹(又叫自平衡二叉樹)
博主推薦閱讀: https://baike.baidu.com/item/%E7%BA%A2%E9%BB%91%E6%A0%91
3>.B-tree索引(B-tree(多路搜索樹,並非二叉的)
B-tree(多路搜索樹,並非二叉的)是一種常見的數據結構。使用B-tree結構能夠顯著減小定位記錄時所經歷的中間過程,從而加快存取速度。按照翻譯,B 一般認爲是Balance的簡稱。這個數據結構通常用於數據庫的索引,綜合效率較高。 博主推薦閱讀: https://baike.baidu.com/item/B-tree/6606402?fr=aladdin
4>.B+樹(MySQL數據庫默認使用該索引)
B+樹是一種樹數據結構,一般用於數據庫和操做系統的文件系統中。B+樹的特色是可以保持數據穩定有序,其插入與修改擁有較穩定的對數時間複雜度。B+樹元素自底向上插入,這與二叉樹剛好相反。 B+Tree索引: 順序存儲,每個葉子節點到根結點的距離是相同的;左前綴索引,適合查詢範圍類的數據 可使用B+Tree索引的查詢類型: 全值匹配:精確全部索引列,如:姓yin,名zhengjie,年齡18 匹配最左前綴:即只使用索引的第一列,如:姓yin 匹配列前綴:只匹配一列值開頭部分,如:姓以y開頭的 匹配範圍值:如:姓yin和姓ma之間 精確匹配某一列並範圍匹配另外一列:如:姓yin,名以x開頭的 只訪問索引的查詢 B+Tree索引的限制: 如不從最左列開始,則沒法使用索引,如:查找名爲zhengjie,或姓爲g結尾 不能跳過索引中的列:如:查找姓yin,年齡18的,只能使用索引第一列 若是查詢中某個列是爲範圍查詢,那麼其右側的列都沒法再使用索引:如:姓yin,名z%,年齡18,只能利用姓和名上面的索引 特別提示: 索引列的順序和查詢語句的寫法應相匹配,才能更好的利用索引 爲優化性能,可能須要針對相同的列但順序不一樣建立不一樣的索引來知足不一樣類型的查詢需求 博主推薦閱讀: https://baike.baidu.com/item/B+%E6%A0%91/7845683
5>.Hash索引
Hash索引: 基於哈希表實現,只有精確匹配索引中的全部列的查詢纔有效,索引自身只存儲索引列對應的哈希值和數據指針,索引結構緊湊,查詢性能好 Memory存儲引擎支持顯式hash索引,InnoDB和MyISAM存儲引擎不支持 適用場景: 只支持等值比較查詢,包括=, <=>, IN() 不適合使用hash索引的場景 不適用於順序查詢:索引存儲順序的不是值的順序 不支持模糊匹配 不支持範圍查詢 不支持部分索引列匹配查找:如A,B列索引,只查詢A列索引無效
6>.空間數據索引R-Tree( Geospatial indexing )
MyISAM支持地理空間索引,可使用任意維度組合查詢,使用特有的函數訪問,經常使用於作地理數據存儲,使用很少
InnoDB從MySQL5.7以後也開始支持
7>.全文索引(FULLTEXT)
在文本中查找關鍵詞,而不是直接比較索引中的值,相似搜索引擎
InnoDB從MySQL 5.6以後也開始支持
8>.聚簇和非聚簇索引
9>.聚簇和非聚簇索引,主鍵和二級索引
10>.冗餘和重複索引
冗餘索引:(A),(A,B)
重複索引:已經有索引,再次創建索引
11>.索引優化策略
獨立地使用列:
儘可能避免其參與運算,獨立的列指索引列不能是表達式的一部分,也不能是函數的參數,在where條件中,始終將索引列單獨放在比較符號的一側
左前綴索引:
構建指定索引字段的左側的字符數,要經過索引選擇性來評估
索引選擇性:
不重複的索引值和數據表的記錄總數的比值
多列索引:
AND操做時更適合使用多列索引,而非爲每一個列建立單獨的索引
選擇合適的索引列順序:
無排序和分組時,將選擇性最高放左側
12>.索引優化建議
只要列中含有NULL值,就最好不要在此例設置索引,複合索引若是有NULL值,此列在使用時也不會使用索引
儘可能使用短索引,若是能夠,應該制定一個前綴長度
對於常常在where子句使用的列,最好設置索引
對於有多個列where或者order by子句,應該創建複合索引
對於like語句,以%或者‘-’開頭的不會使用索引,以%結尾會使用索引
儘可能不要在列上進行運算(函數操做和表達式操做)
儘可能不要使用not in和<>操做
13>.SQL語句性能優化
查詢時,能不要*就不用*,儘可能寫全字段名
大部分狀況鏈接效率遠大於子查詢
多表鏈接時,儘可能小表驅動大表,即小表 join 大表
在有大量記錄的表分頁時使用limit
對於常用的查詢,能夠開啓緩存
多使用explain和profile分析查詢語句
查看慢查詢日誌,找出執行時間長的sql語句優化
三.管理索引實戰
1>.建立測試數據
MariaDB [yinzhengjie]> CREATE TABLE index_test (id INT auto_increment PRIMARY KEY,name CHAR(50),age INT DEFAULT 20); Query OK, 0 rows affected (0.01 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> DELIMITER $$ MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE PROCEDURE pro_testlog() -> BEGIN -> DECLARE i INT; -> SET i = 1; -> WHILE i < 100000 -> DO -> INSERT INTO index_test(name,age) VALUES (CONCAT('yinzhengjie',i),i); -> SET i = i +1; -> END WHILE; -> END$$ Query OK, 0 rows affected (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> DELIMITER ; MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SELECT * FROM index_test; Empty set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CALL pro_testlog; #調用存儲過程,往index_test表中寫入測試數據 Query OK, 1 row affected (34.07 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test LIMIT 10; +----+---------------+------+ | id | name | age | +----+---------------+------+ | 1 | yinzhengjie1 | 1 | | 2 | yinzhengjie2 | 2 | | 3 | yinzhengjie3 | 3 | | 4 | yinzhengjie4 | 4 | | 5 | yinzhengjie5 | 5 | | 6 | yinzhengjie6 | 6 | | 7 | yinzhengjie7 | 7 | | 8 | yinzhengjie8 | 8 | | 9 | yinzhengjie9 | 9 | | 10 | yinzhengjie10 | 10 | +----+---------------+------+ 10 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test GROUP BY id DESC LIMIT 10; +-------+------------------+-------+ | id | name | age | +-------+------------------+-------+ | 99999 | yinzhengjie99999 | 99999 | | 99998 | yinzhengjie99998 | 99998 | | 99997 | yinzhengjie99997 | 99997 | | 99996 | yinzhengjie99996 | 99996 | | 99995 | yinzhengjie99995 | 99995 | | 99994 | yinzhengjie99994 | 99994 | | 99993 | yinzhengjie99993 | 99993 | | 99992 | yinzhengjie99992 | 99992 | | 99991 | yinzhengjie99991 | 99991 | | 99990 | yinzhengjie99990 | 99990 | +-------+------------------+-------+ 10 rows in set (0.00 sec) MariaDB [yinzhengjie]>
2>.查看索引
MariaDB [yinzhengjie]> HELP SHOW INDEX Name: 'SHOW INDEX' Description: Syntax: SHOW {INDEX | INDEXES | KEYS} {FROM | IN} tbl_name [{FROM | IN} db_name] [WHERE expr] SHOW INDEX returns table index information. The format resembles that of the SQLStatistics call in ODBC. This statement requires some privilege for any column in the table. You can use db_name.tbl_name as an alternative to the tbl_name FROM db_name syntax. These two statements are equivalent: SHOW INDEX FROM mytable FROM mydb; SHOW INDEX FROM mydb.mytable; The WHERE clause can be given to select rows using more general conditions, as discussed in https://mariadb.com/kb/en/extended-show/. You can also list a table's indexes with the mysqlshow -k db_name tbl_name command. URL: https://mariadb.com/kb/en/show-index/ MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> DESC index_test; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | char(50) | YES | | NULL | | | age | int(11) | YES | | 20 | | +-------+----------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test; +------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+-- -------------+| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | I ndex_comment |+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+-- -------------+| index_test | 0 | PRIMARY | 1 | id | A | 99659 | NULL | NULL | | BTREE | | |+------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+-- -------------+1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test WHERE id = 99999; #因爲主鍵就是索引,所以查詢起來速度極快 +-------+------------------+-------+ | id | name | age | +-------+------------------+-------+ | 99999 | yinzhengjie99999 | 99999 | +-------+------------------+-------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test WHERE age = 99999; #不難發現,因爲age並非主鍵索引,所以查詢起來速度明顯有所下降 +-------+------------------+-------+ | id | name | age | +-------+------------------+-------+ | 99999 | yinzhengjie99999 | 99999 | +-------+------------------+-------+ 1 row in set (0.05 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test WHERE age = 99999; #很顯然,查詢的不是主鍵索引的列速度會明顯降低,若是第二次調用該語句時查詢時間爲0,那就得觀察緩存是否開啓了。 +-------+------------------+-------+ | id | name | age | +-------+------------------+-------+ | 99999 | yinzhengjie99999 | 99999 | +-------+------------------+-------+ 1 row in set (0.04 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW VARIABLES LIKE 'query_cache%'; #查看緩存是否開啓。 +------------------------------+---------+ | Variable_name | Value | +------------------------------+---------+ | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 1048576 | | query_cache_strip_comments | OFF | | query_cache_type | OFF | | query_cache_wlock_invalidate | OFF | +------------------------------+---------+ 6 rows in set (0.00 sec) MariaDB [yinzhengjie]>
3>.建立索引
MariaDB [yinzhengjie]> HELP CREATE INDEX Name: 'CREATE INDEX' Description: Syntax: CREATE [ONLINE|OFFLINE] [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [index_type] ON tbl_name (index_col_name,...) [index_option] ... index_col_name: col_name [(length)] [ASC | DESC] index_type: USING {BTREE | HASH} index_option: KEY_BLOCK_SIZE [=] value | index_type | WITH PARSER parser_name | COMMENT 'string' CREATE INDEX is mapped to an ALTER TABLE statement to create indexes. See [HELP ALTER TABLE]. CREATE INDEX cannot be used to create a PRIMARY KEY; use ALTER TABLE instead. For more information about indexes, see https://mariadb.com/kb/en/optimization-and-indexes/. URL: https://mariadb.com/kb/en/create-index/ MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE INDEX my_index_test01 ON index_test(name(16)); Query OK, 0 rows affected (0.27 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test01 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM index_test GROUP BY id DESC LIMIT 3; +-------+------------------+-------+ | id | name | age | +-------+------------------+-------+ | 99999 | yinzhengjie99999 | 99999 | | 99998 | yinzhengjie99998 | 99998 | | 99997 | yinzhengjie99997 | 99997 | +-------+------------------+-------+ 3 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE name = 'yinzhengjie99999'; #咱們發現該語句未用到任何索引。 +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ | 1 | SIMPLE | index_test | ALL | NULL | NULL | NULL | NULL | 99659 | Using where | +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE INDEX my_index_test02 ON index_test(name(16)); #緊接着咱們建立一個在index_test表的name字段上建立一個前16個字符的索引. Query OK, 0 rows affected (0.27 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE name = 'yinzhengjie99999'; #咱們發現該查詢語句使用到我們本身建立的索引啦。 +------+-------------+------------+------+-----------------+-----------------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+------------+------+-----------------+-----------------+---------+-------+------+-------------+ | 1 | SIMPLE | index_test | ref | my_index_test02 | my_index_test02 | 49 | const | 1 | Using where | +------+-------------+------------+------+-----------------+-----------------+---------+-------+------+-------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test02 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test02 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE INDEX my_index_03_name_age ON index_test(name,age); #建立複合索引 Query OK, 0 rows affected (0.34 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test02 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: *************************** 3. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_03_name_age Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: *************************** 4. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_03_name_age Seq_in_index: 2 Column_name: age Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 4 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE name = 'yinzhengjie10000'; #利用複合索引第一個字段查詢,發現是能夠利用到索引 +------+-------------+------------+------+--------------------------------------+----------------------+---------+-------+------+---------------- ----------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+------+-------------+------------+------+--------------------------------------+----------------------+---------+-------+------+---------------- ----------+| 1 | SIMPLE | index_test | ref | my_index_test02,my_index_03_name_age | my_index_03_name_age | 151 | const | 1 | Using where; Us ing index |+------+-------------+------------+------+--------------------------------------+----------------------+---------+-------+------+---------------- ----------+1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE age = '10000'; #利用複合索引第二個字段查詢,發現也是能夠利用到索引,可是不推薦,由於有的時候咱們使用複合索引第二個字段查詢是查詢不到的。具體案例參考下面的演示。 +------+-------------+------------+-------+---------------+----------------------+---------+------+-------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+------------+-------+---------------+----------------------+---------+------+-------+--------------------------+ | 1 | SIMPLE | index_test | index | NULL | my_index_03_name_age | 156 | NULL | 99659 | Using where; Using index | +------+-------------+------------+-------+---------------+----------------------+---------+------+-------+--------------------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test02 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> DESC students; +-----------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+---------------------+------+-----+---------+----------------+ | StuID | int(10) unsigned | NO | PRI | NULL | auto_increment | | Name | varchar(50) | NO | | NULL | | | Age | tinyint(3) unsigned | NO | | NULL | | | Gender | enum('F','M') | NO | | NULL | | | ClassID | tinyint(3) unsigned | YES | | NULL | | | TeacherID | int(10) unsigned | YES | | NULL | | +-----------+---------------------+------+-----+---------+----------------+ 6 rows in set (0.01 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM students GROUP BY StuID DESC LIMIT 2; +-------+-----------------------+-----+--------+---------+-----------+ | StuID | Name | Age | Gender | ClassID | TeacherID | +-------+-----------------------+-----+--------+---------+-----------+ | 25 | 齊天大聖孫悟空 | 100 | M | NULL | NULL | | 24 | Xu Xian | 27 | M | NULL | NULL | +-------+-----------------------+-----+--------+---------+-----------+ 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE INDEX my_students_name_age ON students(name,age); #建立一個複合索引,可是注意使用索引時最好不要直接使用後面那個屬性。 Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM students WHERE name = '齊天大聖孫悟空'; #咱們使用複合索引的第一個name索引字段查詢,發現執行SQL語句時能夠利用到索引 +------+-------------+----------+------+----------------------+----------------------+---------+-------+------+-----------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+----------+------+----------------------+----------------------+---------+-------+------+-----------------------+ | 1 | SIMPLE | students | ref | my_students_name_age | my_students_name_age | 152 | const | 1 | Using index condition | +------+-------------+----------+------+----------------------+----------------------+---------+-------+------+-----------------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM students WHERE age = 100; #咱們使用複合索引的第二個age索引字段查詢,發現執行SQL語句時卻不可使用索引啦! +------+-------------+----------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+----------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | students | ALL | NULL | NULL | NULL | NULL | 25 | Using where | +------+-------------+----------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> DESC students; +-----------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+---------------------+------+-----+---------+----------------+ | StuID | int(10) unsigned | NO | PRI | NULL | auto_increment | | Name | varchar(50) | NO | | NULL | | | Age | tinyint(3) unsigned | NO | | NULL | | | Gender | enum('F','M') | NO | | NULL | | | ClassID | tinyint(3) unsigned | YES | | NULL | | | TeacherID | int(10) unsigned | YES | | NULL | | +-----------+---------------------+------+-----+---------+----------------+ 6 rows in set (0.01 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SELECT * FROM students; +-------+-----------------------+-----+--------+---------+-----------+ | StuID | Name | Age | Gender | ClassID | TeacherID | +-------+-----------------------+-----+--------+---------+-----------+ | 1 | Shi Zhongyu | 22 | M | 2 | 3 | | 2 | Shi Potian | 22 | M | 1 | 7 | | 3 | Xie Yanke | 53 | M | 2 | 16 | | 4 | Ding Dian | 32 | M | 4 | 4 | | 5 | Yu Yutong | 26 | M | 3 | 1 | | 6 | Shi Qing | 46 | M | 5 | NULL | | 7 | Xi Ren | 19 | F | 3 | NULL | | 8 | Lin Daiyu | 17 | F | 7 | NULL | | 9 | Ren Yingying | 20 | F | 6 | NULL | | 10 | Yue Lingshan | 19 | F | 3 | NULL | | 11 | Yuan Chengzhi | 23 | M | 6 | NULL | | 12 | Wen Qingqing | 19 | F | 1 | NULL | | 13 | Tian Boguang | 33 | M | 2 | NULL | | 14 | Lu Wushuang | 17 | F | 3 | NULL | | 15 | Duan Yu | 19 | M | 4 | NULL | | 16 | Xu Zhu | 21 | M | 1 | NULL | | 17 | Lin Chong | 25 | M | 4 | NULL | | 18 | Hua Rong | 23 | M | 7 | NULL | | 19 | Xue Baochai | 18 | F | 6 | NULL | | 20 | Diao Chan | 19 | F | 7 | NULL | | 21 | Huang Yueying | 22 | F | 6 | NULL | | 22 | Xiao Qiao | 20 | F | 1 | NULL | | 23 | Ma Chao | 23 | M | 4 | NULL | | 24 | Xu Xian | 27 | M | NULL | NULL | | 25 | 齊天大聖孫悟空 | 100 | M | NULL | NULL | +-------+-----------------------+-----+--------+---------+-----------+ 25 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> CREATE UNIQUE INDEX my_unique_name_test03 ON students(name); #建立惟一鍵索引(該列字段不容許重複) Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM students\G *************************** 1. row *************************** Table: students Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: StuID Collation: A Cardinality: 25 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: students Non_unique: 0 Key_name: _unique_name_test03 Seq_in_index: 1 Column_name: Name Collation: A Cardinality: 25 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> DESC students; +-----------+---------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+---------------------+------+-----+---------+----------------+ | StuID | int(10) unsigned | NO | PRI | NULL | auto_increment | | Name | varchar(50) | NO | UNI | NULL | | | Age | tinyint(3) unsigned | NO | | NULL | | | Gender | enum('F','M') | NO | | NULL | | | ClassID | tinyint(3) unsigned | YES | | NULL | | | TeacherID | int(10) unsigned | YES | | NULL | | +-----------+---------------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM students WHERE name = '齊天大聖孫悟空'; +------+-------------+----------+-------+---------------------+---------------------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+----------+-------+---------------------+---------------------+---------+-------+------+-------+ | 1 | SIMPLE | students | const | _unique_name_test03 | _unique_name_test03 | 152 | const | 1 | | +------+-------------+----------+-------+---------------------+---------------------+---------+-------+------+-------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
4>.刪除索引
MariaDB [yinzhengjie]> HELP DROP INDEX Name: 'DROP INDEX' Description: Syntax: DROP [ONLINE|OFFLINE] INDEX index_name ON tbl_name DROP INDEX drops the index named index_name from the table tbl_name. This statement is mapped to an ALTER TABLE statement to drop the index. See [HELP ALTER TABLE]. To drop a primary key, the index name is always PRIMARY, which must be specified as a quoted identifier because PRIMARY is a reserved word: DROP INDEX `PRIMARY` ON t; URL: https://mariadb.com/kb/en/drop-index/ MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: *************************** 2. row *************************** Table: index_test Non_unique: 1 Key_name: my_index_test01 Seq_in_index: 1 Column_name: name Collation: A Cardinality: 99659 Sub_part: 16 Packed: NULL Null: YES Index_type: BTREE Comment: Index_comment: 2 rows in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> DROP INDEX my_index_test01 ON index_test; #刪除指定表中的索引 Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW INDEXES FROM index_test\G *************************** 1. row *************************** Table: index_test Non_unique: 0 Key_name: PRIMARY Seq_in_index: 1 Column_name: id Collation: A Cardinality: 99659 Sub_part: NULL Packed: NULL Null: Index_type: BTREE Comment: Index_comment: 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
4>.優化表空間
MariaDB [yinzhengjie]> HELP OPTIMIZE TABLE Name: 'OPTIMIZE TABLE' Description: Syntax: OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ... OPTIMIZE TABLE should be used if you have deleted a large part of a table or if you have made many changes to a table with variable-length rows (tables that have VARCHAR, VARBINARY, BLOB, or TEXT columns). Deleted rows are maintained in a linked list and subsequent INSERT operations reuse old row positions. You can use OPTIMIZE TABLE to reclaim the unused space and to defragment the data file. After extensive changes to a table, this statement may also improve performance of statements that use the table, sometimes significantly. This statement requires SELECT and INSERT privileges for the table. OPTIMIZE TABLE is supported for partitioned tables, and you can use ALTER TABLE ... OPTIMIZE PARTITION to optimize one or more partitions; for more information, see [HELP ALTER TABLE], and http://dev.mysql.com/doc/refman/5.5/en/partitioning-maintenance.html. URL: https://mariadb.com/kb/en/optimize-table/ MariaDB [yinzhengjie]>
5>.查看索引的使用
MariaDB [yinzhengjie]> SHOW VARIABLES LIKE 'userstat'; #查看索引的使用功能默認是關閉的 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | userstat | OFF | +---------------+-------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> SET GLOBAL userstat=1; #臨時開啓該功能,也能夠在配置文件中永久定義其開啓,前提是我們知道它是一個服務器參數。 Query OK, 0 rows affected (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> SHOW VARIABLES LIKE 'userstat'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | userstat | ON | +---------------+-------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> SHOW INDEX_STATISTICS; #查看哪些索引被利用了,哪些沒有被利用。 +--------------+--------------+------------+-----------+ | Table_schema | Table_name | Index_name | Rows_read | +--------------+--------------+------------+-----------+ | mysql | tables_priv | PRIMARY | 1 | | mysql | columns_priv | PRIMARY | 2 | | yinzhengjie | index_test | PRIMARY | 1 | +--------------+--------------+------------+-----------+ 3 rows in set (0.00 sec) MariaDB [yinzhengjie]>
四.EXPLAIN
1>.EXPLAIN
經過EXPLAIN來分析索引的有效性
語法格式:EXPLAIN SELECT clause
獲取查詢執行計劃信息,用來查看查詢優化器如何執行查詢
2>.案例演示
MariaDB [yinzhengjie]> HELP EXPLAIN Name: 'EXPLAIN' Description: Syntax: EXPLAIN [explain_type] SELECT select_options explain_type: EXTENDED | PARTITIONS Or: EXPLAIN tbl_name The EXPLAIN statement can be used either as a way to obtain information about how MySQL executes a statement, or as a synonym for DESCRIBE: o When you precede a SELECT statement with the keyword EXPLAIN, MySQL displays information from the optimizer about the query execution plan. That is, MySQL explains how it would process the statement, including information about how tables are joined and in which order. EXPLAIN EXTENDED can be used to obtain additional information. For information about using EXPLAIN and EXPLAIN EXTENDED to obtain query execution plan information, see https://mariadb.com/kb/en/explain/. o EXPLAIN PARTITIONS is useful only when examining queries involving partitioned tables. For details, see http://dev.mysql.com/doc/refman/5.5/en/partitioning-info.html. o EXPLAIN tbl_name is synonymous with DESCRIBE tbl_name or SHOW COLUMNS FROM tbl_name. For information about DESCRIBE and SHOW COLUMNS, see [HELP DESCRIBE], and [HELP SHOW COLUMNS]. URL: https://mariadb.com/kb/en/explain/ MariaDB [yinzhengjie]> MariaDB [yinzhengjie]>
MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE id = 99999; +------+-------------+------------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+------------+-------+---------------+---------+---------+-------+------+-------+ | 1 | SIMPLE | index_test | const | PRIMARY | PRIMARY | 4 | const | 1 | | +------+-------------+------------+-------+---------------+---------+---------+-------+------+-------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]> MariaDB [yinzhengjie]> EXPLAIN SELECT * FROM index_test WHERE age = 99999; +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ | 1 | SIMPLE | index_test | ALL | NULL | NULL | NULL | NULL | 99659 | Using where | +------+-------------+------------+------+---------------+------+---------+------+-------+-------------+ 1 row in set (0.00 sec) MariaDB [yinzhengjie]>
3>.EXPLLAIN輸出信息說明
id: 當前查詢語句中,每一個SELECT語句的編號 複雜類型的查詢有三種: 簡單子查詢 用於FROM中的子查詢 聯合查詢:UNION(注意:UNION查詢的分析結果會出現一個額外匿名臨時表) select_type: 簡單查詢爲SIMPLE 複雜查詢: SUBQUERY 簡單子查詢 PRIMARY 最外面的SELECT DERIVED 用於FROM中的子查詢 UNION UNION語句的第一個以後的SELECT語句 UNION RESULT 匿名臨時表 table: SELECT語句關聯到的表 type: 關聯類型或訪問類型,即MySQL決定的如何去查詢表中的行的方式,如下順序,性能從低到高: ALL: 全表掃描 index:根據索引的次序進行全表掃描;若是在Extra列出現「Using index」表示了使用覆蓋索引,而非全表掃描 range:有範圍限制的根據索引實現範圍掃描;掃描位置始於索引中的某一點,結束於另外一點 ref: 根據索引返回表中匹配某單個值的全部行 eq_ref:僅返回一個行,但與須要額外與某個參考值作比較 const, system: 直接返回單個行 possible_keys: 查詢可能會用到的索引 key: 查詢中使用到的索引 key_len: 在索引使用的字節數 ref: 在利用key字段所表示的索引完成查詢時所用的列或某常量值 rows: MySQL估計爲找全部的目標行而須要讀取的行數 Extra: 額外信息 Using index:MySQL將會使用覆蓋索引,以免訪問表 Using where:MySQL服務器將在存儲引擎檢索後,再進行一次過濾 Using temporary:MySQL對結果排序時會使用臨時表 Using filesort:對結果使用一個外部索引排序 博主推薦閱讀: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html