事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

特性 ACID

  1. 原子性(Atomicity)

事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。

  1. 一致性(Consistency)

数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。

  1. 隔离性(Isolation)

一个事务所做的修改在最终提交以前,对其它事务是不可见的。

  1. 持久性(Durability)

一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
可以通过数据库备份和恢复来实现,在系统发生崩溃时,使用备份的数据库进行数据恢复。

并发一致性问题

在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。

  • 丢失修改

T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。
image.png

  • 脏读

表示一个事务能够读取另一个事务中还未提交的数据
T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
image.png

  • 不可重复读

指在一个事务内,多次读同一数据。
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
image.png

  • 幻影读

指同一个事务内多次查询返回的结果集不一样。
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。

image.png

事务隔离级别

产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁(加锁)来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。

事务隔离级别如下:

隔离级别 说明
未提交读(Read uncommitted) 事务中的修改,即使没有提交,对其它事务也是可见的。
提交读(Read committed) 一个事务只能读取已经提交的事务所做的修改。
可重复读(Repeatable read) 保证在同一个事务中多次读取同样数据的结果是一样的。
可串行化(Serializable) 强制事务串行执行。

事务并发访问引起的问题及使用哪种事务隔离级别避免:

并发访问问题 事务隔离级别
丢失修改 MySQL 所有事务隔离级别在数据库层面上均可避免
脏读 Read-committed事务隔离级别以上可避免
不可重复读 Repeatable-read事务隔离级别以上可避免
幻读 Serializable 事务隔离级别以上可避免

即:

事务隔离级别 \ 并发问题 丢失修改 脏读 不可重复读 幻读
未提交读 (Read uncommitted) 避免 发生 发生 发生
提交读 (Read committed) 避免 避免 发生 发生
可重复读 (Repeatable read) 避免 避免 避免 发生
可串行化 (Serializable) 避免 避免 避免 避免

MySQL事务隔离级别默认是可重复读

封锁

数据库的锁

数据库中一般是悲观锁,认为数据随时会被修改。

  • 共享s锁(存在死锁,所有s锁的事务都想升级成x锁)
  • 排他x锁(写锁,禁止再加任何锁)
  • 更新u锁(允许其他事务读,但是不允许再加u和x锁,当要被更新时升级为x锁)。

    1. ①悲观锁按作用范围分为行锁和表锁<br /> ②乐观锁:如果没有吞吐量瓶颈还是不用它,可能有风险

活锁和死锁

  1. 活锁:活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
  2. 死锁:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。(两个事务永远不能结束,形成死锁)
  3. 死锁的四个特征(死锁产生的4个必要条件):互斥,占有且等待,不可抢占,循环等待。
    • 互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
    • 占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
    • 不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
    • 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
  4. 死锁的预防:
    • 一次封锁法:要求每个事务必须一次将所有要用的数据全部加锁,否则不能继续执行。
    • 顺序封锁法:预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实施封锁。
    • 两段锁协议:指所有事务必须分两个阶段对数据项进行加锁和解锁。1.在对任何数据进行读、写操作前,首先要申请并获得对该数据的封锁。2.在释放一个封锁后,事务不得再申请和获得任何其他封锁。