Mongoing中文社区的精华文章推荐

2018-08-14 16:41:29 浏览数 (1)

《DBA规范利器: MongoDB模式校验》

作者:上海小胖

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

作者:上海小胖


《DBA规范利器: MongoDB模式校验》

作者:上海小胖

一直以来想做一个关于MongoDB的系列专题,但又没有中心思想,所以一直拖着,但现在想想大可不必。完全可以东打一枪,西放一炮。

所以有了今天的第一篇文章。

今天就来说说runCommand的collMod命令

首先我们先来盘算一下,MongoDB创建集合的方式有哪几种:

  • 对希望创建的collection插入数据 db..insert({: })
  • 通过对collection创建索引 db..createIndex({:1}
  • 调用官方API接口显式创建 db.createCollection(

从MongoDB 3.2开始,官方提供了相应接口对Schema作 强一致性 的策略:

当数据进行Insert或者Update的时候,通过创建集合时的validator option的参数控制,按照自定义的validation rules 或者 expressions,达到数据强一致性检查。

db.createCollection( "contacts",

{ validator: { $or:

[

{ phone: { $type: "string" } },

{ email: { $regex: /@mongodb.com$/ } },

{ status: { $in: [ "Unknown", "Incomplete" ] } }

]

}

} )

这里提供了3种验证方式,满足一种即可。若是$and,则需要同时满足。

若不满足,MongoDB将会返回121错误,提示Document failed validation,插入0条。

该特性对MongoDB的Schemaless以及对DBA维护MongoDB表结构、规范等提供了相当大的帮助。

下面图1既是引用官方文档关于validation level的定义:

VALIDATIONLEVEL

DESCRIPTION

“off”

关闭数据校验。

“strict”

默认值。对所有的update和insert有效。

“moderate”

仅对insert和满足校验规则的document做update有效。对已存在的不符合校验规则的document无效。

下面图2既是引用官方文档关于validation action的定义:

VALIDATIONACTION

DESCRIPTION

“error”

默认值。document必须满足校验规则,才能被写入数据哭。

“warn”

document不符合校验规则的,MongoDB允许写入,但会记录一条告警到mongod.log中去。日志内容记录报错信息以及该document的完整记录。

db.runCommand( { collMod: "contacts",

validator: { $or:

[

{ phone: { $type: "string" } },

{ email: { $regex: /@mongodb.com$/ } },

{ status: { $in: [ "Unknown", "Incomplete" ] } }

]

},

validationLevel: "moderate",

validationAction: "warn"

} )

目前正准备着手先培训 开发遵守我们的MongoDB 数据规范,之后再开始对历史数据做清理。个人感觉是个大坑。。不过我选择跳。

总 结

collMod的作用是非常明显的,可以帮助DBA去规避之前由于Schemaless而无法约束开发的窘境。可以就collMod来规范MongoDB的开发过程。

敦促我们DBA去有意识、有必要的使用强一致性对相应MongoDB数据作统一规划。


《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 环节了。

压 测

代码语言:javascript复制
初始化数据./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
手动添加validator需要注意的是,这里的usertable 是YCSB 自带的collection namedb.runCommand( { collMod: "usertable",
  validator: { $or:
  [
  { _id: { $type: "string" } }
  ],
  },
  validationLevel: "strict",
  validationAction: "warn"
} )


./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.logrecordcount=10000000
operationcount=10000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.95
updateproportion=0
scanproportion=0
insertproportion=0.05


requestdistribution=zipfian


./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.logrecordcount=10000000
operationcount=10000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.5
updateproportion=0.5
scanproportion=0
insertproportion=0


requestdistribution=zipfian


./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.logrecordcount=10000000
operationcount=10000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.7
updateproportion=0.2
scanproportion=0
insertproportion=0.1


requestdistribution=zipfian


./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.logrecordcount=10000000
operationcount=10000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.1
updateproportion=0.2
scanproportion=0
insertproportion=0.7


requestdistribution=zipfian


./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.logrecordcount=10000000
operationcount=10000000
workload=com.yahoo.ycsb.workloads.CoreWorkload
readallfields=true
readproportion=0.1
updateproportion=0
scanproportion=0
insertproportion=0.9
  1. 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 对性能的影响真的可以忽略不计啊~~~

0 人点赞