项目里面需要对mongodb的性能进行测试,看了下网上很多做法都是使用YCSB进行测试,因此开始学习使用YCSB。 

参考资料:

  1. YCSB github地址:​​https://github.com/brianfrankcooper/YCSB​​ 
  2. wiki: ​​https://github.com/brianfrankcooper/YCSB/wiki​​ 
  3. 安装参考:​​https://github.com/brianfrankcooper/YCSB/tree/master/mongodb​​ 
  4. 之前的一些测试经验:​​http://www.sdpnosql.net/index.php/archives/3/​​    ​​http://www.sdpnosql.net/index.php/archives/13/​

1 安装

基于参考文档(​​https://github.com/brianfrankcooper/YCSB/tree/master/mongodb​​ )安装java,mvn,ycsb 

基本上安装了python(2.7.5版本以上),java(需要是jdk,而不是jre),mvn和ycsb之后就可以了

1.1 YCSB目录结构

安装好YCSB之后,查看YCSB目录如下:





1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32




​drwxr-xr-x. 3 root root   30 Oct 10 18:20 accumulo​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 aerospike​


​drwxr-xr-x. 2 root root   17 Oct 13 05:03 bin​


​drwxr-xr-x. 3 root root   56 Oct 10 18:20 binding-parent​


​-rw-r--r--. 1 root root  877 Oct 10 18:20 BUILD​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 cassandra​


​-rw-r--r--. 1 root root 7216 Oct 10 18:20 checkstyle.xml​


​drwxr-xr-x. 4 root root   60 Oct 13 03:18 core​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 couchbase​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 distribution​


​drwxr-xr-x. 4 root root 4096 Oct 10 18:20 doc​


​drwxr-xr-x. 4 root root   54 Oct 10 18:20 dynamodb​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 elasticsearch​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 gemfire​


​drwxr-xr-x. 2 root root   36 Oct 10 18:20 hbase094​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 hbase098​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 hbase10​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 hypertable​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 infinispan​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 jdbc​


​-rw-r--r--. 1 root root 8082 Oct 10 18:20 LICENSE.txt​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 mapkeeper​


​drwxr-xr-x. 4 root root   59 Oct 13 03:19 mongodb​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 nosqldb​


​-rw-r--r--. 1 root root  479 Oct 10 18:20 NOTICE.txt​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 orientdb​


​-rw-r--r--. 1 root root 5263 Oct 10 18:20 pom.xml​


​-rw-r--r--. 1 root root 2033 Oct 10 18:20 README.md​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 redis​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 tarantool​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 voldemort​


​drwxr-xr-x. 2 root root 4096 Oct 13 01:09 workloads​



这里面有几个目录需要注意下:

bin:
- 目录下有个可执行的ycsb文件,是个python脚本,是用户操作的命令行接口。ycsb主逻辑是:解析命令行、设置java环境,加载java-libs,封装成可以执行的java命令,并执行

workloads:
- 目录下有各种workload的模板,可以基于workload模板进行个性化修改

core:
- 包含ycsb里各种核心实现,比如DB的虚拟类DB.java,各个db子类都要继承该类;还有比如workload抽象类,如果我们要自定义workload实现也需要继承该类

各种DB的目录:
- 比如mongo,redis等,里面包含了对应测试的源码等。
- 当ycsb mvn编译后,会在对应的目录下生成target文件,ycsb会加载对应target文件中的class类


2 使用

ycsb在执行的时候,分为两阶段:load阶段 和 transaction阶段

2.1 load阶段

该阶段主要用于构造测试数据,ycsb会基于参数设定,往db里面构造测试需要的数据,如:





1




​.​​​​/bin/ycsb​​ ​​load mongodb-async -s -P workloads​​​​/workloada​​ ​​> outputLoad.txt​



mongodb-async

在ycsb中,对于不同的db都有一些选项,比如mongo就有mongodb 和 mongodb-async。 

默认的mongodb表示同步,即load和run使用同步的方式,ycsb会调用mongodb/src底下对应的MongodbClient实现对应的insert/update等操作。如果设置了mongodb-async,ycsb会调用mongodb/src底下对应的AsyncMongoDbClient.java实现

参数设置:





1


2


3


4


5


6


7


8




​Options:​


​-P ​​​​file​​        ​​Specify workload ​​​​file​​ ​​//​​ ​​workload文件​


​-​​​​cp​​ ​​path       Additional Java classpath entries​


​-jvm-args args Additional arguments to the JVM​


​-p key=value   Override workload property ​​​​//​​ ​​一些设置​


​-s             Print status to stderr ​​​​//​​ ​​把状态达到stderr中​


​-target n      Target ops​​​​/sec​​ ​​(default: unthrottled) ​​​​//​​ ​​每秒总共操作的次数​


​-threads n     Number of client threads (default: 1) ​​​​//​​ ​​客户端线程数​



参数解读:

-P workload文件

在ycsb的目录下有多种workload,参考:​​https://github.com/brianfrankcooper/YCSB/wiki/Core-Workloads​​,我们以workloada举例子 

基础配置:





1


2


3


4


5


6


7


8


9


10


11


12




​recordcount=1000     ​​​​# 总共的操作条数​


​operationcount=1000  ​​​​# 总共操作的次数​


​workload=com.yahoo.ycsb.workloads.CoreWorkload=​


 


​readallfields=​​​​true​​  ​​# 在读取的时候,是否需要读取所有字段​


 


​readproportion=0.5  ​​​​# 读取比例​


​updateproportion=0.5 ​​​​# update比例​


​scanproportion=0​


​insertproportion=0​


 


​requestdistribution=zipfian​



workloada的负载比较中,read和update类比例为1:1,里面一些设置参数如上,如果我们再设置mongo的时候,还需要再workload中增加对应的mongo配置,如下:





1


2


3




​mongodb.url=mongodb:​​​​//192​​​​.168.137.10:34001​​​​/ycsb​​​​?  ​​​​# mongodb对应的uri等​


​mongodb.database=ycsb ​​​​# 对应的db​


​mongodb.writeConcern=normal ​​​​# 写级别​



-p选项

-p用于设置一些对应的参数,如果workload中的参数,也可以以-p的方式放在命令行中设置

-s

-s是表示,在运行中,把一些状态打印到stderr中,一般status信息,用于表示在运行中的一些中间状态(比如当前处理了多少请求,还有多少请求等)

-target n

表示1s中总共的操作次数(各个线程加起来的),如果性能不满足,比如最高性能只有100,你设置了1000,那么ycsb会尽量往这个数目去靠近。默认是不做限制

-thread 线程数

设置ycsb client的并发测试线程数,默认是1,单线程,所以再测试的时候,一定要设置这个选项

2.2 transcation阶段

在2.1load数据结束之后,ycsb就可以进行测试了,也就是transaction阶段。在transaction阶段,会基于workload中的比例设置,和线程参数设置进行db的压测。具体参数如上

3 一些自定义操作

由于这次在使用ycsb测试mongodb中主要是为了测试mongodb3.0的性能,同时需要和2.6.9进行对比。而3.0性能写性能大幅度提升是因为锁力度从db锁升级到collection锁。而默认的insert、update操作都是对于同一个collection操作(usertable),没法体现这个优势。 

因此我们需要修改对应的insert、update和read接口实现一次性写多个db。修改如下:

修改mongodb底下的MongoDbClient和AsyncMongoDbClient中关于insert、update、read函数实现
如下:
原来的实现:





1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19




​public final int update(final String table, final String key,​


​final HashMap<String, ByteIterator> values) {​


​try {​


​final MongoCollection collection = database.getCollection(table);​


​final DocumentBuilder query = BuilderFactory.start().add(​​​​"_id"​​​​, key);​


​final DocumentBuilder update = BuilderFactory.start();​


​final DocumentBuilder fieldsToSet = update.push(​​​​"$set"​​​​);​


 


​for​​ ​​(final Map.Entry<String, ByteIterator> entry : values.entrySet()) {​


​fieldsToSet.add(entry.getKey(), entry.getValue().toArray());​


​}​


​final long res =​


​collection.update(query, update, ​​​​false​​​​, ​​​​false​​​​, writeConcern);​


​return​​ ​​res == 1 ? 0 : 1;​


​} catch (final Exception e) {​


​System.err.println(e.toString());​


​return​​ ​​1;​


​}​


​}​


修改后:





1




​public​​ ​​final​​ ​​int​​ ​​update(​​​​final​​ ​​String table, ​​​​final​​ ​​String key, ​​​​final​​ ​​HashMap<String, ByteIterator> values) {<br>    ​​​​// 对原来的update函数做修改,在每次update时都多做几次操作<br>    int ret = updateOneTable(table, key, values);<br>    if (ret != 0) {<br>        return ret;<br>    }<br>    <br>    for (int i = 0; i < TABLE_NUM; ++i) {<br>        String tableName = table + String.valueOf(i);<br>        ret = updateOneTable(tableName, key, values);<br>        if (ret != 0) {<br>            return ret;<br>        }<br>    } <br>    <br>    return 0; <br>} <br><br>public final int updateOneTable(final String table, final String key, final HashMap<String, ByteIterator> values) {<br>    try { <br>        final MongoCollection collection = database.getCollection(table);<br>        final DocumentBuilder query = BuilderFactory.start().add("_id", key);<br>        final DocumentBuilder update = BuilderFactory.start();<br>        final DocumentBuilder fieldsToSet = update.push("$set");<br>        <br>        for (final Map.Entry<String, ByteIterator> entry : values.entrySet()) { <br>            fieldsToSet.add(entry.getKey(), entry.getValue().toArray()); <br>        }<br> <br>        final long res = collection.update(query, update, false, false, writeConcern);<br>        return res == 1 ? 0 : 1; <br>    } catch (final Exception e) {<br>        System.err.println(e.toString()); return 1; <br>    } <br>}​



其中TABLE_NUM可以根据实际需求进行设置(实际中,我们设置了该值为4,表示一次写5个table)


项目里面需要对mongodb的性能进行测试,看了下网上很多做法都是使用YCSB进行测试,因此开始学习使用YCSB。 

参考资料:

  1. YCSB github地址:​​https://github.com/brianfrankcooper/YCSB​​ 
  2. wiki: ​​https://github.com/brianfrankcooper/YCSB/wiki​​ 
  3. 安装参考:​​https://github.com/brianfrankcooper/YCSB/tree/master/mongodb​​ 
  4. 之前的一些测试经验:​​http://www.sdpnosql.net/index.php/archives/3/​​    ​​http://www.sdpnosql.net/index.php/archives/13/​

1 安装

基于参考文档(​​https://github.com/brianfrankcooper/YCSB/tree/master/mongodb​​ )安装java,mvn,ycsb 

基本上安装了python(2.7.5版本以上),java(需要是jdk,而不是jre),mvn和ycsb之后就可以了

1.1 YCSB目录结构

安装好YCSB之后,查看YCSB目录如下:





1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32




​drwxr-xr-x. 3 root root   30 Oct 10 18:20 accumulo​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 aerospike​


​drwxr-xr-x. 2 root root   17 Oct 13 05:03 bin​


​drwxr-xr-x. 3 root root   56 Oct 10 18:20 binding-parent​


​-rw-r--r--. 1 root root  877 Oct 10 18:20 BUILD​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 cassandra​


​-rw-r--r--. 1 root root 7216 Oct 10 18:20 checkstyle.xml​


​drwxr-xr-x. 4 root root   60 Oct 13 03:18 core​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 couchbase​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 distribution​


​drwxr-xr-x. 4 root root 4096 Oct 10 18:20 doc​


​drwxr-xr-x. 4 root root   54 Oct 10 18:20 dynamodb​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 elasticsearch​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 gemfire​


​drwxr-xr-x. 2 root root   36 Oct 10 18:20 hbase094​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 hbase098​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 hbase10​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 hypertable​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 infinispan​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 jdbc​


​-rw-r--r--. 1 root root 8082 Oct 10 18:20 LICENSE.txt​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 mapkeeper​


​drwxr-xr-x. 4 root root   59 Oct 13 03:19 mongodb​


​drwxr-xr-x. 3 root root   43 Oct 10 18:20 nosqldb​


​-rw-r--r--. 1 root root  479 Oct 10 18:20 NOTICE.txt​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 orientdb​


​-rw-r--r--. 1 root root 5263 Oct 10 18:20 pom.xml​


​-rw-r--r--. 1 root root 2033 Oct 10 18:20 README.md​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 redis​


​drwxr-xr-x. 3 root root   46 Oct 10 18:20 tarantool​


​drwxr-xr-x. 3 root root   30 Oct 10 18:20 voldemort​


​drwxr-xr-x. 2 root root 4096 Oct 13 01:09 workloads​



这里面有几个目录需要注意下:

bin:
- 目录下有个可执行的ycsb文件,是个python脚本,是用户操作的命令行接口。ycsb主逻辑是:解析命令行、设置java环境,加载java-libs,封装成可以执行的java命令,并执行

workloads:
- 目录下有各种workload的模板,可以基于workload模板进行个性化修改

core:
- 包含ycsb里各种核心实现,比如DB的虚拟类DB.java,各个db子类都要继承该类;还有比如workload抽象类,如果我们要自定义workload实现也需要继承该类

各种DB的目录:
- 比如mongo,redis等,里面包含了对应测试的源码等。
- 当ycsb mvn编译后,会在对应的目录下生成target文件,ycsb会加载对应target文件中的class类


2 使用

ycsb在执行的时候,分为两阶段:load阶段 和 transaction阶段

2.1 load阶段

该阶段主要用于构造测试数据,ycsb会基于参数设定,往db里面构造测试需要的数据,如:





1




​.​​​​/bin/ycsb​​ ​​load mongodb-async -s -P workloads​​​​/workloada​​ ​​> outputLoad.txt​



mongodb-async

在ycsb中,对于不同的db都有一些选项,比如mongo就有mongodb 和 mongodb-async。 

默认的mongodb表示同步,即load和run使用同步的方式,ycsb会调用mongodb/src底下对应的MongodbClient实现对应的insert/update等操作。如果设置了mongodb-async,ycsb会调用mongodb/src底下对应的AsyncMongoDbClient.java实现

参数设置:





1


2


3


4


5


6


7


8




​Options:​


​-P ​​​​file​​        ​​Specify workload ​​​​file​​ ​​//​​ ​​workload文件​


​-​​​​cp​​ ​​path       Additional Java classpath entries​


​-jvm-args args Additional arguments to the JVM​


​-p key=value   Override workload property ​​​​//​​ ​​一些设置​


​-s             Print status to stderr ​​​​//​​ ​​把状态达到stderr中​


​-target n      Target ops​​​​/sec​​ ​​(default: unthrottled) ​​​​//​​ ​​每秒总共操作的次数​


​-threads n     Number of client threads (default: 1) ​​​​//​​ ​​客户端线程数​



参数解读:

-P workload文件

在ycsb的目录下有多种workload,参考:​​https://github.com/brianfrankcooper/YCSB/wiki/Core-Workloads​​,我们以workloada举例子 

基础配置:





1


2


3


4


5


6


7


8


9


10


11


12




​recordcount=1000     ​​​​# 总共的操作条数​


​operationcount=1000  ​​​​# 总共操作的次数​


​workload=com.yahoo.ycsb.workloads.CoreWorkload=​


 


​readallfields=​​​​true​​  ​​# 在读取的时候,是否需要读取所有字段​


 


​readproportion=0.5  ​​​​# 读取比例​


​updateproportion=0.5 ​​​​# update比例​


​scanproportion=0​


​insertproportion=0​


 


​requestdistribution=zipfian​



workloada的负载比较中,read和update类比例为1:1,里面一些设置参数如上,如果我们再设置mongo的时候,还需要再workload中增加对应的mongo配置,如下:





1


2


3




​mongodb.url=mongodb:​​​​//192​​​​.168.137.10:34001​​​​/ycsb​​​​?  ​​​​# mongodb对应的uri等​


​mongodb.database=ycsb ​​​​# 对应的db​


​mongodb.writeConcern=normal ​​​​# 写级别​



-p选项

-p用于设置一些对应的参数,如果workload中的参数,也可以以-p的方式放在命令行中设置

-s

-s是表示,在运行中,把一些状态打印到stderr中,一般status信息,用于表示在运行中的一些中间状态(比如当前处理了多少请求,还有多少请求等)

-target n

表示1s中总共的操作次数(各个线程加起来的),如果性能不满足,比如最高性能只有100,你设置了1000,那么ycsb会尽量往这个数目去靠近。默认是不做限制

-thread 线程数

设置ycsb client的并发测试线程数,默认是1,单线程,所以再测试的时候,一定要设置这个选项

2.2 transcation阶段

在2.1load数据结束之后,ycsb就可以进行测试了,也就是transaction阶段。在transaction阶段,会基于workload中的比例设置,和线程参数设置进行db的压测。具体参数如上

3 一些自定义操作

由于这次在使用ycsb测试mongodb中主要是为了测试mongodb3.0的性能,同时需要和2.6.9进行对比。而3.0性能写性能大幅度提升是因为锁力度从db锁升级到collection锁。而默认的insert、update操作都是对于同一个collection操作(usertable),没法体现这个优势。 

因此我们需要修改对应的insert、update和read接口实现一次性写多个db。修改如下:

修改mongodb底下的MongoDbClient和AsyncMongoDbClient中关于insert、update、read函数实现
如下:
原来的实现:





1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19




​public final int update(final String table, final String key,​


​final HashMap<String, ByteIterator> values) {​


​try {​


​final MongoCollection collection = database.getCollection(table);​


​final DocumentBuilder query = BuilderFactory.start().add(​​​​"_id"​​​​, key);​


​final DocumentBuilder update = BuilderFactory.start();​


​final DocumentBuilder fieldsToSet = update.push(​​​​"$set"​​​​);​


 


​for​​ ​​(final Map.Entry<String, ByteIterator> entry : values.entrySet()) {​


​fieldsToSet.add(entry.getKey(), entry.getValue().toArray());​


​}​


​final long res =​


​collection.update(query, update, ​​​​false​​​​, ​​​​false​​​​, writeConcern);​


​return​​ ​​res == 1 ? 0 : 1;​


​} catch (final Exception e) {​


​System.err.println(e.toString());​


​return​​ ​​1;​


​}​


​}​


修改后:





1




​public​​ ​​final​​ ​​int​​ ​​update(​​​​final​​ ​​String table, ​​​​final​​ ​​String key, ​​​​final​​ ​​HashMap<String, ByteIterator> values) {<br>    ​​​​// 对原来的update函数做修改,在每次update时都多做几次操作<br>    int ret = updateOneTable(table, key, values);<br>    if (ret != 0) {<br>        return ret;<br>    }<br>    <br>    for (int i = 0; i < TABLE_NUM; ++i) {<br>        String tableName = table + String.valueOf(i);<br>        ret = updateOneTable(tableName, key, values);<br>        if (ret != 0) {<br>            return ret;<br>        }<br>    } <br>    <br>    return 0; <br>} <br><br>public final int updateOneTable(final String table, final String key, final HashMap<String, ByteIterator> values) {<br>    try { <br>        final MongoCollection collection = database.getCollection(table);<br>        final DocumentBuilder query = BuilderFactory.start().add("_id", key);<br>        final DocumentBuilder update = BuilderFactory.start();<br>        final DocumentBuilder fieldsToSet = update.push("$set");<br>        <br>        for (final Map.Entry<String, ByteIterator> entry : values.entrySet()) { <br>            fieldsToSet.add(entry.getKey(), entry.getValue().toArray()); <br>        }<br> <br>        final long res = collection.update(query, update, false, false, writeConcern);<br>        return res == 1 ? 0 : 1; <br>    } catch (final Exception e) {<br>        System.err.println(e.toString()); return 1; <br>    } <br>}​



其中TABLE_NUM可以根据实际需求进行设置(实际中,我们设置了该值为4,表示一次写5个table)