zoukankan      html  css  js  c++  java
  • Mysql实战之索引

    author:JevonWei
    版权声明:原创作品


    索引基础:

    索引:提取索引的创建在的表上字段中的数据,构建出一个独特的数据结构;
    	
    索引的作用:加速查询操作;副作用:降低写操作性能;
    	表中数据子集:把表中某个或某些字段的数据提取出来另存为一个特定数据结构组织的数据;
    		某个字段或某些字段:WHERE子句中用到的字段;
    			
    索引类型:B+ TREE Index,HASH Index、Fulltext Index、Spacial Index 
    	B+ TREE:顺序存储,每一个叶子结点到根结点的距离相同;左前缀索引,适合于范围类型的数据查询;
    	
    		适用于B+ TREE索引的查询类型:全键值、键值范围或键前缀;
    			全值匹配:精确匹配某个值;
    				WHERE COLUMN = 'value';
    			匹配最左前缀:只精确匹配起头的部分;
    				WEHRE COLUMN LIKE 'PREFIX%';						
    			匹配范围值:
    			精确匹配某一列,范围匹配另一列;
    			只用访问索引的查询:覆盖索引;
    				index(Name)
    				SELECT Name FROM students WHERE Name LIKE 'L%';
    										
    		不适用B+ TREE索引:
    			如果查询条件不是从最左侧列开始,索引无效;
    				index(age,Fname), WHERE Fname='Jerry';(无效)    , WHERE age>30 AND Fname='Smith';
    			不能跳过索引中的某列;
    				index(name,age,gender)
    					WHERE name='black' and age > 30;
    					WHERE name='black' AND gender='F'; (无效不可用)
    			如果查询中的某个列是为范围查询,那么其右侧的列都无法再使用索引优化查询;
    				WHERE age>30 AND Fname='Smith'  (无效) 
    			
    	Hash索引:基于哈希表实现,特别适用于值的精确匹配查询;
    		
    		适用场景:
    			只支持等值比较查询,例如=, IN(), <=>
    				
    		不用场景:
    			所有非精确值查询;MySQL仅对memory存储引擎支持显式的hash索引;
    				
    索引优点:
    	降低需要扫描的数据量,减少IO次数;
    	可以帮助避免排序操作,避免使用临时表; 
    	帮助将随机IO转为顺序IO;
    		
    高性能索引策略:
    	(1) 在WHERE中独立使用列,尽量避免其参与运算;
    		WHERE age+2 > 32 ; (索引失效)
    	(2) 左前缀索引:索引构建于字段的最左侧的多少个字符,要通过索引选择性来评估
    		索引选择性:不重复的索引值和数据表的记录总数的比值;
    	(3) 多列索引:
    		AND连接的多个查询条件更适合使用多列索引,而非多个单键索引;
    	(4) 选择合适的索引列次序:选择性最高的放左侧;
    		
    EXPLAIN来分析索引有效性:
    
    	EXPLAIN [explain_type] SELECT select_options  分析select语句索引的有效性
    		
    		explain_type:
    			EXTENDED
    			| PARTITIONS	
    	
    	输出结果:
    				id: 1
    		select_type: SIMPLE
    			table: students
    			type: const
    		possible_keys: PRIMARY
    			key: PRIMARY
    		key_len: 4
    			ref: const
    			rows: 1
    			Extra: 
    			
    		id:当前查询语句中,第个SELECT语句的编号;
    				
    			复杂的查询的类型主要三种:
    				简单子查询
    				用于FROM中的子查询
    				联合查询
    					
    			注意:联合查询的分析结果会出现一个额外的匿名临时表;
    				
    		select_type:查询类型:
    			简单查询:SIMPLE
    			复杂查询:
    				简单子查询:SUBQUERY
    				用于FROM中的子查询:DERIVED
    				联合查询中的第一个查询:PRIMARY
    				联合查询中的第一个查询之后的其它查询:UNION
    				联合查询生成的临时表:UNION RESULT
    					
    		table:查询针对的表;
    			
    		type:关联类型,或称为访问类型,即MySQL如何去查询表中的行
    			ALL:全表扫描;
    			index:根据索引的顺序进行的全表扫描;但同时如果Extra列出现了"Using index”表示使用了覆盖索引;
    			range:有范围限制地根据索引实现范围扫描;扫描位置始于索引中的某一项,结束于另一项;
    			ref:根据索引返回的表中匹配到某单个值的所有行(匹配给定值的行不止一个);
    			eq_ref:根据索引返回的表中匹配到某单个值的单一行,仅返回一个行,但需要与某个额外的参考值比较,而不是常数;
    			const,system:与某个常数比较,且只返回一行;
    				
    		possiable_keys:查询中可能会用到的索引;
    				
    		key:查询中使用的索引;
    			
    		key_len:查询中用到的索引长度;
    			
    		ref:在利用key字段所显示的索引完成查询操作时所引用的列或常量值; 
    			
    		rows:MySQL估计出的为找到所有的目标项而需要读取的行数;
    			
    		Extra:额外信息
    			Using index:使用了覆盖索引进行的查询;
    			Using where:拿到数据后还要再次进行过滤; 
    			Using temporary:使用了临时表以完成查询;
    			Using filesort:对结果使用了一个外部索引排序;
    

    实战:

    数据库中插入大量数据,从而创建索引

    [root@danran ~]# for i in {5..1000};do mysql -e "INSERT INTO mydb.students VALUES ('$i','stu$i',$[$RANDOM%100+1],'F','major$i')";done
    [root@danran ~]# for i in {1001..2000};do mysql -e "INSERT INTO mydb.students VALUES ('$i','stu$i',$[$RANDOM%100+1],'M','major$i')";done
    

    单键索引

    MariaDB [(none)]> create database mydb;  创建数据库
    MariaDB [(none)]> use mydb;
    MariaDB [mydb]> create table students (stuid INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,name VARCHAR(200),age TINYINT UNSIGNED,gender ENUM('F','M'),major VARCHAR(200));  
    	创建students表,子段为stuid,name,age,gender,major
    MariaDB [mydb]> INSERT INTO students (name,age,gender,major) VALUE('dan',16,'M','jian'),('ran',20,'F','quan'),('jevon',19,'M','qiang'),('wei',15,'F','wu die');
    	插入数据到students表
    MariaDB [mydb]> EXPLAIN SELECT name,age FROM students WHERE age >=15; 分析select语句索引的有效性
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    | id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    |    1 | SIMPLE      | students | ALL  | NULL          | NULL | NULL    | NULL | 1819 | Using where |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    MariaDB [mydb]> HELP CREATE INDEX   查看索引创建帮助
    
    创建索引
    MariaDB [mydb]> CREATE INDEX name ON students(name);  \根据students表的name字段创建索引name
    
    显示students表的索引信息
    MariaDB [mydb]> SHOW INDEXES FROM students;
    +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
    +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | students |          0 | PRIMARY  |            1 | stuid       | A         |        2203 |     NULL | NULL   |      | BTREE      |         |               |
    | students |          1 | name     |            1 | name        | A         |         244 |     NULL | NULL   | YES  | BTREE      |         |               |
    +----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    
    显示select语句的索引有效性
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name = 'stu1002';
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+
    | id   | select_type | table    | type | possible_keys | key  | key_len | ref   | rows | Extra                 |
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+
    |    1 | SIMPLE      | students | ref  | name          | name | 203     | const |    1 | Using index condition |
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+	
    
    显示select语句等值查询的索引有效性
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name = 'stu100%';
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+
    | id   | select_type | table    | type | possible_keys | key  | key_len | ref   | rows | Extra                 |
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+
    |    1 | SIMPLE      | students | ref  | name          | name | 203     | const |    1 | Using index condition |
    +------+-------------+----------+------+---------------+------+---------+-------+------+-----------------------+
    
    显示select语句范围查询的索引有效性	
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name LIKE 'stu100%';
    +------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+
    | id   | select_type | table    | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
    +------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+
    |    1 | SIMPLE      | students | range | name          | name | 203     | NULL |   11 | Using index condition |
    +------+-------------+----------+-------+---------------+------+---------+------+------+-----------------------+	
    

    多键索引

    创建多键索引   
    MariaDB [mydb]> CREATE INDEX name_and_age ON students(name,age);  根据students表的name和age字段创建name_and_age多键索引
    
    显示students表的索引信息,其中包含name单键索引和name_and_age多键索引
    MariaDB [mydb]> SHOW INDEXES FROM students; 
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | Table    | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | students |          0 | PRIMARY      |            1 | stuid       | A         |        1765 |     NULL | NULL   |      | BTREE      |         |               |
    | students |          1 | name_and_age |            1 | name        | A         |         160 |     NULL | NULL   | YES  | BTREE      |         |               |
    | students |          1 | name_and_age |            2 | age         | A         |         160 |     NULL | NULL   | YES  | BTREE      |         |               |
    | students |          1 | name         |            1 | name        | A         |         160 |     NULL | NULL   | YES  | BTREE      |         |               |
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    
    有多键索引使用时,单键索引失效   
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name LIKE 'stu100%';
    +------+-------------+----------+-------+-------------------+------+---------+------+------+-----------------------+
    | id   | select_type | table    | type  | possible_keys     | key  | key_len | ref  | rows | Extra                 |
    +------+-------------+----------+-------+-------------------+------+---------+------+------+-----------------------+
    |    1 | SIMPLE      | students | range | name,name_and_age | name | 203     | NULL |   11 | Using index condition |
    +------+-------------+----------+-------+-------------------+------+---------+------+------+-----------------------+
    
    删除单键name索引
    MariaDB [mydb]> DROP INDEX name ON students;
    
    删除单键name索引后,name_and_age多键索引依然存在   
    MariaDB [mydb]> SHOW INDEXES FROM students; 
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | Table    | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    | students |          0 | PRIMARY      |            1 | stuid       | A         |        1630 |     NULL | NULL   |      | BTREE      |         |               |
    | students |          1 | name_and_age |            1 | name        | A         |        1630 |     NULL | NULL   | YES  | BTREE      |         |               |
    | students |          1 | name_and_age |            2 | age         | A         |        1630 |     NULL | NULL   | YES  | BTREE      |         |               |
    +----------+------------+--------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
    
    显示select语句的有效索引为多键索引name_and_age
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name LIKE 'stu100%';
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+
    | id   | select_type | table    | type  | possible_keys | key          | key_len | ref  | rows | Extra                 |
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+
    |    1 | SIMPLE      | students | range | name_and_age  | name_and_age | 203     | NULL |   11 | Using index condition |
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+	
    	
    多键索引name_and_age时,跳过name键直接使用age键时,索引无效,如下
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE age >=50;
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    | id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    |    1 | SIMPLE      | students | ALL  | NULL          | NULL | NULL    | NULL | 1630 | Using where |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    
    多键索引name_and_age时,直接使用单键name或使用多键时,索引有效(以下因为name键过滤太过故索引无效)
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name LIKE 'stu%' AND age >=50;
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    | id   | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    |    1 | SIMPLE      | students | ALL  | name_and_age  | NULL | NULL    | NULL | 1630 | Using where |
    +------+-------------+----------+------+---------------+------+---------+------+------+-------------+
    
    当多键索引过滤不多时,索引可以正常使用,故key索引键值正常显示
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE name LIKE 'stu100%' AND age >=50;
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+
    | id   | select_type | table    | type  | possible_keys | key          | key_len | ref  | rows | Extra                 |
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+
    |    1 | SIMPLE      | students | range | name_and_age  | name_and_age | 205     | NULL |   11 | Using index condition |
    +------+-------------+----------+-------+---------------+--------------+---------+------+------+-----------------------+
    

    多表查询的索引信息

    显示select语句多表查询
    MariaDB [mydb]> SELECT * FROM students WHERE age = 100 UNION SELECT * FROM students WHERE name LIKE 'stu100%';
    
    显示select语句多表查询的有效索引信息
    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE age = 100 UNION SELECT * FROM students WHERE name LIKE 'stu100%'; 
    +------+--------------+------------+-------+---------------+--------------+---------+------+------+-----------------------+
    | id   | select_type  | table      | type  | possible_keys | key          | key_len | ref  | rows | Extra                 |
    +------+--------------+------------+-------+---------------+--------------+---------+------+------+-----------------------+
    |    1 | PRIMARY      | students   | ALL   | NULL          | NULL         | NULL    | NULL | 1630 | Using where           |
    |    2 | UNION        | students   | range | name_and_age  | name_and_age | 203     | NULL |   11 | Using index condition |
    | NULL | UNION RESULT | <union1,2> | ALL   | NULL          | NULL         | NULL    | NULL | NULL |                       |
    +------+--------------+------------+-------+---------------+--------------+---------+------+------+-----------------------+
    

    等值查询的有效索引

    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE stuid=4;
    +------+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
    | id   | select_type | table    | type  | possible_keys | key     | key_len | ref   | rows | Extra |
    +------+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
    |    1 | SIMPLE      | students | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
    +------+-------------+----------+-------+---------------+---------+---------+-------+------+-------+
    

    范围查询的有效索引

    MariaDB [mydb]> EXPLAIN SELECT * FROM students WHERE stuid IN (1,77,1200,1300);
    +------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
    | id   | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
    +------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
    |    1 | SIMPLE      | students | range | PRIMARY       | PRIMARY | 4       | NULL |    4 | Using where |
    +------+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
    

    索引显示的选项:

    id:select语句的ID
    select_type:查询类型:
    	简单查询:SIMPLE
    	复杂查询:
    		简单子查询:SUBQUERY
    		用于FROM中的子查询:DERIVED
    		联合查询中的第一个查询:PRIMARY
    		联合查询中的第一个查询之后的其它查询:UNION
    		联合查询生成的临时表:UNION RESULT						
    table:查询针对的表;				
    type:关联类型,或称为访问类型,即MySQL如何去查询表中的行
    	ALL:全表扫描;表示无索引可用或索引过滤的行太多似无索引过滤 
    	index:根据索引的顺序进行的全表扫描;但同时如果Extra列出现了"Using index”表示使用了覆盖索引;
    	range:有范围限制地根据索引实现范围扫描;扫描位置始于索引中的某一项,结束于另一项;
    	ref:根据索引返回的表中匹配到某单个值的所有行(匹配给定值的行不止一个);
    	eq_ref:根据索引返回的表中匹配到某单个值的单一行,仅返回一个行,但需要与某个额外的参考值比较,而不是常数;
    	const,system:与某个常数比较,且只返回一行;					
    possiable_keys:查询中可能会用到的索引;					
    key:查询中使用的索引;				
    key_len:查询中用到的索引长度;				
    ref:在利用key字段所显示的索引完成查询操作时所引用的列或常量值; 				
    rows:MySQL估计出的为找到所有的目标项而需要读取的行数;				
    Extra:额外信息
    	Using index:使用了覆盖索引进行的查询;
    	Using where:拿到数据后还要再次进行过滤; 
    	Using temporary:使用了临时表以完成查询;
    	Using filesort:对结果使用了一个外部索引排序;
  • 相关阅读:
    Qt学习之路,part1
    1.获取状态栏的高度
    如何在Android Studio中上传代码到Gitee上
    关于类图
    外观模式
    关于类的实例
    SharedPreference中关于editor.apply()和editor.commit()
    活动的4种启动模式
    unittest中case批量管理
    unittest使用
  • 原文地址:https://www.cnblogs.com/JevonWei/p/7517418.html
Copyright © 2011-2022 走看看