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