zoukankan      html  css  js  c++  java
  • 面对Schema free 的MongoDB,如何规范你的schema

    http://www.mongoing.com/archives/2282

    总的来说,限制MongoDB的Schema,有两个主要工具:

    variety:查看collections中各个字段出现的情况

    Document Validation:对于表中数据的字段规则进行限定,Error和Warn等级,Error拒绝插入,Warn写日志

    正文

    大家都知道MongoDB是文档型数据库,是Schema Free的。

    那么MongoDB的文档模型能给我们带来哪些好处呢,在这简单列举几个:

    • json形式-在MongoDB中,开发人员可以直接将一个json数据存储进MongoDB,这对于开发人员来说是非常友好额;
    • 读写性能高-在关系型数据库中,我们经常会进行join、子查询等关联性需求,这时候往往会带来较多的随机IO,而在MongoDB中,我们可以通过合理的数据模型设计来将很多的关联需求通过内嵌、反范式的方式实现,减少了随机IO;
    • schema free-MongoDB的数据模型是灵活的,无需为了Online DDL而操心,不同的document也可以有不同的结构。

    在这,我们不深入探究如何对于MongoDB 的Schema进行设计、建模,有关这部分内容,推荐大家可以阅读TJ在开源中国的年终盛典会上分享《MongoDB 进阶模式设计》,以及《Retail Reference Architecture Part 1 to 4 》。

    在此我们将主要针对进行了初步建模、并正式上线服务后的schema进行巡检与检测的方式来进行讨论。

    Variety

    Variety是一个开源的,非常使用的,检测mongodb表字段类型、分布的一个开源工具。

    正如其github readme中第一句所说”Meet Variety, a Schema Analyzer for MongoDB

    Variety能够帮助我们检测我们MongoDB表中的字段类型、分布,并生产报表,可以让我们非常直观的对现有表结构、字段类型进行分析,并找出数据模型中的隐患。

    下面我们通过例子来进行讲解:

    首先,建立一个表

    db.users.insert({name: "Tom", bio: "A nice guy.", pets: ["monkey", "fish"], someWeirdLegacyKey: "I like Ike!"});
    db.users.insert({name: "Dick", bio: "I swordfight.", birthday: new Date("1974/03/14")});
    db.users.insert({name: "Harry", pets: "egret", birthday: new Date("1984/03/14")});
    db.users.insert({name: "Geneviève", bio: "Ça va?"});
    db.users.insert({name: "Jim", someBinData: new BinData(2,"1234")});
    
    

    我们来看看通过variety获得的结果

    $ mongo test --eval "var collection = 'users'" variety.js
    
    +------------------------------------------------------------------+
    | key | types | occurrences | percents |
    | ------------------ | ------------ | ----------- | -------- |
    | _id | ObjectId | 5 | 100.0 |
    | name | String | 5 | 100.0 |
    | bio | String | 3 | 60.0 |
    | birthday | String | 2 | 40.0 |
    | pets | Array(4),String(1) | 5 | 40.0 |
    | someBinData | BinData-old | 1 | 20.0 |
    | someWeirdLegacyKey | String | 1 | 20.0 |
    +------------------------------------------------------------------+
    

    test是我们的db名,users是表名。我们可以看到,针对我们之前插入的5条数据,variety跑出的结果是:

    所有的document都含有_id,和name字段,60%的document含有bio字段,40%的document含有birthday和pets字段,且pets字段有2个类型的数据(4个array的,1个string的),20%的document含有someBinData和SomeWeirdLegacyKey字段。

    然而生产环境中由于我们的数据量较大,比如一个表有10亿条数据,全部进行扫描会耗时较长,可能我们仅希望对1000条数据进行分析,这时候就可以使用limit来限定。

    $ mongo test --eval "var collection = 'users', limit = 1000" variety.js
    
    +----------------------------------------------------+
    | key | types | occurrences | percents |
    | ----------- | ----------- | ----------- | -------- |
    | _id | ObjectId | 1000 | 100.0 |
    | name | String | 1000 | 100.0 |
    | someBinData | BinData-old | 1000 | 100.0 |
    +----------------------------------------------------+
    

    由于MongoDB的可以通过内嵌来减少联合查询的需求,可以通过反范式来减少随机IO,所以很可能会有嵌套出现在我们的document中。有的时候嵌套的层数太多了,影响我们的统计信息,怎么办,我们可以通过maxDepth来限制。请参考下面的例子:

    db.users.insert({name:"Walter", someNestedObject:{a:{b:{c:{d:{e:1}}}}}});
    
    $ mongo test --eval "var collection = 'users'" variety.js
    
    +----------------------------------------------------------------+
    | key | types | occurrences | percents |
    | -------------------------- | -------- | ----------- | -------- |
    | _id | ObjectId | 1 | 100.0 |
    | name | String | 1 | 100.0 |
    | someNestedObject | Object | 1 | 100.0 |
    | someNestedObject.a | Object | 1 | 100.0 |
    | someNestedObject.a.b | Object | 1 | 100.0 |
    | someNestedObject.a.b.c | Object | 1 | 100.0 |
    | someNestedObject.a.b.c.d | Object | 1 | 100.0 |
    | someNestedObject.a.b.c.d.e | Number | 1 | 100.0 |
    +----------------------------------------------------------------+
    
    $ mongo test --eval "var collection = 'users', maxDepth = 3" variety.js
    
    +----------------------------------------------------------+
    | key | types | occurrences | percents |
    | -------------------- | -------- | ----------- | -------- |
    | _id | ObjectId | 1 | 100.0 |
    | name | String | 1 | 100.0 |
    | someNestedObject | Object | 1 | 100.0 |
    | someNestedObject.a | Object | 1 | 100.0 |
    | someNestedObject.a.b | Object | 1 | 100.0 |
    +----------------------------------------------------------+
    

    又或者我们希望指定统计的条件,比如希望caredAbout为true的,可以这样做:

    $ mongo test --eval "var collection = 'users', query = {'caredAbout':true}" variety.js
    

    又或者是希望进行排序:

    $ mongo test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js
    

    同时我们也可以指定分析结果的format:

    $ mongo test --quiet --eval "var collection = 'users', outputFormat='json'" variety.js
    

    一般在生产中, 我们不会在primary上进行分析, 我们可以在一个priority为0,且为hidden的secondary上进行分析,这时候需要指定slaveOK:

    $ mongo secondary.replicaset.member:31337/somedb --eval "var collection = 'users', slaveOk = true" variety.js
    

    又或者说我们希望将分析结果存在mongo中:

    $ mongo test --quiet --eval "var collection = 'users', persistResults=true" variety.js
    

    并且指定存储的详细信息:

    • resultsDatabase 分析结果所存储的db名
    • resultsCollection 分析结果所存储的collection名
    • resultsUser 分析结果存储的实例的user
    • resultsPass 分析结果所存储的实例的password
    mongo test --quiet --eval "var collection = 'users', persistResults=true, resultsDatabase='db.example.com/variety' variety.js
    

    我们为什么要用Variety呢?

    尽管我们MongoDB是Schema Free的,但是绝大多数情况下, 我们都希望字段类型统一。

    不一致的字段类型可能会为我们的数据带来误差,试想一下,如果某个字段的字段类型不统一,而我们却不知情,这时候很可能会发现业务查询有数据丢失,数据不准确。

    并且在生产环境中,应用的版本在不断迭代,需求不断增多,字段也随之变化,如果在没有规范化的上线流程检查过后,数据库中可能还会存在部分数据的字段确实,比如有的document有a字段,有的却没有,variety也可以帮助我们发现这些问题。

    Document Validation

    MongoDB 3.2推出了很多给力的功能,在这不得不提及Document Validation,Document Validation的出现我想也是MongoDB官方想表达”schema free but you may need some rules”吧,哈哈,纯属臆测。

    简单介绍下Document Validation:

    我们可以为我们schema free的mongodb collection做一些限制。当然这并不是意味着MongoDB变成了关系型数据库,个人觉得这反而更好的突出了MongoDB Schema free的特性。在正确的地方、需要的地方schema free,在适当的地方要有限制。

    假设我们要新建一个表contacts,要有如下约束:

    phone字段为string类型或者email字段要匹配”@mongodb.com”结尾,或者status为”Unknown”或者”Incomplete”

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

    对已经建立了的表,我们可以通过如下方式来做限定:

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

    这里可以看到,多了一个validationLevel参数,我们可以在设置validation的时候指定我们的validationLevel级别:

    • 默认级别是strict,对该collection已有的和以后新增的document都进行validation验证;
    • 可以设置为moderate,仅对已经存在的document进行validation限定;

    同时还有validationAction参数来指定当有不符合validation规则的数据进行update或者insert的时候, 我们mongodb实例如何进行处理。

    • 默认级别为error,mongodb将拒绝这些不符合validation规则的insert和update。
    • 可以设置为warn,mongodb会在日志中记录,但是允许这类insert和update操作。日志中如:

    2015-10-15T11:20:44.260-0400 W STORAGE [conn3] Document would fail validation collection: example.contacts doc: { _id: ObjectId('561fc44c067a5d85b96274e4'), name: "Amanda", status: "Updated" }
    

    validation的限制

    • validation不能对admin、local和config库中的collection进行设置;
    • 不能对system.*这类collections进行validation设置;

  • 相关阅读:
    mysql导出csv文件
    httpclient设置proxy与proxyselector
    91删除数据
    使用RestTemplate post方式提交表单数据
    bootstrap获取总条目数
    获取字符串已utf-8表示的字节数
    mongo批量更新
    重庆大学计算机917考研分析(定期更新考研信息)
    卷积
    LaTeX中自定义enumerate的编号格式
  • 原文地址:https://www.cnblogs.com/zcy-backend/p/6914546.html
Copyright © 2011-2022 走看看