多实例SpringBoot环境下防止MySQL员工表重复行方案咨询
问题场景与咨询
我的项目中有一个MySQL的Employees表,同时部署了10个SpringBoot应用实例,均可向该表写入数据。表结构如下:
| ID | FirstName | LastName |
|---|---|---|
| 1 | Aaa | Aaaa |
| 2 | Bbb | Bbbb |
| 3 | Ccc | Cccc |
我需要确保无法插入已存在FirstName/LastName组合的记录。目前我想到的方案有:
- 先执行select查询,无结果再执行insert,但存在竞态条件,无法保证其他实例不会同时插入;
- 对表加锁;
- 插入时处理违反约束的异常,但会在日志中生成Hibernate错误,不够友好。
现咨询:
- 这种场景下还有哪些可选方案?
- 多实例环境下防止重复行最常用的方案是什么?
解决方案
1. 可选方案
- 数据库联合唯一约束配合
INSERT ... ON DUPLICATE KEY UPDATE/INSERT IGNORE:先给Employees表的FirstName和LastName字段添加联合唯一索引,之后使用INSERT ... ON DUPLICATE KEY UPDATE(重复时更新指定字段,比如更新时间)或INSERT IGNORE(重复时直接忽略插入操作)语句执行写入。这种方式可以避免抛出不必要的异常,也能通过Hibernate的原生SQL或自定义SQL实现,减少日志中的错误输出。 - 分布式锁:基于Redis等中间件实现分布式锁,插入前先针对
FirstName+LastName的组合获取锁,拿到锁后再执行查询和插入操作,确保同一时间只有一个实例能处理该组合。注意设置合理的锁超时时间,避免死锁。 - MySQL原生锁函数
GET_LOCK():利用MySQL的GET_LOCK()函数针对特定键(比如拼接FirstName和LastName作为锁标识)获取会话级锁,操作完成后调用RELEASE_LOCK()释放。这种方式不需要额外中间件,但要注意锁的作用范围和超时机制。
2. 多实例环境下最常用的方案
**数据库联合唯一约束 + INSERT ... ON DUPLICATE KEY UPDATE/INSERT IGNORE**是这类场景下最常用的方案,核心优势如下:
- 可靠性高:由数据库层面保证数据唯一性,彻底规避业务代码的竞态问题;
- 实现简单:不需要引入额外中间件,仅需添加索引和调整插入SQL;
- 性能友好:相比表锁或分布式锁,数据库的唯一约束检查性能损耗更低,且能利用数据库的原生优化机制。
具体实现步骤:
- 给
Employees表添加联合唯一索引:
ALTER TABLE Employees ADD UNIQUE INDEX idx_first_last_name (FirstName, LastName);
- 使用
INSERT ... ON DUPLICATE KEY UPDATE执行插入(示例):
INSERT INTO Employees (FirstName, LastName) VALUES (?, ?) ON DUPLICATE KEY UPDATE LastUpdated = CURRENT_TIMESTAMP;
或者使用INSERT IGNORE忽略重复插入:
INSERT IGNORE INTO Employees (FirstName, LastName) VALUES (?, ?);
通过这种方式,既保证了不会插入重复的FirstName/LastName组合,又能避免Hibernate输出不必要的错误日志,完美适配多实例部署场景。
内容的提问来源于stack exchange,提问作者radekEm
相关产品推荐
相关产品推荐

