前言
这篇博客主要通过进行学习
2021年最新大厂数据库面试讲解,45讲让你透彻理解MySQL索引!
前情回顾,如果想了解更多的数据库底层原理可看我其他的文章
底层知识 | 链接 |
---|---|
DQL、DML、DDL、DCL的定义 | 数据库之DQL、DML、DDL、DCL定义 |
事务四个特性、隔离级别以及面临的问题 | 数据库关于事务的详解分析(全)包含面试常问的细节 |
Mysql的主从复制以及Redis的主从复制 | 关于主从复制的超详细解析(全) |
索引的优化分析、查询截取分析、锁机制 | Mysql优化高级篇(全) |
还有其他细节以及数据库的算法题 可看我的专栏
数据库细节以及数据库算法专栏
关于下文中的博客
主要讲解
- 索引数据结构红黑树、hash、b+树
- 千万级数据表如何用索引快速查找
- 如何基于索引b+树建立高性能索引
- 聚合索引底层数据结构是怎样的
- 聚集索引与覆盖索引到索引下是
- mysql的最左前缀优化原则是什么
- 为什么推荐使用自增整型的主键而不是UUID
- mysql并发支撑底层的buffer pool机制详解
1. 索引数据结构
索引是帮助mysql 是帮助数据排序 且高效获取数据的数据结构
索引的数据结构有:
- 二叉树
- 红黑树
- hash表
- b树
关于这类知识点的补充可看我之前的文章
【数据结构】树和二叉树详细分析(全)
【数据结构】B树和B+树的笔记详细诠释
1.1 二叉查找树
- 二叉查找树
如果要查找,通过二分查找的复杂度进行查找数据,确实优化了性能,减少了io的查询次数
当每次插入的元素都是二叉查找树中最大的元素,二叉查找树就会退化成了一条链表,查找数据的时间复杂度变成了 O(n)
由于树是存储在磁盘中的,访问每个节点,都对应一次磁盘 I/O 操作。树的高度就等于每次查询数据时磁盘 IO 操作的次数,所以树的高度越高,就会影响查询性能
即使是索引,也是要查询位置,再插入
具体插入哪个位置也要进行查找,也就是每一次的查找都要进行io的操作
为了更加平衡,变成一个平衡二叉树,可以改造成一个红黑树
1.2 平衡二叉树
除了平衡二叉查找树,还有很多自平衡的二叉树,比如红黑树,它也是通过一些约束条件来达到自平衡,不过红黑树的约束条件比较复杂
- 红黑树,在自我平衡下,可以控制高度,但是如果数据量大的话,
高度还是很高,还是要多次的io操作
节点是红色或者黑色
根节点是黑色
每个红色节点的两个子节点都是黑色
新插入的节点默认都是黑色
平衡的措施:变色,自旋
不过有一点就是:
不管平衡二叉查找树还是红黑树,都会随着插入的元素增多,而导致树的高度变高,这就意味着磁盘 I/O 操作次数多,会影响整体数据查询的效率
根本原因是因为它们都是二叉树,也就是每个节点只能保存 2 个子节点 ,如果我们把二叉树改成 M 叉树(M>2),那就会更好
1.3 b树
自平衡二叉树虽然能保持查询操作的时间复杂度在O(logn),但是因为它本质上是一个二叉树,每个节点只能有 2 个子节点,那么当节点个数越多的时候,树的高度也会相应变高,这样就会增加磁盘的 I/O 次数,从而影响数据查询的效率
b树:
- 叶子节点具有相同的深度,叶子结点的指针为空
- 所有索引元素不重复
- 节点中的数据索引从左到右递增排列
但是 B 树的每个节点都包含数据(索引+记录),而用户的记录数据的大小很有可能远远超过了索引数据,这就需要花费更多的磁盘 I/O 操作次数来读到「有用的索引数据」
而且,在我们查询位于底层的某个节点(比如 A 记录)过程中,「非 A 记录节点」里的记录数据会从磁盘加载到内存,但是这些记录数据是没用的,我们只是想读取这些节点的索引数据来做比较查询,而「非 A 记录节点」里的记录数据对我们是没用的,这样不仅增多磁盘 I/O 操作次数,也占用内存资源
另外,如果使用 B 树来做范围查询
的话,需要使用中序遍历,这会涉及多个节点的磁盘 I/O 问题,从而导致整体速度下降
1.4 b+树
b+树:
- 非叶子节点不存储data、只存储索引(冗余),可以放更多的索引
- 叶子节点包含所有的索引字段
- 叶子节点用指针连接,可以提高区间访问的性能
b+树的优化有:双向地址,存储的两边的位置
1.5 其他
hash
- 对索引的key进行一次hash计算就可以定位出数据存储的删除
- 很多时候hash索引要比b+树索引更加高效
- 仅能满足= in,不支持范围查询
- hash冲突问题
关于hash冲突问题,如果链表过长,在查询的时候一直遍历,也需要很长的io磁盘查找
最主要的是不能范围查找
2. mysql的存储
mysql的存储:show global status like 'innodb_page_size'; 查看有多少储存
具体的存储是通过innodb、myisam的结构
这两者都是表级别的
myisam的索引文件和数据文件是分离的(非聚集)
其结构为:
frm结尾放存储结构的信息
MYI:表示整张表的索引信息:从根节点一直往下找,通过二分查找,速度比较快,之后找到索引的位置
MYD:表示整张表的信息:找到的索引位置,通过传输给MYD进行读取表的信息
innodb索引实现(聚集)
- 表数据文件本身就是按照b+树组织的一个索引结构文件
- 聚集索引-叶子节点包含了完整的数据记录
- 建议innodb表必须建立主键,并且使用整型的自增主键
- 在非主键索引结构叶子节点存储的是主键值(主要是为了一致性和节省存储空间)
其结构为:
frm
idb结尾的放了索引位置以及表的信息位置
innodb就一个主键索引(聚簇索引),非主键索引可以重复,但是它的主键索引不能重复
至于以下的问题也是经常作为面试题来考察的:
为什么要建立主键:
推荐必须建立主键,这是因为在innodb如果不用主键,数据库会默认帮你建立一个主键而且保证唯一,本身数据库的资源就比较紧张,为了不浪费数据库资源,最好是建立一个主键。
为什么要不用uuid:
为什么不用uuid,本身uuid是字符串,而且占用空间比较大,比较的是每一位,所以建议用整型的自增主键
主键为什么要自增:
避免分裂,如果使用了自增插入效率就比较高
面试题
通过以下的面试题可以更快的掌握面试中常问的知识点以及常见的题型
1. 索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。
索引的原理:就是把无序的数据变成有序的查询
- 把创建了索引的列的内容进行
排序
- 对排序结果生成
倒排表
- 在倒排表内容上拼上
数据地址链
- 在查询的时候,
先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
2. mysql聚簇和非聚簇索引区别
关于这部分可看我之前总结比较详细的文章
Mysql的两种存储引擎详细分析及区别(全)
- 聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是一致的,即:只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的
- 非聚簇索引:
叶子节点不存储数据、存储的是数据行地址
,也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本树的目录,比如我们要找第三章第一节,那我们先在这个目录里面找,找到对应的页码后再去对应的页码看文章。
其优缺点:
优势:
1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高
2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3、聚簇索引适合用在排序的场合
,非聚簇索引不适合
劣势:
1、维护索引很昂贵,特别是插入新行或者主键被更新导至要分页(page sp1it)的时候
。建议在大量插入新行后,选在负载较低的时间段,通过OPTIMIZE TABLE优化表,因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片
2、表因为使用UUId(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫面更慢,所以建议使用int的auto_increment作为主键
3、如果主键比较大的话,那辅助索引将会变的更大
,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用占用更多的物理空间
InnoDB中一定有主键,主键一定是聚簇索引
,不手动设置、则会使用unique索引,没有unique索引,则会使用数据库内部的一个行的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值
MyISM使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键
。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
如果涉及到大数据量的排序、全表扫描、count之类的操作的话,还是MylSAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。
3. mysql索引的数据结构区分
索引的数据结构和具体存储引擎的实现有关,在MySQL中使用较多的索引有Hash索引,B+树索引等,
InnoDB存储引擎的默认索引实现为:B+树索引。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快
;其余大部分场景,建议选择B+索引。
B+树:
B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1
,而且同层级的节点间有指针相互链接。在B+树上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高
。因此,B+树索引被广泛应用于数据库、文件系统等场景。
哈希索引:
哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值
,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;前提是键值都是唯一的
。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
哈希索引也没办法利用索引完成排序
,以及like 'xxx%'这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在哈希碰撞问题。
4. 索引的设计原则
查询更快、占用空间更小
1.适合索引的列是出现在where子句中的列
,或者连接子句中指定的列
2.基数较小的类
,索引效果较差,没有必要在此列建立索引
3.使用短索引
,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配。
4.不要过度索引
。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
5.定义有外键的数据列—定要建立索引
。
6.更新频繁字段不适合创建索引
7.若是不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
8.尽量的扩展索引
,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
9.对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
10.对于定义为text、 image和bit的数据类型的列不要建立索引。
5. mysql锁的类型
关于这部分可看我之前总结比较详细的文章
Mysql中各类锁的机制图文详细解析(全)
- 基于锁的属性分类:共享锁、排他锁。
- 基于锁的粒度分类:行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎)、记录锁、间隙锁、临键锁。
- 基于锁的状态分类:意向共享锁、意向排它锁。
共享锁:
共享锁又称读锁,简称S锁。
当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事务才能对其进行加持写锁。
共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题。
排他锁:
排他锁又称写锁,简称X锁。
当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后.其他事务才能对数据进行加锁。
排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
表锁
表锁是指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问;
特点:粒度大,加锁简单,容易冲突;
记录锁
也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录,记录锁是说事务在加锁后锁住的只是表的某一条记录。
精准条件命中,并且命中的条件字段是唯一索引
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题。
页级锁
是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。
表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
间隙锁:
属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间
,当表的相邻ID之间出现空隙
则会形成一个区间,遵循左开右闭原则。
范围查询并且查询未命中记录,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,在同一个事务里,A事务的两次查询出的结果会不一样。
临建锁:
也属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住
触发条件:范围查询并命中,查询命中了索引
。
结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。
意向锁:
如果当事务A加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排他锁了,不能对整个表加共享锁或排它锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁
,而这个状态就是意向锁。
意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
意向排他锁
当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁。
6. mysql执行计划
对于这个执行计划可看我这篇比较详细的文章
Mysql优化高级篇(全)
执行计划就是sql的执行查询的顺序,以及如何使用索引查询,返回的结果集的行数
1.id :是一个有顺序的编号,是查询的顺序号,有几个select就显示几行。id的顺序是按select 出现的
顺序增长的。id列的值越大执行优先级越高越先执行,id列的值相同则从上往下执行,id列的值为NULL最后执行
2.selectType表示查询中每个select子句的类型
3.table:表示该语句查询的表
4.type:优化sql的重要字段,也是我们判断sql性能和优化程度重要指标。他的取值类型范围:.
- const:通过索引一次命中,匹配
一行数据
- system:表中只有
一行记录
,相当于系统表; - eq_ref:
唯一性索引扫描
,对于每个索引键,表中只有一条记录与之匹配 - ref:非唯一性索引扫描,返回匹配某个值的所有
- range:只检索给定范围的行,使用一个索引来选择行,一般用于between、<、>;index:只遍历索引树;
- ALL:表示全表扫描,这个类型的查询是性能最差的查询之一。那么基本就是随着表的数量增多,执行效率越慢。
5.possible_keys:它表示Mysql在执行该sql语句的时候,可能用到的索引信息
,仅仅是可能,实际不一定会用到。
6.key:此字段是 mysql在当前查询时所真正使用到的索引
。他是possible_keys的子集
7.key_len:表示查询优化器使用了索引的字节数
,这个字段可以评估组合索引是否完全被使用,这也是我
门优化sql时,评估索引的重要指标
9.rows: mysql查询优化器根据统计信息,估算该sql返回结果集需要扫描读取的行数
,这个值相关重要,索引优化之后,扫描读取的行数越多,说明索引设置不对,或者字段传入的类型之类的问题,说明要优化空间越大
10.filtered:返回结果的行占需要读到的行(rows列的值)的百分比,就是百分比越高,说明需要查询到数据越准确,百分比越小,说明查询到的数据量大,而结果集很少
11.extra
- using filesort:表示mysql对
结果集进行外部排序
,不能通过索引顺序达到排序效果。一般有using filesort都建议优化去掉,因为这样的查询cpu资源消耗大,延时大。 - using index:
覆盖索引扫描,表示查询在索引树中就可查找所需数据
,不用扫描表数据文件,往往说明性能不错。 - using temporary:查询有使用临时表,一般出现于排序,
分组和多表join的情况
,查询效率不高,建议优化。 - using where :
sql使用了where过滤,效率较高
。
7. 事务的基本特性和隔离级别
事务基本特性ACID分别是:
- 原子性指的是一个事务中的操作
要么全部成功,要么全部失败
。 - 一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态。
- 隔离性指的是一个事务的修改在最终提交前,
对其他事务是不可见的
。 - 持久性指的是一旦事务提交,所做的修改就会
永久保存到数据库中。
隔离级别:
- read uncommit读未提交,可能会
读到其他事务未提交的数据,也叫做脏读
。
用户本来应该读取到id=1的用户age应该是10,结果读取到了其他事务还没有提交的事务,结果读取结果age=20,这就是脏读。
- read commit读已提交,
两次读取结果不一致,叫做不可重复读
。不可重复读解决了脏读的问题,他只会读取已经提交的事务。
用户开启事务读取id=1用户,查询到age=10,再次读取发现结果=20,在同一个事务里同一个查询读取到不同的结果叫做不可重复读。oracle本身默认就是这种级别
- repeatable read(读已提交之上的)
可重复复读,这是mysql的默认级别,就是每次读取结果都一样
,但是有可能产生幻读
。 - serializable 串行,一般是不会使用的,他会
给每一行读取的数据加锁,会导致大量超时和锁竞争的问题。
脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后—个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
8. 怎么处理慢查询
弄清楚慢在哪里,是没有命中索引还是load了不需要的数据列,还是数据量过大
优化的时候针对上面方式处理
- 分析语句,
可能查询了多余的行或者加载了并不需要的列导致
,进行重写 - 查看使用索引的情况,修改语句或者索引,
让其尽可能命中索引
- 语句分析已经执行不了,可考虑
是否是数据量过大
,是的话可进行横向或者纵向分表
9. mysql主从复制
单机的瓶颈,多个节点,如何保持多个机的一致
需要使用mysql的主从复制原理
Mysql的主从复制中主要有三个线程:
master (binlog dump thread) . slave (I/o thread , sQLthread), Master—条线程和Slave中的两条线程。
- 主节点binlog,主从复制的基础是主库记录数据库的所有变更记录到 binlog。binlog是数据库服务器启动的那一刻起,保存所有修改数据库结构或内容的一个文件。
- 主节点log dump线程,当binlog有变动时,log dump线程读取其内容并发送给从节点。
- 从节点I/O线程接收binlog内容,并将其写入到relay log 文件中。
- 从节点的SQL线程读取relay log文件内容对数据更新进行重放,最终保证主从数据库的一致性。
注:主从节点使用 binglog文件+ position偏移量来定位主从同步的位置,从节点会保存其已接收到的偏移量,如果从节点发生宕机重启,则会自动从position的位置发起同步。
由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
全同步复制
主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
半同步复制
和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成
。
10. Myisam和Innodb的区别
MylSAM:
- 不支持事务,但是每次查询都是原子的
- 支持表级锁,即每次操作是对整个表加锁
- 存储表的总行数;
- 一个MYISAM表有三个文件:
索引文件、表结构文件、数据文件;
- 采用非聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。
lnnoDb:
- 支持ACID的事务,支持事务的四种隔离级别;
- 支持行级锁及外键约束:因此可以支持写并发;
- 不存储总行数;
- 一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
- 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;
因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引
; - 最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。
11. mysql的索引都有哪些
- 普通索引:允许被索引的数据列包含重复的值。
- 唯—索引:可以保证数据记录的唯一性。
主键:是一种特殊的唯一索引,在一张表中只能定义一个主键索引
,主键用于唯一标识一条记录,使用关键字PRIMARY KEY 来创建。
- 联合索引:索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引。
- 全文索引:通过建立倒排索引,
可以极大的提升检索效率,解决判断字段是否包含的问题
(类似模糊查询),是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE table_name ADDFULLTEXT
(column A,column B);创建全文索引
12. mysql的索引作用
- 索引可以极大的提高数据的查询速度。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。但是会降低插入、删除、更新表的速度,因为在执行这些写操作时,还要操作索引文件
(并不是索引越多越好)索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。
如果要建立聚簇索引,那么需要的空间就会更大。如果非聚集索引很多,一旦聚集索引改变,那么所有非聚集索引都会跟着变
。
13. b树和b+树的对比 *
- 单点查询
B 树进行单个索引查询时,最快可以在 O(1) 的时间代价内就查到,而从平均时间代价来看,会比 B+ 树稍快一些
b树每个节点即存索引又存记录,有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比存储即存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O次数会更少
- 插入和删除,b+树效率高,因为冗余节点多数不用动到,b树都要挪动
- 范围查询
B 树和 B+ 树等值查询原理基本一致,先从根节点查找,然后对比目标数据的范围,最后递归的进入子节点查找
存在大量范围检索的场景,适合使用 B+树,比如数据库。而对于大量的单个索引查询的场景,可以考虑 B 树,比如 nosql 的MongoDB
14. 最左前缀的理解
如果想了解更加深层次的理解
可看我这篇文章:Mysql中索引的最左前缀原则图文剖析(全)
特别注意的是
- 最左前缀匹配 会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
- 建立 (a, b, d, c) 的索引,顺序任意调整都可(前提是都使用到了)
15. 读写分离
最根本的原理是主从复制
通过路由的方式 将写请求在 Master 上,读请求在 Slave 上
大致方案如下:(根据请求一层层的往上剖析,看看哪里可以加读写分离)
- 基于中间件(可实现负载均衡)
代码模块和数据库中间增加一个中间件,中间件根据数据库请求转发不同实例到代码模块中
常见的中间件有:mysql-proxy、cobar、mycat、Atlas
对应的mycat之前写过:
可看这篇文章了解一些基本的原理:Mycat框架从入门到精通(全)
对应的面试题:Mycat的常见面试题(全)
- jdbc原始驱动(可实现负载均衡)
对应将其读写驱动,写请求发送从库
- 代码模块(比如spring下的aop拦截器)
通过判定类型来动态选择主从的数据源