我们希望多个事务尽可能的并发执行,来提高效率。但是并发执行的事务之间相互影响会导致数据库状态不一致。
1.锁
一个很自然的方式是给事务要访问的数据加锁。锁表示当前事务对此数据元素的独占权。在事务操作数据库元素前都必须获取该元素上的锁。
读写锁
有些事务可能只是想读而不是修改某数据元素,我们有办法让这种情况更高效。读写锁也叫共享锁/排他锁。读和读兼容,读和写互斥,写和写互斥。
我们在实现读写锁时有一个当前读事务的计数,当读计数为0时才能申请到写锁。
两阶段锁
有一种叫两阶段锁的方法能够保证事务是可串行化的,即:每个事务中,所有加锁操作必须在解锁操作之前。
死锁
死锁是指多个事务竞争资源出现的阻塞现象,一般是持有A资源申请B资源与持有B资源申请A资源相互等待。
避免死锁方法:1是预防死锁发生,2是检测死锁和恢复。比较简单粗暴的方法是设定一个事务超时时间,超时以后回滚事务。
2.乐观锁
普通的锁是一种悲观锁,它们假定只要不加锁就会出错。而乐观锁假定不会出现冲突,当检测到冲突时回滚。
数据库记录读取或修改数据元素的时间戳或者版本号。在修改时验证数据在此期间是否被修改过。如果出现不一致,则重启或者取消当前事务。
如果大多数事务是只读的或者极少并发修改同一元素,则乐观锁更好。在高冲突时,悲观锁性能更好。
3 MVCC
除了通过延迟和终止事务来避免冲突以外,我们还可以通过对竞争对象进行拷贝多份来避免冲突。
多版本并发控制(MVCC)是在修改数据时,总是保持旧的版本不变而产生一个新版本的数据。读操作在多个数据元素版本中找到一个合适的版本进行读取。这样读和写不再冲突,当然写写还是冲突的。
MVCC一般要和悲观锁或乐观锁结合使用。
事务隔离级别
有时候不能完全按照可串行化的要求来执行事务,在有些应用中可以做适当的妥协来提高性能。从需求的角度讲,SQL定义了四种事务的隔离级别。
1. Read Uncommitted(读取未提交内容)
可以读到其他事务未提交的内容,可能在读取后其他事务又撤销回滚了。
2. Read Committed(读取提交内容)
一个事务只能读取到已提交事务的修改内容。当在事务执行期间另一个事务会产生提交,导致两次读取同一数据的内容不一样。
3. Repeatable Read(可重读)
事务并发执行期间,多次读取同一数据元素相同。但是会读到其他事务新增的数据。
4. Serializable(可串行化)
和每个事务顺序执行的效果相同。
上面三种解决并发问题方法的不同组合和实现细节对应不同的隔离级别。
网友评论