一、MongoDB介绍:
1、MongoDB基本介绍
(1)、面向集合存储,易于存储对象类型的数据;
——集合的概念类似RDBMS中的表格(table),不同的是它不需要定义任何模式;
(2)、模式自由(schema-free),可动态增减字段;
——无需定义结构,不同文档可包含不同字段,零负担增减字段业务模式更灵活;
(3)、BSON格式存储(速度更快/可嵌套存储);
——文档型存储,存储值可以是任意数据类型,可嵌套,同json相比查询速度更快;
(4)、支持基础索引,文档索引和组合索引;
——支持常规索引、组合索引甚至文档索引;
(5)、支持复制和故障恢复,支持多种语言驱动;
——支持备份和故障修复;
总结:不是表格胜似表格
2、MongoDB重要特点——高可用
MongoDB的高可用机制主要有两种 分别是主从复制(Master-Slavr)和副本集(Replica Sets)。
(1)主从复制(Master-Slavr):
主从复制的概念非常简单,如下分别为一主一从和一主三从的示意图。
(2)副本集(Replica Sets):
简单来说 副本集就是有自动故障恢复功能的主从集群。主从集群和副本集群最为明显的区别是副本集没有固定的“主节点”。整个集群会选举处一个“主节点(活跃节点)”,当其不能工作时则变更到其他节点。两者看上去是非常类似的,副本集总会有一个活跃节点和一个或者多个备份节点。
副本集合突出的有点是所有的东西都是自动化的。首先它会为你做很多管理工作,自动提升备份节点为活跃节点,以确保运转正常。其次,对于开发者而言也非常的易用。我们只需要为副本集指定一下服务器,驱动程序就会自动找到服务器。
3、MongoDB重要特点——扩展性
MongoDB的扩展方式是分片(自动分片)。通过分片能够增加更多的机器来应对不断增加的负载和数据,还不影响应用。
所谓的分片是指将数据拆分,将其分散在不同机器的过程。其实几乎所有的数据库软件都支持分片,只不过其中很多都是“手动分片”。所谓“手动分片”只是应用程序(业务逻辑层)需要维护与若干不同数据库服务器的连接,每个连接还是完全独立的。应用程序管理不同服务器上的不同数据,存储查询都需要在正确的服务器上进行。显然这种方式并不利于维护,比如说向集群中添加或者从集群中删除节点都是十分困难的。
MongoDB支持自动分片,可以摆脱手动分片的管理困扰。集群自动切分数据,做负载均衡。也就是说应用程序不必知道哪片对应哪些数据甚至不需要知道数据已经被拆分了,应用程序只需要连接路由进程即可。
4、MongoDB重要特点——高性能
性能主要取决于存储引擎。mongodb 支持多种引擎,目前官方已经支持了mmapv1、wiredtiger、in-Memory等,另外在腾讯云上看到的mongorocks是第三方实现的存储引擎之一。关于这块只是大致的说几句。
整个的写内存,就是写磁盘”。数据写入内存之后,要通过操作系统的MMAP机制,特别做数据层的,如果数据存多份的话,就可能会造成数据不一致的问题。MongoDB在提供高性能的同时,数据只存一份。这种情况下,设计提供高性能的同时,可以很好地解决数据一致性问题。
对于WiredTiger则主要是为了提升高并发情况下的性能。总的来说在多线程、批量操作的场景下WiredTiger的性能可以提升好几倍。
MongoDB与Mysql概念对应
数据库 | 关系型数据库(SQL) | MongoDB |
DB | 数据库(databases) | 数据库(databases) |
Table | 表(table) | 集合(Collection) |
数据 | 行(row) | 文档(document) |
字段 | 列(column) | 数据字段(field) |
主键 | 主键(primary key) | 自动将_id设为主键 |
Join操作 | 表连接(table join) | 无join($lookup和内嵌文档达到同样效果) |
聚合 | aggregation(group by) | aggregation pipeline |
事务 | 事务(transactions) | 适当建模避免多文档事务的需要 |
存储方式 | 不同引擎不同存储方式 | 类JSON的文档格式存储(BSON) |
查询语句 | SQL语句 | MongoDB查询方式(类似js函数) |
成熟度 | 成熟度高 | 新兴数据库,成熟度低 |
占用空间 | 占用空间小 | 占用空间大 |
PS:副本集和分片的区别(广义概念上的区别,不是针对mongodb)
分片:一份数据被分开保存在N台机器上,N个机器上的数据组合起来是一份数据。
副本集:同一份数据被保存在N台机器上,每台机器上都有一份数据。
两者是可以嵌套存在的,在高可用场景每个分片又是作为副本集来部署是很常见的。例如腾讯云的cmongo就是如此。
5、关于副本集更多的一些细节
1、单台服务器可以执行的请求,主节点都可以执行(读、写、执行命令、创建索引等)。
2、备份节点不能执行写操作,默认也不能执行读操作。但是通过在备份节点上显式执行setSlaveOk命令之后,就可以从备份节点上读取数据。
副本集成员关系:
6、mongodb支持的集合(collection)数
默认情况下MongoDB的每个数据库的命令空间都保存一个16M的.ns文件,平均每个命名(集合名/索引名)占用约628字节;即一个数据库的命名空间上限约为24000个,如果考虑到每个集合都有一个索引那么最多可建12000个集合。
如果要建更多集合的话MongoDb也是支持的,只需要在启动的时候加上“-nssize”参数,这样对应数据库的命令空间文件就可以变得更大一保存更多的命令。但是这个命令空间依然有着最大2G的上限。即最大情况:可以支持340万左右命名,考虑索引占用的话比较稳妥的集合上限是170万。 这一点与tlist很不一样!!!tlist原则上对key的数量没有限制。
MongoDB在线修改nsSize | winway's blog
注意:以上有限制的是MMAP引擎。最新的WT引擎已经没有这个限制了,就引擎层面可以认为是无限的了。
但是腾讯云的CMongo_Helper也提到表太多的话对性能会有不好的影响;除此之外对备份/回档性能也会有一定影响,因为每个namespace在WT引擎中都对应一个磁盘上的文件。
因此结论就是:一个数据库10万量级的集合数是ok的,百万量级以上有点过了。
验证mongodb数据文件是怎么组织的。
(1)通过进程我们可以知道数据存放路径为 /var/lib/mongo
(2)进入路径发现每个集合对应一个文件、每个索引也对应一个文件,另外还有预写日志journal。此时文件数为50;接下来我们新建一个集合并写入一条数据看看有什么变化。
(3)db.coll.insert({name:"shuozhuoshuozhuo",age:25}),
参考:预先日志journal这篇 文章
二、MongoDB的安装和启动:
如下演示的是源码安装,其实也可以rpm安装,参见 这里 。
rpm链接其实就是如下截图中选择对应rpm包然后copy link的得到的;ecs就这么安装了一下。
关于源码安装要知道的是server、shell、tools、mongos是分别要单独安装的。
1、mongodb的安装
MongoDB 提供了 linux 各个发行版本 64 位的安装包,你可以在官网下载安装包。
安装前我们需要安装各个 Linux 平台依赖包。
Red Hat/CentOS:
sudo yum install libcurl openssl
MongoDB 源码下载地址:Try MongoDB Products | MongoDB
这里我们选择 tgz 下载,下载完安装包,并解压 tgz(以下演示的是 64 位 Linux上的安装) 。
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.4.0.tgz # 下载
tar zxvf mongodb-linux-x86_64-rhel70-4.4.0.tgz # 解压
mv mongodb-linux-x86_64-rhel70-4.4.0 /usr/local/mongodb4 # 将解压包拷贝到指定目录
MongoDB 的可执行文件位于 bin 目录下,所以可以将其添加到 PATH 路径中:
export PATH=<mongodb-install-directory>/bin:$PATH
为你 MongoDB 的安装路径。如本文的 /usr/local/mongodb4 。
export PATH=/usr/local/mongodb4/bin:$PATH
创建数据库目录
默认情况下 MongoDB 启动后会初始化以下两个目录:
- 数据存储目录:/var/lib/mongodb
- 日志文件目录:/var/log/mongodb
我们在启动前可以先创建这两个目录并设置当前用户有读写权限:
sudo mkdir -p /var/lib/mongo
sudo mkdir -p /var/log/mongodb
sudo chown `whoami` /var/lib/mongo # 设置权限
sudo chown `whoami` /var/log/mongodb # 设置权限
2、启动(重启/关闭) Mongodb 服务(注意:启动是启动、连接是连接):
(1)启动mongodb服务
#划分细点说总共有三种启动方式.一种是指令行指定选项;另一种是配置文件指定选项。
方式零:/etc/init.d/mongod start 或service mongod start
注:我云服务器是报错的!!
方式一:指令行指定选项
/*
--dbpath:指定数据库数据存放目录
--logpath:数据库日志存放目录
--bind_ip_all:允许所有ip连接
-f(--config):英爱是指定配置文件
我们可以通过 mongod -h 来查看mongod的更多功能选项。
*/
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --fork #这样启动的话外部访问不了
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --bind_ip_all --fork #如果想允许外部连接的话就这样
方式二:理论上讲启动方式如下
#/etc/mongodb.conf 这个就是配置文件的路径
mongod --config /etc/mongodb.conf
注:我的云服务器也是没起来的
这里列出一些配置的含义:
#数据库数据存放目录
dbpath=/var/lib/mongo
#数据库日志存放目录
logpath=/var/log/mongodb/mongod.log
#以追加的方式记录日志
logappend = true
#端口号 默认为27017
port=27017
#以后台方式运行进程
fork=true
#开启用户认证
auth=true
#关闭http接口,默认关闭http端口访问
nohttpinterface=true
#mongodb所绑定的ip地址(将其注释掉就支持远程连接了)
bind_ip = 127.0.0.1
#启用日志文件,默认启用
journal=true
#这个选项可以过滤掉一些无用的日志信息,若需要调试使用请设置为false
quiet=true
验证启动是否成功有很多方法:
(1)如下应该就是启动成功了。
(2)log/mongodb/mongod.log 日志中看到下下图信息,也能说明启动成功;
(3)ps aux | grep mongod #看到下下下图中的进程也能说明成功;
(4)最主要的是在/usr/local/mongodb4/bin 执行 ./mongo 能连接上去并操作shell更说明成功。
2、重启mongodb:
#这个重启有可能不成功
service mongodb restart
#是在不行就kill,然后在手动重启.
kill -2 pid #2表示中断;9表示强迫终止
(3)停止mongodb
1)可以使用如下命令:
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --shutdown
2)也可以在shell中实现
> use admin
switched to db admin
> db.shutdownServer()
三、MongoDB的使用
MongoDB 后台管理 Shell
如果你需要进入 mongodb 后台管理,你需要先打开 mongodb 安装目录的下的 bin 目录,然后执行 mongo 命令文件。
MongoDB Shell 是 MongoDB 自带的交互式 Javascript shell,用来对 MongoDB 进行操作和管理的交互式环境。
1、连接mongodb数据库
首先是跳转到在/usr/local/mongo4/bin目录;或者把这个目录加入环境变量(主要目的是连接数据库要用到其下的可执行文件"mongo",或者叫客户端)。
(1)连接本地数据库(本机是装了数据库的,现在要连接)。直接执行 ./mongo 即可。
(2)连接远程数据库,格式如下。
./mongo --host 11.xxx.x.180 --port 27017 -u "mongouser" -p "alibaba@mongo"
连接之后进入一个shell交互界面。它就是一个javaScript shell,可以运行一些简单的算术运算:
> 3*9
27
> 5+10
15
2、MongoDB设置用户名密码
本机连接上mongodb,然后进行创建用户名密码的操作。
一、创建管理员用户
(1)应该是切换到管理员权限
use admin
(2)#执行如下指令创建用户名密码
use admin
db.createUser({
user: 'mongouser', // 用户名
pwd: 'qidian@xxx', // 密码
roles:[{
role: 'root', // 角色
db: 'admin' // 数据库
}]
})
(3)查看是否设置成功(这个查看的是当前db有哪些用户)
show users
(4)然后我们就可以在安装mongo的端上执行如下语句连接上面的mongodb了
./mongo --host 9.134.50.94 --port 27017 -u "mongouser" -p "qidian@xxx"
二、添加数据库用户
use mydb
db.createUser({user: "mongouser1", pwd: "qidian@123", roles: [{ role: "dbOwner", db: "mydb" }]})
show users
三、删除用户
db.dropUser("mongouser1") #删除用户的时候需要切换到用户管理的数据库才可以删除;
(5)另外mong可能需要mongo服务器启动的时候启用认证"--auth"
数据库默认角色:
(1)数据库用户角色(Database User Roles)
read:授予User只读数据的权限
readWrite:授予User读写数据的权限
(2)数据库管理角色(Database Administration Roles):
dbAdmin:在当前dB中执行管理操作
dbOwner:在当前DB中执行任意操作
userAdmin:在当前DB中管理User
(3)备份和还原角色(Backup and Restoration Roles):
backup
restore
(4)跨库角色(All-Database Roles):
readAnyDatabase:授予在所有数据库上读取数据的权限
readWriteAnyDatabase:授予在所有数据库上读写数据的权限
userAdminAnyDatabase:授予在所有数据库上管理User的权限
dbAdminAnyDatabase:授予管理所有数据库的权限
(5)集群管理角色(Cluster Administration Roles):
clusterAdmin:授予管理集群的最高权限
clusterManager:授予管理和监控集群的权限,A user with this role can access the config and local databases, which are used in sharding and replication, respectively.
clusterMonitor:授予监控集群的权限,对监控工具具有readonly的权限
hostManager:管理Server
如下这种可能原因是网络不通、服务没起来等。
3、常用指令:
(0)help的使用,如下
1)shell命令行中直接输入 help 即可查看基本帮助与分类
2)如果想查看sh(sharding helpers)按照指引输入 sh.help()即可,其余类似
mongos> help
db.help() help on db methods
db.mycoll.help() help on collection methods
sh.help() sharding helpers
rs.help() replica set helpers
help admin administrative help
help connect connecting to a db help
help keys key shortcuts
help misc misc things to know
help mr mapreduce
show dbs show database names
show collections show collections in current database
show users show users in current database
show profile show most recent system.profile entries with time >= 1ms
show logs show the accessible logger names
show log [name] prints out the last segment of log in memory, 'global' is default
use <db_name> set current database
db.mycoll.find() list objects in collection mycoll
db.mycoll.find( { a : 1 } ) list objects in mycoll where a == 1
it result of the last line evaluated; use to further iterate
DBQuery.shellBatchSize = x set default number of items to display on shell
DBQuery.shellBatchSize
sh.help() → sh.getBalancerState()
db.help() → db.version()
db.help() → db.getCollection()
db.mycoll.help() → db.mycoll.getIndexes()/db.mycoll.createIndex(keypattern[,options])
(1)insert语句和save语句的区别:
insert只是插入操作;save则可以插入或者修改。当新增数据的主键已经存在时insert会抛主键异常不保存当前数据;而save会对当前数据进行修改操作。
(2)常用操作
1、首先是启动mongodb
./usr/local/mongodb4/bin/mongo #对于上述安装路径这样既可
#或者也可以这样启动mongodb服务
mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --fork
2、查看当前版本
db.version()
2、显示数据库(两者均可)
show dbs
show databases
3、使用其中的mydb这个数据库
use mydb
4、查看当前使用的数据库
db
5、删除某个db(例如删除mydb)
> use mydb
switched to db mydb
> db.dropDatabase()
{ "dropped" : "mydb", "ok" : 1 }
注:这块有个坑点,对于4.2及之前版本的分片集群删除一个db后需要有额外的操作才能建立同名的集合
https://docs.mongodb.com/manual/reference/method/db.dropDatabase/index.html
6、查看当前db有哪些table
show tables; #前面应该已经use dbname;
show collections; #其实这里换成collections更好
7、删除某个集合的数据(集合被保留)
db.mycollection.remove({要填条件})
8、删除某个集合(整体删除集合)
db.mycollection.drop()
9、集合重命名(将集合mycollection改成zhangsan_collection)
db.mycollection.renameCollection('zhangsan_collection');
#注:db.集合名称.操作() #下面指令的格式全部都是如此
#当集合名称为纯数字如"2355128746"时常规写法会报错,建议用db.getCollection("2355128746").find()来操作。
9、插入数据(往coll集合中插入数据)
db.coll.save({_id:1000000,age:18,time:1500000001,motto:"学无止境,术有专攻",name:"zhangsan",hight:181,education:{school:"zhejiang university",educational:"master"}})
db.coll.save({_id:1000001,age:18,time:1500000002,motto:"百闻不如一见",name:"zhangsan",hight:180,education:{school:"zhejiang university",educational:"master"}})
db.coll.save({_id:1000002,age:20,time:1500000003,motto:"天下兴亡,匹夫有责",name:"zhangsan",hight:180,education:{school:"zhejiang university",educational:"bachelor"}})
db.coll.save({_id:1000003,age:20,time:1500000004,motto:"一寸光阴一寸金",name:"zhangsan2",hight:181,education:{school:"zhejiang university",educational:"master"}})
db.coll.save({_id:1000004,age:20,time:1500000005,motto:"学无止境,术有专攻",name:"zhangsan3",weight:"60kg",job:{company:"tencent",position:"server",class:6}})
db.coll.save({_id:1000005,age:27,time:1500000006,motto:"己所不欲,勿施于人",name:"zhangsan3",weight:"70kg",job:{company:"alibaba",position:"server",class:6}})
db.coll.save({_id:1000006,age:28,time:1500000007,motto:"好好学习,天天向上",name:"zhangsan3",weight:"70kg",job:{company:"alibaba",position:"server",class:8}})
db.coll.save({_id:1000007,age:29,time:1500000008,name:"zhangsan4",weight:"80kg",job:{company:"baidu",position:"server",class:8}})
db.coll.save({_id:1000008,age:30,time:1500000009,name:"zhangsan4",weight:"75kg",job:{company:"baidu",position:"client",class:8}})
db.coll.save({_id:10000009,age:31,time:150000010,name:"zhangsan4",weight:"60kg",habit:["basketball","football","tennis","swimming"]})
db.coll.save({_id:10000010,age:32,time:150000011,name:"zhangsan4",weight:"60kg",habit:["basketball"]})
注意点:
1)一个集合下_id是唯一的,当_id相同时就会覆盖。
db.coll.save({_id:3000000000,name:"zhangsan"})
10、一次性插入多条数据——用[]表示数组框起来就好
db.coll_3.save([
{
"chan" : 4,
"key" : "3007449353_806454501929481",
"seq" : 2,
"subkey" : "1654501937_2",
"time" : 1654501937180005,
},
{
"chan" : 4,
"key" : "3007449353_806454501929481",
"seq" : 3,
"subkey" : "1654501954_3",
"time" : 1654501954187972,
}
])
11、查看所有数据(查看coll集合下的所有数据)
#db.集合名称.find(); #例如选择db_name后执行 db.db_name.find(); 即可看到数据了
db.coll.find();
12、以缩进后的json格式输出——.pretty()!!!!!!
db.coll.find({name:"zhangsan"}).pretty()
注意:只要在语句末尾加上pretty()就都可以
13、删除文档记录
db.coll.remove({name:"zhangsan"})
db.coll.remove({}) #删除集合下的所有数据:
14、查询某个字段是否存在
db.coll.find({seq:{"$exists":true}})
db.coll.find({job:{"$exists":true}})
15、判断数值数据类型
db.coll_0.find({seq:{$type:1}}) #查询seq字段数据类型为double的数据
db.coll_0.find({seq:{$type:2}}) #查询seq字段数据类型为string的数据
db.coll_0.find({seq:{$type:8}}) #查询seq字段数据类型为bool的数据
注:更多$type对应关系参见如下链接。
16、sort排序
db.coll.find().sort({time:1}) #升序排列
db.coll.find().sort({time:-1}) #降序排列
17、limit读取指定数量的数据
db.coll_name.find({name:"zhangsan"}).limit(5)
18、skip跳过指定数量的数据
db.coll_name.find({name:"zhangsan"}).limit(5).skip(1)
19、正则表达式匹配
db.coll.remove({name:/zhangsan/})
db.posts.find({name:{$regex:"zhangsan"}})
20、条件查询/条件筛选:
db.coll.find({name:"zhangsan"}).pretty()
db.coll.find({name:"zhangsan",hight:181}).pretty()
db.coll.find({"education.school":"zhejiang university"}).pretty()
#嵌套查询筛选
db.coll.find({"education.school":{$eq:"zhejiang university"}}).pretty() #等效上一条
db.coll.find({"education.school":"zhejiang university","education.educational":"bachelor"}).pretty()
db.coll.find({"education.school":{$eq:"zhejiang university"},"education.educational":{$eq:"bachelor"}}).pretty() #等效上一条
#比较操作(大于、小于、等于、不等于)
db.coll.find({time:1500000005}) #等于
db.coll.find({"time":{$eq:1500000005}}) #等于
db.coll.find({"time":{$gt:1500000005}}) #大于
db.coll.find({"time":{$lt:1500000005}}) #小于
db.coll.find({"time":{$ne:1500000005}}) #不等于
db.coll.find({"time":{$gte:1500000005}}) #大于等于
db.coll.find({"time":{$lte:1500000005}}) #小于等于
20、projection:filter的作用是筛选记录;projection的作用是筛选出数据的具体字段
#显示age字段;不显示_id字段。执行find方法的时候总是会显示_id字段,如果不需要改字段设为0即可。
db.coll_name.find({},{_id:0,age:1})
db.coll_name.find({},{age:1}) #仅显示age字段
#逻辑操作(规则很简单就是"$操作符[条件1,条件2,……]"):
#与($and)、或($or),非($not,$nor)
db.coll.find({"$and":[{name:"zhangsan"},{hight:180}]})
db.coll.find({"$and":[{"name":{$eq:"zhangsan"}},{"hight":{$eq:180}}]}) #等效上一行
db.coll.find({"$and":[{"name":{$eq:"zhangsan"}},{"hight":{$eq:180}},{"education.educational":{$eq:"master"}}]})
db.coll.find({"$or":[{time:1500000002},{time:1500000003}]})
db.coll.find({"$or":[{"time":{$eq:1500000002}},{"time":{$eq:1500000003}}]}) #等效上一行
#更新某个字段(前面是条件,后面是对谁重设数值):
db.coll.update({time:1500000009},{$set:{weight:"100kg"}});
db.coll.find({time:1500000009})
db.coll.update({time:1500000009},{$set:{"job.class":11}});
db.coll.find({time:1500000009})
db.coll_name.update({_id:ObjectId("5fc4ff306d3d0b6ef92412ef")},{$set:{age:"22"}});
db.coll_name.find({_id:ObjectId("5fc4ff306d3d0b6ef92412ef")});
db.collection_2852199330.update({"$and":[{"index_name":"qqkfext_2852199330_2300659533"},{"uniqueid":"staff_2852199330_3007044852_2300659533_1612088888088"}]},{$set:{"ext3.value":"asdf"}}) # $set不用双引号括起来是ok的
db.collection_2852199330.update({"$and":[{"index_name":"qqkfext_2852199330_2300659533"},{"uniqueid":"staff_2852199330_3007044852_2300659533_1612088888088"}]},{"$set":{"ext3.value":"asdfe"}}) # $set用双引号括起来也是ok的
db.collection_2852199330.update({"$and":[{"index_name":"qqkfext_2852199330_2300659533"},{"uniqueid":"staff_2852199330_3007044852_2300659533_1612088888088"}]},{"$set":{"ext3":{type:2,value:"qwer"}}}) #直接写一个完整的json是可以的
db.collection_2852199330.update({"$and":[{"index_name":"qqkfext_2852199330_2300659533"},{"uniqueid":"staff_2852199330_3007044852_2300659533_1612088888088"}]},{"$set":{"ext3.type":2,"ext3.value":"jkl"}}) #json分字段写也是可以的
#如果想删除某个字段可以使用 $unset ,然后weight字段就删除了
db.coll.update({time:1500000009},{$unset:{weight:1}});
注:unset这个东东只认key(此处就是weight),value可以是任意的true/1或者其他都没关系,只要看到weight就干掉它。
注意unset这个东东只认key。
value可以是任意的.true.1或者其他都没关系.只要看到 allyreq就会干掉它
#匹配数组 $all
db.coll.find({habit:{$all:["basketball","football"]}})
#$in 包含
db.coll.find({age:{$in:[22,23,24]}})
#数组元素个数 —— $size
db.coll.find({habit:{$size:1}})
#模糊匹配 —— 包含某内容
db.coll.find({motto:/学/})
db.coll.find({motto:{$regex:"学"}})
db.coll.find({name:{$regex:"SAN",$options:$i}})
#模糊匹配 —— 以xxx开头
db.coll.find({motto:/^学/})
db.coll.find({motto:{$regex:"^学"}})
#模糊匹配 —— 以xxx结尾
db.coll.find({motto:/人$/})
db.coll.find({"job.company":{$regex:"cent$"}})
#增加与减少("$inc"修改器)。$inc键的值必须为数字,特别适合分析数据、投票等有变化数值的地方
db.zhangsan.update({name:"zhangsan"},{"$inc":{age:100}})
db.zhangsan.update({name:"zhangsan"},{$inc:{age:100}})
注:如果集合名称恰好是数据库类的一个属性(或者纯数字) db.集合名称.操作() 可能会报错。此时用 db.getCollection("2355128746").操作名() 就好了。如下
(3)数组相关的操作
①查询字段对应的数组包含某值的记录
数据如下:
{
"key" : "event_session_2355005970_984823229245584",
"kfuin" : 2355005970,
"s_a" : [
{
"account" : "984823229245584",
"type" : 1
},
{
"account" : "7894561232",
"type" : 2
}
]
}
#匹配 s_a 数组中 account 字段为 "984823229245584"的数据
db.collection_event_track_970.find({s_a:{$elemMatch:{account:"173821981675568"}}})
②查询数组字段中包含指定数据的记录
#数据如下:
db.shuozhuo.insert({kfuin:2355005970,array:["1111","2222","3333"]})
#查询命令如下:
db.shuozhuo.find({array:{$elemMatch:{$eq:"1111"}}}) #貌似这个更直观写
db.shuozhuo.find({array:{$in:["2222"]}})
③数组修改器——数组中追加/移除元素
db.shuozhuo.find({"_id" : ObjectId("60f9559c51a674832d8a225e")})
{ "_id" : ObjectId("60f9559c51a674832d8a225e"), "kfuin" : 2355005970, "array" : [ "2222", "3333" ] }
#数组新增元素
mongos> db.shuozhuo.update({"_id" : ObjectId("60f9559c51a674832d8a225e")},{$push:{array:"4444"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.shuozhuo.find({"_id" : ObjectId("60f9559c51a674832d8a225e")})
{ "_id" : ObjectId("60f9559c51a674832d8a225e"), "kfuin" : 2355005970, "array" : [ "2222", "3333", "4444" ] }
#弹出末尾元素
mongos> db.shuozhuo.update({"_id" : ObjectId("60f9559c51a674832d8a225e")},{$pop:{array:1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.shuozhuo.find({"_id" : ObjectId("60f9559c51a674832d8a225e")})
{ "_id" : ObjectId("60f9559c51a674832d8a225e"), "kfuin" : 2355005970, "array" : [ "2222", "3333" ] }
#弹出首位元素
mongos> db.shuozhuo.update({"_id" : ObjectId("60f9559c51a674832d8a225e")},{$pop:{array:-1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.shuozhuo.find({"_id" : ObjectId("60f9559c51a674832d8a225e")})
{ "_id" : ObjectId("60f9559c51a674832d8a225e"), "kfuin" : 2355005970, "array" : [ "3333" ] }
#如果一个值不在数组中就把它加进去(这样就不加进去了)
db.shuozhuo.update({"_id":ObjectId("60f9559c51a674832d8a225e"),array:{$ne:"3333"}},{$push:{array:"3333"}})
#对于上面这个问题,更推荐用 $addToSet 来避免重复
db.shuozhuo.update({"_id":ObjectId("60f9559c51a674832d8a225e")},{$addToSet:{array:"3333"}})
④数组的定位修改器
如果数组中多个值,而我们子项对其中的一部分进行操作。此时有两种方法:通过位置或者通过定位操作符("$")。
db.shuozhuo.insert({content:"this is a post!",comments:[{comment:"good post",author:"zhangsan",votes:0},{comment:"I thought it was too short",author:"lisi",votes:3},{comment:"free watches",author:"wangwu",votes:-1}]})
db.shuozhuo.update({},{"$inc":{"comments.0.votes":1}})
4、高级指令——模糊查询(like)/聚合(aggregate)
参见 这里
5、MongoDB常用函数
#获取当前集合的句柄.如下结果等效于"db.mycollection.";
db.getCollection("mycollection")
四、MongoDB其他的一些问题。
0、注意不要db.dropDatabase()
dropDatabase()会导致很多问题,一般情况下drop db下的所有集合来替代drop db。
例如尝试往这个db写数据、从这个db查询数据、drop这个db等都会有一些十分怪异的现象。一下提供几个案例:
(1)drop某个db后在尝试往这个db写数据就会发现写不进去了。
(2)不同机器(作为shell客户端机器、作为程序客户端机器)对同一个实例、进行同样的查询语句有的有查到数据有的查不到数据。处理方法就是在mongos上flushRouterConfig,如下:
mongodb插入数据如果不指定主键的话默认会生成如下样式的主键。关于这个ObjectId实际上也是由讲究的。
ObjectId占用12字节的存储空间,每个字节是两位16进制的数字,也就是说这个id是一个24位的字符串。其含义分别代码时间戳、机器码、PID、计数器。
(1)时间戳是文档创建时的时间(秒时间戳),只是从十进制转化成了十六进制;(2)机器码是生成文档主机的ID,是为了区分更多主机而生成的;(3)PID则是区分同住几下不同mongoDB进程产生的,同样为了防冲突;(4)计数器 前面九个字节保证了同一秒内不同机器不同进程生成的ObjectId不冲突,最后三字节是一个自动增加的计数器,用来确保同一秒内产生的ObjectId也不会冲突;允许256^3大概1600多万条记录的唯一性。如下图:
ObjectId有如下属性或方法(ObjectId — MongoDB Manual):
Attribute/Method | Description |
| Returns the hexadecimal string representation of the object. |
ObjectId.getTimestamp() | Returns the timestamp portion of the object as a Date. |
ObjectId.toString() | Returns the JavaScript representation in the form of a string literal “ |
ObjectId.valueOf() | Returns the representation of the object as a hexadecimal string. The returned string is the |
如果想取出某默认_id(ObjectId)的时候,调用前面的getTimestamp()方法就好了。如:
#如下即可:
mongos> ObjectId("60b6ea2e97d6b9fc1807214a").getTimestamp()
ISODate("2021-06-02T02:17:18Z")
1.1、使用mongo自己生成的主键还是业务方直接指定主键值,这篇文章是建议业务方自己生成。但是也有人更倾向于使用系统默认_id.
以上是我用js脚本插入数据的测试结果。看起来用户自定义_id确实会对写入性能造成负面影响。
mongodb指定索引插入比不指定慢很多。这是因为MongoDB里每一条数据的_id值都是唯一的。在不指定_id插入数据的时候,其_id是系统自动计算生成的。MongoDB通过计算机特征值、时间、进程ID与随机数来确保生成的_id是唯一的。而在指定_id插入时,MongoDB每插一条数据,都需要检查此_id可不可用,当数据库中数据条数太多的时候,这一步的查询开销会拖慢整个数据库的插入速度。
如果用户希望有一个自己能够设置的primary key的话现在推荐的一个方案是给相应字段设置unique index。
关于_id,官网的态度还是倾向于系统自动生成而不是用户自定义(看意思用户自定义也问题不大)。
另外这里还提到一点,那就是对于分片集群如果_id字段不用做分片键的话mongo是无法保证_id全局唯一性的。经过测试其实际含义为:“此时mongo能保证同一个分片内的_id的唯一性,但是不同分片间无法保证”。注:有些时候这也是够用的。
1.2、不要使用自增主键
主要原因就是mongodb是分布式系统,维护这种严格有序的自增大家是很高昂的。另外就是对于分片集群,如果选用这个自增的字段作为分片键的话也会使得新更能受影响。
1.3、ObjectId也是可以排序和比较大小的
ObjectId实际上是可以排序的,同理也是可以比较大小的。它的这个比较应该还是objectid整体的比较,而不是说仅仅比较其中的时间戳。
2、Mongo执行js脚本。
3、mongodb explain操作
(1)通过获得语句执行后的统计信息,用于性能分析。
(2)最新的explain()方法制定了一个新的输出模式,分别是“queryPlanner”(概要模式),“executionStats”(执行状态模式),“allPlansExecution”(所有信息模式),默认是概要模式。语句格式如下:
db.collection1.explain("executionStats").find()
#explain()函数可以放到find()前和后都行
db.collection1.find().explain("executionStats")
(3)explain操作结果详解:
explain这个东西要好好的研究一下,务必:重要的事情说三遍!!! 查询上线前务必 explain、务必分析到位!!!
4、mongodb的索引
这里
5、mongodb慢查询剖析——Profiling
有时间研究
6、Aggregation pipelines
7、mongodb生态的其他工具
(2)mongostat —— 查看mongodb实时的增删改查操作的pqs、以及内存使用、网络吞吐等信息。
(3)mongotop —— 实时查看mongodb在哪些集合上花的读写时间最多,能快速找出实例的热点集合。
(4)mongoexport/mongoimport —— mongoexport支持以JSON或者CSV格式导出mongodb存储的数据,然后使用mongoimport导入到其他实例。
(5)mongooplog —— 可用于两个独立的mongodb实例间的数据同步。
(6)mongofiles —— gridfs的命令行客户端,用于向mongodb存储、读取文件,mongofiles支持put、get、list等接口。
(7)mongosniff —— mongodb的抓包工具,直接下载而今之宝可能并不包含这个工具,需要下载源码编译出来。mongosniff可以抓取某个mongodb实例的所有请求以及应答数据,对于mongodb driver的开发者非常有帮助,也可以用于一些网络问题的定位。
8、mongosh
不知道和我们用的mongo shell有什么区别。就是一个东西吧??
mongosh "mongodb://mongouser:qidian%40mxxgo@11.xxx.6.180:27017"
https://docs.mongodb.com/mongodb-shell/ #以这个链接未切入点可以看到好多东西。
五、FAQ:
1、mongodb的锁机制
和其他数据库一样,MongoDB也是通过锁机制来保证数据一致性的。mongodb使用读写锁来支持并发操作;读锁可以共享,写锁具有排他性。
MongoDB使用多种粒度的锁,例如全局级别的锁(db.copyDatabase())、数据库级别的锁(如collMod/convertToCapped)、集合级别的锁(如createIndexes/dropIndexes)。此外还允许各个引擎在集合以下实现自己的并发控制;例如常用的WriteTiger就是文档级别的锁。
MongoDB中锁的粒度有多细(WT引擎)?
对于大多数读取和写入操作,WiredTiger使用乐观锁进行并发控制。即当存储引擎检查到两个操作之间存在冲突时才会引发写冲突,MongoDB会对用户透明的重试该操作。WiredTiger仅在全局、技术库和集合级别使用意向锁。
一些全局操作(通常是涉及多个数据库的短暂操作)任然需要全局"实例范围"锁定。
2、MongoDB是否支持事务?
由于单个文档可以包含关联数据(译者注:通过内嵌文档或数组的方式),否则它们将在关系模式中的各个父子表之间建模,因此MongoDB的单文档原子操作已经提供了满足大多数应用程序数据完整性需求的事务语义。可以在单个操作中写入一个或多个字段,包括对多个子文档和数组元素的更新。MongoDB提供的保证可确保在文档更新时完全隔离。任何错误都会导致操作回滚,以使客户端获得一致的文档视图。
但是,对于需要对多个文档(在单个或多个集合中)进行读写原子性的情况,MongoDB支持多文档事务:
在版本4.0中,MongoDB支持副本集上的多文档事务。
在版本4.2中,MongoDB引入了分布式事务,它增加了对分片群集上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。
有关MongoDB中事务的详细信息,请参阅 事务页面。
重要
在大多数情况下,与单文档写入相比多文档事务产生的性能成本更高,并且多文档事务的可用性不应代替高效的模式设计。在许多情况下, 非规范化数据模型(嵌入式文档和数组)将继续是您的数据和用例的最佳选择。也就是说,在许多情况下对数据进行适当的建模将最大程度地减少对多文档事务的需求。
有关其他事务使用方面的注意事项(例如运行时限制和oplog大小限制),另请参见 生产注意事项。