前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MongoDB压力测试方法实践-jmeter

MongoDB压力测试方法实践-jmeter

原创
作者头像
刘嘉俊
修改2022-11-29 18:46:36
3.4K0
修改2022-11-29 18:46:36
举报

引言

本文章主要讲解不同场景下,可以使用的MongoDB压测方法。并主要介绍实际业务场景下,如何使用jmeter对MongoDB压测。

一、基准测试,无实际业务场景压测方法

1、使用YCSB工具压测

适用范围:仅对读写比例有要求,对具体插入内容无要求的压测场景。

压测方法:YCSB压测MongoDB

二、有实际业务场景压测方法

业务压测背景

税务数字账户整体业务场景中,存储纳税人主数据、记账明细数据及凭证数据使用的档案库为MongoDB,本次主要对MongoDB进行性能验证。

1、使用jmeter自带“MongoDB Source Config”、“MongoDB Script”组件进行压测(需修改jmeter.properties配置,该组件被jmeter禁用)

适用范围:MongoDB3.*版本,且表中不涉及分片。

压测方法:

(1)替换jmeter自带MongoDB驱动jar包

使用jmeter自带MongoDB驱动版本无法通过用户名/密码方式连接数据库,需替换原有驱动至mongo-java-driver2.12.*-2.14.3版本。

将下载的 mongo-java-driver-*.jar 包放到apache-jmeter-*/lib中,备份原来的 mongo-java-driver-2.11.3.jar 包。

附:mongo-java-driver-2.14.3下载地址:Download mongo-java-driver-2.14.3.jar file

(2)修改jmeter配置文件,解除界面MongoDB 取样器的限制

打开apache-jmeter-*/bin/ jmeter.properties,搜索“not_in_menu”,将 MongoDB 相关的元件入口从这个地方移除即可。

重启 jmeter,就可以在配置元件中,找到 MongoDB Source Config,在取样器下面,找到 MongoDB Script。

(3)编写压测脚本

在 MongoDB Source Config 中配置 Server Address List: 服务器 ip:端口 、在MongoDB Source 中自定义一个资源名

在 MongoDB Script 中配置 MongoDB Source 为上一步配置的资源名 Database Name 中填写数据名,并在script 中,写上mongo语句

插入:db.collection.insert()

查询:这里需要注意,直接使用db.collection.find()会返回报错,需要使用db.collection.find().toArray()。

2、使用jmeter写 groovy 脚本调用 MongoDB(推荐)

适用范围:MongoDB任意版本。

通用压测方法

(1)替换jmeter自带mongo驱动jar包

根据 MongoDB 服务器的版本,下载对应兼容的mongo-java-driver 版本,参考下表。

我这里使用的是 MongoDB 3.6版本,所以使用mongo-java-driver-3.8.2.jar版本。

将下载的 mongo-java-driver-*.jar 包放到apache-jmeter-*/lib中,备份原来的 mongo-java-driver-2.11.3.jar 包。

重启 jmeter。

附:mongo-java-driver-3.8.2下载地址:Download mongo-java-driver-3.8.2.jar file

(2)编写 groovy 脚本

在线程组下,新增 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0}

插入:

代码语言:javascript
复制
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import java.util.Arrays;

try {
	// 建立数据库连接
	MongoClient mongoClient = MongoClients.create("mongodb://xxxx:xxxxx@127.0.0.1:27017/lewis_test");
	// 连接 数据库  lewis_test为数据库名
	MongoDatabase database = mongoClient.getDatabase("lewis_test");
	// 连接 数据集 lewis_test_1为数据集名
	MongoCollection<Document> collection = database.getCollection("lewis_test_1");
	// 构造数据
	Document document = new Document("id", "111111")
		.append("number", 15890764)
		.append("number1", 12234)
		.append("number2", 44321)
		.append("number3", 112311)
		.append("box", new Document("num", "01").append("name", "xxx"))
		.append("box", new Document("num", "02").append("name", "xxx"))
		.append("object", Arrays.asList(new Document("id", "1111111")));
	// 插入1条数据
	collection.insertOne(document);

	return "Document inserted";
}
catch (Exception e) {
	SampleResult.setSuccessful(false);
	SampleResult.setResponseCode("500");
	SampleResult.setResponseMessage("Exception: " + e);
}

查询:

代码语言:javascript
复制
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.FindIterable;
import static com.mongodb.client.model.Filters.*;
import org.bson.types.ObjectId;
import org.bson.Document;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

try {
	// 建立数据库连接
	MongoClient mongoClient = MongoClients.create("mongodb://xxxxx:xxxxxx@127.0.0.1:27017/lewis_test");
	// 连接 数据库  lewis_test为数据库名
	MongoDatabase database = mongoClient.getDatabase("lewis_test");
	// 连接 数据集 lewis_test_1为数据集名
	MongoCollection<Document> collection = database.getCollection("lewis_test_1");

//	主表只涉及1条数据
	Document result = collection.find(eq("id", "11111111")).first();

	return "查询:" + result;
	

}
catch (Exception e) {
	SampleResult.setSuccessful(false);
	SampleResult.setResponseCode("500");
	SampleResult.setResponseMessage("Exception: " + e);
}

进阶压测方法

由于上述脚本中,每次插入、查询操作都会新建一次连接,增加并发后,性能会因为新建连接耗时而影响,无法测试出真实数据

因此,通过在线程组中添加事务控制器,编写连接数据库方法作为连接池;添加循环控制器,编写数据库操作方法产生压力的方法优化脚本。

(1)新建事务控制器,编写连接数据库方法作为连接池

在事务控制器中添加 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0},将连接的返回数据存在公共变量中。

代码语言:javascript
复制
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;

import org.bson.Document;

import java.util.Arrays;

try {
	MongoClient mongoClient = MongoClients.create("mongodb://xxxx:xxxxxx@127.0.0.1:27017/lewis_test?maxPoolSize=100");
	// 连接 数据库  lewis_test为数据库名
	MongoDatabase database = mongoClient.getDatabase("lewis_test");
	// 连接 数据集 lewis_test_1为数据集名
	MongoCollection<Document> collection = database.getCollection("lewis_test_1");
	//存储collection对象
	vars.putObject("collection", collection);
	return "Connected to lewis_test";
}
catch (Exception e) {
	SampleResult.setSuccessful(false);
	SampleResult.setResponseCode("500");
	SampleResult.setResponseMessage("Exception: " + e);
}

(2)在事务控制器中新建循环控制器,编写操作数据库语句

在循环控制器中添加 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0},引用“collection”变量。

代码语言:javascript
复制
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.FindIterable;
import static com.mongodb.client.model.Filters.*;
import org.bson.types.ObjectId;
import org.bson.Document;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

try {
     //获取连接对象collection
	MongoCollection<Document> collection = vars.getObject("collection");

	//主表只涉及1条数据
	Document result = collection.find(eq("id", "111111111")).first();

	return "查询:" + result;
	

}
catch (Exception e) {
	SampleResult.setSuccessful(false);
	SampleResult.setResponseCode("500");
	SampleResult.setResponseMessage("Exception: " + e);
}

Q&A

压测方法

问题

后续操作/问题定位

结论

使用jmeter组件对MongoDB进行压测

mongo-java-driver版本不匹配,无法链接数据库,鉴权失败报错

更换更高版本mongo-java-driver尝试

使用mongo-java-driver2.12.*-2.14.3版本可以成功连接数据库

使用db.collection.find()方法查询失败

jmeter 使用 MongoDB 的 Java 模型,因此它与 shell 有点不同

db.collection.find().toArray()可以成功查询

当需要插入的表带有分片件后,无法进行插入操作

由于mongo为3.6版本,需要对应的mongo-java-driver3.6.*及以上,但jmeter使用该版本无法正常工作。

放弃该压测方法!!该压测方法仅适用于:mongodb3.*,且表中不涉及分片。

创建业务pod,jmeter压接口

pod资源不足,压力会在pod而不是数据库

放弃该方法。

使用YCSB压测

自定义分片的表中进行插入压测,会报错无此主键

YCSB已经将插入的脚本写好,无法自定义插入、查询、删除、更新的内容

放弃该方法,不符合此次压测场景。该压测方法适用于:仅对读写比例有要求,对具体插入内容无要求的压测场景。

使用jmeter通过写 groovy 脚本对 MongoDB进行压测

一个方法里每次建立连接会产生大量耗时,压力无法给到数据库

在线程组中,将建立连接方法写到事务控制器中,将建立连接的对象存入变量中,然后使用循环控制器,获取连接对象,对数据库操作方法进行循环

每一个线程只连接一次数据库,问题解决。

插入数据的id需要递增,当并发量过大时,多线程同时抢一个count计数器,导致发压性能下降

当并发量过大,排在后面的线程提前结束需要新的数,但计数器需要先给他之前的线程分配数。也就是出现了锁。

不使用计数器,通过给id字段附随机数(很大范围)的方式实现,不会出现锁。问题解决。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 一、基准测试,无实际业务场景压测方法
    • 1、使用YCSB工具压测
    • 二、有实际业务场景压测方法
      • 业务压测背景
        • 1、使用jmeter自带“MongoDB Source Config”、“MongoDB Script”组件进行压测(需修改jmeter.properties配置,该组件被jmeter禁用)
          • (1)替换jmeter自带MongoDB驱动jar包
          • (2)修改jmeter配置文件,解除界面MongoDB 取样器的限制
          • (3)编写压测脚本
        • 2、使用jmeter写 groovy 脚本调用 MongoDB(推荐)
          • 通用压测方法
          • (1)替换jmeter自带mongo驱动jar包
          • (2)编写 groovy 脚本
          • 进阶压测方法
          • (1)新建事务控制器,编写连接数据库方法作为连接池
          • (2)在事务控制器中新建循环控制器,编写操作数据库语句
      • Q&A
      相关产品与服务
      云数据库 MongoDB
      腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档