乐趣区

关于数据分析:两组数据量相对大时如何高效进行比对

前言

前阵子我的项目因业务须要,要对接兄弟部门的用户数据,因为兄弟部门并不提供增量用户数据接口,每次只能从兄弟部门那边同步全量用户数据。全量的用户数据大略有几万条。因为是全量数据,因而咱们这边要做数据比对( 注: 用户 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

退出移动版