MYSQL事务的底层原理

作者:京东云开发者-京东物流 张士欣
链接:https://my.oschina.NET/u/4090830/blog/10142911
事务的底层原理
在事务的实现机制上 , MySQL 采用的是 WAL:Write-ahead logging,预写式日志 , 机制来实现的 。
在使用 WAL 的系统中,所有的修改都先被写入到日志中 , 然后再被应用到系统中 。通常包含 redo 和 undo 两部分信息 。
为什么需要使用 WAL,然后包含 redo 和 undo 信息呢?举个例子,如果一个系统直接将变更应用到系统状态中,那么在机器掉电重启之后系统需要知道操作是成功了,还是只有部分成功或者是失败了 。如果使用了 WAL,那么在重启之后系统可以通过比较日志和系统状态来决定是继续完成操作还是撤销操作 。
redo log 称为重做日志,每当有操作时 , 在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作 。
undo log 称为撤销日志 , 当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态 。
MySQL 中用 redo log 来在系统 Crash 重启之类的情况时修复数据,而 undo log 来保证事务的原子性 。
事务 id
一个事务可以是一个只读事务,或者是一个读写事务:可以通过 START TRANSACTION READ ONLY 语句开启一个只读事务 。
在只读事务中不可以对普通的表进行增、删、改操作 , 但可以对用户临时表做增、删、改操作 。
可以通过 START TRANSACTION READ WRITE 语句开启一个读写事务,或者使用 BEGIN、START TRANSACTION 语句开启的事务默认也算是读写事务 。
在读写事务中可以对表执行增删改查操作 。
如果某个事务执行过程中对某个表执行了增、删、改操作,那么 InnoDB 存储引擎就会给它分配一个独一无二的事务 id,针对 MySQL 5.7 分配方式如下:

  • 对于只读事务来说 , 只有在它第一次对某个用户创建的临时表执行增、删、改操作时才会为这个事务分配一个事务 id,否则的话是不分配事务 id 的 。
  • 对于读写事务来说,只有在它第一次对某个表执行增、删、改操作时才会为这个事务分配一个事务 id,否则的话也是不分配事务 id 的 。
  • 有的时候虽然开启了一个读写事务,但是在这个事务中全是查询语句,并没有执行增、删、改的语句,那也就意味着这个事务并不会被分配一个事务 id 。
这个事务 id 本质上就是一个数字,它的分配策略和隐藏列 row_id 的分配策略大抵相同,具体策略如下:
  • 服务器会在内存中维护一个全局变量,每当需要为某个事务分配一个事务 id 时,就会把该变量的值当作事务 id 分配给该事务,并且把该变量自增 1 。
  • 每当这个变量的值为 256 的倍数时,就会将该变量的值刷新到系统表空间的页号为 5 的页面中一个称之为 Max Trx ID 的属性处,这个属性占用 8 个字节的存 储空间 。
  • 当系统下一次重新启动时,会将上边提到的 Max Trx ID 属性加载到内存中,将该值加上 256 之后赋值给全局变量,因为在上次关机时该全局变量的值可能大于 Max Trx ID 属性值 。
  • 这样就可以保证整个系统中分配的事务 id 值是一个递增的数字 。先被分配 id 的事务得到的是较小的事务 id,后被分配 id 的事务得到的是较大的事务 id 。
mvcc全称 Multi-Version Concurrency Control,即多版本并发控制 , 主要是为了提高数据库的并发性能 。
同一行数据平时发生读写请求时,会上锁阻塞住 。但 MVCC 用更好的方式去处理读写请求,做到在发生读写请求冲突时不用加锁 。
这个读是指的快照读,而不是当前读,当前读是一种加锁操作,是悲观锁 。
MVCC 原理
在事务并发执行遇到的问题如下: