数据库事务详解

数据库的事务

定义例子:数据库中有两个用户的银行账户 A:100 元; B:200 元。假设事务是 A 转账 50 元到 B,可以理解为这个事务由两个操作组成:1) A-= 50; 2) B+=50。对于用户来说,数据库对于这个事务只有两个状态:执行事务前的初始状态,即 A:100 元; B:200 元,以及执行事务后的转账成功状态:A:50 元;B:250 元,不会有中间状态,比如钱从 A 已经扣除,却还没转到 B 上:A:50 元; B:200 元。

  1. 原子性

    • 想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,而在mysql中,恢复机制是通过回滚日志实现的,当回滚日志被使用时,可以理解为,在事务中使用的每一条INSERT都对应一条DELETE,每一条UPDATE都对应一条相反的UPDATE语句。
    • 并行执行的事务是实际工作中的常态,在并行任务下,可能出现非常复杂的问题:假设此时有两个事务,当事务1在执行时更改了数据库的数据,但还没有对事务进行提交或者回滚,在此时,事务2对同样的数据进行了读操作并提交了事务;也就是说事务二是依赖于事务一的,当事务1由于一些错误需要回滚时,因为要保证事务的原子性,需要对事务2也进行回滚,但是由于已经提交了事务2,无法回滚,这种现象称为不可恢复安排
    • 所以,如果事务2依赖于事务1,那么事务1必须在事务2提交之前完成提交或回滚的操作
    • 当出现多个事务并行时,例如2依赖于1,3也依赖于1,当1回滚时,为了保证事务的原子性,会将23中的全部工作进行回滚,这种情况叫做级联回滚
  2. 持久性

    • 由于事务具有持久性,当事务被提交后,就无法再次进行回滚了,唯一能撤销已经提交的事务的方式就是创建一个相反的事务对原操作进行[补偿],这也是事务的持久性的体现之一。
    • 事务的持久性是通过重做日志实现的,重做日志分为两部分:一是内存中的重做日志缓冲区(易失),另一个是磁盘上的重做日志文件(持久)
    • 当事务提交时,MYSQL会将重做日志缓存中的内容刷新到重做日志文件,再将内存中的数据更新到硬盘上。
    • 在InnoDB中,重做日志都是以512字节的块的形式进行存储的,同时因为块的大小与磁盘扇区的大小相同,所以重做日志的写入可以保持原子性,不会由于断电导致日志重做日志写入一半留下脏数据。
  3. 原子性和持久性总结

    • 原子性和持久性都是由事务日志保证的,回滚日志用于对事务的影响进行撤销,重做日志在错误处理时对已经提交的事务进行重做
    • 可以保证发生错误或者需要回滚的事务能够成功回滚(原子性)
    • 可以保证在事务提交后,数据没来得及写回磁盘就宕机时,在下次重新启动后能够成功恢复数据(持久性)
  4. 隔离性

    • 如果事务之间没有隔离性就会发生原子性中说到的级联回滚等问题造成性能上的巨大损失。

    • 事务的四个隔离级别

      • READ UNCOMMITED(读未提交):使用查询语句不会加锁,可能会读到未提交的行(Dirty Read)
      • READ COMMITED(读提交):只对记录加记录锁,而不在记录之间加间隙锁,所以允许新的记录插入到被锁定记录的附近,所以再多次使用查询语句时,可能会得到不同的结果(Non-Repeatable Read)
      • REPEATABLE READ(可重复读):多次读取同一范围的数据会返回第一次查询的快照,不会返回不同的数据行,但是可能发生幻读(Phantom Read)
      • SERIALIZABLE(可序列化):InnoDB隐式地将全部的查询语句加上共享锁,使事务按照一定顺序执行,解决了幻读的问题
      • 脏读:一个事务读取了另一个事务还未提交的修改。
      • 不可重复读:在一个事务过程中,可能出现多次读取同一数据但得到不同值的现象。
      • 幻读:在一个事务中,当查询了一组数据后,再次发起相同查询,却发现满足条件的数据被另一个提交的事务改变了。

      以上的所有事务隔离级别都不允许脏写入,也就是当前事务更新了另一个事务已经更新但是还未提交的数据,大部分的数据使用READ COMMITED作为默认的事务隔离级别,但是MYSQL使用了REPEATABLE READ作为默认配置

    • 随着事务的隔离级别变得越来越严格,数据库对于并发执行事务的性能也逐渐下降

    • 隔离级别的实现方式

      • 锁:分为共享锁(读锁)、互斥锁(写锁),读锁保证了读操作可以并发执行,相互不会影响,而写锁保证了在更新数据库数据时不会有其他事务访问或者更改同一条记录造成不可预知的问题
      • 时间戳:除了锁,另一种实现事务的方式就是通过时间戳,读时间戳中包括了所有访问该记录的事务中的最大时间戳,而记录行的写时间戳中保存了将记录改到当前值的事务的时间戳。使用时间戳实现事务的隔离性时,往往都会使用乐观锁,先对数据进行修改,在写回时再去判断当前值,也就是时间戳是否改变过,如果没有改变过,就写入,否则,生成一个新的时间戳并再次更新数据。
      • 多版本和快照隔离:MySQL 就通过文章中提到的回滚日志实现了 MVCC,保证事务并行执行时能够不等待互斥锁的释放直接获取数据。
  5. 隔离性和原子性总结

    • 原子性中级联回滚的问题:如果一个事务在对数据进行写入,这时就会获得一个互斥锁,其他事务想要获得该行数据就必须要等互斥锁的释放,就不会发生级联回滚的问题了
    • MySQL 中使用了 MVCC 等特性,也就是正常的读方法是不需要获取锁的
  6. 一致性
    假定数据库的初始状态是稳定的,或者说对用户来说是一致的。由于事务执行的原子性,即执行失败就回滚到执行前的状态,执行成功就变成一个新的稳定状态。因此,事务的执行会保持数据库状态的一致性。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值