• 14
  • 14
分享
  • 数据库行锁争用问题分析——软件测试圈
  • 曼倩诙谐 2021-03-08 09:53:02 字数 4425 阅读 1746 收藏 14

  某系统的批量程序和联机程序同时发起交易、操作数据库同一张表时,出现了交易间相互影响导致交易失败。

  分析原因为事务A对数据库表操作全表删除时,数据库机制会自动触发产生排他行锁,当行锁数达到“升级到表锁前允许的最大行锁数”时,会自动升级为表锁。但因事务B已对数据库表产生了排他行锁,则事务A自动升级表锁失败。直至事务A消耗掉了数据库“最大锁资源数”,系统会报错“锁资源耗尽”、交易失败。

  因此,设计程序时,如涉及多事务操作数据库同一张表,需考虑事务间的关联影响,是否会引发事务冲突。

  下文将详细描述和分析该问题。

  1 数据库锁

  首先介绍一下数据库锁的相关概念。

  1.1 行锁、页锁和表锁

  行锁是访问数据库的时候,锁定一行或者多行记录。页锁是访问数据库的时候,锁定查询结果所在页。表锁是访问数据库的时候,锁定整张表,在表被锁定期间,其他事务不能对该表进行操作,必须等当前表的锁被释放后才能进行操作。行锁的优点是锁定粒度小,发生锁冲突的概率低,并发度高;缺点是开销大,加锁慢,会出现死锁。表锁的优点是开销小,加锁快,不会出现死锁;缺点是锁定力度大,发生锁冲突概率高,并发度最低。页锁的开销和加锁速度介于表锁和行锁之间,会出现死锁,锁定粒度介于表锁和行锁之间,并发度一般。

  共享锁 (S锁) 也称为读锁,用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。其封锁粒度包括行级、页级和表级。

  排他锁(X锁)也称为独占锁、写锁,用于数据修改操作,例如INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。我们在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突)。其封锁粒度也包括行级、页级和表级。

  1.2 意向锁

  意向锁的产生原因:解决表锁与之前可能存在的行锁冲突,避免为了判断表是否存在行锁而去扫描全表的系统消耗。

  作用:一种快速判断表锁与之前可能存在的行锁冲突的机制。(数据库会帮我们自动加)

  行锁在加锁前要先加意向锁。意向锁是一种表锁。

  事例分析:事务 A 锁住了表中的一行,让这一行只能读,不能写。之后,事务 B 申请整个表的写锁。如果事务 B 申请成功,那么理论上它就能修改表中的任意一行,这与 A 持有的行锁是冲突的。

  数据库需要避免这种冲突,就是说要让 B 的申请被阻塞,直到 A 释放了行锁。数据库要怎么判断这个冲突呢?

  step1:判断表是否已被其他事务用表锁锁表。

  step2:判断表中的每一行是否已被行锁锁住。

  注意step2,因为需要遍历整个表,从而导致判断方法的效率不高。为了更有针对性的进行判断,便产生了意向锁的概念。

  在意向锁存在的情况下,事务 A 必须先申请表的意向共享锁,成功后再申请一行的行锁。

  在意向锁存在的情况下,上面的判断可以改成:

  step1:不变。

  step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务 B 申请表的写锁会被阻塞。

  意向锁分为意向共享锁(共享锁定)和意向排他锁(排他锁定),且意向锁是表级别锁。

  例子-执行update:

  1> update PD set workdate = '20200603' where PDid ='23'

  2> go

  (1 row affected)

  1> sp_lock3x

  2> go

  返回结果:

1-1.png

  解释:

  Sh--共享锁(S锁)

  Ex--独占锁/排他锁(X锁,Exclusive Lock)

  Update--更新锁(U锁)

  Table或者intent--锁发生在表

  Page--锁发生在表

  Row--锁发生在行

  blk--这个进程正在阻塞另一个需要获取一个锁的进程,一旦这个进程处理完成,其他进程就可以继续处理了

  demand--这个进程正在试图获取一个锁

  例子-未执行update前:

  1> sp_lock3x

  2> go

  返回结果:

1-2.png

  1.3触发机制

  所有关系型数据库都存在行锁,不同数据库触发机制大致相同,只是手工生成行锁方法略有不同。

  MySQL数据库,不同的存储引擎支持不同的锁机制。MyISAM和MEMORY存储引擎采用的是表级锁;BDB存储引擎采用的是页面锁,但也支持表级锁;InnoDB存储引擎既支持行级锁,也支持表级锁,但默认情况下是采用行级锁。以InnoDB存储引擎举例,意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他行锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享行锁或排他行锁。

  共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。

  排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。

  Oracle数据库,行锁在操作DML(UPDATE、DELETE、INSERT)语句时,Oracle会自动加上行锁。在select * from table for update 【of column】【nowait|wait 3】时,oracle也会自动加锁;但在缺省情况下,单纯地读数据(SELECT)并不加锁,Oracle通过回滚段来保证用户不读“脏”数据。

  参数【nowait|wait 3】,一般在for update 时加nowait,这样就不用等待其他事务执行了,一判断有事务,立马抛出错误。

  参数【of column】,一般的of column都是在多表操作的时候,能锁定其中一个表的某些列,如果还是同一张表的话,并不能锁定某些列。

  Sybase数据库,支持行锁、页锁、表锁。意向锁是自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,会自动给涉及数据集加排他行锁(X);对于普通SELECT语句,不会加行锁。

  2 现象描述

  测试环境上某系统运行批量程序时,报错“锁资源耗尽”。

  经分析,产生原因为测试环境上跑批量时,准备清理LSB表中小于当日的数据,Sybase数据库执行SQL语句“EXEC SQL delete FROM LSB WHERE appdate < :Workdate”,数据量约300万条。而当时正在同时发起实时联机交易,对LSB表生成了意向排他锁和排他行锁未释放,该批量执行delete语句需使用300w个行锁(锁资源最大可使用100w个)且因排他行锁存在无法升级使用表锁,导致批量失败,报错“锁资源耗尽”。

  3 原因分析

  A:对LSB表执行update语句

  B:EXEC SQL delete FROM LSB WHERE appdate < :Workdate

  C:EXEC SQL delete top 4000 FROM LSB

  WHERE appdate < :Workdate

  当执行A操作时,数据库对LSB先产生一个意向排他锁,再产生一个排他行锁。当执行B操作时,删除300w条记录,会产生300w条行锁(行锁大于5k条,曾试图升级表锁),但发现LSB表存在意向排他锁,升级行锁失败,只能使用行锁(行锁资源上限为100w条),B交易报错“锁资源耗尽”。

  另外,测试环境上很少出现“锁资源耗尽”现象,分析原因为意向排他锁是瞬时的、转瞬即逝的,很快就消失,所以非常偶发。只有当A的排他意向锁与B的行锁升级为表锁的时刻碰撞上,才会出现上述现象。

  当将B方案优化为C方案时,控制每次产生行锁的条数小于5k,避免升级为表锁,这样利于A(前提是A的tps小于500)和C同时操作且互不影响。

  4 应对措施

  通过将“流水表批量清理交易”优化为先查找top 1的记录,若记录存在则delete前top 4000条记录,循环执行,直至小于当日的数据全部清理。

  5 测试验证

  测试环境的数据库配置参数和铺底数据如下:

1-3.png

  查看数据库锁资源大小

  1> sp_configure "number of locks"

  2> go

  返回结果:

1-4.png

  测试发现,当测试环境模拟实时联机交易持续运行(TPS为4-46笔/秒)时,新、旧程序均执行成功,CPU使用率无差异,无死锁,新程序的执行时间长于旧程序。但是,交易背景未复现该问题。

  当测试环境模拟对LSB表执行update语句、但事务不提交、持续产生排他意向锁和排他行锁的场景后,旧程序运行不到1s,报错“锁资源耗尽”,交易失败,复现该问题。而新程序运行30s,交易成功,锁资源使用数约为4000-4010个。

  执行SQL语句,但不提交:

  1> begin transaction

  2> go

  1> update PD set workdate = '20200602' where PDid ='23'

  2> go

  (1 row affected)

  1>

  执行SQL语句,且提交:

  1> begin transaction

  2> go

  1> update PD set workdate = '20200602' where PDid ='23'

  2> go

  (1 row affected)

  1> commit

  2> go

  1>

  因此,当实时联机交易对LSB表生成了排他意向锁未释放时,旧程序复现该问题、交易成功率为0%,新程序执行成功、交易成功率为100%,新程序的清理机制优于旧程序的清理机制。测试通过。

1-5.png

1-6.png

  WHERE appdate < :Workdate

  1、交易背景“update语句,但事务不提交,持续产生排他意向锁和排他行锁”下,旧程序报错。

  2、交易背景“update语句,但事务不提交,持续产生排他意向锁和排他行锁”下,新程序交易成功。

  6 问题启示

  程序设计时,需考虑多支交易对数据库(包括MySQL、Oracle、Sybase等)同一张表产生排他行锁是否相互影响的场景。特别是出现删除大量数据等操作,如其他交易也对该表产生排他行锁,建议控制事务大小,如每次的删除记录条数,避免行锁升级表锁失败从而导致交易报错。

  还需考虑,两个交易均需更新数据库同一张表的记录时,程序均需先锁表,再进行下一步操作,如顺序颠倒易出现交易失败。


作者:童薇   

来源:51Testing软件测试网原创

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          •   什么是自动化测试  在软件测试领域,有两种测试技术:手动测试和自动测试。两种方法都旨在执行测试用例,然后将实际结果与预期结果进行比较。  概括来讲手动测试是一种人工执行操作的测试技术,可确保软件代码完成应有的功能。那么什么是自动化测试呢?相反,这是一种自动运行测试技术,管理测试数据以及利用结果来提高软件质量的实践。  对于一名测试工程师而言,连续的开发周期需要重复执行相同的测试用例组成的测试套件。如果每一次都手动执行此过程,可能会非常重复且耗时,很容易让人产生疲倦感。但是通过利用测试自动化工具,可以更轻松地编写测试套件,按需重手动执行,减轻人为干预并提高测试ROI(自动化测试的投资回报率)...
            1 1 2290
            分享
          • 软件开发人员通常不会考虑的一种测试形式-人工测试。大多数人都以为,因为程序是为了供机器执行而编写的,那么也该由机器来对程序进行测试。这种想法是有问题的。人工测试方法在暴露错误方面是很有成效的。实际上,大多数的软件项目都应使用到一下的人工测试方法:利用错误列表进行代码检查小组代码走查桌面检查同行评审代码检查:所谓代码检查是以组为单位阅读代码,它是一系列规程和错误检查技术的集合。一个代码检查小组通常由四人组成:协调人:以为称职的程序员该程序的编码人员该程序的设计人员测试专家用于代码检查的错误列表:1、数据引用错误是否有引用的变量未赋值或未初始化下标的值是否在范围之内是否存在非整数下标是否存在虚调用...
            0 0 2507
            分享
          • 一、测试流程1.软件开发流程:需求分析—>概要设计—>详细设计—>编码开发—>测试—>维护2.测试流程为:单元测试/集成测试—>系统测试/自动化测试—>性能测试—>验收测试3.目标:3.1 制定完整且具体的测试路线和流程,为快速、高效和高质量的软件测试提供基础流程框架。3.2 最终目标是实现软件测试规范化、标准化、自动化。4.测试流程说明:5.测试需求分析测试需求是整个测试过程的基础;确定测试对象以及测试工作的范围和作用。用来确定整个测试工作(如安排时间表、测试设计等)并作为测试覆盖的基础。而且被确定的测试需求项必须是可核实的。即,它们必须有一个...
            11 11 1594
            分享
          •   本文深入探讨了在实际软件测试项目中遇到的挑战,并反思了如何通过一系列策略来提升个人和测试团队的工作效率。文章首先分析了何为效率及效率提升对人和企业有什么益处,接下来介绍当前测试行业环境下,如经济压力、年龄增长和体力下降等因素对测试人员的影响,接下来介绍了在实际项目中遇到的问题,然后通过遇到的问题提出了通过文档化知识、分享经验、明确职责等方法来优化工作流程。包括内容如下:  1、提升效率的本质  何为效率,效率分为生产效率,时间效率,资源利用效率,管理效率等,对于我们底层工作的人来说和我们密切相关的主要为时间效率,时间效率是指,我们常常用“做事效率”来形容某人完成任务所需的时间与其能力的匹配...
            0 0 662
            分享
          • 当前,软件产品整体的开发测试节奏正在不断的加快。如何快速响应市场需求,在保障产品质量的同时提高软件研发效率?是摆在开发、测试人员面前的一大难题。对软件测试人员来说,提高自动化测试的能力和效率,是应对这一难题的途径之一。通过调研学习,我们对主流自动化测试技术进行简要对比。按照人工介入程度区分,自动化测试技术主要分为两大类别:流量复制技术和场景测试技术。1、流量复制技术流量复制技术的原理是:复制在线Server的请求数据包(比如HTTP流量、TCP流量、链路层流量等),修改流量包头部信息,发送给测试服务器,达到欺骗测试服务器程序的目的,从而为欺骗测试服务器上面的上层应用打下基础。技术发展现状:目前...
            0 1 1757
            分享
      • 51testing软件测试圈微信