一、MongoDB 简介
1. 百科
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
2. 与MySQL相比:
- 数据模型,MongoDB是面向文档的数据库,MySQL是关系型数据库。
- 查询语言,MongoDB不支持join,MySQL支持join。
- 扩展性和性能,MongoDB使用可水平扩展的架构,MySQL使用垂直扩展的架构。
- 可靠性,MongoDB目前只支持单文档事务,MySQL支持复杂事务操作。
3. 与Redis相比:
- 数据存储方式:MongoDB使用文档存储方式,Redis使用键值对存储方式。
- 数据类型:MongoDB可以存储各种类型的数据,包括文本、数字、日期、数组、嵌套文档等等,Redis主要用于存储简单的数据类型,如字符串、哈希表、列表、集合等等。
- 存储方式:MongoDB使用磁盘存储数据,Redis则是将数据存储在内存中。因此,MongoDB可以存储更大量级的数据,而Redis则更适合处理较小、经常被访问的数据。
- 数据访问:MongoDB提供强大的查询检索功能,而Redis仅支持基本的查询操作。
- 扩展性:MongoDB可以水平扩展,支持分布式架构,而Redis主要是通过主从复制的方式实现高可用性和可扩展性。
4. 适合的场景:
- 数据量大。
- 读写操作频繁。
- 数据价值较低,对事务要求不高。
二、MongoDB 创建/删除数据库
db.cloneDatabase("127.0.0.1"); // 从指定主机上克隆数据库
use database;
db.database.insertOne({"name":"B"});
db.dropDatabase();
三、MongoDB 创建/删除集合
db.createCollection("database");
db.database.drop();
四、插入/删除文档
db.collections.insert(obj)
db.collections.insertOne( obj, < optional params > ):返回插入主键
db.collections.insertMany( [objects], < optional params > ):批量插入,返回插入的主键
db.users.insertMany([
{
"name": "Johs",
"age": 10,
"email": "johs@example.com"
},
{
"name": "John",
"age": 30,
"email": "john@example.com"
},
{
"name": "Jane",
"age": 25,
"email": "jane@example.com"
},
{
"name": "Bob",
"age": 40,
"email": "bob@example.com"
}
])
在创建集合期间,MongoDB 在 _id 字段上创建唯一索引,该索引可防止客户端插入两个具有相同的文档
db.collections.save(obj):主键不存在插入,存在则替换
db.collections.drop():删除集合
db.collections.remove({"字段":"值"}):删除满足条件的所有数据
db.collections.remove({"字段":"值"},true):删除满足条件的第一条数据
五、查询文档
db.collections.find(<条件>):查找所有文档,相当于 select * from user
条件字段如果是字符串,表示包含;如果是数组,表示精确匹配;$all:匹配多个元素;$size:元素个数;$slice:截取;$elemMatch:数组中是否有一个元素同时满足所有条件;$type:字段类型;$exists:字段是否存在;null:字段值是否为null或没有该字段
db.collection.findOne()
-
比较查询:$lt 、$lte 、$gt 、$gte 、$ne
db.users.find({"name" : {"$ne" : "John"}}); db.users.find({"age" : {"$gte":20,"$lte": 30}});
-
范围查询:$in、$nin
db.users.find({"age" : {"$in" : [10, 30]}});// in不是between,是 = and =
-
$where:通过js函数自定义查询条件,此时不走索引
db.users.find({"$where" : "function() { return this.age == 10; }"});
-
正则表达式和数组
db.users.find({"name" : {"$regex" : "Ja" }}); db.users.find({"name" : {"$regex" : /Ja/ }}); db.users.find({"name" : /Ja/ });
-
条件查询:$and、$or
db.users.find({"$or":[{"name":"Bob"},{"age":30}]});
-
特殊函数:distinct、count、limit、skip、sort(正数升序)
db.users.distinct(); db.users.countDocuments(); db.users.find().sort({"age":-1}).skip(1).limit(3);
聚合框架aggregate
$match筛选:相当于sql中的where
$project投射:查询指定字段、对字段起别名、使用算术表达式 $add、$subtract、$multiply、$divide、$mod 处理字段、$substrCP截取、$concat拼接、$toUpper/Lower转大小写、日期表达式、$cmp比较非字符串类型、$strcasecmp比较字符串、$and/or/not、$cond 三位运算符、ifNull(bool, default_value)
-
$group 分组、$unwind拆分、$sort、$limit、$skip
示例: db.user.aggregate( {"$match": {"age": {"$gte" : 10} }}, {"$group": {"_id": "$class", "count": {"$sum": 1}}}, {"$sort": {"count": -1}}, {"$skip": 1}, {"$limit": 1} ) 上述聚合函数相当于: SELECT class, COUNT(*) AS count FROM user WHERE age >= 10 GROUP BY classORDER BY count DESC LIMIT 1 OFFSET 1;
六、MongoDB 更新文档
db.collection.update(<query>,<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如
inc...)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
- 默认整体替换掉满足条件的文档而不是单独的修改指定字段的值
db.collection.update(query, update, insertOrUpdate)
若insertOrUpdate为true,则更新策略变为不存在则添加,存在则更新
db.collection.update(query, update, insertOrUpdate, multiUpdate) 批量更新
$set:字段存在则修改,不存在则添加。对应update中整个文档进行替换
$unset:删除字段。
$inc:字段递增/减
$push:字段尾部添加元素,若字段不存在则创建数组
-
$push + $each:添加多个元素
db.user.update({"_id": 1}, {"$push": {"hobby": {"$each": ["money", "xiaojiejie"]}}})
$addToSet:向数组尾部添加不存在的元素,如果存在则不添加
$pop:弹出数组的头部元素或尾部元素: -1:头部,1:尾部。
$pull:删除数组中的指定的值。
db.collection.findAndModify({ “query”: xxx, “update”: xxx,upsert):查找满足添加的值并返回,并且修改满足条件的第一条文档
七、MongoDB 索引
db.collection.createIndex(keys, options); //创建索引
如果查询时发现没有使用到索引,可以使用hint函数强制使用索引查询
1. 索引类型
- 唯一索引 unique;
- 稀疏索引 sparse:包含索引必须唯一,不包含不用校验,对应唯一索引校验null
- 复合索引
- TTL 索引: 设置文档的缓存时间,时间到了会自动删除掉
- 全文索引 text:便于大文本查询(如概要、文章等长文本)
- 二维平面索引:便于2d平面查询;
- 地理空间索引:便于地理查询
2. 索引选项
- 复合索引 name:"", 自定义索引的名称,不配置系统会有默认的索引名。
- 复合索引 background: true, 默认是前台模式, 创建索引是一件即费事又耗费资源的事情,创建索引是在前台模式或者后台模式下创建,在前台模式下创建非常快,但是当有读写请求时会堵塞,在后台模式下当有读写请求时并不堵塞,但是创建索引就会暂时暂停,后台模式要比前台模式慢的多。
- 复合索引 unique/sparse: true,索引
- 复合索引 dropDups: true,是否强制删除其他重复的文档,默认不删除,当索引键值重复时创建失败。
3. 对于索引的使用效率
- 复合索引索引键基数越大,效率越高。一些特殊的操作符不能使用索引,一般取反的操作符索引利用率都比较低。
- 复合索引如果能使用操作符尽量不要使用作符,因为or是执行两次查询操作,然后将结果合并起来,类似于union all,能使用in(单次查询)就不要使用or操作符。
八、MongoDB ObjectId
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
- 前4个字节表示时间戳
- 接下来的3个字节是机器标识码
- 紧接的两个字节由进程id组成(PID)
- 最后三个字节是随机数。
ObjectId();
ObjectId("64d34d4fe31e1a71dd3e5355").getTimestamp(); // getTimestamp 函数可以获取文档的创建时间
九、MongoDB 复制
MongoDB复制是将数据同步在多个服务器的过程。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
1. 添加副本集
replSet 选项
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
实例:
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
以上实例会启动一个名为rs0的MongoDB实例,其端口号为27017。
启动后打开命令提示框并连接上mongoDB服务。
在Mongo客户端使用命令rs.initiate()来启动一个新的副本集,使用rs.conf()来查看副本集的配置,使用 rs.status() 查看副本集状态。
rs.add 命令
rs.add(HOST_NAME:PORT)
实例:
假设你已经启动了一个名为mongod1.net,端口号为27017的Mongo服务。 在客户端命令窗口使用rs.add() 命令将其添加到副本集中,命令如下所示:
rs.add("mongod1.net:27017")
MongoDB中你只能通过主节点将Mongo服务添加到副本集中,判断当前运行的Mongo服务是否为主节点可以使用命令db.isMaster() 。
MongoDB的副本集与我们常见的主从有所不同,主从在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点,不会出现宕机的情况。
十、MongoDB 分片
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。
当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。
上图中主要有如下所述三个主要组件:
- Shard:
用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障 - Config Server:
mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。 - Query Routers:
前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
1. 为什么使用分片
- 复制所有的写入操作到主节点
- 延迟的敏感数据会在主节点查询
- 单个副本集限制在12个节点
- 当请求量巨大时会出现内存不足。
- 本地磁盘不足
- 垂直扩展价格昂贵