【5. 事务】

发布于:2022-11-06 ⋅ 阅读:(406) ⋅ 点赞:(0)

事务的四大特性

  • 原子性(atomicity):事务是最小的执行单位,不允许分割,事务执行时要么全部成功,要么失败回滚。
  • 一致性(consistency):事务执行之前和事务执行结束,数据库完整性没有被破坏,也就是数据从一个正确的状态转换到另一个正确状态。
  • 隔离型(isolation):数据库允许多个并发事务同时对数据进行读写,而隔离是一个事务执行,不影响其他事务的运行效果。
  • 持久性(durability):一个事务一旦提交(结束),对数据库的修改就是永久的,即使系统故障也不会丢失。

并发事务带来的问题

对于服务器来说可能同时处理多个事务。事务有隔离性的特性,理论上在某个事务 对某个数据进行访问 时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样对 性能影响太大 ,我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时 性能尽量高些 。

  • 脏读( Dirty Read ):事务A和事务B交替执行,事务A读取了已经被事务B修改但是还未提交的字段,而此时事务B发生了回滚,那么事务A读到的就是脏数据
    在这里插入图片描述

  • 幻读( Phantom ): 事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,并提交,然后事务A再次查询相同的范围,两次读取得到的结果集不一样了,这就是幻读。

在这里插入图片描述

  • 不可重复读( Non-Repeatable Read ):事务 A 读取 了一个字段,然后 事务 B 更新 了该字段。 之后事务A 再次读取 同一个字段, 值就不同了
    在这里插入图片描述

  • 丢弃修改:俩个写事务A,B同时对A = 0进行+1操作,结果B覆盖了A,导致最终结果是1而不是2,事务被覆盖。
    在这里插入图片描述

幻读和不可重复读区别

  • 不可重复读重点是修改,而幻读重点在于新增或删除。

例如事务A读取工资为1000操作没完成,此时事务B修改1000为2000,此时A在读导致钱是1000.
幻读是事务A查询工资单中大于3000的一共有3人,而此时事务B又加了一条4000元进行,此时事务A读到的就是4人。

事务的隔离级别

  • READ UNCOMMITTED :读未提交,事务发生了修改,即使没有提交,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读、不可重复读、幻读。
  • READ COMMITTED :读已提交,一个事务从开始直到提交前,所做的任何修改时其他事务不可见的。这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。可以避免脏读,但不可重复读、幻读问题仍然存在。
  • REPEATABLE READ :可重复读,事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容。可以避免脏读、不可重复读,但幻读问题仍然存在。这是MySQL的默认隔离级别。
  • SERIALIZABLE :可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读、不可重复读和幻读。
    在这里插入图片描述

事务如何保持四大特性(简要)

数据库如何保持原子性

主要是利用 Innodb 的undo logundo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的 SQL语句,他需要记录你要回滚的相应日志信息。 例如

  • 当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
  • 当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
  • 当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作

undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

数据库如何保持一致性

分为两个层面来说。

  • 从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
  • 从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!

数据库如何保持隔离性

  • 采用加锁的机制

数据库如何保持持久性

主要是利用Innodb的redo log。重写日志, 正如之前说的,MySQL是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再写回到磁盘上。如果此时突然宕机,内存中的数据就会丢失。 怎么解决这个问题? 简单啊,事务提交前直接把数据写入磁盘就行啊。 这么做有什么问题?

  • 只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。
  • 毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。

于是,决定采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo logbinlog内容决定回滚数据还是提交数据。

文章借鉴:https://interviewguide.cn/notes/03-hunting_job/02-interview/03-02-net.html