一、事务
事务是指满足ACID特性的一组操作,数据库中常用的基本概念。使用Commit提交一个事务,Rollback进行回滚。
ACID特性:
- Atomicity 原子性。事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
- Consistency 一致性。数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。
- Isolation 隔离性。一个事务所做的修改在最终提交以前,对其它事务是不可见的。
- Durability 持久性。一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
二、并发一致性问题
问题起因
在并发的情形下,事务隔离性难以保证,因此会出现并发一致性问题。主要有以下几种:
- 丢失修改。T1与T2两个事务并发修改一个数据,T2覆盖了T1的修改结果。
- 脏读。读写并发,T1修改一个数据,T2随后读取了该数据,那么T2读到的是脏数据。
- 不可重复读。T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
- 幻影读( phantom read)。T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
解决方式
产生并发不一致性问题的主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。DBMS提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
封锁
MySQL中提供了两种封锁粒度:行锁和表锁。锁定的数据量越少,发生争用的概率就越小,系统的并发程度就越高;但同时加锁会增加系统开销,因此选择封锁粒度时,需要权衡并发程度与系统开销。
封锁类型:
- 读写锁。
- 互斥锁:简写为X锁(Exclusive),又称为写锁。加锁期间其他事务不能对数据对象加任何锁。
- 共享锁:简写为S锁(Shared),又称为读锁。加锁后可以对数据对象执行读操作,不可执行写操作。其他事务可以对数据对象加读锁,不能加写锁。
- 意向锁。(Intention Locks)。
- 意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。
- 通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。
两段锁协议:加锁和解锁分成两个阶段进行。事务遵循两段锁协议是保证可串行化调度的充分条件。串行执行的事务互不干扰,不会出现并发一致性问题。
MySQL中的锁:
-
InnoDB引擎采用两段锁协议,根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定。
-
也可以显式锁定:
SELECT ... LOCK In SHARE MODE; SELECT ... FOR UPDATE;
隔离级别
- 未提交读。事务中的修改,即使没有提交,对其它事务也是可见的。
- 提交读。一个事务所做的修改在提交之前对其它事务是不可见的。
- 可重复读。保证在同一个事务中多次读取同一数据的结果是一样的。
- 可串行化。强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。需要通过加锁实现。