注册
达梦数据库锁机制
专栏/技术分享/ 文章详情 /

达梦数据库锁机制

DM_336625 2026/06/04 137 0 0
摘要

一、锁机制概述

1.1 为什么需要锁

在多用户并发访问数据库的环境中,多个事务可能同时操作相同的数据。如果不加控制,就可能出现脏读、不可重复读、幻读等数据一致性问题。锁机制正是为了解决这一问题而设计的核心手段:当事务要对数据库对象进行操作前,需要先获取相应的锁,从而对该对象获得一定的控制权,在其他事务释放锁之前,不允许其他事务进行可能引发冲突的操作。

1.2 达梦锁的基本特征1

锁的周期:锁通常在事务开始时获取,在事务提交或回滚时释放。这意味着长时间未提交的事务会长时间持有锁资源,成为阻塞其他事务的潜在源头。

锁的管理方式:达梦数据库采用了MVCC与封锁机制相结合的策略。MVCC通过维护数据的多个版本,使得读操作可以访问旧版本数据而不被写操作阻塞;封锁机制则用于协调写操作之间的冲突。

二、锁模式详解

达梦数据库使用四种不同的锁模式,分别服务于不同的并发控制需求:

2.1 共享锁(Share Lock,S锁)

用途:读操作,防止其他事务修改正在访问的对象。

特点:允许多个事务同时并发读取相同资源,但不允许任何事务修改该资源。这种“共享读、互斥写”的特性使其适用于只读场景。

典型场景:执行SELECT查询时,如果隔离级别较高或使用了特定提示,可能加共享锁。

2.2 排他锁(Exclusive Lock,X锁)

用途:写操作,以独占方式访问对象,防止其他事务读或写。

特点:不允许任何其他事务访问被封锁对象,是最严格的锁模式。通常在修改数据(INSERT、UPDATE、DELETE)或修改对象定义(DDL)时使用。

典型场景:执行UPDATE语句修改某行数据时,会在该行上加排他锁。

2.3 意向共享锁(Intent Share Lock,IS锁)

用途:在只读访问对象时,在更高粒度(如表级)上声明“此事务将在更细粒度上加共享锁”的意图。

特点:多个事务可以同时在同一个对象上加意向锁,意向锁之间相互兼容。意向锁的主要作用是提高锁检测效率——当事务要在表上加排他锁时,只需检查是否已存在表级意向锁,而无需逐行检查行锁。

2.4 意向排他锁(Intent Exclusive Lock,IX锁)

用途:在修改对象数据时,在表级声明“此事务将在更细粒度上加排他锁”的意图。

特点:与意向共享锁类似,多个事务可同时在同一对象上加意向排他锁。

2.5 锁模式的兼容性

锁模式之间的兼容关系决定了并发事务的阻塞行为。下表展示了不同锁模式的兼容性(Y表示相容,N表示不相容):

请求锁模式 →<br />已持有锁模式 ↓ 共享锁(S) 排他锁(X) 意向共享锁(IS) 意向排他锁(IX)
共享锁(S) Y N Y N
排他锁(X) N N N N
意向共享锁(IS) Y N Y Y
意向排他锁(IX) N N Y Y

从表中可以看出,排他锁(X)与任何其他锁模式都不相容——一旦某资源被加上了排他锁,其他事务的任何访问都将被阻塞。而意向锁之间以及与共享锁之间存在一定的相容性,这使得意向锁能够有效解决传统锁机制中锁粒度粗、冲突多的问题。

2.6 锁的手动控制

达梦提供了LOCK TABLE语句,允许用户显式地为表加锁:

sql

-- 给表加共享锁
LOCK TABLE test1 IN SHARE MODE;

-- 给表加排他锁
LOCK TABLE test1 IN EXCLUSIVE MODE;

-- 给表加意向共享锁
LOCK TABLE test1 IN INTENT SHARE MODE;

-- 给表加意向排他锁
LOCK TABLE test1 IN INTENT EXCLUSIVE MODE;

此外,达梦还提供了DBMS_LOCK程序包,用于实现更精细的应用级锁控制,适用于秒杀等需要自定义锁逻辑的场景。

三、锁的粒度

达梦数据库支持多种锁粒度,从粗到细包括:

  • 数据库级锁:控制对整个数据库的访问。
  • 表级锁:通过上述四种锁模式对整张表进行封锁。
  • 行级锁:仅锁定具体的数据行,并发度最高。达梦的行级锁通过TID(事务ID)机制实现,而非传统的行锁结构。

细粒度锁(如行级锁)可以提高并发度,但管理复杂度也更高;粗粒度锁(如表级锁)管理简单,但可能导致更多的锁冲突。达梦通过意向锁机制在两者之间取得了良好的平衡——事务在加行锁的同时,会在表级加意向锁,从而让其他事务在检查表级锁时能够快速判断是否存在行锁冲突,而无需逐行扫描。

四、锁等待、阻塞与死锁

在实际运行中,锁机制可能引发三种需要关注的现象:锁等待、阻塞和死锁。理解三者之间的区别与联系,是进行故障诊断的基础。

4.1 锁等待(Lock Wait)

定义:锁等待是指一个事务在请求某个锁时,由于该锁已被其他事务持有,导致请求事务必须等待。

锁等待本身是正常的并发控制行为——只要等待时间在可接受范围内,且最终能够获取锁,就不会对系统造成实质影响。只有当等待时间过长或形成循环等待时,才会升级为需要干预的问题。

4.2 阻塞(Blocking)

定义:阻塞是指一个事务占用了某资源的锁而未释放,导致其他需要该资源的事务被迫长时间等待。

典型场景

  1. INSERT阻塞:多个事务同时向有主键或UNIQUE约束的表插入相同数据时,后执行的事务会被阻塞,直到先执行的事务提交或回滚。
  2. UPDATE/DELETE阻塞:当多个事务尝试修改已被其他事务修改(未提交)的相同记录时,后执行的事务会发生阻塞。
  3. DDL阻塞:对表执行DDL操作(如加列、删列)时,若表上存在未提交的DML事务,DDL操作会被阻塞,等待DDL_WAIT_TIME参数指定的时间(默认10秒)后报错“锁超时”。

阻塞的解决策略

  • 持有锁的事务尽快提交或回滚,释放锁资源
  • 优化应用逻辑,尽量形成短事务
  • 必要时由DBA终止阻塞源头的事务

4.3 死锁(Deadlock)

定义:死锁是指两个或多个事务形成等待环,每个事务都在等待其他事务释放锁,结果所有事务都无法继续执行。

死锁的典型场景

假设有两个表TEST01和TEST02,以及两个并发事务:

步骤 事务A(会话1) 事务B(会话2)
1 INSERT INTO test01 VALUES(3,'Wang Wu');<br />(test01上有排他锁) INSERT INTO test02 VALUES(3,'Wang Wu');<br />(test02上有排他锁)
2 INSERT INTO test02 VALUES(3,'Wang Wu');<br />(尝试获取test02锁,被事务B阻塞) INSERT INTO test01 VALUES(3,'Wang Wu');<br />(尝试获取test01锁,被事务A阻塞)

此时,事务A等待事务B释放test02的锁,事务B等待事务A释放test01的锁,形成循环等待——死锁发生。

死锁与阻塞的区别

  • 阻塞是单向等待:事务B等待事务A释放资源,事务A可以继续执行。
  • 死锁是循环等待:多个事务相互等待,没有哪个事务可以继续。

达梦的死锁处理策略

  1. 自动检测:达梦采用基于图论的死锁检测算法,通过构建事务等待图来识别死锁。
  2. 自动解除:检测到死锁后,系统自动选择一个事务进行回滚(通常是代价较小的那个),使其释放锁资源,从而解除死锁。
  3. 用户无需干预:死锁发生后,被选中牺牲的事务会自动回滚并报错,其他事务继续执行。用户只需要在应用程序中捕获异常并进行重试即可。

五、锁相关动态视图

达梦数据库提供了一系列动态性能视图,用于监控和分析锁的状态。掌握这些视图是进行锁问题诊断的前提:

视图名称 主要用途 关键字段
V$SESSIONS 显示所有会话的详细信息 SESS_ID, SQL_TEXT, TRX_ID, USER_NAME, CLNT_IP, STATE
V$TRX 显示所有活动事务的信息 ID(事务ID), SESS_ID
V$TRXWAIT 显示事务等待关系 ID(等待中的事务), WAIT_FOR_ID(被等待的事务)
V$LOCK 显示当前系统中锁的状态 TRX_ID, LTYPE, LMODE, BLOCKED, TABLE_ID, TID
V$DEADLOCK_HISTORY 显示历史死锁信息 死锁发生的时间、涉及的事务等
V$TRX_VIEW 显示活动事务视图信息 用于MVCC可见性判断

重要说明

  • 使用V$DEADLOCK_HISTORY需要确保dm.ini中的参数ENABLE_MONITOR=1,否则历史死锁信息不会被记录。
  • V$LOCK视图中的LTYPE字段表示锁类型,可能为OBJECT(对象锁)或TID(TID行锁)。
  • V$LOCK中的BLOCKED字段标识该锁是否处于等待状态(1表示等待中,0表示已获取)。

达梦社区技术 https://eco.dameng.com

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服