目录
常见查找算法
- 顺序查找:遍历
- 二分查找:前提是有序列表
- 哈希查找:最高效,O(1),hash冲突,JDK1.8里HashMap:数组+链表+红黑树(处理hash冲突)
- 二叉查找树/二叉排序树/二叉搜索树:二分查找的思想,时间复杂度就是树的深度为logn。缺点:可能会造成树一边倒,时间复杂度变O(n),查找变慢。故演变出平衡二叉树(AVL)
- 平衡二叉树(AVL树):左右子树高度之差不超过1。
- 红黑树:高效的查找算法结构
- B+树
- B-Tree
- …
MySQL索引背后的数据结构及算法原理:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
红黑树
底层:特殊的二叉查找树
HashMap底层用到红黑树
红黑树的性质
- 每个结不是红色就是黑色
- 不可能有连在一起的红色结点
- 根结点都是黑色的
- 每个红色结点的两个子结点都是黑色。叶子结点都是黑色。
红黑树的变换规则
所有插入的点默认为红色
1 改变颜色
当前结点的父亲是红色,叔叔结点也是红色,
- 把父结点变为黑色
- 把叔叔结点变为黑色
- 把祖父结点变为红色
- 把指针定义到祖父结点设为当前要操作的结点
2 左旋
当前父结点是红色,叔叔结点是黑色的时候,且当前的结点是右子树,左旋以父结点作为左旋.
3 右旋
当前父结点是红色,叔叔结点是黑色的时候,且当前的结点是左子树,右旋
- 把父结点变为黑色
- 把祖父结点变为红色
- 以祖父结点作为旋转
数据库查找(B+树) banlance tree
select * from user where id = 100
select * from user where user_id < 100 and user_id > 50
1、为什么红黑树不能作为数据库索引?
数据存放在在磁盘中,通过红黑树对数据检索资源浪费严重.
- 读取磁盘的次数太多
- 读取浪费太多
2、为什么红黑树可以用在HashMap的查找?
因为HashMap存放的数据放在内存中,不需要经过磁盘.速度快
3、为什么可以用B+树作为索引
B树是N叉树,浪费资源少
B树
BTree有一个非常重要的操作分裂
。
作为索引的缺点:
- BTree所有的点都会存数据,会造成空间浪费。范围查找问题没有解决。
进行范围查找时索引会失效,比如 id<9就要遍历9的所有左子树;
B+树
在B树的基础上给叶子结点加了双向链表的数据结构。
mysql的索引查询原则:最左原则
- 叶子结点连起来了
- 非叶子节点不存储data,只存储key;叶子节点不存储指针。
- 数据和结点一样多
例如
查找小于9的数,B+数会先找到9这个数,然后利用双向链表遍历左边链表。
如果索引失效,遍历所有结点的情况在B+树上也很简单,找到第一个结点1,然后链表遍历接下来的即可。
如果我们创建了a、b、c作为联合索引 ,相当于创建了(a)、(a、b)、(a、b、c)三个索引
userId + age + name 建了索引
select * from user where age = 1 and name = 1 索引没有生效,全表查询
select * from user where userId = 1 and age = 1 and name = 1 会走索引,因为为三个字段建了索引
select * from user where userId = 1 and name = 1 会走一部分索引(useId)
MyISAM / InnoDM 索引的实现方式
MyISAM索引实现
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。
MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。称为非聚集索引
InnoDB索引实现
区别一:
InnoDB的数据文件本身就是索引文件,如下图,可以看到叶节点包含了完整的数据记录,为聚集索引
区别二:
InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,图11为定义在Col3上的一个辅助索引:
回表
在 InnoDB 里,主键索引也被称为聚簇索引,主键索引的节点存的是整行数据。非主键索引也被称为二级索引、普通索引或者辅助索引,非主键索引的叶子节点存的内容是主键的值。根据辅助索引找主键的值,再根据主键找数据,这个操作称为回表。
索引优化建议
- 为什么不建议使用过长的字段作为主键?
因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
- 为什么用非单调(身份证/学号等非单调递增)的字段作为主键在InnoDB中不是个好主意?
因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
在使用InnoDB存储引擎时,如果没有特别的需要,请永远使用一个与业务无关的自增字段作为主键。
联合索引在B+树的存储结构
联合索引(col1, col2,col3)也是一棵B+Tree,其非叶子节点存储的是第一个关键字的索引,而叶节点存储的则是三个关键字col1、col2、col3三个关键字的数据,且按照col1、col2、col3的顺序进行排序。
对应地址指的是数据记录的地址。
如图,联合索引(年龄, 姓氏,名字),叶节点上data域存储的是三个关键字的数据。且是按照年龄、姓氏、名字的顺序排列的。
因此,如果执行的是:
select * from STUDENT where 姓氏='xx' and 名字='xx';
select * from STUDENT where 名字='xx';
结果是无法使用这个联合索引的。因为联合索引中是先根据年龄进行排序的。如果年龄没有先确定,直接对姓氏和名字进行查询的话,就相当于乱序查询一样,因此索引无法生效。因此查询是全表查询。
select * from STUDENT where 年龄=x and 姓氏='x';
那么当执行查询的时候,索引是能生效的,从图中很直观的看出,age=1的是第一个叶子节点的前6条记录,在age=1的前提下,姓氏=’李’的是前3条。因此最终查询出来的是这三条,从而能获取到对应记录的地址。
select * from STUDENT where 年龄=1 and 姓氏='黄' and 名字='安';
索引生效
select * from STUDENT where 年龄=1 and 名字='安';
那么,索引年龄部分能生效,名字部分不能生效。也就是说索引部分生效。