此文不是特别全,建议和另一篇文章结合着看
目录
一起来看一下我们工作中常见的几个数据库问题
下面一起带着这些问题进入以下学习
其实这个原理的提出源于1968年研究的一个问题
作业信息不全部装入主存的情况下能否保证作业的正确运行?
答案是肯定的,研究发现程序和数据的访问都有聚集成群的倾向。即在一个时间段内,仅会使用一小部分数据或代码(空间局部性),并且在未来很快被再次访问的可能性很大(时间局部性)。
这个类似于二八法则,80%时间会去访问20%的数据。
实际场景中就是我想从磁盘中读取1字节数据,实际上读取的是磁盘1个page的数据(页大小通常为4k),当然极端场景下这1字节的数据可能是从2页甚至4页里读取的
上面已经简单介绍了磁盘的存储机制,页是存储器的逻辑块,操作系统往往将主存和磁盘存储划分为连续且大小相等的块,每个存储块为一页,主存和磁盘以页为单位交换数据
hash通常是采用hash算法计算出一个hash值
最简单的就是通过%运算计算出余数,然后将数据放到与余数对应的内存地址上
如果产生了hash碰撞,通常会将数据放到同一地址的链表里
缺点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcXGppoa-1623210785331)(image-20201018220203092.png)]
红黑树也是二叉树的一种,是通过左旋/右旋来维持二叉树的平衡
通过将值较小的放到左叶子节点,数值较大的放到右叶子节点来保证查找效率(类似于二分查找)
缺点
无论是二叉树还是红黑树,都会因为树的深度过深而造成IO次数变多,影响数据读取的效率
结合之前讲的“局部性原理”,一次读取都是4K的整数倍,也就是一个节点是4K的话,那么上图二叉树找13这个数值需要找5次
总结
所以我们提升索引查询效率主要从两个维度,一个是减少IO次数(主要手段),一个是减少单次IO数据量
首先纠正一个错误的认识
没有B-树一说,实际上B-树就是B树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swB7hDkd-1623210785337)(image-20201018221325539.png)]
这里不再赘述,摘自知乎某文的说法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xExLVrn0-1623210785340)(image-20201020172432399.png)]
B树的查询流程:
如上图我要从上图中找到E字母,查找流程如下
(1)获取根节点的关键字进行比较,当前根节点关键字为M,E
(2)拿到关键字D和G,D
(3)拿到E和F,因为E=E 所以直接返回关键字和指针信息(如果树结构里面没有包含所要查找的节点则返回null);
缺点
会存在某个指针对应数据块过大的问题,比如4K一个数据块,每个只存储了1.9k,最终会造成空间浪费,并加深树的结构,导致读取(查)效率降低
B+ 树是B树的基础上做的一个优化
特点
1、B+树的层级更少:相较于B树B+每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
2、B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
3、B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
4、B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树的非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。
因为B+树具备以上特点,所以可以对B+树进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找
B+树能做到3次IO取到百万甚至千万级数据(取决于数据大小)
1、如果表格b采用MyISAM,在数据库/usr/local/mysql/data/user 中会产生3个文件:
b.frm :描述表结构文件,字段长度等
b.MYD(MYData):数据信息文件,存储数据信息(如果采用独立表存储模式)
b.MYI(MYIndex):索引信息文件。
2、如果表格user采用InnoDB,在数据库/usr/local/mysql/data/user中会产生1个或者2个文件:
b.frm :描述表结构文件,字段长度等
如果采用独立表存储模式,/usr/local/mysql/data/user中还会产生b.ibd文件(存储数据信息和索引信息)
如果采用共存储模式的,数据信息和索引信息都存储在ibdata1中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCazgp70-1623210785346)(image-20201020181711863.png)]
注意:
InnoDB是通过B+树结构对主键创建索引,然后叶子节点中存储记录,如果没有主键,那么会选择唯一键,如果没有唯一键,那么会生成一个6位的row_id作为主键
如果创建索引的键是其它字段,那么在叶子节点中存储的是该记录的主键,然后再通过主键索引找到对应的记录
比如有(id,name,age)三列,如果是对name创建索引,那么实际通过name查询的时候会先在name对应的索引树里找到对应记录,但是记录中只存了ID,第二步会通过这个ID去主键索引树找到并将数据返回
综上,主键索引和非主键索引创建和查询方式都是不同的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0OfkhCoE-1623210785352)(image-20201020182832643.png)]
MyISAM也是采用B+树的方式,但是不再存储具体的数据,而是一个地址,然后根据地址去磁盘里读取对应数据
这也是为什么MyISAM会存储两个文件,而InnoDB只会存储一个文件
MyISAM | InnoDB | |
---|---|---|
索引类型 | 非聚簇索引 | 聚簇索引 |
支持事务 | 否 | 是 |
支持表锁 | 是 | 是 |
支持行锁 | 否 | 是 |
支持外键 | 否 | 是 |
支持全文索引 | 是 | 是(5.6 or later) |
适合操作类型 | 大量select | 大量insert、delete、update |
MySQL索引分五类
主键索引
主键是一种唯一性索引,但它必须指定为Primary Key,每个表只能有一个主键(一个主键并不表示一个列,也有可能是多个列组成的复合主键)
(这个我们无需创建,MySQL会自动帮我们创建)
唯一索引
索引列的所有值都只能出现一次,即必须唯一,值可以为空
(主键索引也是唯一索引的一种,除了非空,MySQL会自动帮我们创建)
普通索引
基本的索引类型,值可以为空,没有唯一性的限制
全文索引
全文索引的索引类型为FullText。全文索引可以在varchar、char、text类型的列上创建
(基本不用,现在一般全文检索都用Solor、Lucene、ES)
组合索引
多列值组成的一个索引,专门用于组合搜索
谈到索引,一定要往存储引擎上靠,常见的存储引擎有基于InnoDB、MyIsam、Memory三种
索引有什么优点?
索引的用处?
为什么MySQL不自动为所有字段创建索引?
首先索引越多查询越快这个认知是错误的
索引最终会写到磁盘里,所以查询的时候会有读盘操作会因为索引增多而变慢
增加删除数据MySQL会重新维护索引,而B+树会存在页分裂和页合并问题
每一次读取磁盘数据都是基于磁盘上的”页“
如果插入数据且页已经装满
这时候对页会有一个分裂的操作
如果数据量比较小,且页有富余空间,这时候会有页合并的操作
这个过程会消耗IO
MySQL建表的时候主键应该怎么选择?
自然主键
跟业务挂钩的主键
代理主键
跟业务无关的主键,比如ID列
ID列是否需要设置自增?
需要,这样方便MySQL去维护索引
常见技术名词?
回表
在InnoDB和MyIsam存储引擎中,底层用的数据结构是B+树
而Memory方式底层数据结构为Hash表
而InnoDB下叶子节点直接放置数据
主键索引可以通过索引直接找到数据并返回
但是实际场景下,我们不经常使用主键去查询数据,可能是name列
我们就可以给name列添加索引
但是name的索引叶子节点并不会存储数据,而是会存储主键的值
查找到对应name后会取主键的值,然后再去主键表中取数据的这种方式就叫回表
覆盖索引
首先看两条查询语句
select * from student where name='zhangsan' # 有回表操作
select id from student where name='zhangsan' # 无回表操作
因为基于name的B+树存储的本来就是主键id的值,所以第二条sql不会有回表操作
以上不需要回表操作的过程就被称为索引覆盖(或覆盖索引)
最左匹配
组合索引(多列索引),比如用name和age建立组合索引idx_name_age
那么在查找的时候会先查找name再查找age
select * from student where name='zhangsan' # 走索引
select * from student where name='zhangsan' and age=10 # 走索引
select * from student where age=10 # 不走索引,因为跳过了name直接查age,所以按照最左匹配原则不会走进索引
select * from student where age=10 and name='zhangsan' # 走索引,MySQL优化器会自动优化我们的查询
索引下推
https://www.bilibili.com/video/BV1K64y1F76m?p=6 3:04