事务的特性如何实现
事努有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
- 事务的隔离性由锁机制实现。
- 事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证。
- redo log称为重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
- undo log称为回滚日志,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
有的DBA或许会认为undo是redo的逆过程,其实不然。redo和undo都可以视为是一种恢复操作,但是:
- redo log:是存储引擎层 (innodb)生成的日志,记录的是"物理级别"上的页修改操作,比如页号xxx、偏移量yyy写入了'zzz'数据。主要为了保证数据的可靠性;
- undo log:是存储引擎层 (innodb)生成的日志,记录的是逻辑操作日志,比如对某一行数据进行了insert语句操作,那么 undo log就记录一条与之相反的DELETE操作。主要用于事务的回滚(undo log记录的是每个修改操作的逆操作)和一致性非锁定读(undo log回滚行记录到某种特定的版本--MVCC,即多版本并发控制)。
事务的特性
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。即要么转账成功,要么转账失败,是不存在中间的状态。
如果无法保证原子性会怎么样?就会出现数据不一致的情形,A账户减去100元,而B账户增加100元操作失败,系统将无故丟失100元。
- 一致性(Consistency)
根据定义,一致性是指事务执行前后,数据从一个合法性状态变换到另外一个合法性状态。这种状态是语义上的而不是语法上的,跟具体的业务有关。
那什么是合法的数据状态呢?满足预定的约束的状态就叫做合法的状态。通俗一点,这状态是由你自己来定义的(比如满足现实世界中的约束)。满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的!如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。
- 隔离性(Isolation)
一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。
- 持久性(Durability)
持久性是指—个事务一旦被提交,它对数据库中数据的改变就是永亼性的,接下来的其他操作和数据库故障不应该对其有任何影响。
持久性是通过事务日志来保证的。日志包括了重做日志和回滚日志。当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样做的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到据库系统中的重做日志,重新执行,从而使事务具有持久性。
数据并发问题
脏写
对于两个事务 Session A、 Session B,如果事务 Session A修改了另一个未提交事务 Session B修改过的数据,那就意味着发生了脏写。示意图如下:
脏读
对于两个事务 Session A、 Session B, Session A读取了已经被 Session b更新但还没有被提交的字段。之后若Session B回滚,Session A读取的内容就是临时且无效的。
不可重复读
对于两个事务 Session A、 Session B, Session A读取了一个字段,然后 Session B更新了该字段。之后 Session A再次读取同一个字段,值就不同了。那就意味着发生了不可重复读。
幻读
对于两个事务 Session A、 Session B, Session A从一个表中读取了一个字段,然后 Session B在该表中插入了一些新的行。之后如果 Session A再次读取同一个表,就会多出几行。那就意味着发生了幻读
4种隔离级别
-- 解决脏写
READ UNCOMMITTED:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读。
-- 解决脏写,脏读
READ COMMITTED:读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在。
-- 解决脏写,脏读,不可重复读
REPEATABLE READ:可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避兔脏读、不可重复读,但幻读问题仍然存在。这是MySQ的默认隔离级别。
-- 解决脏写,脏读,不可重复读,幻读
SERIALIZABLE:可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。
MVCC
什么是MVCC
MVCC(MultiVersion Concurrency Control),多版本并发控制。顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
通过间隙锁next-key locking策略防止幻读的出现。
ReadView数据结构
ReadView是一个数据结构,包含4个字段
- m_ids:当前活跃的事务编号集合
- min_trx_id:最小活跃事务编号
- max_trx_id:预分配事务编号,当前最大事务编号+1
- creator_trx_id:ReadView创建者的事务编号
RC隔离级别下的MVCC
- 读已提交:在每一次执行快照读时生成ReadView
事务D第1个select查询,
版本链最近的TRX_ID = 3代入访问规则
1 trx_id(3) = trx_id(4):不成立,不能访问
2 trx_id(3) < min_trx_id(2):不成立,不能访问
3 trx_id(3) > min_trx_id(5):不成立,不能访问
版本链最近的TRX_ID = 2代入访问规则
1 trx_id(2) = trx_id(4):不成立,不能访问
2 trx_id(2) < min_trx_id(2):不成立,不能访问
3 trx_id(2) > min_trx_id(5):不成立,不能访问
版本链最近的TRX_ID = 1代入访问规则
1 trx_id(1) = trx_id(4):不成立,不能访问
2 trx_id(1) < min_trx_id(2):成立,可以访问,张三
事务D第2个select查询,
版本链最近的TRX_ID = 3代入访问规则
1 trx_id(3) = trx_id(4):不成立,不能访问
2 trx_id(3) < min_trx_id(3):不成立,不能访问
3 trx_id(3) > min_trx_id(5):不成立,不能访问
版本链最近的TRX_ID = 2代入访问规则
1 trx_id(2) = trx_id(4):不成立,不能访问
2 trx_id(2) < min_trx_id(3):成立,可以访问,张小三
RR隔离级别下的MVCC
- 可重复读:仅在第一次执行快照读时生成ReadView,后续快照读复用