看看Mysql【深入了解】

文章介绍了几种常见的数据结构在索引中的应用,如哈希表、有序数组和搜索树,重点讨论了B树和B+树在数据库中的优势,特别是B+树在减少磁盘IO方面的优化。同时,文章还探讨了数据库事务的ACID特性,以及在不同隔离级别下解决并发问题的方法,如MVCC机制,强调了在特定场景下事务的重要性。
摘要由CSDN通过智能技术生成

索引

索引是提高查询效率的排好序的数据结构。

我了解的用于索引的数据结构有三种:哈希表,有序数组,搜索树。

哈希表:
对于哈希表而言,他是以键值对的形式存储数据结构的,所以,当数据量开始增多的时候,不可避免的就会出现拉链结构(就是链表),类似于HashMap底层原理。
所以说,存放在哈希表中的数据不是按顺序存放的,而且还会出现拉链结构。

在这里插入图片描述
所以,当我们需要范围查找的时候,哈希表根本不适合区间查询。因此,哈希表只适合等值查询场景。

有序数组:
对于有序数组而言,在等值查询和范围查询的时候,性能都是非常高效的,但是缺点也很明显,就是不适合用来更新。
在这里插入图片描述
查询的时候,直接二分查找就行。
当更新的时候,删除和新增就会去移动数据,成本太高了。所以,有序数组这种数据结构只适合用来做静态存储,比如学校去年毕业的学生,这个是固定了的,不会再变了。

搜索树:
其实,我们可以从最经典的二叉树出发,可以看出来
每个节点的左儿子小于父节点,父节点又小于右儿子
二叉树的查询时间复杂度是O(log(N))。
在这里插入图片描述
问题是,就是我上面简简单单的几个数据,也需要3次磁盘IO(树高3),那如果是一颗树高20的二叉树,光磁盘IO都要20次,成本太过高昂,查询太慢了。
所以,为了减少IO次数,使用N插树。
N插树可以横向扩展,也就是可以让树高减小,减少磁盘IO。

所以,我们对于Mysql为什么选择B树和B+树,也有一定的认识了。问题是,为什么最后选择的是B+树,而不是B树呢。
(B+树的结构各位可以去找找其他的,我这里不过多赘述)。
其实,对于B树而言,他的所有节点都可以用于存放索引的数据,所以,他的查询时间复杂度是不固定的,也行查一次就出来了,也许要一直查到叶子节点,所以,在使用范围查找的时候,B树就蚌埠住了,当然,查询单个的时候,B树还是有价值的,这也是为什么Mongodb选用B树的原因,面向文档,单个查询最划算了(还有一些其他的原因,比如为什么不选择LSM树等,还得各位看官自己去找找)。

对于B+树而言,他有两个特点,一是他所有的非叶子节点只存放指针,也就是说,在与B树同等大小的情况下,B+树的非叶子节点能存放更多的指针(毕竟他不用存放数据),这样他的下一层就能存放更多的节点,这样的话,就能最大程度的存放叶子节点,也就能减小磁盘IO。二是所有的叶子节点只存放索引,对于Mysql而言,他查询所有的节点时间复杂度是一样的,而且,所有的叶子节点都是排好序了的,并且每一块磁盘都有相互指向的指针。

然后就是索引分类,我只说比较有意思的。
1:主键索引(聚簇索引)。
聚簇索引是根据主键去构建的一棵B+树,他的叶子节点存放的是整行的数据,也就是说,查索引(叶子节点)就等于查数据了。当然,有聚簇索引,自然也有非聚簇索引,也被称为二级索引。那么,非聚簇索引他叶子节点存放的是什么呢?
是主键的值。他通过先找到主键的值,再通过主键索引去找数据。也被称为回表。

一般来说,公司自己的数据库构建表的时候,都会有一个自增id作为主键,这样既减轻了字段的大小,不需要用业务逻辑字段去做主键,因为无法保证他是否是有序被插入(你用业务逻辑做主键,他要排序的,万一新增一条最新的数据,却排序到最前面,不太合适)。用自增id就很安全,字段又不大,很合适。

当然,在不同的业务场景下,需求是不一样的,也许就有需求必须是业务逻辑字段做主键啦。

2,覆盖索引。
SQL 只需要通过索引就可以返回查询所需要的数据,也可以认为,查索引(联合索引也可以),索引上面就有你需要select的数据了。

事务

事务:保证一组数据库操作,要么全部成功,要么全部失败。事务是在引擎层实现的。

提到事务,就会想到事务的四大特性:ACID。
但是,对于数据库而言,原子性(Atomicity),隔离性(Isolation),持久性(Durability)是能够保证的,但是关于一致性(Consistency),他是可能依赖于AID,但不仅仅于此,所以C 不仅仅取决于数据库。A I D 来保证C。

然后,当事务并发的时候,带来的问题,脏读,幻读,不可重复读,丢失修改。

我只说幻读和不可重复读的差别:

不可重复读:事务A 先读id =1 的学生的信息,得到他的age=18;事务B然后也去读id=1的学生的信息,得到age=18,问题是,这个时候事务A做了一个修改操作,把这个id=1的学生的age改成20了,然后事务B又去读id= 1的学生,发现age=20,不为18了。

幻读:事务A 先范围查询学生年纪在 19-22岁之间的,假如有10条,这个时候事务B去增加了一条学生为20岁的记录,或者事务B去删除了一条学生为20岁的记录,事务A再次查询的时候,发现查询的记录条数不对。

为了解决事务并发带来的问题,就有了隔离级别的概念!

通常来说 ,事务隔离级别有四种:
在这里插入图片描述

在不同的隔离级别下,会有不同的操作
在读已提交的情况下,每一个SQL语句开始的时候,都会创建一个视图,所以每一次读取的都是数据库最新的值;
在可重复读的情况下,只会在第一个SQL语句开始的时候,创建一个视图,整个事务期间都用的是这一个视图,所以是可重复读。

我这里只介绍 在RR(可重复读)的情况下,是如何保证解决幻读问题。

事务并发会带来的问题有:读读,读写,写写。
在读读的情况下,是不会有并发问题的。但是,在读写,写写的情况下,为了保证并发安全,会对数据库进行加锁。一旦数据量太大,读写,写写的效率会很低(主要是读写)。

所以会有另外一种方法:MVCC

MVCC

数据库每行数据,数据库默认会添加三个字段 ,事务id,回滚指针,自增id。
这里有一种版本链比较规则,就不细说了(可以看看其他文章 )
`理解一下就好,底层不是这样的

然后,读写并发了,这个时候,你写就写,写就是再添加版本链,读就是快照读,在RR的情况下,他每次读都是第一次创建的视图,读这个视图,怎么读都是同一个数据,不会改变了。

所以,这里有个问题:当使用查询语句的时候,需不需要事务?

当然,看需求。如果你是读的时候需要两条读取的数据的和,那么就需要。
你事务A在RR的情况下读 id=18的学生(只查总分) 数学score=80 语文score=80 总分160
结果现在发现这个学生他的成绩搞错了,把数学改成100
B(没开启事务)在RR的情况下,查出来发现总分为180

因为你两次读取不是在同一个视图下读取的,是错误的,所以要加事务,而且还要在同一个事务下。

至于写写冲突,那就看锁去解决了。

BufferPool Redolog Binlog Undolog

未完待续!

仅做学习交流,如有部分知识错误,烦请指正!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值