不同数据库客户端连接方式下Procedure Locking差异的原因咨询
问题背景与现象
我创建了如下测试存储过程:
CREATE OR ALTER PROCEDURE tmp_fab RETURNS (dummy INTEGER) AS BEGIN dummy = 1; suspend; END
随后客户端A执行以下SQL调用该存储过程:
SELECT * FROM tmp_fab
当客户端B尝试修改该存储过程时(此时客户端A的事务仍活跃,未提交/回滚),行为会因客户端B的数据库连接方式不同而产生差异:
修改失败场景
尝试执行如下修改语句:
CREATE OR ALTER PROCEDURE tmp_fab RETURNS (dummy INTEGER) AS BEGIN dummy = 2; suspend; END
提交客户端B的事务时,报错信息如下:
Unsuccessful execution caused by system error that does not preclude successful execution of subsequent statements. Lock conflict on no wait transaction. Unsuccessful metadata update. Object PROCEDURE "TMP_FAB" is in use.
出现该情况的连接方式:
- 使用FireDAC Delphi组件(DriverName = FB)
- 使用FIBPlus Delphi组件
- 使用IBExpert(自带Firebird DLL)
修改成功场景
同样执行上述修改语句,提交客户端B的事务时可正常变更存储过程。
出现该情况的连接方式:
- 使用isql
- 使用DBeaver(基于JDBC)
- 使用FireDAC Delphi组件(DriverName = ODBC)
原因分析
这种差异核心在于不同客户端驱动/工具对事务隔离级别、锁等待策略的默认设置不同,结合Firebird的元数据锁机制导致:
元数据操作的锁特性
Firebird中修改存储过程这类元数据操作,需要获取对象的独占锁。当客户端A的活跃事务正在使用该存储过程时,会持有该对象的共享锁,此时客户端B的修改请求需要的独占锁会与共享锁产生冲突。锁等待策略的差异
- 报错的工具/驱动(如FireDAC FB、FIBPlus、IBExpert)默认使用NO WAIT(无等待)策略:一旦检测到锁冲突,立即抛出错误,不会等待锁释放。
- 成功的工具/驱动(如isql、JDBC-based DBeaver、FireDAC ODBC)默认使用WAIT(等待)策略:会等待一段时间,直到客户端A的事务提交/回滚、锁释放后,再完成元数据修改。
- 事务隔离级别的辅助影响
部分工具的默认隔离级别会延长共享锁的持有周期,比如高隔离级别(如REPEATABLE READ)会让客户端A的锁持有更久;而另一些工具会在元数据操作时自动切换到适配的隔离级别,配合等待策略,就能避开锁冲突。
内容的提问来源于stack exchange,提问作者Fabrizio
相关产品推荐
相关产品推荐

