数据库中的事务是并发操作的,并发操作可以提高系统的工作效率,节省资源。在事务并发操作时,会出现多个事务对某一资源的争用,多个事务对资源进行不同的操作,若不加以控制,会出现数据不一致的问题。因此,在DBMS中需要进行并发控制管理。
并发控制的问题
若不对并发事务进行控制,会出现数据不一致的问题,如脏读、不可重复读、幻读、丢失更新等问题。
-
脏读
多个事务并发运行,访问共享数据,其中一个事务读取了被另一个事务修改的共享数据,但修改数据的那个事务发生了错误,并没有提交事务,此时读取的数据就是脏数据(未提交的修改数据)。 -
不可重复读
多个事务并发执行时,一些事务对共享数据进行多次读操作,但其中一些事务对共享数据进行了修改或删除操作,导致原有数据改变或丢失。 -
幻读
多个事务并发执行时,其中一个事务对共享数据进行了添加操作,导致读两次时,第二次数据比第一次数据新增了一些数据。 -
丢失更新
多个事务并发执行时,其中一个事务对共享数据进行了更新,并改变了前面事务的更新值,导致一个事务对共享数据进行更新,但查询的数据和更新的值不一致。
锁机制
在DBMS中,锁表机制与并发控制调度器结合,实现共享资源的锁定访问。
根据锁定资源类型锁可分为:
- 排他锁(Exclusive Lock):限制其他事务对共享数据的修改、删除和查询操作;
- 共享锁(Share Lock):限制其他事务对共享数据的修改、删除。
另外锁机制还可以运用在多种粒度上,如数据库、表、页面、行。锁定的范围越大,DMBS管理越容易。
锁的相容性
当多个事务对共享资源进行加锁时,操作是否成功,取决于锁之间的相容性。
类型 | 排他锁 | 共享锁 | 无锁 |
---|---|---|---|
排他锁 | 否 | 否 | 是 |
共享锁 | 否 | 是 | 是 |
无锁 | 是 | 是 | 是 |
加锁协议
加锁协议规定了何时使用排他锁和共享锁,何时释放锁。不同规则的加锁协议,解决的数据库一致性问题是不同的。
加锁协议级别 | 排他锁 | 共享锁 | 不丢失更新 | 不脏读 | 可重复读 |
---|---|---|---|---|---|
一级 | 全程加锁 | 不加 | 是 | 否 | 否 |
二级 | 全程加锁 | 开始时加锁,读完数据释放锁定 | 是 | 是 | 否 |
三级 | 全程加锁 | 全程加锁 | 是 | 是 | 是 |
两阶段锁定协议指的是所有并发事务在进行共享数据操作处理时,必须按照两个阶段(增长、缩减)对共享数据进行加锁和解锁申请。在增长阶段,事务可以对共享数据进行加锁申请,但不能释放已有的锁定;在缩减阶段,事务可以对已有的锁定进行释放,但不能对共享数据提出新的加锁申请。
在并发事务运行中,若所有事务都遵从两阶段锁定协议,即可保持数据一致性。
死锁问题
在事务并发运行中,若事务同时锁定两个及以上资源时,可能会出现彼此都不能继续运行的状态,即死锁状态。
- 出现死锁的必要条件
(1)互斥条件:事务对所分配的资源加排他锁,在一段时间内某资源只由一个事务占用;
(2)请求和保持条件:事务已经保持了至少一个资源,但又提出新的资源请求,但该资源被其他资源占用,此时请求事务被阻塞,又不释放已占用资源;
(3)不剥夺条件:事务占用已获得资源,在未使用完前,不能被剥夺,只能在使用完时自动释放;
(4)环路等待条件:事务T0在等待事务T1占用的资源,事务T1在等待事务T2占用的资源,依此类推,事务Tn在等待事务T0占用的资源,造成环路等待。 - 解决方式
(1)在并发事务执行之前,预防死锁;
(2)在死锁出现后,其中一个事务释放资源解除死锁。
这些是数据库系统采用的两种策略用于解决死锁。
事务隔离级别
为了避免在事务并发时,出现读脏数据、不可重读、幻读等问题,可以在DBMS中设置隔离级别来避免出现事务并发带来的问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 丢失更新 |
---|---|---|---|---|
读取未提交(Read Uncommitted) | 可能 | 可能 | 可能 | 可能 |
读取已提交(Read Committed) | 不可能 | 可能 | 可能 | 可能 |
可重复读 (Repeatable Read) | 不可能 | 不可能 | 可能 | 可能 |
可串行化(Serializable) | 不可能 | 不可能 | 不可能 | 不可能 |