问题背景
系统中有个定时任务,会处理一些其它系统送来的文件。
它会批量扫描这些文件中各行数据并记录入表。
业务上要求数据库记录字段KEY需要唯一,如果已有记录则不处理文件中对应内容。
所以代码中最开始的校重逻辑实现是:
处理前先查库中是否有已存在KEY=文件记录的,不存在才insert。
伪代码如下:
dto = DAO.SelectByKey(key);
// 判断是否已存在
if (dto == null) {
insert();
}
// 已经相同KEY数据,不再入表
else {
dealRepeater();
}
上面代码在单线程下没有问题。
但是后来为了提升性能改造成了多线程并发处理,那么上面校重逻辑就失效了。
(线程A先处理,发现库中无该KEY数据,则insert,但在A事务提交前,线程B也进行了处理,查询发现库中无该KEY数据,所以也会insert。最终库中就有了两条相同KEY的数据。)
这是很典型的并发去重问题。那么如何解决呢?
问题解决探讨
1.利用唯一索引
将该表KEY字段设置为唯一性索引,尝试insert。如果抛出“违反唯一性约束异常”,则表示已有重复数据。
伪代码如下:
if (dto != null) {
// 已经相同KEY数据,不再入表
dealRepeater();
} else {
try {
insert();
//违反唯一性约束会报异常:InvocationTargetException
} catch (InvocationTargetException e) {
//说明重复插入,则不再入表
dealRepeater();
}
}
但这样处理的缺点在于:
- 1.需要新增索引;
- 2.通过异常来控制业务逻辑不符合规范。
2.带条件的INSERT
INSERT INTO IF EXISTS
伪代码如下:
INSERT INTO TABEL_NAME_A(ID, KEY, FILED3, ...)
SELECT 'ID', 'KEY', 'FILED3', ...
FROM DUAL
WHERE NOT EXISTS(
SELECT KEY
FROM TABEL_NAME_A
WHERE KEY= 'KEY')
但不同线程不同事务,可能仍然有问题?
3.文件预去重处理
即在定时任务业务处理逻辑之前增加预处理去重逻辑。
将文件内容进行去重处理,然后这样再多线程处理。
保证每一个线程处理的KEY各不相同。
缺点是不太适合此业务多文件间重复KEY的场景。
4.将错就错
由于目前这个定时任务A仅仅是将文件数据入表,实际业务处理在另一个定时任务B处理。那么我们可以将错就错,在B处理前增加去重逻辑。通过SQL查询出重复数据并删除。然后再进行处理。
SQL如下:
扫描二维码关注公众号,回复:
14486124 查看本文章

SELECT A.KEY
FROM TABLE_NAME A
WHERE A.STATE = 'I' // 待处理数据
GROUP BY A.KEY HAVING COUNT(A.KEY) > 1
这是我目前能想到的几种处理方法。
如果你有其他看法,不妨在评论区指点一下。不胜感激!!!