适用产品:达梦数据库(DM8 / DM9)| 错误码:
-6407 锁超时
场景:表中存在位图索引,SELECT 查询(使用位图索引)与 MERGE INTO 并发时,MERGE INTO 报锁超时
表上存在位图索引(BITMAP INDEX),业务中并发执行两类 SQL:
MERGE INTO 该表 ...(目标表是带位图索引的表)当两类 SQL 并发执行时,MERGE INTO 报 [-6407] 锁超时,查询也可能被连带阻塞。去掉位图索引后,问题消失。
-- 现象示例(会话 2 报错)
MERGE INTO T_ORDER t
USING (SELECT 4 AS ID, '0' AS STATUS, 400 AS AMOUNT FROM DUAL) s
ON (t.ID = s.ID)
WHEN MATCHED THEN UPDATE SET t.AMOUNT = s.AMOUNT
WHEN NOT MATCHED THEN INSERT (ID, STATUS, AMOUNT) VALUES (s.ID, s.STATUS, s.AMOUNT);
-- 结果:[-6407] 锁超时
MERGE INTO 本质是 UPDATE(匹配)+ INSERT(不匹配)的组合操作,落在位图索引列上时:
| 时间线 | 会话 1(查询) | 会话 2(MERGE INTO) |
|---|---|---|
| T1 | SELECT 使用位图索引扫描,对位图段加 S 锁(共享),长时间未释放 | — |
| T2 | — | 需要修改位图段 → 申请 X 锁(排他) |
| T3 | — | S 与 X 不相容 → 等待位图段锁释放 |
| T4 | 查询事务迟迟不结束(大查询 / 未提交 / 报表任务) | 等待超过 LOCK_WAIT_TIMEOUT(默认 10 秒)→ [-6407] 锁超时 |
反向同样成立:MERGE INTO 先执行且未提交时,会持有表的独占锁 / 位图段 X 锁,此时并发 SELECT 也会被阻塞。位图索引带来的锁冲突是双向的。
-- 会话 0:建表 + 建位图索引 + 准备数据
DROP TABLE IF EXISTS T_ORDER;
CREATE TABLE T_ORDER (ID INT PRIMARY KEY, STATUS VARCHAR(10), AMOUNT NUMBER(10,2));
-- 关键:在 MERGE 涉及的列上建位图索引(低基数列,看着"很适合"位图)
CREATE BITMAP INDEX IDX_BM_STATUS ON T_ORDER(STATUS);
INSERT INTO T_ORDER VALUES (1,'0',100),(2,'0',200),(3,'1',300);
COMMIT;
-- 模拟"长时间占用":大事务内查询不提交,或报表类长查询
SELECT /*+ INDEX(T_ORDER IDX_BM_STATUS) */ COUNT(*) FROM T_ORDER;
DBMS_LOCK.SLEEP(30); -- 持有锁 30 秒,模拟慢查询/未提交事务
COMMIT;
END;
-- 会话 2:并发执行 MERGE INTO(10 秒内必然报错)
MERGE INTO T_ORDER t
USING (SELECT 4 AS ID, '0' AS STATUS, 400 AS AMOUNT FROM DUAL) s
ON (t.ID = s.ID)
WHEN MATCHED THEN UPDATE SET t.AMOUNT = s.AMOUNT
WHEN NOT MATCHED THEN INSERT (ID, STATUS, AMOUNT) VALUES (s.ID, s.STATUS, s.AMOUNT);
-- 结果:
-- 等待约 10 秒(LOCK_WAIT_TIMEOUT 默认值)后报错:
-- [-6407] 锁超时
-- 会话 0:删除位图索引,换成普通 B-tree 索引
DROP INDEX IDX_BM_STATUS;
CREATE INDEX IDX_BT_STATUS ON T_ORDER(STATUS); -- 普通索引,行级锁粒度
-- 重放 3.1 / 3.2 两个会话:
-- MERGE INTO 正常执行(B-tree 索引只锁受影响的具体索引条目,不锁段/表)
-- 问题消失 ✅
出现 -6407 时,用以下 SQL 确认阻塞链是否指向位图索引表:
-- ① 看锁等待关系:谁在等谁
SELECT T.ID AS 等待事务, T.WAIT_FOR_ID AS 阻塞事务,
L1.LMODE AS 期望锁, L2.LMODE AS 持锁方模式
FROM V$TRXWAIT T
LEFT JOIN V$LOCK L1 ON T.ID = L1.TRX_ID
LEFT JOIN V$LOCK L2 ON T.WAIT_FOR_ID = L2.TRX_ID;
-- ② 定位锁对象是哪个表/索引,以及持锁会话和 SQL(官方推荐组合)
SELECT A.*, B.NAME, C.SESS_ID
FROM V$LOCK A
LEFT JOIN SYSOBJECTS B ON B.ID = A.TABLE_ID
LEFT JOIN V$SESSIONS C ON A.TRX_ID = C.TRX_ID;
-- ③ 确认表上是否存在位图索引(嫌疑对象)
SELECT OWNER, TABLE_NAME, INDEX_NAME, INDEX_TYPE
FROM DBA_INDEXES
WHERE INDEX_TYPE = 'BITMAP'
AND TABLE_NAME IN ('T_ORDER'); -- 换成实际表名
-- ④ 查看锁超时相关参数
SELECT * FROM V$DM_INI WHERE PARA_NAME IN
('LOCK_WAIT_TIMEOUT','DDL_WAIT_TIME','BLDR_WAIT_TIME');
| 参数 | 说明 | 默认值 |
|---|---|---|
| LOCK_WAIT_TIMEOUT | 普通事务锁等待超时(秒) | 10 |
| DDL_WAIT_TIME | DDL 锁等待超时(秒) | 60 |
| BLDR_WAIT_TIME | 批量装载(dmfldr)锁等待超时(秒) | 600 |
| 方案 | 操作 | 适用场景 | 优先级 |
|---|---|---|---|
| 删除位图索引,改用 B-tree | DROP INDEX IDX_BM_STATUS; CREATE INDEX IDX_BT_STATUS ON T_ORDER(STATUS); |
有 MERGE / DML 并发的业务表 | ⭐ 首选 |
| 读写分离 | 生产库不建位图索引;只读库 / 分析库(数据仓库副本)上建 | 统计查询需求强,但写入不能停 | 推荐 |
| 汇总表 / 物化视图 | 统计口径用独立汇总表,业务表不建位图 | 报表 / COUNT 类统计 | 可选 |
| 缩短查询事务 | 大查询 / 报表查询单独开只读事务,及时提交;避免长事务持锁 | 查询本身可优化 | 辅助 |
| 调大 LOCK_WAIT_TIMEOUT | ALTER SYSTEM SET 'LOCK_WAIT_TIMEOUT'=60;(动态) |
仅临时缓解,不治本 | ⚠️ 慎用 |
| 关闭位图索引创建开关(新版本) | 检查 ENABLE_CREATE_BM_INDEX_FLAG 参数 |
从源头防止误建位图索引 | 可选 |
-- 落地示例(首选方案)
DROP INDEX IDX_BM_STATUS; -- 删位图索引
CREATE INDEX IDX_BT_STATUS ON T_ORDER(STATUS); -- 建普通 B-tree 索引
-- 若确需位图能力:仅在只读库/分析库上建
-- CREATE BITMAP INDEX IDX_BM_STATUS_RO ON T_ORDER(STATUS); -- 只读节点执行
位图索引使用红线:
MERGE INTO 前自查:目标表的 WHERE / 更新列上是否有位图索引?有则先评估并发风险。
锁监控常态化:定期查 V$LOCK + V$TRXWAIT,提前发现"位图段锁"类阻塞。
新版本可控制建索引开关:如参数 ENABLE_CREATE_BM_INDEX_FLAG(部分新版本支持),防止误建。
位图连接索引(BITMAP JOIN INDEX)同理:同一时间只允许一个事务更新关联表,OLTP + MERGE 场景一律禁用。
表上存在位图索引 + SELECT 查询 + MERGE INTO = 锁超时(-6407)。位图索引按"键值位图段"加锁(读 S / 写 X 互斥),MERGE INTO 一写一大片,和 SELECT 并发必然等锁。删掉位图索引换 B-tree,问题立解——OLTP 写入表(尤其有 MERGE 的表)永远不要用位图索引。
文章
阅读量
获赞
