如何在JMeter中实现无冲突的数据删除?
解决并行线程组POST/GET/DELETE数据库操作冲突的方案
针对你遇到的并行线程组操作数据库时的查询-删除冲突问题,以下是几个不依赖同步定时器、能保障吞吐量的可行方案:
1. 软删除+延迟物理删除
- 给数据表新增一个
status字段(比如VARCHAR(20),可选值active/deleted),默认值为active - DELETE线程组不直接执行物理删除,而是执行状态更新:
UPDATE table SET status = 'deleted' WHERE id = ? - GET线程组只查询状态为活跃的数据:
SELECT * FROM table WHERE status = 'active' AND [你的其他查询条件] - 新增独立的定时清理线程组(比如每10分钟执行一次),批量删除标记为
deleted且超过阈值时长的记录:
核心线程组的操作完全无锁无阻塞,清理操作后台异步执行,不会影响主流程吞吐量。DELETE FROM table WHERE status = 'deleted' AND create_time < DATE_SUB(NOW(), INTERVAL 10 MINUTE)
2. 数据分片隔离
- 按时间或批次对数据做逻辑分片,新增
shard_key字段(比如按小时生成,格式YYYYMMDDHH) - POST线程组写入数据时,自动填充当前时间对应的
shard_key - GET线程组仅查询当前和最近N个分片的数据(比如当前小时+上一小时的分片)
- DELETE线程组只删除早于N+1个分片的数据(比如删除两小时前的分片)
- 示例:
- POST写入:
INSERT INTO table (shard_key, ...) VALUES ('2024052014', ...) - GET查询:
SELECT * FROM table WHERE shard_key IN ('2024052014', '2024052013') AND [你的其他条件] - DELETE删除:
DELETE FROM table WHERE shard_key < '2024052012'
三个线程组操作完全不重叠的数据集合,从根源避免冲突,批量操作也能保障吞吐量。
- POST写入:
3. 基于队列的生产消费解耦
- 利用Redis内存队列实现数据ID的分发:
- POST线程组新增数据后,将记录ID推入Redis的
active_data列表(用LPUSH) - GET线程组从列表左侧取ID查询(
LPOP或BLPOP,设置短超时) - DELETE线程组从列表右侧取ID删除(
RPOP或BRPOP)
- POST线程组新增数据后,将记录ID推入Redis的
- 队列操作是原子性的,确保同一个ID不会同时被GET和DELETE处理,三个线程组操作完全解耦,数据库层面无交叉操作,吞吐量最大化。
- 可根据业务调整:比如GET查询完成后再将ID移到删除队列,确保数据被查询后再删除。
4. 数据库行级锁+重试机制
- 如果必须物理删除,在DELETE操作前加行级锁,避免GET读取待删除记录:
- DELETE线程组先执行锁查询,跳过已锁定的记录避免阻塞:
拿到ID后再执行DELETE操作SELECT id FROM table WHERE [删除条件] FOR UPDATE SKIP LOCKED - GET线程组添加重试控制器(设置1-2次短超时重试),将"记录不存在"的单次失败转为可恢复的场景
行级锁粒度小,不会影响其他记录操作,吞吐量损失极小。
- DELETE线程组先执行锁查询,跳过已锁定的记录避免阻塞:
额外优化建议
- 调整GET请求的断言逻辑,将"记录不存在"的情况标记为非错误(比如返回空结果时视为成功),避免这类场景被统计为失败请求
- 平衡三个线程组的并发数比例,比如让POST并发数略高于GET和DELETE,确保有足够的活跃数据供GET查询
内容的提问来源于stack exchange,提问作者Singularity
相关产品推荐
相关产品推荐

