本文章主要讲解不同场景下,可以使用的MongoDB压测方法。并主要介绍实际业务场景下,如何使用jmeter对MongoDB压测。
适用范围:仅对读写比例有要求,对具体插入内容无要求的压测场景。
压测方法:YCSB压测MongoDB
税务数字账户整体业务场景中,存储纳税人主数据、记账明细数据及凭证数据使用的档案库为MongoDB,本次主要对MongoDB进行性能验证。
适用范围:MongoDB3.*版本,且表中不涉及分片。
压测方法:
使用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
打开apache-jmeter-*/bin/ jmeter.properties,搜索“not_in_menu”,将 MongoDB 相关的元件入口从这个地方移除即可。
重启 jmeter,就可以在配置元件中,找到 MongoDB Source Config,在取样器下面,找到 MongoDB Script。
在 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()。
适用范围:MongoDB任意版本。
根据 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
在线程组下,新增 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0}
插入:
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);
}
查询:
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);
}
由于上述脚本中,每次插入、查询操作都会新建一次连接,增加并发后,性能会因为新建连接耗时而影响,无法测试出真实数据。
因此,通过在线程组中添加事务控制器,编写连接数据库方法作为连接池;添加循环控制器,编写数据库操作方法产生压力的方法优化脚本。
在事务控制器中添加 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0},将连接的返回数据存在公共变量中。
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);
}
在循环控制器中添加 JSR223 Sampler, 语言选择 groovy {Groovy 3.0.7 / Groovy Scripting Engine 2.0},引用“collection”变量。
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);
}
压测方法 | 问题 | 后续操作/问题定位 | 结论 |
---|---|---|---|
使用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 删除。