为何带IF NOT EXISTS的H2主键添加语句在并发下报错?
问题分析与解决
问题本质
这是H2数据库内存模式下,并发执行初始化脚本时的竞态条件bug。
原因解释
- 当多个连接同时通过
INIT=RUNSCRIPT执行初始化脚本时,CREATE TABLE IF NOT EXISTS是原子性操作,只会创建一次表。 - 但
ALTER TABLE ADD CONSTRAINT IF NOT EXISTS的检查与添加操作并非原子:多个连接可能同时通过“主键是否存在”的检查,随后尝试添加同一个主键约束,导致冲突报错。 - 连接池重试后错误消失,是因为重试时已有连接成功添加了主键,后续检查会发现约束已存在,不再执行添加操作。
解决办法
将主键定义整合到建表语句中(最稳妥的方案)
修改QRTZ_CALENDARS的建表语句,直接在表定义里声明主键:CREATE TABLE IF NOT EXISTS QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR (200) NOT NULL, CALENDAR IMAGE NOT NULL, PRIMARY KEY (SCHED_NAME, CALENDAR_NAME) );这样
CREATE TABLE的原子性会确保主键只被定义一次,不会出现并发冲突。确保初始化脚本仅执行一次
不要通过JDBC URL的INIT参数让每个连接都执行脚本,而是在应用启动阶段,用单个连接执行一次初始化逻辑,避免并发执行脚本的情况。升级H2版本
检查H2的最新版本,部分后续版本可能修复了这个并发场景下的约束检查原子性问题。
内容的提问来源于stack exchange,提问作者Jakub Bochenski
相关产品推荐
相关产品推荐

