什么是MVCC
MVCC
,全称Multi-Version Concurrency Control
,即多版本并发控制。mvcc,它是一种并发控制方法,一般在数据库管理系统中,实现数据库的并发访问,在编程语言中实现事务内存。总结:主要为了提升并发性能
为什么需要MVCC
数据库原生的锁
最原生的锁,锁住一个资源后会禁止其他任何线程访问同一个资源。但是很多应用的一个特点都是读多写少的场景,很多数据的读取次数远大于修改的次数,而读取数据间互相排斥显得不是很必要。读写锁的出现
读锁和读锁之间不互斥,而写锁和写锁、读锁都互斥。这样就很大提升了系统的并发能力。之后人们发现并发读还是不够mvcc概念出现
能不能让读写之间也不冲突的方法,就是读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据。当然快照是一种概念模型,不同的数据库可能用不同的方式来实现这种功能
MVCC适用于的事务隔离级别
MVCC只在 READ COMMITTED (读取已提交) 和 REPEATABLE READ (可重复读) 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED (读取未提交) 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE (可串行化) 则会对所有读取的行都加锁。
MVCC实现原理✔
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决
读写冲突
,它的实现原理主要是依赖记录中的3个隐式字段
,undo日志
,**Read View
** 来实现的。3个隐式字段
1 `DB_TRX_ID`, `DB_ROLL_PTR`, `DB_ROW_ID
列名 长度(字节) 作用 DB_TRX_ID 6 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除) DB_ROLL_PTR 7 写入回滚段的撤销日志记录(若行已更新,则撤销日志记录包含在更新之前重建行内容所需的信息) DB_ROW_ID 6 行标识(隐藏单调自增id) 比如:
id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 18 1 空 空
DB_ROW_ID
是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID
是当前操作该记录的事务 ID ,而DB_ROLL_PTR
是一个回滚指针,用于配合 undo日志,指向上一个旧版本事务A:对数据进行了修改(将name中的张三改为李四)
- 第一步:用排他锁锁定这一条记录
id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 18 1 空 空
- 第二步:UNDOLOG会记录日志,作为旧记录,既在
undo log
中有当前行的拷贝副本
UNDO_LOG id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 18 1 空 空
- 第三步:将回滚指针的值copy到UNDOLOG中
UNDO_LOG id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR(这就是存储回滚指针的值) 1 张三 18 1 空 ox29349384
- 第四步:修改当前的name值并且修改隐藏字段的事务 ID 为当前
事务 1
的 ID, 我们默认从1
开始,之后递增,回滚指针指向拷贝到undo log
的副本记录,既表示我的上一个版本就是它
id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR(这就是存储回滚指针的值) 1 李四 18 1 1 ox29349384 事务B:事务A修改但未提交,同时对事务B也对该行数据做了修改
下表就是事务B做出的改变(改变的是年龄)
id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 30 1 2 ox23874982 上表的ox23874982指的地址是下表的地址
UNDO_LOG id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 18 1 1 ox29349384 上表的ox29349384指的地址是下表的地址
id name age DB_ROW_ID DB_TRX_ID DB_ROLL_PTR 1 张三 18 1 空 空 所以总结:
如果有当前事务,最早事务,最晚事务
最早事务ID<当前事务ID<最晚事务ID
- 事务的排他锁形式修改数据
- 修改之前先把数据放到undolog,通过回滚指针关联
- 失败了从undolog回滚
undo日志
insert undo log
代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃update undo log
事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除
在不考虑redo log 的情况下利用undo log工作的简化过程为:
序号 动作 1 开始事务 2 记录数据行数据快照到undolog 3 更新数据 4 将undolog写到磁盘 5 将数据写到磁盘 6 提交事务 1)为了保证数据的持久性数据要在事务提交之前持久化
2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务
执行流程如下:
*一、比如一个有个事务插入 persion 表插入了一条新记录,记录如下,
name
为 小明 ,age
为 10 岁,*隐式主键
*是 1,*事务 ID
*和*回滚指针
*,我们假设为 NULL*二、 现在来了一个事务 1对该记录的 name 做出了修改,改为 小红
- 在
事务 1
修改该行(记录)数据时,数据库会先对该行加排他锁
- 然后把该行数据拷贝到
undo log
中,作为旧记录,既在undo log
中有当前行的拷贝副本Undo日志
拷贝完毕后,修改该行
name
为小红,并且修改隐藏字段的事务 ID 为当前事务 1
的 ID, 我们默认从1
开始,之后递增,回滚指针指向拷贝到undo log
的副本记录,既表示我的上一个版本就是它上个表的回滚指针地址指的是下个表
事务提交后,释放锁