目录
什么是MVCC?🌸
MVCC
,全称 Multi-Version Concurrency Control
,即多版本并发控制。mvcc,它是一种并发控制方法,一般在数据库管理系统中,实现数据库的并发访问,在编程语言中实现事务内存。
总结:主要为了提升并发性能
为什么需要MVCC
- 数据库原生的锁
最原生的锁,锁住一个资源后会禁止其他任何线程访问同一个资源。但是很多应用的一个特点都是读多写少的场景,很多数据的读取次数远大于修改的次数,而读取数据间互相排斥显得不是很必要。
- 读写锁的出现
读锁和读锁之间不互斥,而写锁和写锁、读锁都互斥。这样就很大提升了系统的并发能力。之后人们发现并发读还是不够
- mvcc概念出现
能不能让读写之间也不冲突的方法,就是读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据。当然快照是一种概念模型,不同的数据库可能用不同的方式来实现这种功能
总结:
MVCC 就是因为大佬们,不满意只让数据库采用悲观锁这样性能不佳的形式去解决读-写冲突问题,而提出的解决方案,所以在数据库中,因为有了 MVCC,所以我们可以形成两个组合:
- MVCC + 悲观锁
MVCC解决读写冲突,悲观锁解决写写冲突
- MVCC + 乐观锁
MVCC 解决读写冲突,乐观锁解决写写冲突
普及一下常见锁🔒(知道可以跳过)
读锁:也叫共享锁、S锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
写锁:又称排他锁、X锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
表锁:操作对象是数据表。Mysql大多数锁策略都支持,是系统开销最低但并发性最低的一个锁策略。事务t对整个表加读锁,则其他事务可读不可写,若加写锁,则其他事务增删改都不行。
行级锁:操作对象是数据表中的一行。是MVCC技术用的比较多的。行级锁对系统开销较大,但处理高并发较好。
MVCC适用于的事务隔离级别
MVCC只在 READ COMMITTED (读取已提交) 和 REPEATABLE READ (可重复读) 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED (读取未提交) 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE (可串行化) 则会对所有读取的行都加锁。
MVCC实现原理✔
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突
,它的实现原理主要是依赖记录中的 3个隐式字段
,undo日志
,Read View
来实现的。
3个隐式字段
DB_TRX_ID
, DB_ROLL_PTR
, DB_ROW_ID
简单来说:第一个
事务id(每处理一次加一) ,第二个是指向undolok的一个指针 ,第三个是用于存在聚集索引中的id
比如:
DB_ROW_ID
是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID
是当前操作该记录的事务 ID ,而 DB_ROLL_PTR
是一个回滚指针,用于配合 undo日志,指向上一个旧版本
事务A:对数据进行了修改(将name中的张三改为李四)
- 第一步:用排他锁锁定这一条记录
- 第二步:UNDOLOG会记录日志,作为旧记录,既在
undo log
中有当前行的拷贝副本
- 第三步:将回滚指针的值copy到UNDOLOG中
- 第四步:修改当前的name值并且修改隐藏字段的事务 ID 为当前
事务 1
的 ID, 我们默认从1
开始,之后递增,回滚指针指向拷贝到undo log
的副本记录,既表示我的上一个版本就是它
事务B:事务A修改但未提交,同时对事物B也对该行数据做了修改
首先会看到有两个对改行修改的数据,
undo日志
- insert undo log
代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
- update undo log
事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除
在不考虑redo log 的情况下利用undo log工作的简化过程为:
1)为了保证数据的持久性数据要在事务提交之前持久化
2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务
执行流程如下:
一、比如一个有个事务插入 persion 表插入了一条新记录,记录如下,name
为 小明 , age
为 10 岁,隐式主键
是 1,事务 ID
和回滚指针
,我们假设为 NULL
二、 现在来了一个事务 1
对该记录的 name
做出了修改,改为 小红
- 在
事务 1
修改该行(记录)数据时,数据库会先对该行加排他锁
- 然后把该行数据拷贝到
undo log
中,作为旧记录,既在undo log
中有当前行的拷贝副本
- 拷贝完毕后,修改该行
name
为小红,并且修改隐藏字段的事务 ID 为当前事务 1
的 ID, 我们默认从1
开始,之后递增,回滚指针指向拷贝到undo log
的副本记录,既表示我的上一个版本就是它
- 事务提交后,释放锁