书中的目录,就是充当索引的角色,方便我们快速查找书中的内容,所以索引是以空间换时间的设计思想。
索引和数据位于存储引擎中,MySQL默认的存储引擎是InnoDB。
MySQL的数据是持久化的,即数据(索引+记录)保存在磁盘上,即使断电,数据也不会丢失。
因此,当我们通过索引查找某行数据的时候,实际上是:
查询过程中会发生多次磁盘IO,而磁盘非常非常非常慢,我们希望索引的数据结构能在尽可能少的磁盘的 I/O 操作中完成查询工作:
一次磁盘访问的代价大约是几十万条指令。这意味着为了节省一次磁盘访问,我们愿意进行大量的运算。《数据结构与算法设计——Java语言描述》
因此,适合MySQL索引的数据结构,应该满足:在尽可能少的磁盘IO操作中查询某个记录 / 执行范围查找。
哈希?只适合等值查询,不适合范围查询。
二分查找?NoNoNo,插入和删除需要移动后续元素, O ( n ) O(n) O(n)的代价对磁盘来说太大太大。
二叉查找树?想法不错,但存在退化成链表的可能性。
平衡二叉查找树 / 红黑树 ?不管平衡二叉查找树还是红黑树,都会随着插入的元素增多,而导致树的高度变高,相较于多叉树,其需要访问的节点数更多,这就意味着磁盘 I/O 操作次数多,会影响整体数据查询的效率。对于磁盘来说,当树的节点数很大时, log 2 N \log_2 N log2N远大于 log M N \log_M N logMN,增加一个节点能够保存的索引数M,可以使树变得更矮胖。
B树?B 树的每一个节点最多可以包括 M − 1 M-1 M−1个数据项和 M M M个子节点,超过这些要求的话就会分裂节点。B树的每个节点都包含数据(索引+记录),而用户的记录数据的大小很有可能远远超过了索引数据,在查询过程中会将无用的记录数据加载进内存,这就需要花费更多的磁盘 I/O 操作次数来读到有用的索引数据。
B树参考:http://t.csdn.cn/kYHFi
是B树的变体,一棵M阶的B+树主要有这些特点:
因此,我们可以知道,二者的主要区别:
原理说明:https://juejin.cn/post/6929833495082565646?searchId=202307261122017B0C2F66AFF8CF77995A
在线模拟:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html
插入:
删除:找到包含关键值的节点,如果关键值是当前节点的最大/最小值且存在于父节点中,则删除时需要相应调整父节点的值。
从数据结构的角度来看,MySQL 常见索引有 B+Tree 索引、HASH 索引、Full-Text 索引。
在创建表时,InnoDB存储引擎会根据不同的场景选择不同的列作为聚簇索引的索引列:
其他索引都属于辅助索引(Secondary Index),也叫做二级索引或者非聚簇索引。
主键索引的 B+Tree 和二级索引的 B+Tree 区别如下:
从字段特性的角度来看,索引分为主键索引、唯一索引、普通索引、前缀索引。
CREATE TABLE table_name (
....
PRIMARY KEY (index_column_1) USING BTREE
);
CREATE TABLE table_name (
....
UNIQUE KEY(index_column_1,index_column_2,...)
);
CREATE TABLE table_name (
....
INDEX(index_column_1,index_column_2,...)
);
CREATE INDEX index_name
ON table_name(index_column_1,index_column_2,...);
从字段个数的角度来看,索引分为单列索引、联合索引。
联合索引具有最左匹配原则,即按照最左优先的方式进行索引的匹配。使用联合索引进行查询的时候,如果不遵循最左匹配原则,联合索引会失效。
比如(a, b, c)联合索引,是先按 a 排序,在 a 相同的情况再按 b 排序,在 b 相同的情况再按 c 排序。所以,b 和 c 是全局无序,局部相对有序的,这样在没有遵循最左匹配原则的情况下,是无法利用到索引的。
Q1:
select * from t_table where a > 1 and b = 2
,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree?A1:只有a,进行索引扫描时,可以定位到符合a>1的第一条记录,然后沿着记录所在链表向后扫描
Q2:
select * from t_table where a >= 1 and b = 2
,联合索引(a, b)哪一个字段用到了联合索引的 B+Tree?A2: a和b,可以定位到a=1、b=2的第一条记录,再向后扫描
实际开发工作中建立联合索引时,要把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的 SQL 使用到。
由于索引占用空间,且创建、维护索引需要耗费时间,比如进行插入、更新、删除时,MySQL不仅要保存数据,还要维护索引,所以需要合理地选择使用索引。
什么时候适合使用索引?
主键自动建立唯一索引
字段唯一,比如UUID
经常使用Where查询的字段,这样能够提高整个表的查询速度
经常使用GROUP BY和ORDER BY的字段,这样查询时不用再做一次排序
查询中排序的字段,排序的字段如果通过索引访问将大大提高排序速度
什么时候不需要创建索引?
like语句的前导模糊查询不能使用索引,like查询是以%开头,索引会失效
负向条件查询不能使用索引,负向条件有:!=
、<>
、not in
、not exists
、not like
等,优化案例:
select * from doc where status != 1 and status != 2; --优化前
select * from doc where status in (0,3,4); --优化后
查询条件中带有or,除非所有的查询条件都建有索引
如果列类型是字符串,那在查询条件中需要将数据用引号引用起来,否则索引失效,因为强制类型转换会导致全表扫描:
select * from user where phone=13800001234;
select * from user where phone='13800001234';
索引列上参与计算,索引失效,尽量不要在索引列上做任何操作(计算、函数),例子:
select * from doc where YEAR(create_time) <= '2016'; --优化前
select * from doc where create_time <= '2016-01-01'; --优化后
违背最左匹配原则,索引失效:
select * from employees.titles where emp_no < 10010' and title='Senior Engineer'and from_date between '1986-01-01' and '1986-12-31';
(empno、title、fromdate),emp_no 可以用到索引,而title 和 from_date 则使用不到索引
如果MySQL估计全表扫描要比使用索引要快,索引失效
如果明确知道只有一条结果返回,limit 1 能够提高效率
select * from user where login_name=?;
select * from user where login_name=? limit 1