目录
mvcc :解决了脏读和不可重复读的问题,并发时不用加锁就可以达成读已提交和可以重复读的目的
我们有一条sql发送给mysql,mysql服务端对sql进行解析,缓存,再交由存储引擎取读写磁盘
前言:先把整个数据库详细的说一下
假设我们来设计一个数据库存储,可以把他想象成一个字典,每个词相当于我们的一行数据。
我们想查找数据,需要有目录,目录就是我们的索引,innodb提供了两种索引:一种是主键索引、另一种是非主键索引,这个后面会说。
我们要找一行数据,有索引的话,判断是否是主键索引
步骤1:主键索引的话,B+树查找,查找到具体主键数据,这里很多人都没说明白,就贴张图-Data,这个data指的是地址,可以理解为哪个page页,和字典一样,然后在page内用二分法查找
步骤2:非主键索引的话,B+树查找,找到主键,经过回表回到步骤1
如何设计一个page?如果没有索引肯定是全表扫描
有个概念:page分配了一个固定大小为16k的磁盘空间,我们的一行数据存储在page里,没有紧挨着存储的,因为我们会删除,会插入,会更新,所需空间会变,所以我们是逻辑存储
page 肯定要记录页号,双向链表记录前后页地址。
想象一下:我们要想在字典的一页中找某个单词,是不是遍历查找
那么这个遍历我们再优化一下,我们是不是维护一个数组slot来存储具体范围的数据,先根据这个数组做二分查找,判断具体是哪一区域,然后根据链表遍历
注意: 由于在page里面,是逻辑存储,删除是逻辑删除,更新会在链表后面增加一个节点,原来占用的磁盘空间会被释放,所以表用久了,会有内存随便,和逻辑删除的无用数据
· 由于内存操作比磁盘操作要快,所以mysql引入了一个概念:缓冲区。就是说mysql启动之后,会在内存中开辟一个指定大小的内存空间 buffer pool。
如果要你来设计一个buffer pool怎么来设计?
1、加载一条数据时候,把整个page页加载到内存里,那么我们的内存就会有很多个page。buffer pool的存储的基本单元就是page。那么我们可以维护一个大小的空闲链表,提前将内存分区,有page直接找个内存地方存储
2、维护一个映射关系:内存中的page地址和磁盘的page地址映射,方便写入,更新等操作直接找到内存中的page进行操作
3、还需要维护一个非空闲链表:但是这个链表,要有淘汰策略LRU:冷热分区、固定时间冷-》热,指定次数更新热区,冷区规则:淘汰尾或刷脏页到磁盘
4、当然有可能内存中的数据和磁盘里的数据不一致,还没有完成刷盘,我们就要维护一个脏页链表
mysql存储引擎原理、设计
数据页page(固定大小16k)
头
已分配内容
未分配内容
尾
page维护策略:为了解决删除碎片问题:最好做定时收缩
顺序保证
插入策略
页内查询:跳表概念
mysql innodb 存储引擎内存管理
请求到内存里检索:一个一个page加载到内存里
页面淘汰:LRU -》 LFU(redis:按访问频次淘汰,增长因子和衰减因子) -》
避免热数据被淘汰:冷热分区LRU,冷数据固定时间迁移到热数据、热数据到冷数据
Buffer pool:
page:
freelist:空闲page链表
flush list:脏页链表
pagehash:内存page和磁盘page映射关系
LRU: 冷热lru如何实现的?
事务
事务特性
aid -》 c
原子性:事务要不全部成功,要不全部失败
隔离性:事务的隔离
持久性:事务提交,永久有效
一致性:aid已经保证了一致性
并发
脏读:读到未提交的数据
不可重复读:两次读取的不一样
幻读:虽然事务隔离了,但是看着没问题,但是执行不下去
隔离级别
读未提交
读已提交:会造成 RC
可重复读:但会造成幻读问题 RR
串行读 加锁,性能很低,基本不用
事务实现原理
mvcc :解决了脏读和不可重复读的问题,并发时不用加锁就可以达成读已提交和可以重复读的目的
下面以RR为例
具体过程:维护了undo_log . 每次开启事务后,第一个未加锁的select 会产生一个read view记录。
在查询的时候,会并发很多事情,有的人在你查询之前就把这条数据更新了,你是可以看到他更新的数据的。如果你查完,另一个人把这条数据更新了,你是不能知道这条数据的。如果你查的时候,另一个人想更新这条数据,还在填资料,你也是不能知道的。这三个场景是必要条件。
所以mysql维护了一个不用加锁的机制:mvcc来解决这个问题。
当前读:一般遇不到。都是加锁才能叫当前读。 加共享锁(s/读锁)shaer in lock mode、排它锁(x/写锁)for update、间隙锁
快照读:产生一个reader view
reader view:维护的一个链表:活跃的事务列表
undo log
用于回滚,mvcc用到的
redo log
内存中存了redolog 刷盘到磁盘的redolog之后,内存中的redolog回收,磁盘的redolog写入磁盘中时,磁盘中的redolog回收
innodb锁:锁都是加在主键上的
类型
共享锁:读锁 lock in share mode
排它锁:写锁 for update
锁力度:
行锁:
间隙锁
Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。
表锁
注: update delete insert 都会自动加排它锁 for update ,是先select 再操作
间隙锁的作用:解决幻读,无法插入数据到数据间隙中。
注意间隙锁会造成死锁
如果我们来设计间隙锁的规则?
解决问题:满足我们条件的数据不能新增?
1、如果我们删除name=?的话,如果name不是索引,name是全局加锁
如果name有索引,根据name找到所有的主键,在每个主键的前后(根据链表可以找到)增加间隙锁
2、如果我们删除age > ?的话,同 1
3、如果我们删除id > 4 , 间隙锁加在(4,无穷大)或(4, 下一个主键id)