MongoDB Validator 是否会因性能影响而成为摆设?

由来

  承接 DBA规范利器: MongoDB模式校验, 又正好在发布后的当天,2017年7月16日,举办了MongoDB中国社区上海站,在会上,TJ总分享了3.6中 Validator 会变得更加的强硬。这个时候就有一个疑惑了,是否如此的强一致性、检查性,会对我们在日常操作中,产生明显的性能影响呢? 由此,引发了我写这篇文章的欲望。

回顾

先来回顾一下上篇中提到的2个方法,在本篇中我们会用到

创建集合的时候,由DBA来创建,并指定validator 规则,针对日后的统一规范

db.createCollection( "contacts",
   { validator: { $or:
      [
         { phone: { $type: "string" } },
         { email: { $regex: /@mongodb\.com$/ } },
         { status: { $in: [ "Unknown", "Incomplete" ] } }
      ]
   }
} )

集合创建完毕,需要创建validator,针对历史数据进行清理,需要注意的是,创建之前,需要清理脏数据。(这里的脏数据仅指数据格式不统一的)

db.runCommand( { collMod: "contacts",
                 validator: { $or:
                    [
                       { phone: { $type: "string" } },
                       { email: { $regex: /@mongodb\.com$/ } },
                       { status: { $in: [ "Unknown", "Incomplete" ] } }
                    ]
                 },
                 validationLevel: "moderate",
                 validationAction: "warn"

} )

准备工作

MongoDB的压测工具,业界良心,YCSB,只可惜YAHOO! 已不复当年之用了。。

https://github.com/brianfrankcooper/YCSB

这是YCSB的地址,我们只需要用到其中的mongodb部分,当然了,整个zip包还是需要一起打包下来的。

简单的使用这里就不赘述了,小胖权当各位大佬都是精通YCSB者了。

硬件配置

服务器

  • Product Name: ProLiant DL360 Gen9
  • Storage: RAID10, SSD, 1600GB
  • CPU*2: Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz, (8/8 cores; 16 threads)*2
  • Memory: 64GB*2

数据库

  • 单节点
  • wiredTigerCacheSizeGB=4
  • 其他全部默认

部分代码解读

众所周知,由于YCSB是通过workload.conf + command 的方式进行压测的。因此在第一步load 完数据后,我们是需要通过动态添加validator 的,因此我们需要确定2件事情
- YCSB 的query 条件是什么?(弄清楚了query 条件,我们就可以针对该query 条件添加对应的validator 了)
- YCSB 的insert 条件是什么?

也许会有人问,CRUD 为什么只涉及 select 和 insert 呢?因为update 和 delete ,无非就是select + insert, 所以我们只需要搞清楚2个即可。那么上代码。

我们先来看insert, 我将略去不必要的代码

YCSB/mongodb/src/main/java/com/yahoo/ycsb/db/MongoDbClient.java

@Override
  public Status insert(String table, String key,
      HashMap values) {
    try {
      MongoCollection collection = database.getCollection(table);
      Document toInsert = new Document("_id", key);
      for (Map.Entry entry : values.entrySet()) {
        toInsert.put(entry.getKey(), entry.getValue().toArray());
      }

      if (batchSize == 1) {
        if (useUpsert) {

……

  }

我们来观察一下insert 的代码,可以看到有一条关键代码 Document toInsert = new Document("_id", key); 拿了key 作为参数传给了_id,因此,我们可以断定他的insert 条件就是以_id 为条件。其实也就是修改了_id 为ObjectId。

再来看query

@Override
  public Status read(String table, String key, Set fields,
      HashMap result) {
    try {
      MongoCollection collection = database.getCollection(table);
      Document query = new Document("_id", key);

      FindIterable findIterable = collection.find(query);

……
  }

同样的,我们的query 也是通过_id 来获取的。

如上,我们已经非常明确的知道了YCSB 在做CRUD 操作的时候的具体过程了。

接下来就是实cao 环节了。

压测

  1. 初始化数据
    ./bin/ycsb load mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_load_t100.log

  2. 手动添加validator
    需要注意的是,这里的usertable 是YCSB 自带的collection name

    db.runCommand( { collMod: "usertable",
                     validator: { $or:
                        [
                           { _id: { $type: "string" } }
                        ],
                     },
                     validationLevel: "strict",
                     validationAction: "warn"
    } )
    
  3. ./bin/ycsb run mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_run_t100_r95_i5.log

    recordcount=10000000
    operationcount=10000000
    workload=com.yahoo.ycsb.workloads.CoreWorkload
    readallfields=true
    readproportion=0.95
    updateproportion=0
    scanproportion=0
    insertproportion=0.05
    
    requestdistribution=zipfian
    
  4. ./bin/ycsb run mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_run_t100_r50_u50.log

    recordcount=10000000
    operationcount=10000000
    workload=com.yahoo.ycsb.workloads.CoreWorkload
    readallfields=true
    readproportion=0.5
    updateproportion=0.5
    scanproportion=0
    insertproportion=0
    
    requestdistribution=zipfian
    
  5. ./bin/ycsb run mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_run_t100_r70_u20_i10.log

    recordcount=10000000
    operationcount=10000000
    workload=com.yahoo.ycsb.workloads.CoreWorkload
    readallfields=true
    readproportion=0.7
    updateproportion=0.2
    scanproportion=0
    insertproportion=0.1
    
    requestdistribution=zipfian
    
  6. ./bin/ycsb run mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_run_t100_r10_u20_i70.log

    recordcount=10000000
    operationcount=10000000
    workload=com.yahoo.ycsb.workloads.CoreWorkload
    readallfields=true
    readproportion=0.1
    updateproportion=0.2
    scanproportion=0
    insertproportion=0.7
    
    requestdistribution=zipfian
    
  7. ./bin/ycsb run mongodb -s -P workloads/mongodb.ycsb -threads 100 -p mongodb.url=mongodb://localhost:27017/ycsb?w=0 > ycsb_validation_run_t100_r10_i90.log

    recordcount=10000000
    operationcount=10000000
    workload=com.yahoo.ycsb.workloads.CoreWorkload
    readallfields=true
    readproportion=0.1
    updateproportion=0
    scanproportion=0
    insertproportion=0.9
    
    requestdistribution=zipfian
    

压测报告

压测一共做了2轮,第一轮是没有validator 的,第二轮是有validator的,为了保证数据的无差异性,第一轮压测后,重新load 了数据。

所有的压测数据以及压测过程,都在github 上了

https://github.com/MiracleYoung/mongodb_stressing_validator

r:0.95, i:0.05

CRUD normal doc validation doc
[INSERT], AverageLatency(us) 54.32783133598422 50.644348350531864
[INSERT], 95thPercentileLatency(us) 52.0 50.0
[READ], AverageLatency(us) 24059.437908215754 23709.003255993488
[READ], 95thPercentileLatency(us) 75839.0 74495.0
[UPDATE], AverageLatency(us) null null
[UPDATE], 95thPercentileLatency(us) null null
[OVERALL], RunTime(ms) 248575.0 244206.0
[OVERALL], Throughput(ops/sec) 40229.307050186064 40949.034831249024

r:0.5, u:0.5

CRUD normal doc validation doc
[INSERT], AverageLatency(us) null null
[INSERT], 95thPercentileLatency(us) null null
[READ], AverageLatency(us) 6654.8808580182795 6861.01072835122
[READ], 95thPercentileLatency(us) 21999.0 21935.0
[UPDATE], AverageLatency(us) 62.51089386161971 65.22678019721495
[UPDATE], 95thPercentileLatency(us) 68.0 69.0
[OVERALL], RunTime(ms) 337878.0 347989.0
[OVERALL], Throughput(ops/sec) 29596.481570270927 28736.54052283262

r:0.7, u:0.2, i:0.1

CRUD normal doc validation doc
[INSERT], AverageLatency(us) 252.44167431584393 344.1996127709654
[INSERT], 95thPercentileLatency(us) 108.0 110.0
[READ], AverageLatency(us) 2647.108107555894 2730.1629217311306
[READ], 95thPercentileLatency(us) 9327.0 9223.0
[UPDATE], AverageLatency(us) 248.62104869988795 310.29020023763366
[UPDATE], 95thPercentileLatency(us) 69.0 70.0
[OVERALL], RunTime(ms) 195966.0 205159.0
[OVERALL], Throughput(ops/sec) 51029.260177785945 48742.682504788965

r:0.1, u:0.2, i:0.7

CRUD normal doc validation doc
[INSERT], AverageLatency(us) 82.1098431094292 81.66241673973236
[INSERT], 95thPercentileLatency(us) 93.0 93.0
[READ], AverageLatency(us) 20279.290684018884 20223.295329881992
[READ], 95thPercentileLatency(us) 66943.0 67327.0
[UPDATE], AverageLatency(us) 62.3647426526138 59.5288535378221
[UPDATE], 95thPercentileLatency(us) 66.0 65.0
[OVERALL], RunTime(ms) 212787.0 211929.0
[OVERALL], Throughput(ops/sec) 46995.352159671405 47185.61404998844

r:0.1, i:0.9

CRUD normal doc validation doc
[INSERT], AverageLatency(us) 129.41064849336593 81.56726077106597
[INSERT], 95thPercentileLatency(us) 98.0 83.0
[READ], AverageLatency(us) 911.6383231943234 857.076724559656
[READ], 95thPercentileLatency(us) 2103.0 2171.0
[UPDATE], AverageLatency(us) 62.3647426526138 59.5288535378221
[UPDATE], 95thPercentileLatency(us) 66.0 65.0
[OVERALL], RunTime(ms) 88974.0 83188.0
[OVERALL], Throughput(ops/sec) 112392.38429204037 120209.64562196471

结论

说明:

当我的wiretiger 内存控制在4GB时,我的数据已经超出内存了。

YCSB 的样本数据,默认是1KB 一条doc,1KW * 1KB = 10GB

思考

从压测报告中,可以看出,validator 对于大多数场景下(不排除少部分我并没有测试到的场景,比如range,其实YCSB也有,这里不再赘述)是不影响数据库的使用性能的。warning 意在打出warning 报告,并不会abort,所以也不会对程序有任何的阻塞。

由此,DBAer 可以用validator 大施拳脚,整顿MongoDB Schema Standard!

后续

洗了个澡,思考了一下,意识到一个问题,会不会因为SSD太快了,数据不够辣鸡导致无差异呢?由此,做了第二次压测,这次只做了一个场景,那就是r:0.05, i:0.95,因为validator的检查一定是在写入数据的时候。

这次的压测数据量是1WW 条,operation 操作数是5KW条。mongodb 内存提升到16G。

结果如下:

r:0.05, i:0.95

CRUD normal doc validation doc
[INSERT], AverageLatency(us) 47.76224959524256 66.52576949011765
[INSERT], MinLatency(us) 12.0 12.0
[INSERT], MaxLatency(us) 375295.0 1.6490495E7
[INSERT], 95thPercentileLatency(us) 59.0 59.0
[INSERT], 99thPercentileLatency(us) 235.0 220.0

由以上结论可以得出:
- 从平均值上可以简单得出在大并发、持续性的输入到数据库的时候,是会带来一定的性能消耗。
- 结合最大、最小、95% 和99% 的数据上可以得出,平均值的延迟是由于波谷的一些少数慢插入导致的。
- 综合之前的所有压测报告,我们发现其实validator 对性能的影响真的可以忽略不计啊~~~

关于上海小胖(MiracleYoung)

DevOps & DBA                           MySQL, MongoDB, Redis, etc.                     CSDN: http://edu.csdn.net/lecturer/course_list           Blog: https://segmentfault.com/u/shanghaixiaopang/articles 

MongoDB Validator 是否会因性能影响而成为摆设?》有1个想法

发表评论