1、事务
(1)ACID:原子性,一致性(事务执行前后满足约束条件,例如转账前后总钱数不变),隔离性,持久性
几种不一致现象:丢失修改(read uncommitted即可避免,因为加了写锁)、脏读、不可重复读、幻读
【1】脏读:事务处理过程中读取了另一个未提交事务的数据
【2】不可重复读:一个事务范围内多次查询返回不同的数据。是由于查询间隔内,另一个事务修改并提交了数据
【3】幻读:例如所有行某项从1改为2,另一事务插入一条1的数据。
幻读和不可重复读都是读取到了另一条已经提交的事务,幻读针对一批数据,不可重复读针对一个数据项。
(2)隔离级别:所有的隔离级别都不允许脏写(即丢失修改),如果一个数据项已经被另一个尚未提交的事务修改,则不允许写该数据项(表现在所有的隔离级别的写锁都是在事务提交之后释放)。
【1】未提交读(read uncommitted),允许脏读
【2】已提交读(read committed),可避免脏读,有不可重复读和幻读问题
【3】可重复读(repeatable read),Mysql默认,避免脏读和不可重复读,有幻读问题
【4】可串行化(serializable),加表锁
2、并发控制(隔离级别的实现)
(1)基于锁的实现:利用读锁(共享锁)和写锁(排他锁),有读锁时只允许读锁进入,有写锁时不允许其他锁进入。为了防止写锁饿死,通常不允许写锁之后的读锁进入该写锁正在等待的读锁。
锁管理器使用锁表(数据项的散列表)实现封锁。用链表解决数据项的散列冲突,数据项除了有指向下一个数据项的指针,还有指向它管理的事务的链表的指针(已被授予或者等待该数据项锁的事务)。解锁时,移出事务链表的队头,检查后面的事务(如果有多个读事件,它们同时获得锁),事务先到先得,可以避免锁请求饿死。
【1】两阶段封锁协议(会产生死锁)
增长阶段:事务可以获得锁,但不能释放锁。
缩减阶段:事务可以释放锁,但不能获得锁。
【2】严格两阶段封锁协议(避免级联回滚)
所有排他锁必须在事务提交后释放。
【3】强两阶段封锁协议
所有锁必须在事务提交后释放。
四种隔离级别的锁实现:
【1】read uncommitted:读数据不加锁,写数据时加行级写锁,提交后释放。不会对读产生影响,可以防止两个同时写操作(避免脏写)。
【2】read committed:读数据加行级读锁,读完释放,写数据加行级写锁,事务结束释放。两次读之间可以有其他事务获得写锁,会产生不可重复读。
【3】repeatable read:读数据加行级读锁,事务结束释放,写数据加行级写锁,事务结束释放。会产生幻读。
【4】serializable:读加表级读锁,事务结束释放,写加表级写锁,事务结束释放。
(2)基于时间戳
事务启动时,将该时刻赋予该事务,时间戳小的事务先执行。
比较事务的时间戳TS和数据项的读写的时间戳WT(X),RT(X)
读写并发时,【1】若事务读X,若TS>WT(X),允许事务T操作,更新RT(X),否则回滚T。【2】若事务写X,若TS>RT(X),允许操作,更新WT(X),否则回滚T。
写写并发时,若事务T写X,若TS>WT(X),允许事务T操作,更新WT(X),否则回滚。
(3)基于有效性确认
分为读阶段、有效性确认阶段和写阶段。
3、乐观锁和悲观锁
乐观锁:使用时间戳或版本号,为表增加一个时间戳或版本号字段,更新时检查是否一致,不一致则回滚。
悲观锁:数据库的行级/表级 读锁/写锁等。