两组数据量相对大时,如何高效进行比对
前言
前阵子项目因业务需要,要对接兄弟部门的用户数据,因为兄弟部门并不提供增量用户数据接口,每次只能从兄弟部门那边同步全量用户数据。全量的用户数据大概有几万条。因为是全量数据,因此我们这边要做数据比对(注: 用户username是唯一),如果同步过来的数据,我们这边没有,就要做插入操作,如果我们这边已经有,就要做更新操作。本文就来聊聊当数据量相对大时,如何进行对比
比对逻辑
因用户username是唯一的,因此我们可以利用用户username来进行比对匹配
比对实现
1、方案一:两层嵌套循环比对
即: 将接口的全量数据和我们数据库的全量数据进行循环比对
示例
  @Override
    public void compareAndSave(List<User> users, List<MockUser> mockUsers) {
        List<User> addUsers = new ArrayList<>();
        List<User> updateUsers = new ArrayList<>();
        for (MockUser mockUser : mockUsers) {
            for (User user : users) {
                if(mockUser.getUsername().equals(user.getUsername())){
                    int id = user.getId();
                    BeanUtils.copyProperties(mockUser,user);
                    user.setId(id);
                    updateUsers.add(user);
                }else{
                    User newUser = new User();
                    BeanUtils.copyProperties(mockUser,newUser);
                    addUsers.add(newUser);
                }
            }
        }
    }
用这种方法,我在测试环境压了30万条数据,比对数据等了大概20分钟后,直接OOM
2、方案二:使用布隆过滤器
即: 比对开始前,先将我们这边的数据压入布隆过滤器,然后通过布隆过滤器来判定接口数据
示例
  @Override
    public void compareAndSave(List<User> users,List<MockUser> mockUsers){
        List<User> addUsers = new ArrayList<>();
        List<User> updateUsers = new ArrayList<>();
        BloomFilter<String> bloomFilter = getUserNameBloomFilter(users);
        for (MockUser mockUser : mockUsers) {
            boolean isExist = bloomFilter.mightContain(mockUser.getUsername());
            //更新
            if(isExist){
               User user = originUserMap.get(mockUser.getUsername());
               int id = user.getId();
               BeanUtils.copyProperties(mockUser,user);
               user.setId(id);
               updateUsers.add(user);
            }else{
                User user = new User();
                BeanUtils.copyProperties(mockUser,user);
                addUsers.add(user);
            }
        }
    }
用这种方法,我在测试环境压了30万条数据,比对耗时1秒左右
3、方案三:使用list + map比对
即:比对开始前,先将我们这边数据存放到map中,map的key为username,value为用户数据,然后遍历接口数据,进行比对
示例
  @Override
    public void compareAndSave(List<User> users, List<MockUser> mockUsers) {
        Map<String,User> originUserMap = getOriginUserMap(users);
        List<User> addUsers = new ArrayList<>();
        List<User> updateUsers = new ArrayList<>();
        for (MockUser mockUser : mockUsers) {
             if(originUserMap.containsKey(mockUser.getUsername())){
                 User user = originUserMap.get(mockUser.getUsername());
                 int id = user.getId();
                 BeanUtils.copyProperties(mockUser,user);
                 user.setId(id);
                 updateUsers.add(user);
             }else{
                 User user = new User();
                 BeanUtils.copyProperties(mockUser,user);
                 addUsers.add(user);
             }
        }
    }
用这种方法,我在测试环境压了30万条数据,比对耗时350毫秒左右
总结
这三种方案,两层循环效率是最低,而且随着数据量增大会有OOM的风险。采用布隆过滤器,存在误判的风险,为了降低误判风险,只能降低误判率,可以通过参数指定,但这也增加判断时间。用map可以说是效率最好,他本质是将时间复杂度从O(n2)降低到O(n)。不过这种方案可能也不是最优方案,事后和朋友讨论下,他说可以用啥双向指针啥,因为我在算法这方面没有深入研究,因此本文就没演示了
demo链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-comparedata
相关文章
- Spring Boot中对接Twilio以实现发送验证码和验证短信码
 - Spring Boot 3.5:这次更新让你连配置都不用写了,惊不惊喜?
 - Spring Boot+Pinot实战:毫秒级实时竞价系统构建
 - SpringBoot敏感配置项加密与解密实战
 - SpringBoot 注解最全详解,建议收藏!
 - Spring Boot 常用注解大全:从入门到进阶
 - SpringBoot启动之谜:@SpringBootApplication如何让配置化繁为简
 - Springboot集成Kafka原理_spring集成kafka的原理
 - Spring Boot中@Data注解的深度解析与实战应用
 - 大佬用1000字就把SpringBoot的配置文件讲的明明白白!
 
