前几天在生产环境踩了个坑:给一张大表加主键,结果查询语句直接hang住了。排查过程简直让人怀疑人生,最后发现是锁模式冲突导致的。
复现现场
Session1(运维操作):
-- 先建索引
create index pk_test on test (ID);
-- 再加主键
alter table test add constraint pk_test primary key (ID) using index;
Session2(业务查询):
select * from test;
结果:Session2直接卡死。
深入分析:锁模式冲突
通过dump共享池(alter system dump shared_pool
),发现表的handler上存在:
- X模式的LOCK(排他锁)
- X模式的PIN(排他PIN)
此时业务查询需要获取S模式的LOCK(共享锁),与DDL操作的X模式LOCK发生互斥,导致查询阻塞。
Oracle中DDL操作(如ADD CONSTRAINT)需要获取表级排他锁(X),而查询需要共享锁(S),两者不兼容。
最佳实践:如何优雅地加主键?
错误方式(会锁表):
直接执行:
alter table test add constraint pk_test primary key (ID);
Oracle会自动创建唯一索引,但整个过程会长时间持有X锁,阻塞查询。
正确姿势(分三步走):
步骤1:提前建唯一索引(非阻塞)
create unique index pk_test on test(ID) online;
- 使用
online
关键字:不锁表(允许DML操作) - 确保字段有非空约束(主键要求):
alter table test modify (ID not null);
步骤2:验证索引和数据
-- 检查索引状态
select status from user_indexes where index_name='PK_TEST';
-- 检查数据唯一性
select ID, count(*) from test group by ID having count(*)>1;
步骤3:关联主键约束(秒级完成)
alter table test add constraint pk_test primary key (ID) using index pk_test;
此时只是元数据操作,无需重新扫描表,几乎瞬间完成。
避坑指南:生产环境操作建议
操作阶段 | 命令 | 锁模式 | 影响 |
---|---|---|---|
建唯一索引 | create unique index online | 无 | 不阻塞查询/DML |
加非空约束 | alter table modify not null | 行级锁 | 快速完成 |
加主键约束 | alter table add constraint | 元数据锁 | 毫秒级完成 |
关键检查项:
- 字段非空:主键列必须有非空约束(否则会报错
ORA-02431
)。 - 数据唯一性:提前检查是否有重复值(否则加主键会失败)。
- 索引状态:确保索引状态为
VALID
(不可用索引会导致主键失效)。
“大表加主键,先online建唯一索引,再秒级绑定约束,千万别让DDL直接上桌!”