如何在MongoDB中的$match中使用聚合运算符(例如$year或$dayOfMonth)?

我拥有一个包含create_date属性的文档的集合.我想通过汇总管道发送这些文件,对他们做一些工作.理想情况下,我想在使用$match进行任何其他工作之前使用$match进行过滤,以便我可以利用索引,但是我无法弄清楚如何在我的新的$year / $month / $dayOfMonth操作符中使用$match表达式.
有一些例子浮动了如何在$项目操作中使用运算符,但我担心,通过将$项目作为我的管道中的第一步,我没有访问我的索引(MongoDB文档表明第一个表达式必须是$match以利用索引).

样品数据:

{
    post_body: 'This is the body of test post 1',
    created_date: ISODate('2012-09-29T05:23:41Z')
    comments: 48
}
{
    post_body: 'This is the body of test post 2',
    created_date: ISODate('2012-09-24T12:34:13Z')
    comments: 10
}
{
    post_body: 'This is the body of test post 3',
    created_date: ISODate('2012-08-16T12:34:13Z')
    comments: 10
}

我想通过一个汇总管道运行它,以获得9月份所有帖子的总评论

{
    aggregate: 'posts',
    pipeline: [
         {$match:
             /*Can I use the $year/$month operators here to match Sept 2012?
             $year:created_date : 2012,
             $month:created_date : 9
             */
             /*or does this have to be 
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt: {$date:'2012-10-01T04:00:00Z'} }
             */
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

这是有效的,但匹配失去对更复杂查询的任何索引的访问:

{
    aggregate: 'posts',
    pipeline: [
         {$project:
              {
                   month : {$month:'$created_date'},
                   year : {$year:'$created_date'}
              }
         },
         {$match:
              {
                   month:9,
                   year: 2012
               }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

如您已经找到的,您不能在文档中不匹配的字段(它的工作方式与查找工作方式完全相同),如果您首先使用$project,那么您将失去使用索引的能力.
您可以做的是将您的努力结合如下:

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt:  {date:'2012-10-01T04:00:00Z'} 
                  }}
             }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

以上只给出了9月的聚合,如果你想聚合多个月,你可以举个例子:

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  { $gte:'2012-07-01T04:00:00Z', 
                    $lt: '2012-10-01T04:00:00Z'
                  }
         },
         {$project: {
              comments: 1,
              new_created: {
                        "yr" : {"$year" : "$created_date"},
                        "mo" : {"$month" : "$created_date"}
                     }
              }
         },
         {$group:
             {_id: "$new_created",
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

你会得到如下结果:

{
    "result" : [
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 7
            },
            "totalComments" : 5
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 8
            },
            "totalComments" : 19
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 9
            },
            "totalComments" : 21
        }
    ],
    "ok" : 1
}

Mongodb启动方法:设定参数启动;从设置文件启动

接手的MongoDB只有一个日志文件,体积非常大,排错不便。在找解决办法的时候发现MongoDB的启动文件配置项超级多,于是产生了解释配置参数的想法。

mongod服务有两种启动方式

一种是通过配置文件
$ ./mongod -f /app/mongodb/mongodb27017/conf/mongodb.conf

一种直接指明参数
$./mongod --dbpath=/app/mongodb/db --port=27017 --fork --logpath=/app/mongodb/db/mongodb.log

由于安装文件没有默认的配置文件,需要配置的参数项又比较多,推荐使用配置文件的方式启动服务。下面详细介绍配置

storage:
    dbPath: "/data/mongodb/data”   #数据目录
    directoryPerDB: true      #将不同DB的数据分子目录存储,基于dbPath,默认为 false
    engine: “wiredTiger"      #存储引擎,3.2后默认wiredTiger 可选 mmapv1
    wiredTiger:
        engineConfig:
            cacheSizeGB: 15     #Mongodb吃内存,并且不会主动释放,默认的缓存大小为max(1/2maxmem,256M),可参照系统总内存进行设置。
            journalCompressor: snappy     #journal日志的压缩算法,可选值为“none”、“snappy”、“zlib”。压缩差别可百度,总体来说snappy最合适。
            directoryForIndexes: true #是否将索引和collections数据分别存储在dbPath单独的目录中。默认值为false,放在一个目录。
        collectionConfig:
            blockCompressor: snappy      #collection数据压缩算法,可选值“none”、“snappy”、“zlib”
        indexConfig:
            prefixCompression: true   #是否对索引数据使用“前缀压缩” ,对那些经过排序的值存储,可以减少索引数据的内存使用量。默认值为true。
    journal:
        enabled: true     #是否开启journal日志持久存储,journal日志用来数据恢复,是mongod最基础的特性,通常用于故障恢复。
        commitIntervalMs: 100   #New in version 3.2.  日志提交间隔
systemLog:
    destination: file  #日志输出目的地,可为 file 或 syslog; if file, you must also specify systemLog.path  
    path: "/var/log/mongodb/mongodb.log"
    logAppend: true   #启动或重启后是否追加写入
    logRotate: rename   #防止一个日志文件特别大,可选项:rename(重命名日志文件,默认值);reopen(使用linux日志rotate特性,关闭并重新打开此日志文件,可以避免日志丢失,但是logAppend必须为true)
    timeStampFormat: ctime  #时间格式 默认为 iso8601-local
replication:
     oplogSizeMB: 10240
     replSetName: getui-bi
     enableMajorityReadConcern: false
processManagement:
    fork: true    #守护进程模式启动,默认 false
    pidFilePath: "/var/run/mongodb/mongod.pid”  #配合"fork:true"参数,将mongod/mongos进程ID写入指定的文件,如果不指定,将不会创建PID文件
net:
    bindIp: 127.0.0.1  
    port: 27017
    ipv6: false   #是否支持mongos/mongod多个实例之间使用IPV6网络,默认值为false。此值需要在整个cluster中保持一致。
    maxIncomingConnections: 10000   #进程允许的最大连接数,默认:65536
    wireObjectCheck : false  #当客户端写入数据时,mongos/mongod是否检测数据的有效性(BSON),如果数据格式不良,此insert、update操作将会被拒绝;默认值为true
    unixDomainSocket:
        enabled : true
security:
    keyFile: /opt/mongodb/etc/mongodb-keyfile #指定分片集或副本集成员之间身份验证的key文件存储位置。
    authorization: enabled   #打开访问数据库和进行操作的用户角色认证

回到写这篇博客的原因,Mongodb的日志量较大,可通过上述rename的方式进行日期分类,也可设置日志级别和安静模式,使日志量减小。

systemLog:         #系统日志配置
   verbosity: <int> #日志级别,0:默认值,包含“info”信息,1~5,即大于0的值均会包含debug信息
   quiet: <boolean>  #"安静",此时mongod/mongos将会尝试减少日志的输出量。不建议在production环境下开启,否则将会导致跟踪错误比较困难。 

python操作mongodb根据_id查询数据的实现方法

本文实例讲述了python操作mongodb根据_id查询数据的实现方法。分享给大家供大家参考。具体分析如下:

_id是mongodb自动生成的id,其类型为ObjectId,所以如果需要在python中通过_id查询,就需要转换类型

如果pymongo的版本号小于2.2,使用下面的语句导入ObjectId

from pymongo.objectid import ObjectId

如果pymongo的版本号大于2.2,则使用下面的语句

from bson.objectid import ObjectId

查询代码如下:

collection.find_one({'_id':ObjectId('50f0d76347f4ec148890ef1e')})

MongoDB学习笔记(7)— 条件操作符

描述

条件操作符用于比较两个表达式并从mongoDB集合中获取数据。

在本章节中,我们将讨论如何在MongoDB中使用条件操作符。

MongoDB中条件操作符有:

  • (>) 大于 – $gt
  • (<) 小于 – $lt
  • (>=) 大于等于 – $gte
  • (<= ) 小于等于 – $lte

我们使用的数据库名称为”runoob” 我们的集合名称为”col”,以下为我们插入的数据。

为了方便测试,我们可以先使用以下命令清空集合 “col” 的数据:

db.col.remove({})

插入以下数据

>db.col.insert({
    title: 'PHP 教程', 
    description: 'PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['php'],
    likes: 200
})

>db.col.insert({title: 'Java 教程', 
    description: 'Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['java'],
    likes: 150
})

>db.col.insert({title: 'MongoDB 教程', 
    description: 'MongoDB 是一个 Nosql 数据库',
    by: '菜鸟教程',
    url: 'http://www.runoob.com',
    tags: ['mongodb'],
    likes: 100
})

使用find()命令查看数据:

> db.col.find()
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb" ], "likes" : 100 }

MongoDB (>) 大于操作符 – $gt

如果你想获取 “col” 集合中 “likes” 大于 100 的数据,你可以使用以下命令:

db.col.find({likes : {$gt : 100}})

类似于SQL语句:

Select * from col where likes > 100;

输出结果:

> db.col.find({likes : {$gt : 100}})
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
>

MongoDB(>=)大于等于操作符 – $gte

如果你想获取”col”集合中 “likes” 大于等于 100 的数据,你可以使用以下命令:

db.col.find({likes : {$gte : 100}})

类似于SQL语句:

Select * from col where likes >=100;

输出结果:

> db.col.find({likes : {$gte : 100}})
{ "_id" : ObjectId("56066542ade2f21f36b0313a"), "title" : "PHP 教程", "description" : "PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "php" ], "likes" : 200 }
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb" ], "likes" : 100 }
>

MongoDB (<) 小于操作符 – $lt

如果你想获取”col”集合中 “likes” 小于 150 的数据,你可以使用以下命令:

db.col.find({likes : {$lt : 150}})

类似于SQL语句:

Select * from col where likes < 150;

输出结果:

> db.col.find({likes : {$lt : 150}})
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb" ], "likes" : 100 }

MongoDB (<=) 小于操作符 – $lte

如果你想获取”col”集合中 “likes” 小于等于 150 的数据,你可以使用以下命令:

db.col.find({likes : {$lte : 150}})

类似于SQL语句:

Select * from col where likes <= 150;

输出结果:

> db.col.find({likes : {$lte : 150}})
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
{ "_id" : ObjectId("5606654fade2f21f36b0313c"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb" ], "likes" : 100 }

MongoDB 使用 (<) 和 (>) 查询 – $lt 和 $gt

如果你想获取”col”集合中 “likes” 大于100,小于 200 的数据,你可以使用以下命令:

db.col.find({likes : {$lt :200, $gt : 100}})

类似于SQL语句:

Select * from col where likes>100 AND  likes<200;

输出结果:

> db.col.find({likes : {$lt :200, $gt : 100}})
{ "_id" : ObjectId("56066549ade2f21f36b0313b"), "title" : "Java 教程", "description" : "Java 是由Sun Microsystems公司于1995年5月推出的高级程序设计语言。", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "java" ], "likes" : 150 }
>

MongoDB 查询文档 http://www.runoob.com/mongodb/mongodb-query.html
MongoDB $type 操作符 http://www.runoob.com/mongodb/mongodb-operators-type.html

1、一些简写说明:

$gt -------- greater than  >

$gte --------- gt equal  >=

$lt -------- less than  <

$lte --------- lt equal  <=

$ne ----------- not equal  !=

$eq  --------  equal  =

2、模糊查询

查询 title 包含”教”字的文档:

db.col.find({title:/教/})

查询 title 字段以”教”字开头的文档:

db.col.find({title:/^教/})

查询 titl e字段以”教”字结尾的文档:

db.col.find({title:/教$/})

MongoDB 在系统数据库local上无法创建用户的解决方法

我们知道,MongoDB的Oplog (operations log)记录了用户的最近一段时间的操作(时间长短主要受设置的oplogSize和程序的写入更新量的影响)。那么,如果其他部门(例如BI团队)需要抽取数据,从 local.oplog.rs中读取解析一个不错的选择。

oplog位于local数据下面,为了将权限最小化,大家需要创建此库的权限(还可以将权限细化到集合,再次不讨论)。

习惯性的,在local数据库下面创建,但是报错了。

执行脚本

db.createUser(
{
user: "testuser_local",
pwd: "testuser_local",
roles: [ { role: "read", db: "local" } ]
}
)

报错信息

2018-XX-XXT14:48:30.437+0800 E QUERY [thread1] Error: couldn't add user: Cannot create users in the local database :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1290:15
@(shell):1:1

查看mongoDB的官网介绍,发现确实不可以在local数据库下面创建账号

未分类

其解决方案是,我们转到admin数据库下面,创建账号。

未分类

此时可以创建成功。

注意:

(1)在程序端配置连接字符串时,相应的需要添加登入验证数据库参数 –authenticationDatabase admin

(2)通过NoSQLBooster登入时,Auth DB 选择执行创建命令的数据库名字(本实例为admin)

未分类

Default Database 的编辑项,选择oplog所在的local数据库

未分类

登入成功

(但是在测试过程中,发现此工具在这个小权限下,登入可以成功,但是有时候执行命令时报错,而通过 MongoDB shell 执行不报错。还需探究根本原因)

(3) 建议数据的拉取,在辅助节点上拉取,减少主库的压力。

记录下在linux配置mongodb+nginx+node的过程

mongodb:

1、下载mongodb3.4版本,地址: https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.4.5.tgz

2、通过Xftp把文件放到已经创建好的目录里面。

3、使用tar xxx 命令,将压缩解压到当前的工作路径。顺便cd到解压后的文件夹,把所有文件的移到上一层,并删掉这个文件夹。现在目录如下

未分类

4、在bin的外层级新建一个文件夹树:data/db,以后这个db就是放数据的地方了。

5、进入到bin,使用mongod –dbpath ../data/db,就可以启动mongodb,默认端口是27017。

6、访问curl localhost:27017,看是否有这段信息,有则代表已成功启动。

未分类

7 如果bin文件夹没有index.html,可以自己建一个,开启成功后会打开这个index。

nginx:

1、先使用命令 rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm 安装nginx的安装yum源。

2、安装成功后/etc/yum.repos.d会有nginx.repo文件。

3、使用yum install nginx -y,安装nginx,默认地址为/etc/nginx。我安装完后是没有html文件夹的,可以自己添加一个html文件夹,里面放一个index.html。

4、使用cd进nginx文件夹。使用nginx即可开启nginx服务。

5、如果想修改nginx.conf,修改完后使用nginx -s -reload即可重启nginx服务。

此时可以在window界面使用浏览器直接访问地址了。如,我的centos的ip是123.456.7.89,并在nginx上监听了80端口,如下:

listen       80;
server_name  localhost;
location / {
        proxy_pass  http://localhost:27017;  #被代理的服务器的域名
    proxy_redirect     off;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    }

6、打开浏览器,地址栏输入123.456.7.89,成功后提示

未分类

如果提示拒绝访问,可能是centos并没有开启80端口。

使用如下命令:

## 开放指定端口(永久)
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload
## 查看已开放的端口
firewall-cmd --list-ports

未分类

则是再重复步骤6即可。

nodejs:

1、使用命令安装源

V8.x: 

#curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -
V7.x:

#curl --silent --location https://rpm.nodesource.com/setup_7.x | bash -
V6.x:

#curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -
V5.x:

#curl --silent --location https://rpm.nodesource.com/setup_5.x | bash -

2、 yum install -y nodejs

Python + MongoDB 小型程序利器

|作为一个用着和领扣 LeetCode 同样技术栈 —— Python 的程序员,对于平时一些小的想法和 Demo 自然是通过 Python 来解决,但是在学习和使用的过程中,对于数据的存储一直难以统一,最初使用纯文本文件存储,发现对于格式化索引来说纯文本存储效率太低,之后又转了 MySQL 存储,但是发现过于复杂,对于一些热更新数据来说写起来十分不雅观,限制太多,无奈便换成了 JSON 格式存储,当然,那是在现在使用的 MongoDB 之前了。

MongoDB 概要

MongoDB 用起来其实比较随意,相关命令遍写的感觉和 Python 这类弱类型语言很相似,用起来比较 Geek。

未分类

MongoDB 是一个面向文档的数据库,目前由 10gen 开发并维护,它的功能丰富,齐全,完全可以替代 MySQL。 MonogDB 的一些亮点:

  • 使用 JSON 风格 语法,易于掌握和理解:MongoDB 使用 JSON 的变种 BSON 作为内部存储的格式和语法。针对 MongoDB 的操作都使用 JSON 风格语法,客户端提交或接收的数据都使用 JSON 形式来展现。相对于 SQL来说,更加直观,容易理解和掌握。
  • Schema-less,支持嵌入子文档:MongoDB 是一个 Schema-free 的文档数据库。一个数据库可以有多个Collection,每个 Collection 是Documents的集合。Collection 和 Document 和传统数据库的 Table 和 Row并不对等。无需事先定义 Collection,随时可以创建。
  • Collection中可以包含具有不同 schema 的文档记录。 这意味着,你上一条记录中的文档有3个属性,而下一条记录的文档可以有10个属 性,属性的类型既可以是基本的数据类型(如数字、字符串、日期等),也可以是数组或者散列,甚至还可以是一个子文档(embed document)。这样,可以实现逆规范化(denormalizing)的数据模型,提高查询的速度。

未分类

如果在本地测试或者仅仅是为了临时丢一些数据进去的话,安装并启动 mongod 后直接在命令行下 mongo 即可完成连接,默认没有连接密码,如果看到类似如下提示的话,说明 MongoDB 已经安装完成了:

未分类

Python + MongoDB

下面让 Python 连接上 MongoDB:

安装 PyMongo:

pip3 install pymongo

在 Python 中引入:

import pymongo

指定数据表并连接:

# 默认的 MongoDB 监听地址
myclient = pymongo.MongoClient("mongodb://localhost:27017/")

# 使用上一步建立的 myclient 连接,并且使用 leetcode 数据库
db = myclient["leetcode"]

# 使用 db 连接的 leetcode 数据库中的 articles 表
table = db['articles']

增删改查:

# 定义我们要插入的数据,JSON 格式,在 Python 中就是 Dict 格式
post = {"author": "Nova Kwok",
        "text": "LeetCode is in China!",
        "tags": ["mongodb", "python", "pymongo"],
        "date": datetime.datetime.utcnow()}

插入一条记录:

# 插入一条记录并返回插入 ID
post_id = posts.insert_one(post).inserted_id

查询记录,这里我们需要多 import 一个包,pprint:

import pprint
pprint.pprint(posts.find_one())

返回结果:

{u'_id': ObjectId('...'),
u'author': u'Nova Kwok',
u'date': datetime.datetime(...),
u'tags': [u'mongodb', u'python', u'pymongo'],
u'text': u'LeetCode is now in China!'}

Mongodb的索引

想把Mongodb真正的使用好,不是那么简单,不能只会增删改查,还需要练习内功。
内功在武侠小说里面是一个人发展强大起来的重要基础,在我们Mongodb中练习内功也有这样的作用。
开始今天的内功学习。

为什么需要索引

索引:提高查询效率最有效的手段。是解决查询速度缓慢而退出的一种特殊的数据结构,以易于遍历的形式存储部分数据内容;索引数据存储在内存当中,同样加快了索引查找数据的效率。

从索引的简介中了解两个个知识点:

  • 目的提高查询速度。

  • 索引存储在内存当中。

索引针对的是查询速度缓慢,数据量大特别是数据量在百万级别,千万级别以及以上的数据量。
索引能大大减少查询时间的损耗。

eg:自己写过一段Monodb中的关联查询,数据表数据在百万级别,没有使用索引的时刻查询时间在7s,使用索引后查询时间是0.3s。效率大大提高。

Mongodb的索引机制

在往Mongodb中插入文档,每个文档都会经过底层的存储引擎持久化操作之后,会展示一个位置信息。
通过这个位置 信息,就能从存储引擎中读取到数据。不同的存储引擎处处位置的信息不同。选择合适的引擎也能帮助我们快速的查找数据。
eg: wiredtiger引擎生成一个KEY值,通过KEY去访问对应的文档。mmapv1引擎里面位置信息是通过文件id与文件内的偏移量决定的。

索引的类型

在Mongodb中有很多种索引支持,包含以下索引类型:单字段索引,联合索引,多key索引,文本索引, 地理位置索引,哈希索引.不同的索引类型支持不同类型的数据格式和查询需求。

单字段索引

单字段索引是针对单个字段进行设置索引的操作。

//创建索引的语法db.getCollection('test').createIndex({name:1})
{    "createdCollectionAutomatically" : false,    "numIndexesBefore" : 1,    "numIndexesAfter" : 2,    "ok" : 1.0}
数字1 是索引里面的数据按照升序进行排序,需要按照降序排序的索引可以写-1db.getCollection('test').createIndex({name:-1})

代码中针对name字段进行了创建索引,特别是Mongodb的主键_Id索引也是单字段索引。

联合索引

联合索引在单字段索引上进行了多个字段操作,将多个字段合并为一个索引的联合索引。

//创建索引的语法还是一样的。db.getCollection('test').createIndex({name:1,phone:1})
{    "createdCollectionAutomatically" : false,    "numIndexesBefore" : 2,    "numIndexesAfter" : 3,    "ok" : 1.0}

在查询字段中引入联合索引,在查询语句操作时需要按照联合索引的顺序进行查询,否则不能走索引的操作。
eg:我们创建索引时name在前 phone在后。

//find操作db.getCollection('test').find({name:"qiiq"})
db.getCollection('test').find({name:"qiiq",phone:12512135})
这两种操作是能走联合索引。//下面两种操作时不能走联合索引db.getCollection('test').find({phone:12512135,name:"qiiq"})
db.getCollection('test').find({phone:12512135})

多key索引

多key索引:当内容是数组或者list集合创建的一种索引。该索引会为数组中的每个字段创建索引。

子文档索引

该索引用来嵌入子文档中的字段进行创建索引。操作也可以有复合索引,单字段索引。

db.getCollection('test').createIndex({"user.name":1})

索引的属性

在Mongodb中不仅支持多个类型的索引,还能对索引增加一些额外的属性。

  • 唯一索引:在Mongodb中_id就是利用单字段索引加唯一索引的属性,构成的。

  • 部分索引(3.2版本之后新增):仅索引符合指定过滤器表达式集合中的文档。部分索引有较低的存储要求,降低索引的创建与维护。

  • 稀疏索引: 确保索引仅包含具有索引字段的文档的条目。会跳过没有索引字段的文档。

  • TTL索引:在一定时间后自动从集合中删除文档的一种索引。

索引的操作

索引的操作包含 创建,查看 ,删除,重建操作。

索引的创建

我们在前面的操作操作中已经使用索引的创建

db.getCollection('test').createIndex({"user.name":1})
db.collection.createIndex(keys,选项)

1、keys,要建立索引的参数列表。如:{KEY:1},其中key表示字段名,1表示升序排序,也可使用使用数字-1降序。

2、options,可选参数,表示建立索引的设置。可选值如下:

  • background,Boolean,在后台建立索引,以便建立索引时不阻止其他数据库活动。默认值 false。

  • unique,Boolean,创建唯一索引。默认值 false。

  • name,String,指定索引的名称。如果未指定,MongoDB会生成一个索引字段的名称和排序顺序串联。

  • dropDups,Boolean,创建唯一索引时,如果出现重复删除后续出现的相同索引,只保留第一个。

  • sparse,Boolean,对文档中不存在的字段数据不启用索引。默认值是 false。

  • v,index version,索引的版本号。

  • weights,document,索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。

查看索引

getIndexes()查看集合的所有索引。

db.getCollection('test').getIndexes()
[
    {        "v" : 2,        "key" : {            "_id" : 1
        },        "name" : "_id_",        "ns" : "test.test"
    },
    {        "v" : 2,        "key" : {            "name" : 1.0
        },        "name" : "name_1",        "ns" : "test.test"
    },
    {        "v" : 2,        "key" : {            "name" : 1.0,            "phone" : 1.0
        },        "name" : "name_1_phone_1",        "ns" : "test.test"
    }
]

totalIndexSize()查看集合索引的总大小。

db.getCollection('test').totalIndexSize()69632 //单位字节

索引的优化

慢查询查看

在mysql数据库中,有慢查询语句的展示,在Mongodb中也有这样的实现名字是Profiling。
更改Mongodb的阈值,有三个级别的性质。

  • 0 代表的是不开启慢分析性质。

  • 1 根据处理时间将超过阈值的请求记录都记录到system.profile集合中。

  • 2 所有记录都将记录到集合system.profile中。
    在随着业务的发展,刚开始创建的索引可能不符合现在的业务需求。索引的数量并不是越多越好。
    索引能帮助我们提高查询的性能,但是会影响到插入和更新的性能。写入与更新操作每次都需要把索引更新。
    在此就可以根据慢请求的日志,进行索引创建的调整。

索引分析

Mongodb中有一个命令explain();帮助我们进行查询的慢分析。

db.getCollection("test").find().explain()
{    "queryPlanner" : {        "plannerVersion" : 1,        "namespace" : "test.test",        "indexFilterSet" : false,        "parsedQuery" : {},        "winningPlan" : {            "stage" : "COLLSCAN",  //代表的是进行的全盘扫描,没有利用到索引。当然也是查询条件中没有指定条件语句所致
            "direction" : "forward"
        },        "rejectedPlans" : []
    },    "serverInfo" : {        "host" : "237ae74dd4d9",        "port" : 27017,        "version" : "4.0.3",        "gitVersion" : "7ea530946fa7880364d88c8d8b6026bbc9ffa48c"
    },    "ok" : 1.0}

在name字段增加索引,执行查询计划。

db.getCollection("test").find({"name":"frq"}).explain()
{    "queryPlanner" : {        "plannerVersion" : 1,        "namespace" : "test.test",        "indexFilterSet" : false,        "parsedQuery" : {            "name" : {                "$eq" : "frq"
            }
        },        "winningPlan" : {            "stage" : "FETCH",            "inputStage" : {                "stage" : "IXSCAN",                "keyPattern" : {                    "name" : 1.0,                    "phone" : 1.0
                },                "indexName" : "name_1_phone_1",                "isMultiKey" : false,                "multiKeyPaths" : {                    "name" : [],                    "phone" : []
                },                "isUnique" : false,                "isSparse" : false,                "isPartial" : false,                "indexVersion" : 2,                "direction" : "forward",                "indexBounds" : {                    "name" : [ 
                        "["frq", "frq"]"
                    ],                    "phone" : [ 
                        "[MinKey, MaxKey]"
                    ]
                }
            }
        },        "rejectedPlans" : [ 
            {                "stage" : "FETCH",  执行完索引后,进行FETCH,读取出最终的                "inputStage" : {                    "stage" : "IXSCAN",  // 重点是这里 用到了索引字段,先在索引中查找。                    "keyPattern" : {                        "name" : 1.0
                    },                    "indexName" : "name_1",                    "isMultiKey" : false,                    "multiKeyPaths" : {                        "name" : []
                    },                    "isUnique" : false,                    "isSparse" : false,                    "isPartial" : false,                    "indexVersion" : 2,                    "direction" : "forward",                    "indexBounds" : {                        "name" : [ 
                            "["frq", "frq"]"
                        ]
                    }
                }
            }
        ]
    },    "serverInfo" : {        "host" : "237ae74dd4d9",        "port" : 27017,        "version" : "4.0.3",        "gitVersion" : "7ea530946fa7880364d88c8d8b6026bbc9ffa48c"
    },    "ok" : 1.0
}

mongoDB之创建用户与认证模式启动

mongoDB创建用户

不管是mongoDB还是MySQL,我们往往都需要创建用户来保证安全性,在这里对这方面内容做一个简单的总结~

创建用户

创建用户有帮助增加数据库安全的作用,在mongoDB中需要下列步骤

  1. 创建管理员
  2. 授权认证
  3. 给使用的数据库添加用户

在shell中执行以下操作

sudo service mongod start         # 启动mongod
mongo                             # 以非授权的方式启动

这时我们可以访问到任意一个不需要认证的数据库(比如我们用一个可视化客户端打开)

未分类

如果使用授权方式启动mongoDB,而我们不去登录的话我们也是无法使用的,所以我们要先创建一个管理员账号

创建管理员账号

首先非授权方式启动我们的mongoDB

> use admin # 创建admin数据库
> db.createUser({user:"admin",pwd:"admin",roles:["root"]}) # 创建一个用户名为admin,密码为admin,身份为管理员的User
> db.auth("admin","admin") # 进行认证
1       # 显示1,认证成功

这样,我们的数据库就有了自己的管理员
下面我们为创建一个有用户的db

> use demo1 # 切换数据库
switched to db demo1

 # 创建一个用户名为demo1user,密码为demo1,身份为数据库用户,拥有demo1的User
> db.createUser({user:"demo1user",pwd:"demo1",roles:[{role:"dbOwner",db:"demo1"}]})
Successfully added user: {
    "user" : "demo1user",
    "roles" : [
        {
            "role" : "dbOwner",
            "db" : "demo1"
        }
    ]
}

手动以认证模式启动

经过我的尝试,我发现以认证方式启动mongd的方式有很多,我选择一种我比较喜欢的列在下面:
在安装mongoDB之后,如果是使用和我一样的方式安装的,那么将会自动生成一个配置文件,位于/etc/mongod.conf,我们也可以用这样的方式启动mongod

sudo mongod -f /etc/mongod.conf --auth # 认证模式启动
sudo mongod -f /etc/mongod.conf  # 非认证模式启动

默认通过认证模式启动

一切脱离版本的配置都是耍流氓
如果不想每次都带上–auth参数的haunted,我们可以在配置文件中修改,也就是对/etc/mongod.conf进行修改,我们以官网文档为准https://docs.mongodb.com/manual/reference/configuration-options/

未分类

关于用户认证方面的配置,点击上面的security仔细阅读一下

下面贴上我的配置文件片段(最后两行就是默认以用户认证方式启动)

# mongod.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.
storage:
  dbPath: /var/lib/mongodb
  journal:
    enabled: true
#  engine:
#  mmapv1:
#  wiredTiger:

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

# network interfaces
net:
  port: 27017
  bindIp: 127.0.0.1


# how the process runs
processManagement:
  timeZoneInfo: /usr/share/zoneinfo

security:
  authorization: enabled

如此一来,不管是sudo mongod -f /etc/mongod.conf还是sudo service mongod start都是以认证模式启动的了。

数据库中间件 MyCAT 源码分析 —— SQL ON MongoDB

本文主要基于 MyCAT 1.6.5 正式版

1. 概述

可能你在看到这个标题会小小的吃惊,MyCAT 能使用 MongoDB 做数据节点。是的,没错,确实可以。
吼吼吼,让我们开启这段神奇的“旅途”。

本文主要分成四部分:

  1. 总体流程,让你有个整体的认识
  2. 查询操作
  3. 插入操作
  4. 彩蛋,????彩蛋,????彩蛋

建议你看过这两篇文章(非必须):

  1. 《MyCAT 源码分析 —— 【单库单表】插入》https://link.juejin.im/?target=http%3A%2F%2Fwww.iocoder.cn%2FMyCAT%2Fsingle-db-single-table-insert%2F%3Fself
  2. 《MyCAT 源码分析 —— 【单库单表】查询》https://link.juejin.im/?target=http%3A%2F%2Fwww.iocoder.cn%2FMyCAT%2Fsingle-db-single-table-select%2F%3Fself

2. 主流程

未分类

  1. MyCAT Server 接收 MySQL Client 基于 MySQL协议 的请求,翻译 SQL 成 MongoDB操作 发送给 MongoDB Server。
  2. MyCAT Server 接收 MongoDB Server 返回的 MongoDB数据,翻译成 MySQL数据结果 返回给 MySQL Client。

这样一看,MyCAT 连接 MongoDB 是不是少神奇一点列。

未分类

Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。JDBC是面向关系型数据库的。

MyCAT 使用 JDBC 规范,抽象了对 MongoDB 的访问。通过这样的方式,MyCAT 也抽象了 SequoiaDB 的访问。可能这样说法有些抽象,看个类图压压惊。

未分类

是不是熟悉的味道。不得不说 JDBC 规范的精妙。

3. 查询操作

SELECT id, name FROM user WHERE name > '' ORDER BY _id DESC;

未分类

看顺序图已经很方便的理解整体逻辑,我就不多废话啦。我们来看几个核心的代码逻辑。

3.1 查询 MongoDB

// MongoSQLParser.java
public MongoData query() throws MongoSQLException {
   if (!(statement instanceof SQLSelectStatement)) {
       //return null;
       throw new IllegalArgumentException("not a query sql statement");
   }
   MongoData mongo = new MongoData();
   DBCursor c = null;
   SQLSelectStatement selectStmt = (SQLSelectStatement) statement;
   SQLSelectQuery sqlSelectQuery = selectStmt.getSelect().getQuery();
   int icount = 0;
   if (sqlSelectQuery instanceof MySqlSelectQueryBlock) {
       MySqlSelectQueryBlock mysqlSelectQuery = (MySqlSelectQueryBlock) selectStmt.getSelect().getQuery();

       BasicDBObject fields = new BasicDBObject();

       // 显示(返回)的字段
       for (SQLSelectItem item : mysqlSelectQuery.getSelectList()) {
           //System.out.println(item.toString());
           if (!(item.getExpr() instanceof SQLAllColumnExpr)) {
               if (item.getExpr() instanceof SQLAggregateExpr) {
                   SQLAggregateExpr expr = (SQLAggregateExpr) item.getExpr();
                   if (expr.getMethodName().equals("COUNT")) { // TODO 待读:count(*)
                       icount = 1;
                       mongo.setField(getExprFieldName(expr), Types.BIGINT);
                   }
                   fields.put(getExprFieldName(expr), 1);
               } else {
                   fields.put(getFieldName(item), 1);
               }
           }

       }

       // 表名
       SQLTableSource table = mysqlSelectQuery.getFrom();
       DBCollection coll = this._db.getCollection(table.toString());
       mongo.setTable(table.toString());

       // WHERE
       SQLExpr expr = mysqlSelectQuery.getWhere();
       DBObject query = parserWhere(expr);

       // GROUP BY
       SQLSelectGroupByClause groupby = mysqlSelectQuery.getGroupBy();
       BasicDBObject gbkey = new BasicDBObject();
       if (groupby != null) {
           for (SQLExpr gbexpr : groupby.getItems()) {
               if (gbexpr instanceof SQLIdentifierExpr) {
                   String name = ((SQLIdentifierExpr) gbexpr).getName();
                   gbkey.put(name, Integer.valueOf(1));
               }
           }
           icount = 2;
       }

       // SKIP / LIMIT
       int limitoff = 0;
       int limitnum = 0;
       if (mysqlSelectQuery.getLimit() != null) {
           limitoff = getSQLExprToInt(mysqlSelectQuery.getLimit().getOffset());
           limitnum = getSQLExprToInt(mysqlSelectQuery.getLimit().getRowCount());
       }
       if (icount == 1) { // COUNT(*)
           mongo.setCount(coll.count(query));
       } else if (icount == 2) { // MapReduce
           BasicDBObject initial = new BasicDBObject();
           initial.put("num", 0);
           String reduce = "function (obj, prev) { " + "  prev.num++}";
           mongo.setGrouyBy(coll.group(gbkey, query, initial, reduce));
       } else {
           if ((limitoff > 0) || (limitnum > 0)) {
               c = coll.find(query, fields).skip(limitoff).limit(limitnum);
           } else {
               c = coll.find(query, fields);
           }

           // order by
           SQLOrderBy orderby = mysqlSelectQuery.getOrderBy();
           if (orderby != null) {
               BasicDBObject order = new BasicDBObject();
               for (int i = 0; i < orderby.getItems().size(); i++) {
                   SQLSelectOrderByItem orderitem = orderby.getItems().get(i);
                   order.put(orderitem.getExpr().toString(), getSQLExprToAsc(orderitem.getType()));
               }
               c.sort(order);
               // System.out.println(order);
           }
       }
       mongo.setCursor(c);
   }
   return mongo;
}

3.2 查询条件

// MongoSQLParser.java
private void parserWhere(SQLExpr aexpr, BasicDBObject o) {
   if (aexpr instanceof SQLBinaryOpExpr) {
       SQLBinaryOpExpr expr = (SQLBinaryOpExpr) aexpr;
       SQLExpr exprL = expr.getLeft();
       if (!(exprL instanceof SQLBinaryOpExpr)) {
           if (expr.getOperator().getName().equals("=")) {
               o.put(exprL.toString(), getExpValue(expr.getRight()));
           } else {
               String op = "";
               if (expr.getOperator().getName().equals("<")) {
                   op = "$lt";
               } else if (expr.getOperator().getName().equals("<=")) {
                   op = "$lte";
               } else if (expr.getOperator().getName().equals(">")) {
                   op = "$gt";
               } else if (expr.getOperator().getName().equals(">=")) {
                   op = "$gte";
               } else if (expr.getOperator().getName().equals("!=")) {
                   op = "$ne";
               } else if (expr.getOperator().getName().equals("<>")) {
                   op = "$ne";
               }
               parserDBObject(o, exprL.toString(), op, getExpValue(expr.getRight()));
           }
       } else {
           if (expr.getOperator().getName().equals("AND")) {
               parserWhere(exprL, o);
               parserWhere(expr.getRight(), o);
           } else if (expr.getOperator().getName().equals("OR")) {
               orWhere(exprL, expr.getRight(), o);
           } else {
               throw new RuntimeException("Can't identify the operation of  of where");
           }
       }
   }
}

private void orWhere(SQLExpr exprL, SQLExpr exprR, BasicDBObject ob) {
   BasicDBObject xo = new BasicDBObject();
   BasicDBObject yo = new BasicDBObject();
   parserWhere(exprL, xo);
   parserWhere(exprR, yo);
   ob.put("$or", new Object[]{xo, yo});
}

3.3 解析 MongoDB 数据

// MongoResultSet.java
public MongoResultSet(MongoData mongo, String schema) throws SQLException {
   this._cursor = mongo.getCursor();
   this._schema = schema;
   this._table = mongo.getTable();
   this.isSum = mongo.getCount() > 0;
   this._sum = mongo.getCount();
   this.isGroupBy = mongo.getType();

   if (this.isGroupBy) {
       dblist = mongo.getGrouyBys();
       this.isSum = true;
   }
   if (this._cursor != null) {
       select = _cursor.getKeysWanted().keySet().toArray(new String[0]);
       // 解析 fields
       if (this._cursor.hasNext()) {
           _cur = _cursor.next();
           if (_cur != null) {
               if (select.length == 0) {
                   SetFields(_cur.keySet());
               }
               _row = 1;
           }
       }
       // 设置 fields 类型
       if (select.length == 0) {
           select = new String[]{"_id"};
           SetFieldType(true);
       } else {
           SetFieldType(false);
       }
   } else {
       SetFields(mongo.getFields().keySet());//new String[]{"COUNT(*)"};
       SetFieldType(mongo.getFields());
   }
}
  • 当使用 SELECT * 查询字段时,fields 使用第一条数据返回的 fields。即使,后面的数据有其他 fields,也不返回。

3.4 返回数据给 MySQL Client

// JDBCConnection.java
private void ouputResultSet(ServerConnection sc, String sql)
       throws SQLException {
   ResultSet rs = null;
   Statement stmt = null;

   try {
       stmt = con.createStatement();
       rs = stmt.executeQuery(sql);

       // header
       List<FieldPacket> fieldPks = new LinkedList<>();
       ResultSetUtil.resultSetToFieldPacket(sc.getCharset(), fieldPks, rs, this.isSpark);
       int colunmCount = fieldPks.size();
       ByteBuffer byteBuf = sc.allocate();
       ResultSetHeaderPacket headerPkg = new ResultSetHeaderPacket();
       headerPkg.fieldCount = fieldPks.size();
       headerPkg.packetId = ++packetId;
       byteBuf = headerPkg.write(byteBuf, sc, true);
       byteBuf.flip();
       byte[] header = new byte[byteBuf.limit()];
       byteBuf.get(header);
       byteBuf.clear();
       List<byte[]> fields = new ArrayList<byte[]>(fieldPks.size());
       for (FieldPacket curField : fieldPks) {
           curField.packetId = ++packetId;
           byteBuf = curField.write(byteBuf, sc, false);
           byteBuf.flip();
           byte[] field = new byte[byteBuf.limit()];
           byteBuf.get(field);
           byteBuf.clear();
           fields.add(field);
       }
       // header eof
       EOFPacket eofPckg = new EOFPacket();
       eofPckg.packetId = ++packetId;
       byteBuf = eofPckg.write(byteBuf, sc, false);
       byteBuf.flip();
       byte[] eof = new byte[byteBuf.limit()];
       byteBuf.get(eof);
       byteBuf.clear();
       this.respHandler.fieldEofResponse(header, fields, eof, this);

       // row
       while (rs.next()) {
           RowDataPacket curRow = new RowDataPacket(colunmCount);
           for (int i = 0; i < colunmCount; i++) {
               int j = i + 1;
               if (MysqlDefs.isBianry((byte) fieldPks.get(i).type)) {
                   curRow.add(rs.getBytes(j));
               } else if (fieldPks.get(i).type == MysqlDefs.FIELD_TYPE_DECIMAL ||
                       fieldPks.get(i).type == (MysqlDefs.FIELD_TYPE_NEW_DECIMAL - 256)) { // field type is unsigned byte
                   // ensure that do not use scientific notation format
                   BigDecimal val = rs.getBigDecimal(j);
                   curRow.add(StringUtil.encode(val != null ? val.toPlainString() : null, sc.getCharset()));
               } else {
                   curRow.add(StringUtil.encode(rs.getString(j), sc.getCharset()));
               }
           }
           curRow.packetId = ++packetId;
           byteBuf = curRow.write(byteBuf, sc, false);
           byteBuf.flip();
           byte[] row = new byte[byteBuf.limit()];
           byteBuf.get(row);
           byteBuf.clear();
           this.respHandler.rowResponse(row, this);
       }
       fieldPks.clear();
       // row eof
       eofPckg = new EOFPacket();
       eofPckg.packetId = ++packetId;
       byteBuf = eofPckg.write(byteBuf, sc, false);
       byteBuf.flip();
       eof = new byte[byteBuf.limit()];
       byteBuf.get(eof);
       sc.recycle(byteBuf);
       this.respHandler.rowEofResponse(eof, this);
   } finally {
       if (rs != null) {
           try {
               rs.close();
           } catch (SQLException e) {
           }
       }
       if (stmt != null) {
           try {
               stmt.close();
           } catch (SQLException e) {
           }
       }
   }
}

// MongoResultSet.java
@Override
public String getString(String columnLabel) throws SQLException {
   Object x = getObject(columnLabel);
   if (x == null) {
       return null;
   }
   return x.toString();
}
  • 当返回字段值是 Object 时,返回该对象.toString()。例如:
mysql> select * from user order by _id asc;
+--------------------------+------+-------------------------------+
| _id                      | name | profile                       |
+--------------------------+------+-------------------------------+
| 1                        | 123  | { "age" : 1 , "height" : 100} |

4. 插入操作

未分类

// MongoSQLParser.java
public int executeUpdate() throws MongoSQLException {
   if (statement instanceof SQLInsertStatement) {
       return InsertData((SQLInsertStatement) statement);
   }
   if (statement instanceof SQLUpdateStatement) {
       return UpData((SQLUpdateStatement) statement);
   }
   if (statement instanceof SQLDropTableStatement) {
       return dropTable((SQLDropTableStatement) statement);
   }
   if (statement instanceof SQLDeleteStatement) {
       return DeleteDate((SQLDeleteStatement) statement);
   }
   if (statement instanceof SQLCreateTableStatement) {
       return 1;
   }
   return 1;
}

private int InsertData(SQLInsertStatement state) {
   if (state.getValues().getValues().size() == 0) {
       throw new RuntimeException("number of  columns error");
   }
   if (state.getValues().getValues().size() != state.getColumns().size()) {
       throw new RuntimeException("number of values and columns have to match");
   }
   SQLTableSource table = state.getTableSource();
   BasicDBObject o = new BasicDBObject();
   int i = 0;
   for (SQLExpr col : state.getColumns()) {
       o.put(getFieldName2(col), getExpValue(state.getValues().getValues().get(i)));
       i++;
   }
   DBCollection coll = this._db.getCollection(table.toString());
   coll.insert(o);
   return 1;
}

5. 彩蛋

1、支持多 MongoDB ,并使用 MyCAT 进行分片。

MyCAT 配置:https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2FYunaiV%2FMycat-Server%2Ftree%2F1.6%2Fsrc%2Ftest%2Fresources%2Fmulti_mongodb

2、支持 MongoDB + MySQL 作为同一个 MyCAT Table 的数据节点。查询时,可以合并数据结果。

查询时,返回 MySQL 数据记录字段要比 MongoDB 数据记录字段全,否则,合并结果时会报错。

MyCAT 配置:https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2FYunaiV%2FMycat-Server%2Ftree%2F1.6%2Fsrc%2Ftest%2Fresources%2Fsingle_mongodb_mysql

3、MongoDB 作为数据节点时,可以使用 MyCAT 提供的数据库主键字段功能。

MyCAT 配置:https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2FYunaiV%2FMycat-Server%2Ftree%2F1.6%2Fsrc%2Ftest%2Fresources%2Fsingle_mongodb