1.事务介绍:
- 一般是指要做的或所做的事情。 在计算机 术语 中是指访问并可能更新数据库中各种 数据项 的一个程序 执行单元 (unit)
2.数据库事务具有ACID四大特性。
- ACID是以下4个词的缩写:
- 原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败 。
- 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏 。
- 隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。
- 持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失 。
3.隔离级别:
- 四种隔离级别分别为:
1)未提交读(READ UNCOMMITTED/RU) 如果一个事务读到了另一个未提交事务修改过的数据,那么这种 隔离级别 就称之为 未提交读 会产生脏读的情况
2) 已提交读(READ COMMITTED/RC) 不可重复读:一个事务因读取到另一个事务已提交的update。导致对同一条记录读取两次以上的结果不一致。
如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那么这种 隔离级别 就称之为 已提交读,会产生幻读的情况。
3)可重复读(REPEATABLE READ/RR) 在一些业务场景中,一个事务只能读到另一个已经提交的事务修改过的数据,但是第一次读过某条记录后,
即使其他事务修改了该记录的值并且提交,该事务之后再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据。那么这种 隔离级别 就称之为 可重复读,会产生幻读的情况。
4)串行化(SERIALIZABLE) 最高的默认级别,强制事务串行执行(即一个事务一个事务执行)。效率极其低下。
幻读:一个事务因读取到另一个事务已提交的insert数据或者delete数据。导致对同一张表读取两次以上的结果不一致。
不可重复读: 一个事务因读取到另一个事务已提交的update。导致对同一条记录读取两次以上的结果不一致。
4.讲讲事务和MVCC底层原理详解:
比如现在有一个例子:
一个转账的一个案例:
丢失更新
两个事务针对同一数据都发生修改操作时,会存在丢失更新问题。
4.1解决方案:LBCC:
使用LBCC(LBCC,基于锁的并发控制,英文全称Lock Based Concurrency Control)可以解决上述的 问题。查询总额事务会对读取的行加锁,等到操作结束后再释放所有行上的锁。因为用户A的存款被锁,导致转账操作被阻塞,直到查询总额事务提交并将所有锁都释放。 示例:这种方案比较简单粗暴,就是一个事务去读取一条数据的时候,就上锁,不允许其他事务来操作4.2:解决方案2:MVVC:
MVCC(MVCC,多版本的并发控制,英文全称:Multi Version Concurrency Control)机制可
以解决这个问题。查询总额事务先读取了用户A的账户存款,然后转账事务会修改用户A和用户B账户存
款,查询总额事务读取用户B存款时不会读取转账事务修改后的数据,而是读取本事务开始时的数据副本(在REPEATABLE READ隔离等级下)
只支持RC,RR隔离级别。
示例:MVCC使得数据库读不会对数据加锁,普通的SELECT请求不会加锁,提高了数据库的并发处理能力
5.Mysql的MVCC实现(重点):
- MVCC是用于数据库提供并发访问控制的并发控制技术MVCC最大的好处:相信也是耳熟能详:读不加锁,读写不冲突。
- 多版本并发控制仅仅是一种技术概念,并没有统一的实现标准,
- 其核心理念就是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。
5.1MVVC的实现机制:
mvvc是通过undo log+readview实现的:
undo log:
- 是一个回滚日志就是提交一些未提交操作的原始状态 然后它会记录版本号 :
- 如果有主键是2个隐藏列 如果没有则是3个隐藏列
- 隐藏列呢就是三个部分组成
- 1.rowid:如果没有主键则会自动生产
- 2.回滚指针:指向记录的上一个版本号
- 3.事务id:记录了操作这条事务的id(这个id是唯一的而且是自增的) 然后每次更新都会有生产一个新版本通过回滚指针指向旧版本 形成一个版本链
- 执行删除操作的时候是不会直接删除的是进行了一个打了一个删除标记,真正删除是通过purge线程执行清除操作的
eadView:
-
相当于一个快照,只要readview不改变则读取的结果就是一样的
- readview生产时刻就是执行select操作时生产的一个select对应一个ReadView select执行完毕那么ReadView就会失效
- 它就是一个数组(m_ids) 记录了当前时刻数据库活跃的事务id列表,它的可见性判断呢可以根据m_ids与事务id进行判断 判断条件可以根据(如果都可读以最新版本为准):
句个列子比如:
如果数据库没有活跃的事务,那么ReadView(m_ids)中包含的就是将要生成的事务id。
在REPEATABLE READ(RR)隔离级别下,MVCC具体是如何操作的。
1.SELECT InnoDB 会根据以下两个条件检查每行记录: 1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的事务编号小于或等于当前事务的事务编号) 这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。 2. 删除的行要事务ID判断,读取到事务开始之前状态的版本。 只有符合上述两个条件的记录,才能返回作为查询结果。 2.INSERT InnoDB为新插入的每一行保存当前事务编号作为行版本号。 3.DELETE InnoDB为删除的每一行保存当前事务编号作为行删除标识。 4.UPDATE InnoDB为插入一行新记录,保存当前事务编号作为行版本号,同时保存当前事务编号到原来的行作为行删除标识。MVCC是如何解决隔离级别的呢?
RC事务隔离级别的实现:
读已提交:当前事务可以读取其他事务提交的结果
实现方案:当前事务执行select语句时会生产一个ReadView,如果再次执行这个selct语句继续生成ReadView。
RR事务隔离级别的实现:
可重复读:当前事务中执行的select语句的多次执行结果都是相同的,不管其他事务有没有提交。
实现方案:在当前事务执行select语句生产一个ReadView,之后同一个select使用同一个ReadView
RC和RR的区别:
- READ COMMITTD 、 REPEATABLE READ 这两个隔离级别的一个很大不同就是生成 ReadView 的时机不同
- READ COMMITTD 在每一次进行普通 SELECT 操作前都会生成一个 ReadView
- 而 REPEATABLE READ 只在第一次进行普通 SELECT 操作前生成一个 ReadView ,之后的查询操作都重复这个 ReadView就好了。
- 在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。