数据库系列5 索引

2019-02-13  本文已影响0人  莫小归

参考:
MySQL索引背后的数据结构及算法原理:https://www.jianshu.com/p/fae74b6a54e3
数据库索引的数据结构:https://www.jianshu.com/p/46349daf531c
索引与算法:https://www.jianshu.com/p/3cd2ff996216
数据库索引创建与优化:https://www.jianshu.com/p/6446c0118427
B-Tree与B+Tree:https://www.cnblogs.com/vincently/p/4526560.html

一.索引概述

1.定义
2.索引的缺点

二.索引分类

1.基本索引类型
2.B+Tree索引
B-Tree

1)有效减少IO次数(平衡 + 节点数据多/深度小)
2)不能解决范围查找
3)内节点存放指针+数据

B+Tree

1)节点可存放更多Key(数据地址只存在叶子节点)
2)叶子节点双向连接,方便范围查找
3)内节点只存放指针

3.哈希索引
4.常见存储引擎支持的索引
索引 InnoDB MyISAM Memory
B-Tree 支持 支持 支持
Hash 不支持 不支持 支持
R-Tree 不支持 支持 不支持
Full-text 暂不支持 支持 支持

三.索引与查询优化

1.查询效率与IO次数/IO介质
2.索引使用场景
3.前缀索引
4.InnoDB的自增主键与插入优化
5.选择整型或较小的定长字符串作为索引

四.应用场景

1.最左前缀原理与相关优化
EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26' AND emp_no='10001' AND title='Senior Engineer';
+----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref               | rows | Extra |
+----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+
|  1 | SIMPLE      | titles | const | PRIMARY       | PRIMARY | 59      | const,const,const |    1 |       |
+----+-------------+--------+-------+---------------+---------+---------+-------------------+------+-------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001';
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | titles | ref  | PRIMARY       | PRIMARY | 4       | const |    1 |       |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND from_date='1986-06-26';
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | titles | ref  | PRIMARY       | PRIMARY | 4       | const |    1 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
EXPLAIN SELECT * FROM employees.titles WHERE from_date='1986-06-26';
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | titles | ALL  | NULL          | NULL | NULL    | NULL | 443308 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND title LIKE 'Senior%';
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | titles | range | PRIMARY       | PRIMARY | 56      | NULL |    1 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no < '10010' and title='Senior Engineer';
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table  | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | titles | range | PRIMARY       | PRIMARY | 4       | NULL |   16 | Using where |
+----+-------------+--------+-------+---------------+---------+---------+------+------+-------------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no='10001' AND left(title, 6)='Senior';
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | titles | ref  | PRIMARY       | PRIMARY | 4       | const |    1 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
EXPLAIN SELECT * FROM employees.titles WHERE emp_no - 1='10000';
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | titles | ALL  | NULL          | NULL | NULL    | NULL | 443308 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
2.常见索引失效情况

以students表为例,建表语句如下

CREATE TABLE `students` (
  `stud_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `email` varchar(50) NOT NULL,
  `phone` varchar(1) NOT NULL,
  `create_date` date DEFAULT NULL,
  PRIMARY KEY (`stud_id`)
 
)

INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('1', 'admin', 'student1@gmail.com', '18729902095', '1983-06-25');
INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('2', 'root', '74298110186@qq.com', '2', '1983-12-25');
INSERT INTO `learn_mybatis`.`students` (`stud_id`, `name`, `email`, `phone`, `create_date`) VALUES ('3', '110', '7429811086@qq.com', '3dsad', '2017-04-28');
CREATE INDEX index_name_email ON students(email);
CREATE INDEX index_name_phone ON students(phone);

or连接单个索引字段时,可使用索引,如:

EXPLAIN select * from students where stud_id='1'  or phone='18729902095';
EXPLAIN select * from students where stud_id='1'  or email='742981086@qq.com';

or连接多个索引字段时,会依次扫描索引email和索引phone,再合并两个扫描结果,如果需要合并的结果集非常大,合并将非常耗时,导致索引失效。如:

EXPLAIN select * from students where phone='18729902095' or email='742981086@qq.com'
EXPLAIN select * from students where stud_id='1'  or phone='222' or email='742981086@qq.com'
CREATE INDEX index_name_email ON students(email);
# 使用了index_name_email索引
EXPLAIN select * from students where email like '742981086@qq.com%'

# 没有使用index_name_email索引,索引失效
EXPLAIN select * from students where email like '%742981086@qq.com'
EXPLAIN select * from students where email like '%742981086@qq.com%'
create index index_email_phone on students(email,phone);
# 使用了 index_email_phone 索引
EXPLAIN select * from students where email='742981086@qq.com' and  phone='18729902095'
EXPLAIN select * from students where phone='18729902095' and  email='742981086@qq.com'
EXPLAIN select * from students where email='742981086@qq.com' and name='admin'

# 没有使用index_email_phone索引,复合索引失效
EXPLAIN select * from students where phone='18729902095' and name='admin'
CREATE INDEX index_name ON students(name);
# 使用索引
EXPLAIN select * from students where name='110'

# 没有使用索引
EXPLAIN select * from students where name=110
3.注意
select A, B, C from TABLE where A in (m, n, p) and B=b;

推荐索引:(A,B)
对于in谓词后的列表,MySQL将循环列表中数据,然后分别与后续索引字段联合。上述查询将被MySQL拆分为(A=m and B=b) union (A=n and B=b) union (A=p and B=b),然后再分别以这三个拆分条件扫描联合索引(A,B)。由于与or相比,in列表条件不重复,合并结果集时不存在去重操作,所以查询效率高,能够使用索引

select A, B, C, D from TABLE where A=a and B>b and C>c;

推荐索引:(A, B, C, D)或(A, C, B, D),具体使用哪个索引根据B和C的选择性定
1)需要满足最左前缀原则
2)建立复合索引时,将选择性高的字段放在前面进行索引过滤,将选择性低的字段放在后面进行索引覆盖扫描

4.一个索引失败的案例(索引建立在区分度低的列)
mysql> desc s1;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| id     | int(11)     | YES  |     | NULL    |       |
| name   | varchar(20) | YES  |     | NULL    |       |
| gender | char(5)     | YES  |     | NULL    |       |
| email  | varchar(50) | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

现象:
1)没有为name字段添加索引时查询慢
2)为name字段添加索引后查询速度变快
3)如果查询条件为name='egon',速度又变慢了

建立索引前后查询速度变化

原因:
1)name字段区分度很低,大多数name都被赋值为egon
2)根据B+树结构,name字段索引的树高很高
3)当查询条件正好是name='egon'时,IO次数较大,查询效率低,与全表扫描速度相近

欲去不得去,薄游成久游

上一篇下一篇

猜你喜欢

热点阅读