一、政策背景:国产化软件适配,为什么不能只停留在“能连接数据库”?
2026年9月,工业和信息化部发布《“人工智能+软件”专项行动实施方案》(工信部信发〔2026〕209号)。
根据工信部9月11日发布的政策解读,《实施方案》围绕软件生产方式变革、基础软件及传统软件智能化升级、软件研发效率提升和智能化技术应用等方向作出部署。
对Java企业级软件开发者而言,这一政策方向具有一定启示意义:
软件系统的升级不能只关注新增功能,还需要重视底层技术架构、软件兼容性、运行稳定性、安全性以及持续优化能力。
需要说明的是,《实施方案》并没有要求所有在线考试系统必须采用达梦或人大金仓数据库,也没有规定统一的数据库性能指标。
本文讨论的国产数据库适配,是结合企业私有化部署、信创环境建设和软件工程实践提出的技术方案。
1.1 国产数据库适配不是修改一个JDBC地址
部分Java项目在适配国产数据库时,容易采取如下思路:
原来使用MySQL,现在更换数据库驱动,修改连接字符串,能正常登录就认为迁移成功。
例如:
MySQL | v 更换JDBC驱动 | v 达梦 / 人大金仓 | v 连接成功 | v 适配完成?实际上,连接成功仅仅证明基础数据库访问链路已经建立。
真正的适配还包括:
JDBC驱动与JDK版本兼容性;
SQL方言与函数差异;
数据类型与字段精度;
自增主键和序列机制;
分页及批量写入语法;
事务隔离与锁行为;
数据库连接池稳定性;
SQL优化器与索引执行方式;
高并发情况下的连接与锁资源竞争;
数据备份、恢复和版本升级。
对于普通业务系统,部分问题可能在日常运行中才逐渐暴露。
但是在线考试系统有一个非常明显的特点:
业务压力往往不是均匀出现,而是在特定时间突然集中。
例如考试开始前的集中登录,以及考试结束前大量考生在短时间内提交试卷。
因此,国产数据库适配不仅要考虑SQL能否执行,还必须验证考试关键时段的稳定性。
二、在线考试系统的数据库压力,与普通管理系统有什么不同?
在分析数据库性能之前,需要先理解业务流量结构。
一个典型的企业在线考试系统,通常包含以下阶段。
| 考试阶段 | 主要数据库操作 | 可能出现的问题 |
|---|---|---|
| 考前登录 | 查询用户、组织、考试资格 | 连接请求集中 |
| 统一开考 | 读取试卷、试题及考试安排 | 大量相似查询 |
| 考试进行中 | 自动保存答案、更新答题状态 | 高频写入 |
| 集中交卷 | 更新考试状态、提交答卷 | 锁等待、事务积压 |
| 自动阅卷 | 查询答案、计算客观题成绩 | 数据库读写压力增加 |
| 成绩查询 | 查询成绩、统计排名 | 报表慢SQL |
| 管理端监考 | 查询参考状态和异常记录 | 高频状态查询 |
其中,最需要重点测试的是集中交卷阶段。
2.1 为什么几千人同时考试,不等于几千个数据库连接?
假设某企业有3000名员工同时参加在线考试。
从业务角度来看,系统需要支持3000个在线会话。
但数据库连接并不需要与在线用户数量一一对应。
原因在于:
HTTP请求一般只会在执行具体数据库操作时短时间占用连接。
请求完成之后,连接应该返回连接池,供其他请求重复使用。
例如:
3000名在线考生 | v Nginx | v Spring Boot应用节点 | v HikariCP | v 数据库连接复用 | v 达梦 / 人大金仓一个连接可以在不同时间依次服务多个请求。
因此:
在线人数、应用线程数、HTTP并发请求数和数据库连接数,是四个不同概念。
错误地把连接池配置成与在线人数相同,不但未必提高性能,还可能导致数据库内存消耗增加、上下文切换增多,以及锁竞争加剧。
三、达梦与人大金仓JDBC适配:从基础配置开始
以下以Spring Boot应用为例,分别说明两种数据库的连接配置。
3.1 达梦DM8 JDBC配置
达梦官方技术文档中,常见的JDBC驱动类为:
dm.jdbc.driver.DmDriver基础连接地址示例:
jdbc:dm://192.168.10.20:5236Spring Boot配置示例:
文件:application-dm.yml
spring: datasource: driver-class-name: dm.jdbc.driver.DmDriver url: jdbc:dm://192.168.10.20:5236 username: ${DB_USERNAME} password: ${DB_PASSWORD}达梦驱动包的具体名称和版本应根据JDK以及数据库服务器版本选择。
项目应优先采用厂商验证的JDBC驱动,并纳入Maven依赖管理或企业内部制品仓库。
不建议未经兼容性验证,就直接把旧版驱动复制到新版Java项目中。
3.2 人大金仓KingbaseES JDBC配置
人大金仓官方文档中,常见驱动类为:
com.kingbase8.Driver连接地址示例:
jdbc:kingbase8://192.168.10.30:54321/examdbSpring Boot配置示例:
文件:application-kingbase.yml
spring: datasource: driver-class-name: com.kingbase8.Driver url: jdbc:kingbase8://192.168.10.30:54321/examdb username: ${DB_USERNAME} password: ${DB_PASSWORD}这里的IP、端口及数据库名称均为示例。
实际项目必须按照客户部署环境修改,并使用权限最小化的业务账号。
3.3 JDBC适配时容易忽略的差异
| 项目 | 达梦DM8 | 人大金仓KingbaseES |
|---|---|---|
| JDBC驱动类 | dm.jdbc.driver.DmDriver | com.kingbase8.Driver |
| 常见URL前缀 | jdbc:dm:// | jdbc:kingbase8:// |
| 默认端口示例 | 5236 | 54321 |
| JDBC标准访问 | 支持 | 支持 |
| SQL执行计划 | EXPLAIN、EXPLAIN FOR等 | EXPLAIN、EXPLAIN ANALYZE等 |
| 事务隔离 | 以具体达梦版本和隔离级别实现为准 | 支持常用事务隔离级别,实际行为需核验 |
| ORM及方言 | 需要匹配DM方言 | 需要匹配KingbaseES方言 |
| JDBC驱动内部连接池 | 按具体驱动功能配置 | 应避免与应用连接池无意义叠加 |
这里需要重点强调:
JDBC接口统一,并不代表SQL实现和事务行为完全一致。
例如,同一条SQL在两种数据库中可能获得不同的执行计划。
同一个事务隔离级别名称,也可能由于数据库实现方式不同而产生不同的锁行为或异常处理要求。
因此,业务适配应建立完整的测试用例,而不能只通过连接测试判断。
四、HikariCP连接池优化:为什么maximumPoolSize不是越大越好?
HikariCP是Spring Boot项目中常用的JDBC连接池。
其性能优化的重点,不是盲目增加连接数量,而是减少无效等待,提高连接复用效率,并避免数据库资源过度竞争。
4.1 推荐的基础配置示例
下面给出一个适合开展性能测试的配置起点。
文件:application.yml
spring: datasource: hikari: pool-name: exam-write-pool maximum-pool-size: 20 connection-timeout: 3000 validation-timeout: 1000 max-lifetime: 1500000 keepalive-time: 120000 auto-commit: true各参数说明如下。
| 参数 | 示例值 | 主要作用 |
|---|---|---|
| maximum-pool-size | 20 | 当前应用实例允许的最大连接数 |
| connection-timeout | 3000ms | 获取连接的最长等待时间 |
| validation-timeout | 1000ms | 连接有效性检测超时 |
| max-lifetime | 1500000ms | 连接最大生命周期 |
| keepalive-time | 120000ms | 空闲连接保活检测周期 |
| auto-commit | true | 连接默认自动提交行为 |
注意,上述参数并不是达梦或人大金仓的通用最优配置,而是一个用于测试和调优的初始样例。
max-lifetime需要结合数据库端、网络设备和连接代理的超时策略设置,通常应低于相关基础设施主动关闭连接的时限。
keepalive-time需要小于max-lifetime,且不能代替对数据库连接超时问题的实际排查。
4.2 为什么这里没有配置minimum-idle?
HikariCP官方文档建议,在追求稳定响应和连接复用的场景中,可以不显式设置minimumIdle,让连接池按照默认固定容量方式运行。
这样有助于避免集中请求到来时临时大量创建数据库连接。
但是,这种选择需要考虑数据库连接资源成本。
对于长期空闲、连接资源紧张的系统,也可以通过压测决定是否采用动态空闲连接数量。
4.3 连接池容量应该如何计算?
假设系统采用3个Java应用节点。
每个节点设置:
maximumPoolSize = 20那么应用连接池的理论总上限为:
3 × 20 = 60此外,数据库还需要预留:
管理员维护连接;
数据库监控连接;
定时任务连接;
备份和其他管理任务所需连接;
其他业务系统的连接;
高可用及数据库集群相关资源。
因此,不能只参考单个应用的maximumPoolSize。
推荐先确定数据库能够稳定承担的连接和并发工作量,再结合应用实例数量分配连接池预算。
4.4 连接池耗尽时,应该先排查什么?
如果日志出现类似:
Connection is not available, request timed out after 3000ms首先检查:
是否存在长时间不提交的事务;
是否有慢SQL持续占用连接;
是否发生锁等待;
是否存在连接泄漏;
业务线程是否在持有连接时调用外部接口;
数据库CPU、I/O或内存是否达到瓶颈;
所有应用节点连接数之和是否超过合理容量。
连接池超时只是表面现象,真正原因可能是SQL慢、锁等待或连接长时间未释放。
不建议一出现连接池超时,就直接把最大连接数从20增加到200。
五、Spring Boot如何监控HikariCP连接池?
只有连接池配置,没有监控指标,很难确定调优是否有效。
对于Spring Boot应用,可以使用Actuator和Micrometer进行监控。
5.1 添加Actuator依赖
在pom.xml中添加:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>具体版本由项目的Spring Boot依赖管理确定。
配置示例:
management: endpoints: web: exposure: include: health,info,metrics安全提醒:
生产环境中的Actuator接口必须设置访问控制,不应直接暴露在公网。
如需对接Prometheus,可以根据部署环境配置相应Registry及受保护的指标端点。
5.2 重点监控哪些指标?
建议重点关注:
hikaricp.connections.active hikaricp.connections.idle hikaricp.connections.pending hikaricp.connections.max hikaricp.connections.timeout实际暴露的指标名称及格式,需要以项目所使用的Spring Boot、Micrometer和HikariCP版本为准。
其中最值得关注的是:
active:已经被业务使用的连接数量。
idle:当前可复用的空闲连接数量。
pending:正在等待数据库连接的请求数量。
timeout:获取连接超时的情况。
如果在集中交卷阶段出现:
active 接近 maximumPoolSize idle 接近 0 pending 持续上升就说明连接池出现明显压力。
但此时仍然不能直接判断数据库连接数量不足。
还需要结合慢SQL、事务耗时和数据库等待事件分析。
六、SQL优化实战:考试业务中哪些SQL最容易变慢?
在线考试系统中的SQL性能问题,通常与人员规模、题库数量和历史数据增长有关。
例如:
某企业初次上线时只有几千名用户。
系统使用多年之后,可能积累了大量考试记录、答卷明细和培训档案。
原来执行很快的查询,随着数据量增加可能逐渐变慢。
典型场景包括:
查询某场考试全部参考人员;
查询某人的历史考试记录;
查询未参加考试的人员;
汇总部门考试完成率;
统计岗位培训达标率;
查询历史答题明细。
6.1 示例:考试人员查询
假设存在以下业务表:
EXAM_ATTEMPT | +-- ID +-- EXAM_ID +-- USER_ID +-- ATTEMPT_NO +-- STATUS +-- START_TIME +-- SUBMITTED_AT需要查询某场考试中已经提交试卷的人员。
SQL如下:
SELECT ID, USER_ID, STATUS, SUBMITTED_AT FROM EXAM_ATTEMPT WHERE EXAM_ID = ? AND STATUS = ? ORDER BY ID;当记录数量增加时,如果没有合适索引,可能出现较大范围的数据扫描和排序开销。
可以考虑建立复合索引:
CREATE INDEX IDX_EXAM_STATUS_ID ON EXAM_ATTEMPT ( EXAM_ID, STATUS, ID );这个索引的设计思路是:
首先按照EXAM_ID定位考试范围,然后按照STATUS过滤状态,最后利用ID支持排序或有序读取。
但是否能够取得预期效果,仍取决于数据分布、查询返回比例和数据库优化器的选择。
建立索引以后必须重新分析执行计划,不能默认索引一定生效。
6.2 为什么不建议一律使用SELECT *?
例如:
SELECT * FROM EXAM_ANSWER WHERE ATTEMPT_ID = ?;如果答题明细表包含较大的文本字段、附件路径或扩展内容,查询所有列可能造成不必要的数据传输与内存占用。
对于只需要统计答题数量的场景,应改成:
SELECT COUNT(*) FROM EXAM_ANSWER WHERE ATTEMPT_ID = ?;对于只需要试题编号与答案内容的场景,应明确指定对应列。
虽然减少返回列并不一定自动消除数据库内部I/O,但能够降低无关字段处理和传输成本。
6.3 为什么函数可能影响索引使用?
假设按时间查询考试记录。
一种写法是:
SELECT ID, USER_ID FROM EXAM_ATTEMPT WHERE EXTRACT(YEAR FROM SUBMITTED_AT) = 2026;如果数据库需要逐条对时间列计算年份,可能影响普通时间索引的利用。
更适合分析的写法是:
SELECT ID, USER_ID FROM EXAM_ATTEMPT WHERE SUBMITTED_AT >= ? AND SUBMITTED_AT < ?;例如传入:
开始时间:2026-01-01 00:00:00 结束时间:2027-01-01 00:00:00这里采用左闭右开区间,有助于避免时间精度边界带来的错误。
还需要注意JDBC参数的数据类型,避免对索引列进行隐式类型转换。
七、达梦与人大金仓如何查看SQL执行计划?
SQL优化不能只依靠开发经验。
实际调优时,建议先采集慢SQL及参数信息,再分析执行计划。
7.1 达梦数据库EXPLAIN
达梦支持使用EXPLAIN查看SQL执行计划。
示例:
EXPLAIN SELECT ID, USER_ID, STATUS FROM EXAM_ATTEMPT WHERE EXAM_ID = 1001 AND STATUS = 'SUBMITTED';达梦执行计划中,常见操作符包括:
| 操作符 | 含义 |
|---|---|
| CSCN2 | 聚簇索引扫描等扫描操作 |
| SSEK2 | 二级索引扫描 |
| BLKUP2 | 根据索引结果回表 |
| HASH JOIN | 哈希连接 |
| NEST LOOP | 嵌套循环连接 |
| SLCT2 | 过滤 |
| PRJT2 | 投影处理 |
需要注意,看到CSCN2并不一定说明SQL存在问题。
如果目标表数据量很小,或者查询需要返回表中绝大部分数据,扫描方式可能反而更加合理。
达梦还提供EXPLAIN FOR等功能,用于获取更丰富的执行计划信息。
需要实际执行统计时,可根据具体数据库版本使用AUTOTRACE或相应的性能分析工具。
7.2 人大金仓EXPLAIN
KingbaseES支持通过EXPLAIN查看执行计划。
例如:
EXPLAIN SELECT ID, USER_ID, STATUS FROM EXAM_ATTEMPT WHERE EXAM_ID = 1001 AND STATUS = 'SUBMITTED';在测试环境中,也可以针对安全的查询语句分析实际执行情况:
EXPLAIN ANALYZE SELECT ID, USER_ID, STATUS FROM EXAM_ATTEMPT WHERE EXAM_ID = 1001 AND STATUS = 'SUBMITTED';需要注意:
EXPLAIN ANALYZE会实际执行SQL,不适合在不了解后果的情况下直接用于生产环境中的修改、删除等语句。
7.3 两种数据库的SQL调优应如何比较?
建议采用相同业务查询,对比以下内容:
是否使用预期索引;
是否存在大范围扫描;
估算行数与实际行数是否接近;
是否产生大量排序;
是否存在回表开销;
是否出现锁等待;
查询返回数据量是否一致;
相同数据规模下的实际执行时间。
这里不能简单认为,某条SQL在达梦中采用索引扫描,而人大金仓采用其他扫描方式,就说明前者性能一定更好。
真正的性能判断仍应以真实业务负载和实际执行数据为依据。
八、事务隔离差异:这是国产数据库适配中容易被忽略的问题
在线考试系统不仅要快,还要保证业务数据一致。
特别是:
同一考生不能重复生成冲突的正式交卷结果;
考试状态不能被异常请求随意修改;
已提交成绩不能被未经授权覆盖;
考试进行中,答案保存不能出现错误覆盖;
阅卷任务不能因重复消费而反复计分。
这些都与数据库事务有关。
8.1 达梦与人大金仓的事务隔离行为不同
根据达梦数据库技术文档,常见DM版本提供读未提交、读提交和串行化等隔离级别,默认使用读提交;可重复读的映射或支持情况需要以具体版本和兼容模式为准。
KingbaseES文档则描述了读提交、可重复读和可串行化等隔离行为。
因此,对于原本依赖MySQL可重复读事务语义的Java项目,不能直接把原有事务配置迁移到达梦。
例如:
@Transactional( isolation = Isolation.REPEATABLE_READ )这段代码能否达到预期效果,应根据实际数据库和驱动验证。
8.2 在线考试业务推荐如何处理?
对于一般的答题保存、交卷状态更新等业务,可以优先研究读提交隔离级别,并结合业务唯一约束、行锁和幂等规则保障一致性。
示例:
@Transactional( isolation = Isolation.READ_COMMITTED, timeout = 5 ) public void submitExam(...) { // 考试提交业务 }其中:
READ_COMMITTED用于声明读提交隔离。
timeout = 5表示事务超时配置示例,具体行为还取决于事务管理器和JDBC驱动的支持情况。
5秒不是适用于全部系统的标准值。
对于长时间SQL或数据库锁等待,应该通过独立监控与测试设置合理的超时和恢复机制。
另外,Spring事务默认依赖代理机制,同一个类内部直接调用带有@Transactional的方法,可能无法触发预期事务拦截。
这一问题与具体国产数据库无关,但在Java项目迁移过程中同样需要检查。
九、集中交卷实战:如何降低事务冲突并防止重复提交?
在线考试系统最需要关注的核心业务之一,就是集中交卷。
例如,某场考试规定10:00结束。
临近结束时,大量考生可能在短时间内提交答案。
如果每次交卷都触发大量SQL、同步阅卷及完整报表统计,很容易放大数据库压力。
9.1 不推荐的交卷流程
考生点击交卷 | v 保存全部答案 | v 计算全部成绩 | v 更新部门统计 | v 更新培训档案 | v 生成证书 | v 返回交卷成功这个流程的问题是:
整个接口承载了太多业务操作。
如果所有操作都包含在同一个长事务中,可能造成数据库连接长时间占用。
如果涉及外部服务,还可能进一步增加不确定性。
9.2 推荐的交卷处理架构
考生提交试卷 | v 验证考试身份和状态 | v 检查已确认保存的答案版本 | v 短事务完成交卷状态变更 | v 同事务写入待处理事件 | v 数据库提交成功 | v 返回交卷受理结果 | v 异步阅卷任务 | v 成绩计算与校验 | v 成绩及培训档案更新这里需要强调一个关键点:
只有交卷状态及必要的提交数据已经可靠持久化,系统才能向考生返回成功。
不能在消息尚未可靠保存时,就简单告诉考生“试卷提交成功”。
9.3 使用幂等机制避免重复交卷
一个常见场景是:
考生点击交卷后,由于网络延迟,没有及时收到成功提示。
考生再次点击交卷。
如果服务端没有幂等保护,就可能重复执行后续阅卷或成绩处理流程。
可以按照考试作答记录建立明确状态:
IN_PROGRESS | v SUBMITTED | v GRADING | v GRADED同一份作答记录只允许按照合法状态流转。
例如:
UPDATE EXAM_ATTEMPT SET STATUS = 'SUBMITTED', SUBMITTED_AT = CURRENT_TIMESTAMP WHERE ID = ? AND STATUS = 'IN_PROGRESS';如果更新行数为1,表示本次请求完成了状态转换。
如果更新行数为0,应再次核验当前状态和操作权限。
不能直接把0行更新当作任何情况下的交卷成功。
9.4 Spring Boot代码示例
以下是一个简化的事务提交示例。
假设系统已经具备:
已认证且完成授权检查的考生;
独立的考试作答记录;
连续递增、可确认的答案保存版本;
可靠的数据库事务管理;
待处理事件Outbox表。
@Service public class ExamSubmitService { private final JdbcTemplate jdbcTemplate; public ExamSubmitService( JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Transactional( isolation = Isolation.READ_COMMITTED, timeout = 5 ) public String submit( long attemptId, long confirmedSeq ) { // 锁定当前作答记录 String sql = """ SELECT STATUS, LAST_SAVED_SEQ FROM EXAM_ATTEMPT WHERE ID = ? FOR UPDATE """; AttemptState state = jdbcTemplate.queryForObject( sql, (rs, rowNum) -> new AttemptState( rs.getString("STATUS"), rs.getLong("LAST_SAVED_SEQ") ), attemptId ); if (state == null) { throw new IllegalArgumentException( "作答记录不存在" ); } if ("SUBMITTED".equals(state.status())) { return "ALREADY_SUBMITTED"; } if (!"IN_PROGRESS".equals(state.status())) { throw new IllegalStateException( "当前状态不可交卷" ); } // 确认最终答案已经持久化 if (state.lastSavedSeq() != confirmedSeq) { throw new IllegalStateException( "存在未确认保存的答案" ); } jdbcTemplate.update(""" UPDATE EXAM_ATTEMPT SET STATUS = 'SUBMITTED', SUBMITTED_AT = CURRENT_TIMESTAMP WHERE ID = ? AND STATUS = 'IN_PROGRESS' """, attemptId); // 与交卷状态处于同一个数据库事务 jdbcTemplate.update(""" INSERT INTO EXAM_OUTBOX (EVENT_ID, ATTEMPT_ID, EVENT_TYPE, STATUS) VALUES (?, ?, ?, ?) """, UUID.randomUUID().toString(), attemptId, "EXAM_SUBMITTED", "NEW" ); return "SUBMITTED"; } public record AttemptState( String status, long lastSavedSeq ) {} }该代码仅展示事务边界及Outbox设计思路,不是一套可直接上线的完整交卷接口。
正式项目还必须确保:
服务端先校验考生与作答记录的归属关系;
自动保存答案与更新
LAST_SAVED_SEQ采用一致的行锁和事务规则;答案保存序列连续、可确认,不能只取最大序号掩盖中间丢失的请求;
最终交卷必须验证考试时间、答题完整性及必要的状态;
Outbox事件需要唯一约束、可靠投递及消费者幂等处理;
重复提交时必须返回该考生已有的合法提交结果;
发生超时或数据库异常时,不能在结果未知的情况下盲目重新创建作答记录。
对于答案数量较多的考试,还可以根据实际设计采用已确认自动保存版本或最终答卷快照,但不能牺牲答案完整性来追求接口响应速度。
9.5 为什么将阅卷和统计适当异步化?
交卷操作最核心的业务目标,是可靠记录考生已经提交试卷。
而客观题评分、统计分析、部门排名和证书生成等操作,可以依据实际考试规则进行后续处理。
例如:
可靠交卷记录 | v Outbox事件 | v 消息投递 | v 阅卷服务 | +-- 客观题规则评分 | +-- 主观题人工/AI辅助阅卷 | v 成绩确认 | v 统计报表更新通过这种方式,可以缩短关键交卷事务占用数据库连接的时间。
但异步方案也必须具备重复消息处理、失败重试、死信处理、积压监控及业务对账机制。
异步处理不等于数据可以丢失,也不等于交卷成功就代表成绩已经计算完成。
十、慢SQL优化之外,还需要注意数据库锁等待
在实际高并发考试系统中,有时SQL执行时间较长,并不是因为查询语句本身复杂,而是因为等待其他事务释放锁。
例如:
一个后台管理事务长时间占用某场考试的关键记录。
此时大量交卷请求同时尝试更新相关数据,可能形成锁等待队列。
10.1 常见问题来源
长事务持有行锁;
多个请求重复更新同一汇总记录;
SQL没有合适索引,导致访问范围过大;
不同业务按照不一致顺序更新多张表;
在事务中执行外部接口调用;
自动保存与最终交卷缺少明确的并发控制规则。
10.2 为什么不能在每次交卷时同步更新部门总人数?
假设某部门有大量人员同时交卷。
如果每个请求都更新同一条部门统计记录:
部门考试统计记录 ^ | 大量交卷事务这条记录就可能成为热点。
更合理的设计是:
首先完成每名考生独立作答记录的可靠提交,然后通过异步聚合或定期计算更新部门统计数据。
这样可以减少集中交卷期间对同一行数据的频繁争用。
但如果业务必须实时展示准确的已交卷人数,也需要设计满足一致性要求的统计机制,不能简单依赖存在延迟的缓存作为唯一依据。
十一、JMeter压力测试:如何模拟集中交卷?
前面的配置与代码调整,最终都需要通过实际压力测试验证。
对于在线考试系统,推荐使用Apache JMeter设计接近真实业务的压测流程。
11.1 为什么不能只压测登录接口?
部分项目的并发测试只模拟大量用户登录。
这只能验证登录相关接口的能力,并不能反映集中考试提交阶段的数据库压力。
推荐至少设计以下场景:
场景A:集中登录。
检查用户认证、考试资格查询以及数据库连接池是否稳定。
场景B:统一开考。
检查试卷读取、题目获取和静态资源访问压力。
场景C:考试进行中。
模拟考生答题、定期保存答案、查询剩余考试时间等操作。
场景D:集中交卷。
重点模拟短时间内大量考生同时提交试卷。
场景E:考后成绩查询。
验证阅卷完成后集中查询成绩和部门统计的表现。
11.2 JMeter测试计划示例
建议建立以下测试结构:
Test Plan | +-- HTTP Request Defaults | +-- CSV Data Set Config | +-- HTTP Cookie Manager | +-- Thread Group | +-- 登录 | +-- 查询考试安排 | +-- 获取试卷 | +-- 自动保存答案 | +-- Synchronizing Timer | +-- 提交试卷 | +-- 查询提交状态通过CSV文件给不同虚拟用户分配独立的测试账号、考试记录和答案数据。
不建议所有线程共用同一个考生账号,否则容易产生与真实业务不符的状态冲突。
11.3 如何模拟瞬时交卷?
JMeter提供Synchronizing Timer,可以让一定数量的测试线程在指定位置等待,然后集中放行。
例如,可在提交试卷请求之前配置该定时器。
需要注意:
同步分组数量不能超过当前测试执行器能够实际到达该位置的线程数量;
应合理配置等待超时,防止测试线程一直阻塞;
对于分布式压测,同步定时器主要在单个JMeter执行器内部协调线程;
如果需要模拟大量用户在某个时间窗口交卷,还应设计符合目标到达率的负载模型。
11.4 命令行执行压测
例如:
jmeter -n \ -t exam-load.jmx \ -l exam-results.jtl \ -e \ -o exam-report参数说明:
-n:非图形模式执行。
-t:指定测试计划文件。
-l:保存测试结果。
-e:生成测试报告。
-o:指定报告输出目录。
实际执行前,应根据测试机资源设置线程数、连接复用、超时、请求参数和结果保存方式。
大规模压测还需要确认压力发生器本身没有成为瓶颈。
十二、达梦与人大金仓性能比较,应该采用什么标准?
对于不同数据库,不能在没有测试的情况下直接给出性能排名。
如果希望开展客观横向对比,建议统一以下条件:
使用相同或可比的服务器CPU、内存及磁盘;
使用相同规模和分布特征的考试业务数据;
使用功能一致的SQL和索引设计;
分别采用厂商验证的JDBC驱动;
记录数据库版本与关键参数;
保持应用业务逻辑和压测负载一致;
分别采集预热、稳态及峰值阶段的数据;
每组测试重复执行,记录结果波动。
12.1 建议的测试记录表
以下为待填写的实测模板,不包含虚构数据。
| 测试指标 | 达梦DM8 | 人大金仓KingbaseES |
|---|---|---|
| 数据库版本 | 待记录 | 待记录 |
| JDBC驱动版本 | 待记录 | 待记录 |
| HikariCP连接池配置 | 待记录 | 待记录 |
| 并发虚拟用户数 | 待实测 | 待实测 |
| 交卷请求吞吐量 | 待实测 | 待实测 |
| 交卷接口P95延迟 | 待实测 | 待实测 |
| 交卷接口P99延迟 | 待实测 | 待实测 |
| 请求成功率 | 待实测 | 待实测 |
| 数据持久化成功率 | 待实测 | 待实测 |
| 数据库CPU峰值 | 待实测 | 待实测 |
| 数据库锁等待情况 | 待实测 | 待实测 |
| 连接池等待情况 | 待实测 | 待实测 |
| 答卷一致性校验 | 待实测 | 待实测 |
12.2 为什么必须关注P95和P99?
平均响应时间有时无法充分反映考试高峰期的异常请求。
例如,如果绝大多数交卷请求响应很快,但少部分考生持续等待,平均值可能掩盖尾部延迟问题。
因此,建议关注:
P50:典型请求的响应水平;
P95:尾部延迟情况;
P99:更极端的慢请求情况;
超时率:是否出现不可接受的等待;
交卷一致性:是否发生重复或遗漏;
数据库资源:性能是否依靠过度消耗资源获得。
需要明确的是:
压测中的HTTP请求成功,不代表考试答案已经全部正确落库。
测试结束后还需要对考试记录、答案数量、作答版本、最终提交状态和阅卷结果进行业务级对账。
十三、结合宏远培训考试系统,如何理解国产数据库适配的实际价值?
对于企业培训考试平台,数据库适配不能脱离具体业务功能。
根据宏远培训考试系统公开的产品资料,该系统提供组织管理、培训计划、在线课程、题库管理、随机组卷、考试安排、防作弊监考、成绩统计、证书档案、数据分析及私有化部署等能力,并支持国产化环境适配。
宏远培训考试系统已完成从原.NET架构向Java技术架构的升级,能够根据项目环境开展Linux及相关国产化技术适配。
这些业务与技术基础,为国产数据库环境下的持续优化提供了应用场景。
需要区分的是,以下属于结合产品现有业务功能提出的数据库性能优化方案,不代表本文所有配置和压测结果已经在宏远各产品版本中完成验证。
13.1 集团组织和人员管理:重点优化权限查询
宏远培训考试系统支持集团、子公司、部门、班组等多级组织管理。
对于集团型企业,培训和考试任务通常按组织范围分配。
例如:
集团总部 | +-- 子公司A | | | +-- 部门 | | | +-- 班组 | +-- 子公司B在数据库设计中,应考虑组织节点、用户组织关联、岗位及角色权限之间的关系。
对于经常查询的权限范围,可以采用合理的索引、预计算或受控缓存方式进行优化。
但权限缓存必须具备失效机制。
例如,员工调离某部门后,系统应及时更新相关授权范围,避免继续访问原部门考试资料。
13.2 题库与随机组卷:减少开考高峰期的数据库查询
宏远培训考试系统支持题库分类、批量导入、随机组卷和多种考试配置方式。
在统一开考场景下,如果每个考生进入考试页面时都执行大量复杂题库查询,数据库可能承受不必要的压力。
可以考虑按照考试规则提前完成部分试卷准备工作,并为每位考生持久化必要的试卷分配关系或试卷快照。
考试配置 | v 题库规则检查 | v 试卷生成或预分配 | v 持久化试卷关联 | v 考生开考 | v 读取本人试卷在满足考试公平性和随机化要求的前提下,这种方案可以减少开考瞬间重复执行的复杂查询。
需要注意:
试卷缓存不能破坏题目权限、试卷快照一致性和正式考试保密要求。
13.3 自动保存与集中交卷:重点保护答卷完整性
在线考试系统真正重要的,不只是交卷接口响应速度。
更重要的是考生已经确认保存的答案是否能够正确保留。
对于宏远这类支持集中考试管理的系统,数据库优化应重点围绕:
答题自动保存;
最终交卷确认;
请求幂等;
异常恢复;
成绩计算;
历史答卷追溯。
这部分最适合结合前面介绍的短事务、唯一约束、可靠事件和数据库对账机制开展技术改造或优化验证。
13.4 数据报表:避免大型统计影响正在进行的考试
宏远培训考试系统支持部门培训完成率、考试通过率、成绩统计及相关数据分析能力。
这些功能是企业培训管理的重要组成部分。
但随着历史数据量增加,大型统计任务可能与在线考试争用数据库资源。
例如:
在线考试 | v 实时答卷写入 | +------------------+ | v 大型统计查询 | v 数据库资源竞争可以根据项目规模采用:
合理设计统计索引;
按考试批次生成汇总表;
将非实时统计任务放在低峰期;
使用经过验证的缓存或预聚合结果;
必要时评估读写分离或独立分析库。
如果采用数据库读写分离,需要考虑主备同步延迟。
例如,考生刚提交试卷后,不能因读取到尚未同步的备库而错误提示交卷失败。
13.5 私有化部署与国产化环境适配
宏远培训考试系统支持本地化、私有云和内网部署,并面向企业信创环境提供适配能力。
对于采用国产数据库的客户,建议实施按版本管理的兼容性矩阵。
例如:
| 验证项目 | 验证内容 |
|---|---|
| 操作系统 | 发行版、内核及架构 |
| JDK | 版本、运行环境兼容性 |
| 数据库 | 厂商、具体版本、补丁级别 |
| JDBC驱动 | 版本和连接参数 |
| SQL方言 | 业务SQL及ORM映射 |
| 业务功能 | 培训、题库、考试、证书、报表 |
| 压力测试 | 登录、开考、自动保存、集中交卷 |
| 异常恢复 | 连接中断、事务失败、节点重启 |
| 数据迁移 | 历史人员、题库、成绩及培训档案 |
这样的矩阵,比简单写一句“支持国产数据库”更有技术价值。
具体到达梦和人大金仓,应以目标版本的兼容测试及项目交付结果确认适配程度。
十四、实际项目中最容易犯的六个错误
错误一:连接池耗尽就直接增加最大连接数
连接池耗尽可能由慢SQL、锁等待或连接泄漏造成。
应先定位真正原因,再决定是否需要增加容量。
错误二:直接复制原MySQL的事务配置
不同数据库的隔离级别及实现行为存在差异。
正式迁移时应验证事务一致性、锁冲突和失败重试机制。
错误三:只测试数据库连接,不测试正式考试流程
连接测试成功不能证明试卷、答案保存和成绩统计业务已经正确适配。
错误四:建立索引后不检查执行计划
索引不是越多越好。
过多索引还可能增加答题保存、交卷和数据维护的写入成本。
错误五:所有交卷业务放进一个长事务
容易导致连接占用时间增加,也可能放大数据库锁冲突。
应结合业务一致性要求缩短事务边界。
错误六:只看吞吐量,不核对答卷完整性
即使压测结果显示每秒能够处理大量请求,也需要验证考生答案没有丢失、重复覆盖或产生不一致的成绩记录。
十五、推荐的国产数据库适配实施步骤
对于已经运行的Java培训考试系统,可以按照以下顺序推进。
第一阶段:功能兼容性验证。
完成JDBC驱动、数据库连接、数据类型、SQL方言、事务处理和全部核心业务功能验证。
第二阶段:SQL性能基线测试。
确定考试登录、组卷、答题保存、交卷、成绩查询和报表统计的SQL基线。
第三阶段:连接池与事务优化。
根据实际负载调整HikariCP配置,缩短长事务,治理锁等待与慢SQL。
第四阶段:集中交卷专项压测。
通过JMeter模拟真实考试业务,分析数据库连接使用、尾部延迟、交卷成功率和数据一致性。
第五阶段:生产环境观察。
建立连接池、数据库资源、慢SQL及业务错误率监控,并形成可复核的优化记录。
这五个阶段完成后,才能根据实际测试结果说明某一项目环境下达到了怎样的性能水平。
十六、总结:国产数据库适配的目标,不只是运行成功,而是可靠完成考试
2026年软件产业持续向智能化、国产化和高质量发展推进。
对于Java企业级应用而言,数据库适配是技术升级中需要认真解决的基础问题。
但从实际工程经验来看,数据库性能优化并不存在一套适用于全部项目的固定参数。
HikariCP连接池的容量,需要根据数据库资源及SQL执行特点确定。
事务隔离级别的选择,需要兼顾数据一致性和并发能力。
复合索引的设计,需要结合查询条件与执行计划验证。
集中交卷场景的性能优化,需要同时考虑数据持久化、幂等控制和业务一致性。
而达梦与人大金仓之间的性能差异,必须依靠相同条件下的真实测试数据判断,不能根据品牌、技术背景或者单一测试结果下结论。
结合宏远培训考试系统的Java架构、私有化部署、国产化适配、多级组织管理、题库组卷、在线考试及数据分析等功能,可以进一步研究适用于不同企业环境的数据库优化方案。
对于煤矿、电力、制造业和大型集团,在线考试系统的价值不只是能够让员工通过浏览器参加考试,而是在重要培训考核任务中,保障考试过程、答案数据和成绩档案的可靠性。
在线考试系统真正的高性能,不是数据库能够建立多少个连接,而是在大量用户同时访问、集中交卷和业务异常发生时,仍然能够可靠保存每一份答卷。