MongoDB爱好者
垂直技术交流平台

使用JMeter做MongoDB性能测试

我们先了解一点MongoDB的知识,然后,学习构建一个用于测试的脚本。
作者 Konstantin Tonkov 2019年1月2日
对大多数应用环境来说,数据库是一个关键要素。如何存储数据以及在哪里存储数据,对整个系统的性能会产生巨大影响。因此,在做开发之前,数据库的选择肯定是最重要的决定之一。对数据库进行性能测试有助于你达成此项决定,这也是你在开发过程中的一项重要工作。
这篇文章会教你使用Apache JMeter™进行开源MongoDB数据库测试。我们看看到如何来做:
* 连接MongoDB
* 在MongoDB中写入文档(译者注:此处文档指表中的记录行)
* 从MongoDB中读取文档
* 在MongoDB中更新文档
* 从MongoDB中删除文档

使用JMeter进行性能测试

如果你对应用程序出现性能问题,既可能是低效的数据库查询问题,也可能是不充足的数据库服务器。如果你有一个关系型数据库,JMeter的JDBC请求案例允许你执行一个SQL查询并评估其性能。但有时候,一个非关系数据库对于你的需求来说是一个更有效的选择,因此你需要使用JMeter加载测试以找到一个不同的方法。
MongoDB是一种非常流行的非关系型数据库,它使用“文档”这种结构存储数据。 MongoDB的实例发送给一个查询。不过,这一操作在查询执行期间会实现对数据库的锁定。这会限制你一次只能发起一个请求,这对性能测试来说是不够的。
幸运的是,通过使用JSR223样例和MongoDB Java驱动库,你可以在Java中写请求测试你的MongoDB样例。我们来了解一点关于MongoDB的知识,然后学习构建一个用于测试的脚本。

MongoDB是什么?

MongoDB是一个免费的,开源的,跨平台的,非关系型,基于文档的数据库,其数据存储于JSON类文档:

{

firstName: “Tester”,

lastName: “Testovsky”,
age: 30,
occupation: “QA engineer”,
skills: [
“JMeter”,
“Load Testing”,
“Bad Puns”
]
address: [{
city: “Quality-city”,
street: “Performance ave.”,
house: 12
}]
}

一个文档是一组字段值对,此处的值可以是任何BSON数据类型,数组,其他文档和文档数组。
在MongoDB中,文档存储在所谓的“集合”(类似于关系型数据库的表)当中。集合存储在数据库中,每个MongoDB服务器包含大量数据库。

MongoDB Java 驱动

通过java代码使用有力的MongoDB Java 驱动控制你的MongoDb实例是可以实现的。这个库为你提供了连接MongoDB实例的能力;用它可以创建,读取,更新和删除文档乃至做更多工作。这里可以找到完整的3.0版的API文档。还有特别有用的带有实例和教程参考指南。
为了在JMeter脚本中使用MongoDB Java 驱动,下载最近的mongo-java-driver jar 文件,并将其放在你的JMeter主文件夹的ib/ext文件夹下面。
注意:迄今为止,JMeter发布版有一个旧版本的存放在mongo-java驱动库。这会导致大量的兼容性问题,因此,为了免出问题,从lib文件夹下删除旧有的jar格式 mongo-java驱动文件。
我们来看一下,我们如何在一个JSR233案例使用这个驱动完成基本操作来评估我们的数据库的性能。

JMeter连接MongoDB数据库

为了测试你的数据库性能,你需要首先通过你的JMeter脚本连接数据库。这可以通过JMeter JSR223案例实现。你可以使用这个例子评估一个连接过程的性能,然后使用这种建立的连接检查查询DB入口的性能。依赖你的数据库系统配置,可能需要在连接过程中完成指定的行为。我们来看一些基本案例。
使用指定的端口27017连接localhost上的MongoDB客户端:

import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
MongoClient mongoClient = MongoClients.create();

你可以指定一个连接串作为MongoClients.create() 方法的参数:
MongoClient mongoClient = MongoClients.create("mongodb://mongohost:27017");
如果你需要提供连接到MongoDB客户端的证明:
MongoClient mongoClient = MongoClients.create("mongodb://user:password@mongohost/?authSource=userdb&ssl=true");
你可能经常会使用JMeter变量作为一个MongoClients.create()方法的参数。为了保证你的脚本的可读性,你可以使用一个MongoClientSettings类。例如:
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import java.util.Arrays;
String mongoUser = vars.get("mongoUser");
String userDB = vars.get("userDB");
char[] password = vars.get("password").toCharArray();
MongoCredential credential = MongoCredential.createCredential(mongoUser, userDB, password);
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings {builder ->
builder.hosts(Arrays.asList(new ServerAddress(vars.get("mongoHost"),vars.get("mongoPort").toInteger())))}
.build();
MongoClient mongoClient = MongoClients.create(settings);

在你和客户端建立连接之后,可以访问数据库和集合:
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
MongoDatabase database = mongoClient.getDatabase("jmeter_test");
MongoCollection collection = database.getCollection("blazemeter_tutorial");

1. 这是在JMeter变量“mongoHost,” “databaseName,” 和 “collectionName.”中定义的连接一个数据库的完整代码。我们会在随后的JMeter脚本中使用。
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 {
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings {builder ->
builder.hosts(Arrays.asList(new ServerAddress(vars.get("mongoHost"),vars.get("mongoPort").toInteger())))}
.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoDatabase database = mongoClient.getDatabase(vars.get("databaseName"));
MongoCollection collection = database.getCollection(vars.get("collectionName"));
vars.putObject("collection", collection);
return "Connected to " + vars.get("collectionName");
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}

现在,当你有了一个MongoCollection对象,你可以最终使用文档开始工作,例如将数据存储在数据库中。

如何创建一个文档并使用JMeter将其插入到MongoDB数据库中

如果你的应用程序创建新的文档并将其插入数据库,然后检查的将一个新文档插入数据库中的过程的性能很重要。根据以前的例子我们可以使用JSR223案例。
首先,我们导入必要的库:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.Arrays;

现在,我们创建一个文档对象,并设置其字段和值:
Document document = new Document("firstName", "Expert")
.append("lastName", "Protocolson")
.append("age", 37)
.append("occupation", "DevOps")
.append("skills", Arrays.asList("System Administration", "Linux"))
.append("address", new Document("city", "Systemberg")
.append("street", "Data Line")
.append("house", 42));

接下来,我们把创建的文档插入我们的集合:
collection.insertOne(document);
每个MongoDB文档会拥有一个具有唯一值的”_id”字段。如果文档创建时没有这样的字段或值,Java驱动会自动将一个具有唯一值的”_id”字段插入集合。不需要手动提供”_id”字段。
创建一个文档列表并将其插入集合:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.List;
import java.util.ArrayList;
List documents = new ArrayList();
documents.add(document1);
documents.add(document2);
collection.insertMany(documents);

下面是创建一个新文档和插入一个集合过程的完整代码,我们会在我们后面的JMeter脚本中使用。
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.Arrays;
try {
MongoCollection collection = vars.getObject("collection");
Document document = new Document("firstName", "Expert")
.append("lastName", "Protocolson")
.append("age", 37)
.append("occupation", "DevOps")
.append("skills", Arrays.asList("System Administration", "Linux"))
.append("adress", new Document("city", "Systemberg")
.append("street", "Data Line")
.append("house", 42));
collection.insertOne(document);
return "Document inserted";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}

为了从集合中获取文档,你要使用MongoCollection对象的find()方法,我们会把代码放到JSR223样例中。例如,以下代码:
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import java.util.List;
List result = collection.find();

会发现集合中的所有文档,并将其写入到结果列表中。
你可以把一个文档对象作为过滤器传递给find()方法:
List result = collection.find(new Document("age", new Document("$gte", 18)
.append("$lt", 66))
.append("occupation", "Developer"));

我们可以在这里找到所有年龄大于等于18岁,小于66岁的程序员。使用Filters的helper方法可以获取同样的列表:
import static com.mongodb.client.model.Filters.*;
List result = collection.find(and(gte("age", 2), lt("age", 5), eq("occupation", "Developer")));

以下是在我们的集合中找到一个文档的完整代码。我们会在后面的JMeter脚本中使用。
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import org.bson.Document;
import org.bson.types.ObjectId;
try {
MongoCollection collection = vars.getObject("collection");
Document result = collection.find(eq("firstName", "Expert")).first();
vars.put("exampleDocumentId", result.get("_id").toString());
return "Document with id=" + result.get("_id") + " found";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);

You can find a list of useful filter operations here.
你可以在这里找到一个有用的过滤器列表。

在数据库中使用一个文档

要更新集合中的文档,你可以使用MongoCollection对象的updateOne()方法。同样的方法可以如前文所述,用于查询更新文档。例如,JSR223样例中的代码:
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import org.bson.types.ObjectId;
collection.updateOne(
eq("_id", new ObjectId("5bb43f18ce8cdca890b72422")),
combine(set("occupation", "Project Manager"), set("address.city", "New Codeshire"), currentDate("lastModified")));

在集合中,让”_id”字段值等于”57506d62f57802807471dd41″,给文档改变 “occupation”和”address.city” 字段值,并设置”lastModified”字段为当前日期。
如果你需要编辑几个文档,可以使用updateMany()方法:
collection.updateOne(
and(eq("address.city", "Saint Java"), eq("address.street", "Bugs street")),
combine(set("address.street", "Features blvd."), currentDate("lastModified")));

以上代码改变了所有居住在Saint Java城的居民的街道名称,将值Bugs street改为Features blvd。
下面是更新我们文档值的完整代码。我们会在后面的JMeter脚本中使用。
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.*;
import org.bson.Document;
import org.bson.types.ObjectId;
try {
MongoCollection collection = vars.getObject("collection");
collection.updateOne(
eq("_id", new ObjectId(vars.get("exampleDocumentId"))),
combine(set("occupation", "Project Manager"), set("adress.city", "New Codeshire"), currentDate("lastModified")));
return "Document with id=" + vars.get("exampleDocumentId") + " modified";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);

所有更新操作的列表可以在这里找到。

从数据库中删除文档

删除文档非常类似于找到文档。使用MongoCollection对象的deleteOne()方法来删除匹配指定过滤器的第一个文档,或者使用deleteMany()删除所有匹配文档。我们会使用JSR223样例。
下面谈谈如何从集合中删除一个文档(是的,我们会在后面的JMeter脚本中使用它):
import com.mongodb.client.MongoCollection;
import static com.mongodb.client.model.Filters.*;
import org.bson.Document;
try {
MongoCollection collection = vars.getObject("collection");
collection.deleteOne(eq("occupation", "Project Manager"));
return "Document deleted";
}
catch (Exception e) {
SampleResult.setSuccessful(false);
SampleResult.setResponseCode("500");
SampleResult.setResponseMessage("Exception: " + e);
}

创建你的JMeter测试计划

现在,让我们尝试写一个简单的JMeter脚本来评估我们的MongoDB配置的性能。在我们的脚本中会给每一个连接数据库的操作创建一个JSR223样例:包括插入,读取,更新,和删除文档操作。
先决条件:你已经安装了一个MongoDB客户端,并运行在你的本地主机的默认端口27017上,建立了一个连接。有一个带有空集合”blazemeter_tutorial”的空数据库。
1.在测试计划或者用户定义的变量中,指定必要的变量:
mongoHost: localhost
mongoPort: 27017
databaseName: jmeter_test
collectionName: blazemeter_tutorial

2.给你的测试计划添加一个线程组。
>右击->添加->线程(用户)->线程组
在以下步骤中,我们会考察我们的样例以测试基本的MongoDB操作:
连接到一个数据库
创建一个文档
读取该文档
修改该文档
删除该文档
所有的这些步骤都假设以前的操作执行成功,如果你在任何步骤遇到错误,我们会中断线程的执行以阻止进一步的错误。要这么做,我们需要设置“在一个样例错误后执行的操作”以在我们的线程组“停止线程”。

写一个JMeter MongoDB样例

3.在你的线程组添加一个JSR223。这些是你正在创建的MongoDB样例。
右击->添加->样例->JSR223样例
4.将样例命名为“Connect to DB”,在“Connecting JMeter to the MongoDB Database”区域放置代码,在样例中标记为1.
5.添加另一个JSR223样例,将其命名为“Write to the DB”,在“How to Create a Document and Insert it into the MongoDB Database with JMeter”区域放置代码,在样例中标记为2.
6.添加另一个JSR223样例,将其命名为“Read from DB”,在“Querying Documents” 区域放置代码,在样例中标记为3.
7.添加另一个JSR223样例,将其命名为“Update the Document”,在“Updating a Document in the Database”区域放置代码,在样例中标记为4.
8.添加另一个JSR223样例,将其命名为“Delete a Document”,在“Deleting Documents from the Database”区域放置代码,在样例中标记为5.
9.添加一个查看结果树监听器。
右击->添加->监听器->查看结果树
运行脚本,在监听器中查看结果:
可以看到我们的“Connect to DB”样例已经成功的返回了一个“Connected to blazemeter_tutorial”响应。
“Write to a DB”样例返回了一个成功的“Document inserted”响应。
我们在响应中看到找到了请求的文档。
这种响应告诉我们文档已经被修改。
最后,我们看到文档被从数据库删除。
我们所有的样例完成了相关操作。
现在,为了评估我们的MongoDB配置的性能,我们可以增加线程的数量,增加文档和查询的数量和复杂度,使用简单的数据写监听器而不是查看结果树监听器,并从命令行运行我们的脚本。
尽管在这个例子中,我们使用了非常基础的配置;在你们的性能测试中,你应该使用一个适用于你项目的实际配置。而且,你的测试文档和查询应该类似于你在工作应用中的期望。

使用Java请求样例

在以前的例子中,我们使用JSR223样例评估到MongoDB的请求。你可以考虑使用一个Java请求样例来替换。我们可以用同样的方法访问一个数据库,并用于Java请求样例在Java类中操作文档。
而且,有一个类似于Morphia ODM(文档对象映象器)的框架,可以使创建文档更加简单。
正如我们刚刚看到了,使用JMeter样例操作MongoDB是很容易的。但记住,计划你的测试环境和测试数据是一个获取有用的的MongoDB配置性能分析非常重要的步骤,这一步无可替代。

使用BlazeMeter加载测试

一旦你创建了JMeter脚本,将其上传到BlazeMeter并在云上平滑的运行你的测试。使用SaaS接口去扩展和运行你的测试会更容易,和联盟合作,获得更高级的报告。
要了解更多,从这开始。只要把你的URL放在下面的盒子里,你的测试会在几分钟内开始。

image

赞(1)
未经允许不得转载:MongoDB中文社区 » 使用JMeter做MongoDB性能测试

评论 抢沙发

评论前必须登录!