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

mongodb数据性能测试

  • 一,mongodb数据性能测试
        • 1,mongodb数据库创建和索引设置
        • 2,线程池+批量方式插入数据
        • 3,一千万数据性能测试
        • 4,两千万数据性能测试
        • 5,五千万数据性能测试
        • 6,一亿条数据性能测试
        • 7,压测
        • 8,总结

一,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 可视化工具,如下图,很多操作都是可以在这个可视化图形界面上面直接操作的
在这里插入图片描述

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即可将数据放回,当然这里没有在高流量的情况下进行压测。

在这里插入图片描述

4,两千万数据性能测试

此时archive集合来到了两千万条,每条数据和之前一样,平均大小是674字节,数据总大小来到了10.92G,内存大小12.65g,索引总大小是913m
在这里插入图片描述

接下来测试查询效率,依旧使用上面的这个parentId,由于设置的是parentId+category的联合唯一索引,接下来两个参数一起查

db.archive.find({parentId:"2405291858848274156091867143",category:"score"})

2000万的数据查询结果如下,只需要21ms,和上面的25ms慢了将近4ms,但是这4ms可以忽略

在这里插入图片描述

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

在这里插入图片描述

为了保证拿到的parentId是一次没有查询过的,手动的插入一批数据,手动单条插入20条数据,耗时600ms,在插入数据时会改变索引,插入数据会稍微慢些。此时的插入操作都是在多线程插入大量数据的时候测试的

db.archive.insertOne({parentId:"2024111222337",category:"score1",content:"cbasbsadhpasdbsaodgs"})
db.archive.insertOne({parentId:"2024111222337",category:"score2",content:"cbasbsadhpasdbsaodgs"})
....

此时第一次查询这条数据,共耗时153ms,共查出20条数据

在这里插入图片描述

再第二次查询之后,花费78ms,内部应该也是会将查询结果加入到缓存中,方便第二次查询

在这里插入图片描述

在上面的插入操作中由于会破坏到索引结构,因此耗时久一点。接下来看这个更新操作,

db.archive.updateOne(
    { parentId: "2024111222337",category:"score1" },
    { $set: { content: "cbasbsadhpasdbsaodgsscore" } }
);

其结果如下,更新了一条数据,只花费了13毫秒的时间,因此更新操作速度是很快的。由于这里每一条数据都是唯一数据,因此不测试批量更新

在这里插入图片描述

最后测试删除数据,将这20条数据全部删除,总共花费18毫秒

在这里插入图片描述

6,一亿条数据性能测试

数据通过多线程+批量插入的方式来到一亿条,存储大小15.5g,索引长度是6g

db.archive.countDocuments()  //查询共有多少条数据
100082694

在这里插入图片描述

接下来往里面重新插入一部分数据,往里面插入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"})

在这里插入图片描述

更新数据如下,只需要10ms

db.archive.updateOne(
    { parentId: "20240531101059",category:"score1" },
    { $set: { content: "cbasbsadhpasdbsaodgsscore" } }
);

在这里插入图片描述

7,压测

以下压测都是数据达到1亿之后进行测试的,并且都是使用的2核4g的服务器

在1s内同时1000个线程插入数据,每个线程插入20条数据,中位数24,吞吐量391

在这里插入图片描述

在1s内10000个线程插入数据,也是每个线程批量插入20条数据,可以发现就算是2核4g这么垃圾的轻量级服务器,10000qps也是毫无压力的

在这里插入图片描述

插入数据会破坏索引,相对于修改和查询是更慢的,接下来测试1s内10000个线程同时执行增改查,吞吐量可以达到2251.7

在这里插入图片描述

部分代码片段如下,让10000个线程随机的执行增改查的操作,在1s内是毫无压力的

在这里插入图片描述

8,总结

通过上面的数据以及mongodb的响应来看,mongodb的性能还是非常不错的。看看GPT对这种数据的评价,gpt也认为mongodb是非常合适的。当然不管什么数据和业务,只要其本质是 json 数据,不管json内部结构多复杂,用mongodb都是非常合适的。mongodb还适合存一些订单数据,地理数据,大数据等等,其应用范围是非常广泛的

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/711374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java新特性与性能调优

引言 Java不断演进&#xff0c;每个新版本都引入了新的特性和改进&#xff0c;帮助开发者在提高生产力的同时&#xff0c;也能更好地优化程序性能。本文将详细介绍Java新版本中的重要特性&#xff0c;如从Java 8到Java 17&#xff0c;并探讨性能调优的方法&#xff0c;包括JVM调…

Python自动化办公(二) —— 查看文件夹中的PDF文件数量

Python自动化办公&#xff08;二&#xff09; —— 查看文件夹中的PDF文件数量 在日常办公中&#xff0c;我们经常需要统计某个文件夹中的PDF文件数量。手动操作不仅费时费力&#xff0c;而且容易出错。幸运的是&#xff0c;使用Python可以轻松实现这个过程。本文将介绍如何使用…

【字符串函数2】

5. strncpy 函数的使用和模拟实现 选择性拷贝 char * strncpy ( char * destination, const char * source, size_t num ); 1.拷贝num个字符从源字符串到目标空间。 2.如果源字符串的⻓度⼩于num&#xff0c;则拷⻉完源字符串之后&#xff0c;在⽬标的后边 追加0 &#…

MyBatis-Plus整合达梦数据库

文章目录 1. 环境准备2. 创建Spring Boot项目3. 引入依赖4. 配置数据源5. 配置MyBatis-Plus6. 创建实体类7. 创建Mapper接口8. 创建Service类9. 创建Controller类10. 创建Mapper XML文件11. 测试12. 进一步优化12.1 配置分页插件12.2 配置乐观锁插件13. 总结🎉欢迎来到Java学…

案例学习-存量更新规划实施探索(武汉)

案例学习-存量更新规划实施探索&#xff08;武汉&#xff09; 武汉市在早期旧城更新实践中发现零散化的更新往往导致资源配置分散、城市建设破碎化等弊病&#xff0c;特别是由于过于强调项目自身“经济平衡”&#xff0c;在实施过程中也逐步暴露出住宅占比过大、强度偏高、公服…

屹晶微EG3002 单通道功率MOSFET驱动芯片 贴片SOP8

EG3002作为一款功率MOSFET驱动芯片&#xff0c;它的应用领域主要取决于其技术参数和性能特点。根据之前提供的信息&#xff0c;EG3002可能适用于以下领域&#xff1a; 1. 电源管理&#xff1a;用于高效率电源转换器&#xff0c;如开关电源&#xff08;SMPS&#xff09;、电池充…

yolov8通过训练完成的模型生成图片热力图--论文需要

源代码来自于网络 使用pytorch_grad_cam&#xff0c;对特定图片生成热力图结果。 安装热力图工具 pip install pytorch_grad_cam pip install grad-cam# get_params中的参数&#xff1a; # weight&#xff1a; # 模型权重文件&#xff0c;代码默认是yolov8m.pt # c…

希亦、添可、石头洗地机哪款好用?2024洗地机深度测评

今年的洗地机市场竞争异常激烈&#xff0c;各大品牌纷纷推出了自己的旗舰产品。这对消费者来说是个好消息&#xff0c;因为有更多的选择空间。然而&#xff0c;面对如此多的优质洗地机&#xff0c;选择合适的一款也成了一种“幸福的烦恼”。 作为一个专业的测评人士&#xff0…

【笔记】【矩阵的二分】668. 乘法表中第k小的数

力扣链接&#xff1a;题目 参考地址&#xff1a;参考 思路&#xff1a;二分查找 把矩阵想象成一维的已排好序的数组&#xff0c;用二分法找第k小的数字。 假设m行n列&#xff0c;则对应一维下标范围是从1到mn&#xff0c;初始&#xff1a; l1; rmn; mid(lr)/2 设mid在第i行&a…

【源码】综合股票币币合约交易所源码/etf交易所源码/美股港股台股交易所源码

支持多国语言 全开源可二开的一个版本&#xff01;支持虚拟货币 ETF 外汇 美股 A股 港股 台股。 前端是VUE开发&#xff08;带vue工程源码&#xff09;后端JAVA开发&#xff01;搭建也相对简单。 总的来说功能非常强大&#xff0c;适合线上运营的一个版本&#xff0c;有兴趣的可…

Linux--08---挂载分区

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.查看系统磁盘分区情况1.lsblk 查看2.fdisk -l 2.挂载未分区磁盘1. 创建分区2. 格式化分区3. 创建挂载点4. 挂载分区5. 更新 /etc/fstab6.验证挂载 3.修改挂载的磁…

样本学习:当AI遇上“少见多怪”

东汉名臣牟融在其著作《牟子》写道&#xff1a;“少所见&#xff0c;多所怪&#xff0c;睹橐驼&#xff0c;谓马肿背。”意思是见闻少的人遇到不常见的事物就觉得奇怪&#xff0c;见到骆驼也以为是背肿了的马。因此&#xff0c;后人总用“少见多怪”来嘲笑见识浅陋的人。然而&a…

springboot依赖管理和自动配置

依赖管理和自动配置 依赖管理和自动配置依赖管理什么是依赖管理修改自动仲裁/默认版本号 starter场景启动器starter场景启动器基本介绍官方提供的starter第三方starter 自动配置自动配置基本介绍SpringBoot自动配置了哪些?如何修改默认配置如何修改默认扫描包结构resources\ap…

profile-3d-contrib,github三维立体图的使用

图片展示: 提示: 这个profile-3d-contrib存储库有时候会出现问题,导致又有使用这个存储库svg的用户显示出现问题. 参考: https://zhuanlan.zhihu.com/p/681786778 原仓库链接&#xff1a; GitHub - yoshi389111/github-profile-3d-contrib: This GitHub Action creates a Gi…

南通国际高中有哪些?南通惠立学校高中部校长见面日重磅来袭

惠灵顿&#xff08;中国&#xff09;自2011年成立以来&#xff0c;一直坚持深耕国际与双语教育&#xff0c;拥有丰厚的办学经验。依托于集团化的深厚经验南通惠立学校于2024-2025学年开设9-11年级&#xff0c;这所南通国际高中为高中学生搭建一个集卓越升学成果、强大师资、纯正…

零散的面试题

★1.java常见的引用类型 强:普通的变量引用 软:内存够时,GC不会主动删除,内存不够时,GC会删除 弱:一旦执行GC就会被删除 虚:用了感觉没用 ★2.JDK1.8新特性 lambda表达式(极大简化了匿名内部类的创建&#xff0c;促进函数式编程的风格)函数式接口(只能有一个抽象方法的接口 )日…

模型 WOOP

说明&#xff1a;系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。不再拖延和懒惰&#xff0c;让梦想照进现实。 1 WOOP模型的应用 1.1 WOOP模型提高自己健身习惯 如果你想要养成健身的习惯&#xff0c;那么使用WOOP模型来提高自己健身习惯&#xf…

数据库系统概念(第七周 第二堂)(E-R模型转关系模式)

前言 前一堂课我们深入研究了E-R模型的画法和要点&#xff0c;学习E-R模型肯定是为了给数据库表格设计提供帮助。数据库表格设计就是关系模式设计&#xff0c;数据库表就是关系模式的实例化。所以本堂课&#xff0c;我们来看E-R模型如何转为关系模式。 转化原则 转化步骤 转…

深入理解指针(三)

目录 1.字符指针变量 2. 数组指针变量 2.1 数组指针变量是什么? 2.2 数组指针变量怎么初始化 3. ⼆维数组传参的本质 4. 函数指针变量 4.1 函数指针变量的创建 4.2 函数指针变量的使用 4.3 两段有趣的代码 4.3.1 typedef关键字 5. 函数指针数组 6. 转移表 1.字符指…

springboot与flowable(7):流程变量

一、启动时添加流程变量 拿第一个流程图举例&#xff0c;创建一个新的流程定义。 Testvoid contextLoads() {DeploymentBuilder deployment repositoryService.createDeployment();deployment.addClasspathResource("process01/FirstFlow.bpmn20.xml");deployment.…