背景
目前需要将大量数据(存在少量的重复数据)初始化写入数据库表(该表开始无数据),在避免重复数据写入如何保证写入速率?
网络上已有方案
网络上已有的方案大概分为两类:
通过SQL实现
1.先写入,后去重。
2.insert ignore。insert ignore语法会忽略执行出错的行,因此写入数据以第一条为准。
3.replace into和insert … on duplicate key update。这两种语句都是通过替换原有的重复数据来避免重复数据写入的问题。
通过代码逻辑控制
1.通过try…catch…
public void insertData(BufferedReader fileInputStream) throws IOException{
int n = 0;
String lineString = null;
List<Map> dataList = new ArrayList();
while ((lineString = fileInputStream.readLine()) != null) {
Map map = buildData(lineString); //组装数据
n++;
if ((n) % 1000 == 0) {
try {
batchinsert(dataList); //批量插入
} catch (Exception e) {
for(Map tempMap:dataList){
singleInsert(tempMap); // 循环单条进行处理,先删除,后新增
}
}
dataList.clear();
}
}
if (!dataList.isEmpty()) {
......
}
}
弊端:
不应该用try…catch…来实现业务逻辑。
批量写入时,存在重复,就会进入单条处理逻辑,而单条处理逻辑会很耗时间。如果重复数据分布均匀的话,会影响程序效率。
2.在执行插入前对数据进行去重
public void insertData(BufferedReader fileInputStream) throws IOException{
int n = 0;
String lineString = null;
List<Map> dataList = new ArrayList();
Set keySet = new HashSet();
while ((lineString = fileInputStream.readLine()) != null) {
String tempKey = getKey(lineString); //获取主键
if(keySet.contains(tempKey)){ //跳过已存在的数据
continue;
}
keySet.add(tempKey);
Map map = buildData(lineString); //组装数据,
n++;
if ((n) % 1000 == 0) {
batchinsert(dataList); //批量插入
dataList.clear();
}
}
if (!dataList.isEmpty()) {
......
}
}
弊端:
集合会随着程序执行而变得越来越大,进而占用很多资源。
本文方案
通过代码逻辑控制。
上面说明了集合去重的缺陷。那么是否存在一种其它的数据结构能够实现数据去重的效果了?答案是bitmap,布隆过滤器。
布隆过滤器:可以判断一个元素是否存在。具体原理可网上查询
true:这个元素可能存在于这个集合当中
false:这个元素一定不存在于这个集合当中
到此我们可以解决题中问题了。
通过布隆过滤器判断元素是否存在,不存在的数据放入列表A,可能存在的数据放入列表B。当列表A数据量达到1000时,批量写入数据库;当列表B数据量达到1000时,循环单条写入数据库;
public void insertData(BufferedReader fileInputStream) throws IOException{
//谷歌Guava工具类 BloomFilter
int insertions = 10000000;
double fpp = 0.0001;
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), insertions, fpp);
int n = 0;
int m = 0;
String lineString = null;
List<Map> dataListA = new ArrayList();
List<Map> dataListB = new ArrayList();
while ((lineString = fileInputStream.readLine()) != null) {
Map map = buildData(lineString); //组装数据,
String tempKey = getKey(lineString); //获取主键
if(bloomFilter.mightContain(tempKey)){
dataListB.put(map);
n++;
}else {
dataListA.put(map);
bloomFilter.put(tempKey);
m++;
}
if ((n) % 1000 == 0) {
batchinsert(dataListA); //批量插入
dataListA.clear();
}
if ((m) % 1000 == 0) {
for(Map tempMap:dataListB){
singleInsert(tempMap); // 循环单条进行处理,先删除,后新增
dataListB.clear();
}
}
}
if (!dataListA.isEmpty()) {
batchinsert(dataListA); //批量插入
}
if (!dataListB.isEmpty()) {
for(Map tempMap:dataListB){
singleInsert(tempMap); // 循环单条进行处理,先删除,后新增
}
}
}
PS:代码为假代码,且没经过验证,谨慎参考
版权声明:本文为weixin_43828710原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。