曂沅仴駦 发表于 2024-7-12 09:49:49

【Mongodb-01】Mongodb亿级数据性能测试和压测

一,mongodb数据性能测试

如需转载,请标明出处:https://zhenghuisheng.blog.csdn.net/article/details/139505973
之前公司将用户的游戏数据存储在mysql中,就是直接将json数据存储到mysql数据库内里,几个月不到,数据库内里已经有两亿条数据,而且每行中每个json数据量也比力大,导致占用的磁盘容量也比力大,因此为了解决mysql带来多方面的瓶颈,最终选择使用mongodb来取代mysql。为了测试mongodbdb的性能以及是否满意需求,因此做了以下测试,对mongodb在高流量时验证其增删改查的服从,以及对其进行压测
服务器配置:2核4g轻量级服务器 磁盘容量 70GB
每条数据大概在500个字节,索引有一个id主键索引,还有一个parentId和category的团结唯一索引,这里两个字段能保证唯一性,因此用唯一索引服从更优
1,mongodb数据库创建和索引设置

首先在Java代码中创建一个实体类,用这个类作为json对象插入到文档中即可。
@Data
public class Archive {
    private String id;
    //账号id
    private String parentId;
    private String category;
    private String content;
}
随后在mongodb中创建一个数据库,然后再该库下面建立一个名为 archive 的聚集,mongodb的聚集就是类似于mysql的表,两者概念是一样的。由于后期数据量可能非常大,因此根据mongodb官方文档所说,在数据插入前,尽量提前建立索引,为了满意业务需求,这里选择创建一个团结索引,由于我这边业务能保证要加索引的两个字段的唯一性,因此选择直接添加唯一索引
db.users.createIndex({parentId: 1,category:1}, {unique: true})
如果navicate操纵不方便的话,可以安装一个 Mongodb Compass 可视化工具,如下图,很多操纵都是可以在这个可视化图形界面上面直接操纵的
https://img-blog.csdnimg.cn/direct/512757ac78f04887a5f8d371251f5e90.png
2,线程池+批量方式插入数据

由于这边重要是io操纵将数据插入,不需要计算之类的,因此选择使用io密集型线程池,接下来自界说一个线程池
@Slf4j
public class ThreadPoolUtil {
    public static ThreadPoolExecutor pool = null;
    public static synchronized ThreadPoolExecutor getThreadPool() {
      if (pool == null) {
            //获取当前机器的cpu
            int cpuNum = Runtime.getRuntime().availableProcessors();
            int maximumPoolSize = cpuNum * 2 ;
            pool = new ThreadPoolExecutor(
                  maximumPoolSize - 2,
                  maximumPoolSize,
                  5L,   //5s
                  TimeUnit.SECONDS,
                  new LinkedBlockingQueue<>(),//数组有界队列
                  Executors.defaultThreadFactory(), //默认的线程工厂
                  new ThreadPoolExecutor.AbortPolicy());//直接抛异常,默认异常
      }
      return pool;
    }
}
第二步就是界说一个线程任务,到时将任务丢到线程池内里,其代码如下,该任务实现Callable接口,每个线程插入10万条,每次批量插入100条数据,大概就是需要1000次
@Data
public class ArchiveTask implements Callable {
    private MongoTemplate mongoTemplate;
    public ArchiveTask(MongoTemplate mongoTemplate){
      this.mongoTemplate = mongoTemplate;
    }
    @Override
    public Object call() throws Exception {
      List<Archive> list = new ArrayList<>();
      for (int i = 1; i <= 100000; i++) {
            Archive archive = new Archive();
            archive.setCategory("score");
            archive.setId(SnowflakeUtils.nextOrderId());
            archive.setParentId(SnowflakeUtils.nextOrderId());
            Map<String,String> map = new HashMap<>();
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < 15; j++) {
                sb.append(UUID.randomUUID());
            }
            map.put("key" + i, sb.toString());
            archive.setContent(JSON.toJSONString(map));
            list.add(archive);
            if (i%100 == 0){
                mongoTemplate.insertAll(list);
                list.clear();        //手动gc,100个对象没被引用会被回收
                list = new ArrayList<>();
            }
      }
      return null;
    }
}
最后界说一个测试类大概一个接口,我这边使用接口,部分代码如下,循环100次,就是会创建100个线程任务,随后将这个线程任务丢到线程池中,100乘以100000就是1万万条数据
@Resource
private MongoTemplate mongoTemplate;
static ThreadPoolExecutor threadPool = ThreadPoolUtil.getThreadPool();
@GetMapping("/add")
public void test(){
        for (int i = 0; i < 100; i++) {
                ArchiveTask archiveTask = new ArchiveTask(mongoTemplate);
                threadPool.submit(archiveTask);
    }
        log.info("数据添加完成");
}
3,一万万数据性能测试

mongodb性能测试,此时archive 聚集中已有10134114条数据,平均每条数据大小674字节,1千多万条,此时的存储大小为5.5个g,索引的总大小为459m
接下来通过唯一索引查询一条数据,这里直接通过parentId查询一条数据,此时数据还是在不绝插入的
db.archive.find({parentId:"2405291858848274156091867143"})
是的,如下图所示,1000多万条数据内里查询,只需要25ms即可将数据放回,当然这里没有在高流量的环境下进行压测。
https://img-blog.csdnimg.cn/direct/12bb345991e94ff9bf8bfb4c5984d1c2.png
4,两万万数据性能测试

此时archive聚集来到了两万万条,每条数据和之前一样,平均大小是674字节,数据总大小来到了10.92G,内存大小12.65g,索引总大小是913m
https://img-blog.csdnimg.cn/direct/3856c1ce5fb8463a93672cc35a23d874.png
接下来测试查询服从,仍旧使用上面的这个parentId,由于设置的是parentId+category的团结唯一索引,接下来两个参数一起查
db.archive.find({parentId:"2405291858848274156091867143",category:"score"})
2000万的数据查询效果如下,只需要21ms,和上面的25ms慢了快要4ms,但是这4ms可以忽略
https://img-blog.csdnimg.cn/direct/5d59c7e290314c4db3e3a2dd15da42d0.png
5,五万万数据性能测试

由于70G的磁盘容量已经只剩48G,因此在content字段将500字节的值调小,调整到150个字节,以便能插入更多数据。将上面的StringBuilder拼接的15个uuid改成1个uuid
map.put("key" + i,UUID.randomUUID().toString());
此时数据来到50245694条数据,每条数据平均大小372kb,总存储大小12.66g,内存中的总大小17.45g,索引大小现在只有2.8g
https://img-blog.csdnimg.cn/direct/1ff80429e29043a88286ef5ef1aff0af.png
为了保证拿到的parentId是一次没有查询过的,手动的插入一批数据,手动单条插入20条数据,耗时600ms,在插入数据时会改变索引,插入数据会轻微慢些。此时的插入操纵都是在多线程插入大量数据的时候测试的
db.archive.insertOne({parentId:"2024111222337",category:"score1",content:"cbasbsadhpasdbsaodgs"})
db.archive.insertOne({parentId:"2024111222337",category:"score2",content:"cbasbsadhpasdbsaodgs"})
....
此时第一次查询这条数据,共耗时153ms,共查出20条数据
https://img-blog.csdnimg.cn/direct/5aece2cf5b0d4189af65d3c939d03561.png
再第二次查询之后,花费78ms,内部应该也是会将查询效果参加到缓存中,方便第二次查询
https://img-blog.csdnimg.cn/direct/0796f4cf8018415fadd7b4328f5297c1.png
在上面的插入操纵中由于会破坏到索引布局,因此耗时久一点。接下来看这个更新操纵,
db.archive.updateOne(
    { parentId: "2024111222337",category:"score1" },
    { $set: { content: "cbasbsadhpasdbsaodgsscore" } }
);
其效果如下,更新了一条数据,只花费了13毫秒的时间,因此更新操纵速度是很快的。由于这里每一条数据都是唯一数据,因此不测试批量更新
https://img-blog.csdnimg.cn/direct/7e3f67eb933a413981adc6e60ff772c5.png
最后测试删除数据,将这20条数据全部删除,统共花费18毫秒
https://img-blog.csdnimg.cn/direct/0f471b5a71cb498283e973b2b2ebe755.png
6,一亿条数据性能测试

数据通过多线程+批量插入的方式来到一亿条,存储大小15.5g,索引长度是6g
db.archive.countDocuments()//查询共有多少条数据
100082694
https://img-blog.csdnimg.cn/direct/e8d771d0b08f48e0985dff4a18b3a08a.png
接下来往内里重新插入一部分数据,往内里插入20条数据,大概花费160多ms,插入数据会导致索引重构,以是耗时久一些,批量插入性能会更快。重新插入的数据可以保证这条数据没被查过,并且知道parentId是什么
db.archive.insertOne({parentId:"20240531101059",category:"score1",content:"abcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxyabcdefghijklmnopqrstuvwxy"})
....
接下来测试查询数据,只需要19ms
db.archive.find({parentId:"20240531101054"},{parentId:1,category:1}) //只返回部分字段
db.archive.find({parentId:"20240531101058"})
https://img-blog.csdnimg.cn/direct/002c9fa098854f21902e0553dca47330.png
更新数据如下,只需要10ms
db.archive.updateOne(
    { parentId: "20240531101059",category:"score1" },
    { $set: { content: "cbasbsadhpasdbsaodgsscore" } }
);
https://img-blog.csdnimg.cn/direct/634a34cf340f4c2a842f7ee42c702635.png
7,压测

以下压测都是数据达到1亿之后进行测试的,并且都是使用的2核4g的服务器
在1s内同时1000个线程插入数据,每个线程插入20条数据,中位数24,吞吐量391
https://img-blog.csdnimg.cn/direct/ed1ebbce006948f2833de5b092b14df6.png
在1s内10000个线程插入数据,也是每个线程批量插入20条数据,可以发现就算是2核4g这么垃圾的轻量级服务器,10000qps也是毫无压力的
https://img-blog.csdnimg.cn/direct/c8a9d69119e94595b293b1c57e23dd5f.png
插入数据会破坏索引,相对于修改和查询是更慢的,接下来测试1s内10000个线程同时执行增改查,吞吐量可以达到2251.7
https://img-blog.csdnimg.cn/direct/1256de73f7a044ce95925ede26aed2ee.png
部分代码片段如下,让10000个线程随机的执行增改查的操纵,在1s内是毫无压力的
https://img-blog.csdnimg.cn/direct/99aa4cae8a5f4e81b98af143f1da5964.png
8,总结

通过上面的数据以及mongodb的相应来看,mongodb的性能还黑白常不错的。看看GPT对这种数据的评价,gpt也以为mongodb黑白常符合的。当然不管什么数据和业务,只要其本质是 json 数据,不管json内部布局多复杂,用mongodb都黑白常符合的。mongodb还适合存一些订单数据,地理数据,大数据等等,其应用范围黑白常广泛的
https://img-blog.csdnimg.cn/direct/dfd80dcd7e0549aa85a38938f917a6ae.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Mongodb-01】Mongodb亿级数据性能测试和压测