有些时候我们在多线程或多服务器处理的时候,难免会遇到写相同的数据到数据库中的情况。下面测试三种不同方式来插入数据的耗时。
1. 环境
- 硬件:阿里云 4 vCPU(Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz) 16 GiB
- 系统:CentOS 7.6 64位
- MongoDB:3.2.8版本,直接安装默认配置,未做任何优化
- Java环境:JDB 8u92
- MongoDB驱动: mongodb-driver 3.8.2
ps: 突然发现环境都好老了,不想更新不想折腾哈。
2. 生成数据
这里测试插入10万条数据,其中有1万条是重复的。数据包含一个_id、一个随机字符串、一个随机浮点数和一个时间戳。
final int count = 100000; // 10万条 总数据量
final int repeat = 10000; // 1万条 重复数据
// 生成 9万 条随机数据 列表
List<Document> list = new ArrayList<>();
for (int i = 0; i < count-repeat; i++){
Document test = new Document("_id",""+i)
.append("data1", UUID.randomUUID().toString())
.append("date2",Math.random())
.append("time",System.currentTimeMillis());
list.add(test);
}
// 随机抽取 1万 条数据随机插入列表
for (int i = 0; i < repeat; i++){
list.add((int)(Math.random()*list.size()),list.get((int)(Math.random()*list.size())));
}
3. 三种插入数据方式
以循环单条插入数据做测试,db连接一次并不释放。
// 第一种使用update,直接插入并更新操作
long time0 = System.currentTimeMillis();
MongoCollection<Document> test1 = db.getCollection("test1");
for (Document data : list){
UpdateOptions updateOptions = new UpdateOptions();
updateOptions.upsert(true);
test1.replaceOne(eq("_id", data.get("_id")), data, updateOptions);
}
long time1 = System.currentTimeMillis();
Log.trace("test1 time="+(time1-time0));
// 第二种,先查找,不存在就插入
MongoCollection<Document> test2 = db.getCollection("test2");
for (Document data : list){
if(test2.find(eq("_id", data.get("_id"))).first() == null){
test2.insertOne(data);
}
}
long time2 = System.currentTimeMillis();
Log.trace("test2 time="+(time2-time1));
// 第三种,直接插入,try catch异常不处理
MongoCollection<Document> test3 = db.getCollection("test3");
for (Document data : list){
try{
test3.insertOne(data);
}catch (Exception e){
}
}
long time3 = System.currentTimeMillis();
Log.trace("test3 time="+(time3-time2));
4. 测试
- 10万条其中1万条重复得到耗时为
2020-03-10 01:29:33.046 [TRACE] - test1 time=20055
2020-03-10 01:30:01.720 [TRACE] - test2 time=28674
2020-03-10 01:30:16.502 [TRACE] - test3 time=14782
如果数据表中已经存在这10万条数据,再插入重复的10万条数据的耗时
2020-03-10 01:36:17.880 [TRACE] - test1 time=21037 2020-03-10 01:36:32.812 [TRACE] - test2 time=14932 2020-03-10 01:36:50.868 [TRACE] - test3 time=18056
数据理改到100万条数据,其中1万条重复测试
2020-03-10 01:45:24.889 [TRACE] - test1 time=175464 2020-03-10 01:50:16.116 [TRACE] - test2 time=291226 2020-03-10 01:52:37.650 [TRACE] - test3 time=141535
5. 结论
- 第一种:使用update方式插入数据,这个耗时与是否存在当前数据相差不是很大,它是先查找到对应数据,如果存在修改,不存在就插入新数据。
- 第二种:先查找就更容易理解了,如果数据库里都有了,省了一步骤插入操作,所以会快很多。
- 第三种:如果数据库存在的量少,就直接插入了。如果存在的量大,多的消耗就是java处理insertOne抛出的异常上了。
- 综上所述:在确定数据重复存不超过一半的情况下,使用先查找的方法最耗时。而直接插入捕获异常的方式最省时。如果纯重复数据,建议用第三种方法来处理。