热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

数据库的并发控制和封锁协议分析

数据库的并发控制和封锁协议分析[摘要]数据库可以提供给多个用户共享数据信息资源,所以就必须对并发的事务进行控制,这种并发控制必须引入一些封锁协议以保证数据的完整性。本文针对数据库并发操作中可能产生的数据不一致性问题进行分析,并给出解决的

数据库的并发控制和封锁协议分析 [摘 要] 数据库可以提供给多个用户共享数据信息资源,所以就必须对并发的事务进行控制,这种并发控制必须引入一些封锁协议以保证数据的完整性。本文针对数据库并发操作中可能产生的数据不一致性问题进行分析,并给出解决的

数据库的并发控制和封锁协议分析

[摘 要] 数据库可以提供给多个用户共享数据信息资源,所以就必须对并发的事务进行控制,这种并发控制必须引入一些封锁协议以保证数据的完整性。本文针对数据库并发操作中可能产生的数据不一致性问题进行分析,并给出解决的方法。

[关键词] 数据库 并发控制 一致性 封锁 封锁协议

一、引言

目前主流的关系数据库通常都允许多个用户同时使用和共享,所以也都具有并发控制的机制,也就是控制数据库,防止多用户并发使用数据库时造成数据错误和程序运行错误,以保证数据的完整性。

二、事务与并发控制的概念

当多用户并发存取数据时,就会产生多个事务同时存取同一数据的情况,从而引起严重的数据错误和程序运行错误。那么我们来看,什么是事务及并发控制呢?

事务就是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的很小的工作单位。例如,在SQL语言中,定义事务的语句有三条:

BEGINTRANSACTION;

COMMIT;

ROLLBACK;

其中的BEGINTRANSACTION 是事务开始的标记,而以COMMIT或者ROLLBACK结束,COMMIT 用于提交事务的所有操作,ROLLBACK 则在事务运行过程中一旦发生了某种故障而使事务无法继续执行的时候,系统就将事务中对数据库的所有刚刚完成的操作全部撤消,滚动回到事务开始时的状态。为了充分利用系统资源,使数据库的共享资源得以有效利用,必须可以使多个事务并行的执行,而数据库对并行执行的事务进行的控制就是并发控制。

三、事务进行并发操作可能引起的数据不一致问题

由于种种原因,都可能引起数据库的数据遭到破坏,比如多个事务在并行运行的时候,不同的事务的操作产生了交叉执行,或者,事务在运行过程中被强行停或者中断。因此,事务在进行并发操作的时候很可能引起数据的不一致,下面我们看一个具体的例子。例如飞机票的联网销售系统,如果有以下的操作序列:

1.甲售票处(设置为T1 事务)读出某班次的机票剩余数A ,

设A=20

2.乙售票处(设置为T2 事务)读出同班次的机票剩余数A ,也是20

3.甲售票处(T1 事务)卖出一张机票,修改剩余数减一(A← A-1),把A=19 写回数据库中

4.乙售票处(T2 事务)也卖出一张机票,修改剩余数减一(A← A-1),把A=19 写回数据库中从这些操作中,我们看到,乙售票处的修改数据覆盖了甲售票处修改的数据,实际发生了两张机票的销售,而数据库中却错误的存入19,少了一张。参看图1 的情况。这种情况是并发操作引起数据不一致的第一种情况,叫做丢失修改(Lost Update),第二种是不可重复读(Non-Repeatable Read),

第三种是读“脏”数据(DirtyRead)。

\

下边看第二种情况,不可重复读是指事务T1 读数据以后,T2执行更新操作,就使T1无法再现原先读取的数据,得到与上一次不同的结果,例如图2 。

\

读“脏”数据是指T1 修改某数据并将其写回数据库,T2 读取同一数据后,T1 由于某种原因被撤消,T1 执行回滚,恢复到原始的数据,T2 就读取到了过程中的一个作废的数据,这个数据就是一种垃圾数据,称之为“脏”数据,也是不正确的。参看图3 。

\

从以上例子我们看到,数据不一致性的主要原因就是并发操作没有对事务进行一定的隔离,所以,正确的调度应该使一个用户的事务不受到其他事务的干扰,从而避免数据的不一致性。

四、在并发控制中采用封锁协议解决数据的不一致性并发控制的主要方法是封锁(Locking)。就是要用正确的方式调度并发操作,使一个用户的事务在执行过程中不受其他事务的干扰,从而避免造成数据的不一致性。封锁是使事务对它要操作的数据有一定的控制能力。封锁通常具有3 个环节:第一个环节是申请加锁,即事务在操作前要对它将使用的数据提出加锁申请; 第二个环节是获得锁,即当条件成熟时,系统允许事务对数据进行加锁,从而事务获得数据的控制

权;第三个环节是释放锁,即完成操作后事务放弃数据的控制权。

基本的封锁类型有以下两种:

1.排它锁(Exclusive Locks,简称X锁)

排它锁也称为独占锁或写锁。一旦事务T 对数据对象A 加上排它锁(X 锁),则只允许T 读取和修改A ,其他任何事务既不能读取和修改A ,也不能再对A 加任何类型的锁,直到T释放A 上的锁为止。

2.共享锁(Share Locks,简称S 锁)

共享锁又称读锁。如果事务T 对数据对象A 加上共享锁(S锁),其他事务只能再对A加S 锁,不能加X 锁,直到事务T 释放A 上的S 锁为止。在对数据进行加锁时,另外需要约定并执行一些规则和协议,其中包括何时申请锁,保持锁的时间以及何时释放等,这些规则就称为封锁协议(Locking Protocol),其总共分为以下三级:

(1)一级封锁协议。一级封锁协议是事务T 在修改数据之前必须先对其加X 锁,直到事务结束才释放。

(2)二级封锁协议。二级封锁协议是事务T 对要修改数据必须先加X 锁,直到事务结束才释放X 锁;对要读取的数据必须先加S 锁,读完后即可释放S 锁。

(3)三级封锁协议。三级封锁协议是事务T 在读取数据之前必须先对其加S 锁,在要修改数据之前必须先对其加X 锁,直到事务结束后才释放所有锁。

执行了封锁协议之后,就可以克服数据库操作中的数据不一致所引起的问题。

\

从图4 的情况我们看到事务T1 在执行过程中独自占用并加X锁,直到处理完之后再释放锁,T2 虽然也需要使用,但是在封锁协议的约束之下,T2 所要求的X 锁就被拒绝,因此必须处于等待状态,直到T1 释放之后,T2 才获得使用的权利,这样就不会发生使用冲突,避免了数据的丢失。这里我们看到,此处实际上是执行了一级封锁协议。

\

通过图5 ,能够清楚的看到,由于施行了封锁协议,使事务T1 使用了共享锁占用A,B 两块数据,这样T2 需要加上的X 锁就无法实现,(如果是S 锁,虽然可以加上,但也不能够随便修改数据,只是读取一下数据。)当T1 释放锁之后,T2 就可以得到并使用锁了,这样读取的数据B 仍然还是100,不影响A+B 的结果,这就是可重复读取。因此我们看到,其实这里用的就是三级封锁协议参看图6,事务T1 在对数据C 修改之前,先加上了X锁,修改后写回数据库,这时T2 请求在C上添加S 锁,因为T1 加了X 锁,T2只好等待,当T1 因为某种原因撤销了修改的数据后,C 就恢复了原来的数据100,等T1 释放X 锁后T2 获得C 上的S锁, 读到的还是C = 1 0 0,因此避免了读出“脏”数据。这里使用的其实就是二级封锁协议。。

\

通过以上内容,数据库由于采用一定的封锁协议避免了数据的不一致性问题,这使得数据库的并发控制有效而且有益,从而使得多项事务可以并行的操作数据库的共享资源了。这就是数据库合理的进行调度,避免了冲突,避免了数据的不一致。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了通过mysql命令查看mysql的安装路径的方法,提供了相应的sql语句,并希望对读者有参考价值。 ... [详细]
author-avatar
我负天下人0
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有