MySQL事务底层实现

in MySQL with 0 comment

事务总的来说还是那个A(原子性)C(一致性)I(隔离性)D(持久性)。

一致性的实现

什么叫事务,百科是这样解释的

指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
简单的说,事务就是并发控制的单位,是用户定义的一个操作序列。

在看下ACID各自的解释

原子性,简单的说,这个事务,要么成功commit,要么失败rollback。
隔离性,避免并发状态下事务间数据的互相干扰。
持久性,持久的存储。事务提交或者回滚,都必须要尽快的写入到磁盘中。

那一致性,其实从它的概念上来讲,就是一个事务要求的最小的基础,其他的AID,就是为了保证事务的一致性的而存在的。因为有了原子性、隔离性和持久性,才能够保证一个事务的一致性。
一致性,是个整体概念,保证所有的逻辑操作在事务执行前后都具有完整的特性。

原子性的实现

这个事务,要么成功commit,要么失败rollback。其实更简单的来说,原子性需要保证,事务发生了异常,能够rollback。

因为能够提交就是一个程序的正确操作。
而若回滚,是出现特殊的情况,才会选择回滚操作。

rollback

在事务运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库的所有已经完成的操作全部撤销,回滚到事务开始时的状态。
这里的操作特指数据库更新操作,并且回滚后,事务进入提交状态,因为回滚是回滚到事务开始时的状态。

原子性的核心,就是一个可回滚的操作。
他的实现就是使用了MySQL的undo log日志系统。(其实也有redo log,但是redo log只是做了一个日志缓冲,所以主要还是通过undo log完成的)

undo log(回滚日志)

undo log事务中,用于存放数据被修改时操作相反的逻辑操作

比如将name为ls的修改为zs
那么undo log 里面就存放的name的值是ls

其实在事务中,每当执行一条SQL语句对数据产生了影响,就会记录下来与之相反的操作到undo log中,例如,更新会记录之前的状态,删除会形成insert,添加会形成delete,一旦事务被回滚,则执行undo log中记录的操作,来完成恢复到之前的状态。这里是个逻辑恢复。

MySQL技术内幕:nnodb存储引擎(第二版) 第七章 7.2.2 undo log

持久性的实现

一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。但是事务回滚,这个数据是不会改变的。所以在持久性的体现主要在事务提交。在MySQL实现持久性,主要是通过redo log保证的。

redo log(重做日志)

redo log包括两部分,重做日志缓冲(redo log buffer)和重做日志文件(redo log file),前者是易失的缓存,后者是持久化的文件。
当事务提交时,必须先将事务的所有日志写入日志文件进行持久化。
比如某个事务

begin;
insert into table1 …
insert into table2 …
commit;

这条事务在每次insert操作都会将数据写入缓冲池和redo log buffer中。
在最后一次insert操作,会将redo log buffer中的数据,全部写到redo log file文件中。

在提交前,MySQL会将redo log file文件中的数据,写入binlog日志文件中。再持久性的写入磁盘中。

redo log和binlog的区别

  1. redo log是innodb的存储引擎产生的,而binlog是数据库的server层实现的。如果使用MySQL,换其他存储引擎,那么可能没有redo log,但是还是会有binlog。
  2. binlog是一种逻辑日志,记录对应的SQL语句,而redo log记录了物理日志,是针对每个数据页的修改。
  3. binlog只有在事务提交后完成一次写入,对于一个事务而言,在binlog中只有一条记录。而redo log在事务进行中不断被写入,而且是并发写入的,不是顺序写入的。
  4. redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

隔离性的实现

隔离性,就是根据容忍程度,定义了四个隔离级别,解决三个问题。

事务读写影响的三个问题

  1. 脏读:读到了别的事务尚未提交(commit)的变更,别人没提交,我读到了。
  2. 幻读:也是读到了别的事务提交的内容,但是跟上面的不同之处在于,读到了原本不存在的记录。
  3. 不可重复读:别的事务提交了变更,被当前事务读到了。然后导致本事务多次select的结果不一样,读到了别的事务提交的内容。
不可重复读,主要是读到了别的事务update的内容。
幻读,是读到了别的事务insert的内容。

在将隔离级别的时候,先看下MySQL中的锁

MySQL中的锁

InnoDB中,实现了两种标准的行级锁:

普通的Select语句不会加锁(快照读)
Select … lock in share mode语句能够获得共享锁
Select … for updateUpdatedelete语句能够获得排它锁(当前读)

锁兼容

当一个事务A已经获得了某一行的共享锁,那么另一个事务B可以立刻获得这一行的共享锁,因为不会改变r的数值,这种叫做锁兼容。

如果这时候有事务C希望获得行r的排它锁,那么就必须等待事务A和事务B释放行r的共享锁之后,才能获得排它锁,这种叫做锁不兼容。

column1共享锁(读)排他锁
共享锁(读)不冲突冲突
排他锁冲突冲突