(转)Mysql Transaction

ReZero lol

MySql Transaction

事务如何保证

  1. 原子性: undo log
    • binlog记载的是update/delete/insert这样的SQL语句,而redo log记载的是物理修改的内容(xxxx页修改了xxx)。即redo log 记录的是数据的物理变化,binlog 记录的是数据的逻辑变化。

    • binlog的作用是复制和恢复而生的。

    • 主从服务器需要保持数据的一致性,通过binlog来同步数据。

    • 如果整个数据库的数据都被删除了,binlog存储着所有的数据变更情况,那么可以通过binlog来对数据进行恢复。而redo log 存储的是物理数据的变更,如果我们内存的数据已经刷到了磁盘了,那redo log

的数据就无效了。所以redo log不会存储着历史所有数据的变更,文件的内容会被覆盖的

  1. 持久性: redo log

    • redo log事务开始的时候,就开始记录每次的变更信息,而binlog是在事务提交的时候才记录

    • 如果写redo log失败了,那我们就认为这次事务有问题,回滚,不再写binlog。

    • 如果写redo log成功了,写binlog,写binlog写一半了,但失败了怎么办?我们还是会对这次的事务回滚,将无效的binlog给删除(因为binlog会影响从库的数据,所以需要做删除操作)

    • 如果写redo log和binlog都成功了,那这次算是事务才会真正成功。

    • 简单来说:MySQL需要保证redo log和binlog的数据是一致的,如果不一致,那就乱套了。

  2. 两阶段提交

    • 阶段1:InnoDBredo log 写盘,InnoDB 事务进入 prepare 状态

    • 阶段2:binlog 写盘,InooDB 事务进入 commit 状态

    • 每个事务binlog的末尾,会记录一个 XID event,标志着事务是否提交成功,也就是说,恢复过程中,binlog 最后一个 XID event 之后的内容都应该被 purge。

  3. undo_log: 回滚和多版本控制(MVCC)

    • undo log主要存储的也是逻辑日志,比如我们要insert一条数据了,那undo log会记录的一条对应的delete日志。我们要update一条记录时,它会记录一条对应相反的update记录(原子性保证的前提)

    • undo log存储着修改之前的数据,相当于一个前版本,MVCC实现的是读写不阻塞,读的时候只要返回前一个版本的数据就行了。

关于锁

锁协议

  • 无法像应用一样预知用哪些数据,直接锁对应的数据即可,因而数据库提出了两段锁协议
  1. 加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁(共享锁,其它事务可以继续加共享锁,但不能加排它锁),在进行写操作之前要申请并获得X
    锁(排它锁,其它事务不能再获得任何锁)。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
  2. 解锁阶段:当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。

加锁方式

  1. 读提交:读不加锁,写加锁

    • 如果一个条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,再由MySQL Server层进行过滤

    • 实际改进:在MySQL Server过滤条件,发现不满足后,会调用unlock_row
      方法,把不满足条件的记录释放锁 (违背了二段锁协议的约束)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。

  2. 不可重复读

    • 悲观锁:事务开启时就锁数据,锁数据期间别的无法修改,自然看到的就一直是对应的数据了,此方法低效且无法处理插入这种幻读的情况。

    • 所以采取了乐观锁 :MVCC 这种模式

MVCC

  • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。

  • INSERT时,保存当前事务版本号为行的创建版本号

  • DELETE时,保存当前事务版本号为行的删除版本号

  • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行

    • Mysql 的 可重复读解决了 幻读的 问题,因为幻读插入的数据版本号高于当前版本,因此不会读到。但这也意味着读的不是最新的数据,因此称为快照读,与之对应的就是锁数据来保持当前版本读,称为当前读。

    • 为了解决当前读中的幻读 问题,MySQL事务使用了Next-Key锁。Next-Key锁是行锁和GAP(间隙锁)的合并。RR级别中,事务A在update后加锁,事务B无法插入新数据,这样事务A在update
      前后读的数据保持一致,避免了幻读。这个锁,就是Gap锁,所谓Gap锁就是按索引字段将修改数据附近的区间进行加锁,进而保证不可插入,但弊端就在于对没有索引的字段会锁全表,而有索引的字段在没有命中数据的情况下,仍然回对该区间加锁

加锁只针对当前读,快照读不需要加锁

读锁和写锁都是最基本的锁模式,它们的概念也比较容易理解。
读锁,又称共享锁(Share locks,简称 S 锁),加了读锁的记录,所有的事务都可以读取,但是不能修改,并且可同时有多个事务对记录加读锁。写锁,又称排他锁(Exclusive locks,简称 X 锁),或独占锁,对记录加了排他锁之后,只有拥有该锁的事务可以读取和修改,其他事务都不可以读取和修改,并且同一时间只能有一个事务加写锁。(注意:这里说的读都是当前读,快照读是无需加锁的,记录上无论有没有锁,都可以快照读)

  • Post title:(转)Mysql Transaction
  • Post author:ReZero
  • Create time:2020-07-29 10:10:00
  • Post link:https://rezeros.github.io/2020/07/29/mysql-transaction/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
 Comments