概述
前几天公司项目莫名部署出错, 今将相关数据脱敏后整理文章记录.
项目在 Docker 环境部署时启动失败, 报错信息 Table 'QRTZ_LOCKS' doesn't exist;, 提示 Quartz 相关表不存在导致项目无法启动, 且表名为大写.
部分报错信息:
Caused by: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: Table 'QRTZ_LOCKS' doesn't exist
at org.quartz.impl.jdbcjobstore.StdRowLockSemaphore.executeSQL(StdRowLockSemaphore.java:184)
at org.quartz.impl.jdbcjobstore.DBSemaphore.obtainLock(DBSemaphore.java:113)
at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:238)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.clearAllSchedulingData(JobStoreSupport.java:2002)
at org.quartz.core.QuartzScheduler.clear(QuartzScheduler.java:1547)
at org.quartz.impl.StdScheduler.clear(StdScheduler.java:239)
at com.lubanu.quartz.service.impl.SysJobServiceImpl.init(SysJobServiceImpl.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
... 28 common frames omitted
Caused by: java.sql.SQLSyntaxErrorException: Table 'QRTZ_LOCKS' doesn't exist
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:972)
at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:3241)
at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_executeQuery(FilterEventAdapter.java:459)
at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:3238)
at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.executeQuery(PreparedStatementProxyImpl.java:175)
at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java:213)
at org.quartz.impl.jdbcjobstore.StdRowLockSemaphore.executeSQL(StdRowLockSemaphore.java:123)
... 41 common frames omitted
原因分析
- 直接原因: Quartz 框架的常见问题
-
- Windows 连接 MySQL 时会启用大小写不敏感, 且建表时会将表名以及字段名转换为小写
- 基于 Quartz 的定时任务模块在首次连接到数据库时自动建表, 创建的表结构为小写
- Quartz 框架执行的 SQL 语句为大写, 在 Windows 上测试时, 默认为大小写不敏感所以并未出现问题
- 项目部署时, Linux 环境连接 MySQL 不会默认开启大小写不敏感, 导致 Quartz 框架执行的大写 SQL 语句无法匹配小写的数据库表, 发生报错
- 根本原因: 开发和部署环境不统一, 缺少测试
-
- 开发环境 (Windows) 与部署环境 (Linux) 连接 MySQL 时的默认配置不一致, 导致发生冲突
- 为什么旧版本没有问题: 旧版本没用到Quartz相关的表
-
- 旧版本的定时任务以内存模式运行, 没有使用 Quartz 相关表
- 在内存模式下, 多机器同时运行后端服务时会导致任务重复执行, 所以新版本添加了Quartz 的配置, 使用相关表进行分布式管理, 所以出现问题
修复措施
- 将服务器端的表结构改为大写, 与 Quartz 框架匹配; 或修改MySQL配置为大小写不敏感
- 环境一致性: 确保开发、测试和生产环境一致.
- 测试覆盖: 完善测试用例, 覆盖开发以及部署环境等不同场景.
- 文档记录: 将事故案例复盘并留档.
参考:
编写日期: 2025年1月10日