MongoDB重复数据插入性能测试

有些时候我们在多线程或多服务器处理的时候,难免会遇到写相同的数据到数据库中的情况。下面测试三种不同方式来插入数据的耗时。

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抛出的异常上了。
  • 综上所述:在确定数据重复存不超过一半的情况下,使用先查找的方法最耗时。而直接插入捕获异常的方式最省时。如果纯重复数据,建议用第三种方法来处理。
0%