事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
特性 ACID
- 原子性(Atomicity)
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
- 一致性(Consistency)
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。
- 隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其它事务是不可见的。
- 持久性(Durability)
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
可以通过数据库备份和恢复来实现,在系统发生崩溃时,使用备份的数据库进行数据恢复。
并发一致性问题
在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。
- 丢失修改
T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。
- 脏读
表示一个事务能够读取另一个事务中还未提交的数据
T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
- 不可重复读
指在一个事务内,多次读同一数据。
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
- 幻影读
指同一个事务内多次查询返回的结果集不一样。
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
事务隔离级别
产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁(加锁)来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
事务隔离级别如下:
隔离级别 | 说明 |
---|---|
未提交读(Read uncommitted) | 事务中的修改,即使没有提交,对其它事务也是可见的。 |
提交读(Read committed) | 一个事务只能读取已经提交的事务所做的修改。 |
可重复读(Repeatable read) | 保证在同一个事务中多次读取同样数据的结果是一样的。 |
可串行化(Serializable) | 强制事务串行执行。 |
事务并发访问引起的问题及使用哪种事务隔离级别避免:
并发访问问题 | 事务隔离级别 |
---|---|
丢失修改 | MySQL 所有事务隔离级别在数据库层面上均可避免 |
脏读 | Read-committed事务隔离级别以上可避免 |
不可重复读 | Repeatable-read事务隔离级别以上可避免 |
幻读 | Serializable 事务隔离级别以上可避免 |
即:
事务隔离级别 \ 并发问题 | 丢失修改 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读 (Read uncommitted) | 避免 | 发生 | 发生 | 发生 |
提交读 (Read committed) | 避免 | 避免 | 发生 | 发生 |
可重复读 (Repeatable read) | 避免 | 避免 | 避免 | 发生 |
可串行化 (Serializable) | 避免 | 避免 | 避免 | 避免 |
封锁
数据库的锁
数据库中一般是悲观锁,认为数据随时会被修改。
- 共享s锁(存在死锁,所有s锁的事务都想升级成x锁)
- 排他x锁(写锁,禁止再加任何锁)
更新u锁(允许其他事务读,但是不允许再加u和x锁,当要被更新时升级为x锁)。
①悲观锁按作用范围分为行锁和表锁<br /> ②乐观锁:如果没有吞吐量瓶颈还是不用它,可能有风险
活锁和死锁
- 活锁:活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
- 死锁:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。(两个事务永远不能结束,形成死锁)
- 死锁的四个特征(死锁产生的4个必要条件):互斥,占有且等待,不可抢占,循环等待。
- 互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
- 占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
- 不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
- 循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
- 死锁的预防:
- 一次封锁法:要求每个事务必须一次将所有要用的数据全部加锁,否则不能继续执行。
- 顺序封锁法:预先对数据对象规定一个封锁顺序,所有事务都按这个顺序实施封锁。
- 两段锁协议:指所有事务必须分两个阶段对数据项进行加锁和解锁。1.在对任何数据进行读、写操作前,首先要申请并获得对该数据的封锁。2.在释放一个封锁后,事务不得再申请和获得任何其他封锁。