详解数据库隔离级别与锁的关系

事务处理是为了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不会产生矛盾,即数据的一致性(Consistency)。

而要达到数据一致性,需要三方面的努力:

  • 原子性(Atomic): 在同一项业务处理过程中,事务保证了对多个数据的修改,要么同时成功,要么同时失败。
  • 隔离性(Isolation): 在不同的业务处理过程中,事务保证了各业务正在读、写的数据相互独立,不会彼此影响。
  • 持久性(Durability):事务应该保证所有成功被提交的数据修改都能够正确的被持久化,不丢失数据。

这就是事务的 ACID。

数据库的隔离级别:

  • 读未提交:(Read Uncommitted)
  • 读已提交(Read Committed) 大多数数据库默认的隔离级别
  • 重复读(Repeatable-Read) mysql数据库所默认的级别
  • 可串行化(Serializable)

锁的类型:

  • 写锁(Write Lock, 也叫排他锁,eXclusive Lock, 简写为X-Lock) : 如果数据有加写锁,则持有写锁的事务才能对数据进行写入操作,数据加持着写锁时,其他事务不能写入数据,也不能施加读锁。
  • 读锁(Read Lock, 也叫共享锁, Shared Lock, 简写为S-Lock):多个事务可以对同一个数据添加多个读锁,数据被加上读锁后不能再被加上写锁,所以其他事务不能对该数据进行写入,但仍然可以读取。对于持有读锁的是,如果该数据只有它自己一个事务加了读锁,则允许直接将其升级为写锁,然后写入数据。
  • 范围锁(Range Lock):对于某个范围直接加排他锁,在这个范围内的数据不能被写入。

隔离性保证了每个事务各自读、写的数据相互独立,不会彼此影响。隔离性是与并发密切相关的,因为如果没有并发,所有的事务操作都是串行的,那就不需要任何隔离,或者说本身就具有了隔离性。

那么,要怎样在并发下实现串行的数据访问呢,同步加锁,也即使用上面锁。

可串行化(Serializable)

串行化访问提供了最高强度的隔离性,而可串行化完全符合程序员对数据加锁的理解,如果不考虑性能优化,对事务的所有读、写的数据全部都加上读锁、写锁和范围锁即可做到可串行化。而其中包括加锁和解锁两阶段去处理读锁、写锁与数据间的关系,称为两阶段锁(Two-Phase Lock, 2PL)。

而在实际中,不考虑性能肯定是不行的,并发控制理论决定了隔离程度和并发能力是相互抵触的,隔离程度越高,并发访问时的吞吐量就越低。

现代数据库一定会提供除可串行化以外的其他隔离级别供用户使用,让用户自主调节隔离级别,根本目的是为了让用户可以调节数据库的加锁方式,取得隔离性和吞吐量之间的平衡。

可重复读(Repeatable Read)

可串行化的下一个隔离级别是可重复读,可重复读对所涉及的数据加读锁和写锁,且一直持有至事务结束,但不再加范围锁。

可重复读比可串行化弱化的地方在于幻读问题(Phantom Read),它是指在事务执行过程中,两个完全相同的范围查询得到了两个不同的结果集。

 

读已提交(Read Commited)

可重复读的下一个隔离级别是读已提交,读已提交对事务涉及的数据加的写锁会一直持续到事务结束,但加的读锁在查询操作完成后会马上释放。

读已提交比可重复读弱化的地方在于不可重复读问题(Non-Repeatable Read),它是指在事务执行过程中,对同一行数据的两次查询得到不同的结果。

 

读未提交(Read Uncommitted)

读已提交的下一个级别是读未提交(Read Uncommitted), 它只会对事务涉及的数据加写锁,且一直持续到事务结束,但完全不加读锁。

读未提交比读已提交弱化的地方在于脏读问题(Dirty Read),它是指在事务执行过程中,一个事务读取到了另一个事务未提交的数据。

这里再读一次写锁的定义:写锁禁止其他事务施加读锁,而不是禁止事务读取数据,如果事务T1读取数据前并不需要加读锁的话,就会导致事务T2未提交的数据页马上能被事务T1所读到。

读未提交会有脏读的问题,但是不会有脏写问题(Dirty Write),即一个事务没提交之前的修改可以被另一个事务的修改覆盖掉。

一般在谈论隔离级别时不会将完全不隔离纳入讨论,而是将读未提交视为最低级的隔离级别。

 

其实不同隔离级别以及幻读、不可重复读、脏读等问题都只是表面现象,是各种锁在不同加锁时间上组合应用所产生的结果,以锁为手段来实现隔离性才是数据库表现出不同隔离级别的根本原因。

除了都是以锁来实现外,以上四种隔离级别还有另外一个共同特点,就是幻读、不可重复读、脏读等问题都是由于一个事务在读数据的过程中,受另一个写数据的事务影响而破坏了隔离性。针对这种“一个事务读+另一个事务写”的隔离问题,有一种名为 多版本并发控制(Multi-Version Concurrency Control, MVCC)的无锁优化方案被主流的商业数据库广泛采用。

多版本并发控制(Multi-Version Concurrency Control, MVCC)

MVCC的基本思想是对数据库的任何修改都不会直接覆盖之前的数据,而是产生一个新版本与老版本共存,以此达到读取时可以完全不加锁的目的。

 

posted @ 2024-11-11 14:39  Vincent-yuan  阅读(18)  评论(0编辑  收藏  举报