前言

有一天上午,邮件中报了几次Duplicate key的问题引起了我的注意。

这个Duplicate key问题有点蹊跷,不是因为insert表时,表中创建了唯一索引,而插入的数据确实有重复,而引起的Duplicate key问题。

还没到这一步。

Map<String,Entity> entityMap = getData().stream().collect(
Collectors.toMap(item -> item.getCode(),Function.identity()))

在这一行代码中抛的异常。

getData()方法是获取数据库中的所有指标数据。

而底层的查询sql,时根据code做了分组聚合的,因此查询出的数据,不会出现重复的code。

分析原因

上面的getData()方法,如果一次性获取了数据库中的所有指标数据,sql中按code做了分组处理,是不会出现这种问题的。

我查看了一下代码,发现getData()方法里面包含了分页获取数据的逻辑。

public List<Integer> getData() {
   SearchEntity searchEntity = new SearchEntity();
   searchEntity.setPageNo(1);
   searchEntity.setPageSize(200);
   List<Entity> dataList = dataMapper.getData(searchEntity);
   List<Entity> result = Lists.newArrayList(dataList);
   
   while(CollectionUtils.isNotEmpty(dataList)) {
      searchEntity.setPageNo(searchEntity.getPageNo()+1);
      dataList = dataMapper.getData(searchEntity);
      result.addAll(dataList);
   }
   return result;
}

上面这段代码的逻辑是,每次取200条数据,一一页从数据库中获取数据,保存到result集合中,最后返回该result集合 。

而getData方法中的sql是这样的:

select 
  code,
  name
from data
group by code

这种情况下,最后result集合中出现了重复的code数据。

如果不做分页,而是一次性获取所有的数据,也不会有问题。

上面这个Duplicate key问题,其实是在分页过程中产生的重复数据问题。

那这里是什么问题呢?

我仔细查了一下系统日志。发现在这个异常出现之前,系统当时还执行了另外一个job。

那个job每隔一个小时执行一次,会先删除data表中的数据,再重新插入数据,数据内容不变,但 id值变大了。

如果在那个job的执行过程中,分页查询data表的数据,可能会出现第一页查到某个code的数据。然后job把那条数据删除了,插入了一条新数据。此时,第二页查询时,可能又会查出那个code的数据。

如何解决问题?

既然原因已经找到了,那么如何解决问题呢?

其实出现上面这个问题的根本原因是,result集合中确实出现了重复的数据。

我们把result集合去重一下,不就可以解决问题吗?

我们只需要加一个Set codeSet = Set.newHashSet();

在让result集合中添加数据之前,先判断一下codeSet集合是否存在数据,如果已存在,则不添加。

Set<String> codeSet = Sets.newHashSet();
for(Entity entity: dataList) {
  if(!codeSet.contains(entity.getCode())) {
     result.add(entity);
     codeSet.add(entity.getCode());
  }
}

如果不存在,才添加数据到result集合,同时把code添加到 codeSet中。

分页之后将数据汇总到某个集合,然后将这个集合转换成map,很容易产生重复key的问题,我们日常工作中需要特别注意一下 。

最后修改:2026 年 06 月 06 日
如果觉得我的文章对你有用,请随意赞赏