数据库并发一致性问题
在并发环境下,事务的隔离性难以保障,就会出现很多并发一致性的问题
为方便表示,用“T”表示事务
-
丢失修改
T1和T2都对一个数据进行修改,T1先修改,T2后修改,T2的修改覆盖掉了T1的修改,则使得T1对数据库数据的修改丢失。 -
读脏数据
T1修改一个数据,T2紧接着读取到了这个数据,随后T1又回滚了此次操作,那么T2就读到了脏数据。 -
不可重复读
T2读取了一个数据,T1对这个数据进行修改后,T2又在此读取这个数据,就会发现两次读取的结果不同。 -
幻影读
T1要求读取某个范围的数据,T2在这个范围内插入了新的数据,T1再次读这个范围的数据就会和第一次读取的结果不同。
**结论:**这些并发中产生的问题破坏了事务特性中的隔离性,解决这种问题主要有两种方法,分别从并发控制和事务的隔离级别出发
1. 并发控制:像java语言一样,可使用锁来更好的控制并发问题
2. 事务隔离级别:使用数据库管理系统提供的数据库事务的隔离级别来控制并发问题
解决方法一:锁
在此使用MYSQL数据库为例分析
MYSQL中提供两种锁机制,分别为行锁和表锁
这里系统开销与并发程度是一对矛盾体:
-
应该尽量只对需要使用的那部分资源加锁,而不是对所有资源(整张表)加锁,锁定的数据量越小,发生锁竞争的可能性就越小,系统的并发程度就会越高,在相同的时间内处理的业务也就更多;
-
但是锁锁定的数据量越少,即锁的粒度越小,那么对于锁的各种操作(包括锁的检测、获取、释放等)会增加大量的系统开销,
-
读写锁
1.写锁(排它锁),简写为X锁
2.读锁(共享锁),简写为S锁
规定:一个事务对数据A添加了X锁,对A进行读取和更新,其他事务不能对A加任何锁
一个事务对数据A添加了S锁,对A进行读取,其他事务可以对A进行读取,但是不能修改
意向锁
意向锁(IX或IS锁)作用:可以更容易的支持多粒度加锁
在原来的行锁和表锁存在下,事务每一次想要对表进行加锁,都需要检测其他事务是否对本表以及本表中的任意一行加了锁,这种检测是非常耗时的,那么引入了意向锁,表示一个事务想要对表中的某个数据行加锁(X锁或者S锁) -
1.一个事务在对某个数据行对象加S锁之前,必须要先获取本表的IS锁或者更强的锁(S锁、IX锁或者X锁)
-
2.一个事物在对某个数据行对象加X锁之前,必须获取表的IX锁
-
通过意向锁,此事务只需检测其他事务是否对本表加了IX/IS/S/X锁,而不需要逐行检测。 因为在对数据行加锁之前,必须要获取到表的某个锁,反正若事务检测到本表被添加了锁,那么就证明本表中某个数据行被加锁(正在使用),就不再浪费时间去逐行检测。
IS与IX锁之间都是兼容的,因为他们只是表示想要对表加锁,并不是真正意义上的加锁
S锁只与S锁和IS锁兼容,也就是说事务想要对数据行加S锁,其他事务可以获得已经对表或者表中的行的S锁
解决方法二:事务的隔离级别
- 未提交读(READ UNCOMMITTED):事务中的修改在没有提交之前对其他事务是可见的
- 提交读(READ COMMITTED):一个事务只能读取已经提交的事务所做的修改。在没有提交之前对其他事务是不可见的。
- 可重复读(REPEATABLE READ):保证在同一个事务中多次读取同样数据的结果是一样的。
- 可串行化(SERIALIZABLE):强制事务串行执行,这个需要加锁实现。