MongoDB复习一波

162 阅读8分钟

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

MongoDB

NoSQL 列存储Hbase 键值 Redis 图像 Neo4j 文档 Mongodb

安装

tar -zxvf MongoDB-linux-x86_64-4.1.3.tgz
./bin/mongod -f mongo.conf
# 配置:
dbPath=/data/mongo/ # 数据库目录
port=27017 # 监听端口
bind_ip=0.0.0.0 # 监听IP
fork=true # 是否以后台启动的方式登录
logpath=/data/mongo/MongoDB.log #日志路径
logappend=true # 是否追加日志
auth=false # 是否开启用户密码登录
# 连接
./bin/mongo --host=  --port=

命令

数据库

show dbs;
use 数据库名; #没有的话则创建
db.createCollection("集合名")
show tables; 
show collections;
db.集合名.drop();
db.dropDatabase();

集合

db.集合名.insert(文档)
db.集合名.insert([文档], [文档])
db.test.insert({name:"张", birthday: new ISODate("2000-07-01")})

_id类型是ObjectId类型, 是一个12字节BSON类型的数据,格式是

前4位是时间戳, 接下来3位是机器标识码, 接下来2位字节是PID, 接下来三个字节是随机数。

可以用ObjectId("对象ID字符串").getTimeStamp()

db.集合名.find(条件)
操作格式
等于{key:value}
大于{key: {$gt: value}}
大于等于{key: {$gte: value}}
不等于{key: {$ne: value}}
and{key1:value1, key2:value2}
or{$or:[{key1:value1}, {key2:value2}]
not{key:{not:{操作符:value}}
db.集合名.find({条件}).sort({排序字段:排序方式})).skip(跳过的行数).limit(一页显示多少数据)
$set: 设置字段值
$unset: 删除指定字段
$inc: 自增
db.集合名.update(
	<query>,  # update查询条件
	<update>, # $set
	{
	  upsert: <boolean>, # 可选, 不存在就插入,默认false
	  multi: <boolean>, # 默认false, 只更新找到的第一条记录
	  writeConcern: <document> # 可选, 指定mogod对写操作的回执行为,比如写行为是否需要确认
	}
)
db.集合名.update({条件}, {$set: {key,value}}, {multi:true})
writeConcern 包括以下字段: { w: <value>, j: <boolean>, wtimeout: <number> } 
w:指定写操作传播到的成员数量 比如:
w=1(默认):则要求得到写操作已经传播到独立的Mongod实例或副本集的primary成员的确认 
w=0:则不要求确认写操作,可能会返回socket exceptions和 networking errors 
w="majority":要求得到写操作已经传播到大多数具有存储数据具有投票的(data-bearing voting )成员(也就是 members[n].votes 值大于0的成员)的确认
j:要求得到Mongodb的写操作已经写到硬盘日志的确认 比如: 
j=true:要求得到Mongodb(w指定的实例个数)的写操作已经写到硬盘日志的确认。j=true本身并不保证因为副本集故障而不会回滚。 
wtimeout:指定write concern的时间限制,只适用于w>1的情况 wtimeout在超过指定时间后写操作会返回error,即使写操作最后执行成功,当这些写操作返回时, MongoDB不会撤消在wtimeout时间限制之前执行成功的数据修改。 如果未指定wtimeout选项且未指定write concern级别,则写入操作将无限期阻止。 指定wtimeout值 为0等同于没有wtimeout选项
db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> } )
参数说明: 
query :(可选)删除的文档的条件。 
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。 
writeConcern :(可选)用来指定mongod对写操作的回执行为。

聚合

  • 单目聚合操作

    count(), distinct()

    db.test.distinct("city")
    
  • 聚合管道

    db.test.aggregate([{$group:{_id:"$city", city_count:{"$sum:1"}}}])
    db.test.aggregate([{$group:{_id:"$city", city_avg:{"$avg:$salary"}}}])
    
    db.test.aggregate([{$group:{_id:"$city", city_name:{"$push:$city"}}}])
    
    表达式描述
    $sum总和
    $avg平均值
    $min最小值
    $max最大值
    $push在结果文档中插入值到一个数组中
    $addToSet在结果文档中插入值到一个数组,值不重复
    $first获取第一个
    $last获取最后一个

    管道操作

    $group : 分组

    $project: 修改文档结构名

    $match: 过滤数据

    $limit: 限制返回数

    $skip: 跳过

    $sort: 排序

    $geoNear: 接近某个位置的有序文档

  • MapReduce

    能够在多台Server上并行执行复杂的聚合逻辑。

    db.test.mapReduce(
       function() {emit(this.city, this.salary);},# map
       function(key, value){return Array.avg(value)},# reduce 
       {
         query:{salary:{$gt: 1000}},
         out: "cityAvgsal",
         finalize: function(key, value){
            return value +5000;
         }
       }
    )
    

    out: 统计结果存放集合

    query: 条件

    sort: 发往map前排序

    limit: map文档上限

    finalize: 对reduce结果进行修改

    verbose: 是否包括时间信息,默认false

索引

单键索引:

支持所有类型的单个字段索引,索引排序顺序无关紧要, 可以在任一方向读取索引。

db.test.createIndex({name:1}, {background: true})
db.test.getIndexes()
db.test.totalIndexSize()
db.test.reIndex()
db.test.dropIndex("name")
db.test.dropIndexes()
过期索引TTL

可以支持文档在一定时间后自动删除,目前TTL只能在单字段上建立,并且必须是日期类型。

db.test.createIndex({name:1}, {expireAfterSeconds: 31})

复合索引

db.test.createIndex({name:1, score:-1})

多键索引

针对属性包数组, 支持对数组中element创建索引, 支持strings, numbers和nested documents

db.test.createIndex({ratings:1})

地理空间索引

2dsphere 用于存储和查找球面上的点

2d 索引用来存储和查找平面上的点

全文索引

提供了针对string内容的文本查询。 一个集合最多支持一个Text Index

db.test.createIndex({"name":"text"})
db.test.find({"$text":{"$search": "coffee"}})

哈希索引

针对属性的哈希值进行索引

db.test.createIndex({"name":"hashed"})

索引管理

explain 接收不同的参数

  • queryPlanner
  • executionStats
  • allPlansExecution

慢查询

  • 开启内置的查询分析器

    db.setProfifilingLevel(n,m),n的取值可选0,1,2

    • 0表示不记录
    • 表示记录慢速操作,如果值为1,m必须赋值单位为ms,用于定义慢速查询时间的阈值
    • 表示记录所有的读写操作
  • 查询监控结果

    db.system.profile.find().sort({millis:-1}).limit(3)
    
  • 解读explain结果 确定是否缺少索引

索引底层实现

MongoDB使用B-树,所有节点都有Data域,只要找到指定索引就可以进行访问,单次查询从结构上来看要快于MySql。

B-树的特点:

  • 多路 非二叉树

  • 每个节点 既保存数据 又保存索引

  • 搜索时 相当于二分查找

B+ 树的特点:

  • 多路非二叉

  • 只有叶子节点保存数据

  • 搜索时 也相当于二分查找

  • 增加了 相邻节点指针

从上面我们可以看出最核心的区别主要有俩,一个是数据的保存位置,一个是相邻节点的指向。就是这俩造成了MongoDB和MySql的差别。

B+树相邻接点的指针可以大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和data 在一起 适合随机读写 ,而区间查找效率很差。

  • B+树相邻接点的指针可以大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和data 在一起 适合随机读写 ,而区间查找效率很差。
  • B+树更适合外部存储,也就是磁盘存储,使用B-结构的话,每次磁盘预读中的很多数据是用不上的数据。因此,它没能利用好磁盘预读的提供的数据。由于节点内无 data 域,每个节点能索引的范围更大更精确

逻辑结构和数据模型

MongoDB 与 MySQL 中的架构相差不多,底层都使用了可插拔的存储引擎以满足用户的不同需要。用户可以根据程序的数据特征选择不同的存储引擎,在最新版本的 MongoDB 中使用了 WiredTiger 作为默认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最好的性能和存储率。在存储引擎上层的就是 MongoDB 的数据模型和查询语言了,由于 MongoDB 对数据的存储与 RDBMS有较大的差异,所以它创建了一套不同的数据模型和查询语言。

数据模型

内嵌

内嵌的方式指的是把相关联的数据保存在同一个文档结构之中。MongoDB的文档结构允许一个字段或者一个数组内的值作为一个嵌套的文档

引用

引用方式通过存储数据引用信息来实现两个不同文档之间的关联,应用程序可以通过解析这些数据引用来访问相关数据。

存储引擎

存储引擎是MongoDB的核心组件,负责管理数据如何存储在硬盘和内存上。MongoDB支持的存储引擎有 MMAPv1 ,WiredTiger和InMemory。InMemory存储引擎用于将数据只存储在内存中,只将少量的元数据 (meta-data)和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取所需 的数据,InMemory存储引擎大幅度降低了数据查询的延迟(Latency)。从mongodb3.2开始默认的存储 引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1,mongodb4.x版本不再支持MMAPv1存储引擎。

配置
storage: 
	journal: 
		enabled: true 
	dbPath: /data/mongo/ 
	##是否一个库一个文件夹 
	directoryPerDB: true 
	##数据引擎 
	engine: wiredTiger 
	##WT引擎配置 
	WiredTiger: 
		engineConfig: 
			##WT最大使用cache(根据服务器实际情况调节)
			cacheSizeGB: 2
			##是否将索引也按数据库名单独存储 
			directoryForIndexes: true 
			journalCompressor:none ##(默认snappy) 
			##表压缩配置 
		collectionConfig: 
			blockCompressor: zlib #(默认snappy,还可选none、zlib) 
		##索引配置 
		indexConfig: 
		prefixCompression: true
优势
  • 文档空间分配

    wiredTiger使用BTree NMAPV1线性存储, 需要padding

  • 并发级别

    wiredTiger 文档级别 MMAPV1 表级锁

  • 数据压缩

    snappy和zlib, MMAPV1无压缩

  • 内存使用

    wiredTiger可以指定内存的使用大小

  • cache

    wiredTiger可以使用二级缓存WireTiger Cache, FileSystem Cache保证Disk上的数据最终一致性。wiredTiger只有journal日志。

文件

WiredTiger.basecfg: 存储基本配置信息,与 ConfigServer有关系

WiredTiger.lock: 定义锁操作

table*.wt: 存储各张表的数据

WiredTiger.wt: 存储table* 的元数据

WiredTiger.turtle: 存储WiredTiger.wt的元数据

journal: 存储WAL(Write Ahead Log)

原理

wiredTiger写操作会默认写入cache,并持久化到WAL中, 每60s或Log文件达到2G做一次checkpoint,初始化时,恢复到最新的快照状态,然后根据WAL恢复数据,保证数据的完整性.

Cache是基于BTree的,节点是一个page,root page是根节点,internal page是中间索引节点,leafpage真正存储数据,数据以page为单位读写。WiredTiger采用Copy on write的方式管理写操作(insert、update、delete),写操作会先缓存在cache里,持久化时,写操作不会在原来的leaf page上进行,而是写入新分配的page,每次checkpoint都会产生一个新的root page。

checkpoint

  • 对所有的table进行一次checkpoint,每个table的checkpoint的元数据更新至WiredTiger.wt

  • 对WiredTiger.wt进行checkpoint,将该table checkpoint的元数据更新至临时文件 WiredTiger.turtle.set

  • 将WiredTiger.turtle.set重命名为WiredTiger.turtle。

  • 上述过程如果中间失败,WiredTiger在下次连接初始化时,首先将数据恢复至最新的快照状态,然后根据WAL恢复数据,以保证存储可靠性。

Journaling

在数据库宕机时 , 为保证 MongoDB 中数据的持久性,MongoDB 使用了 Write Ahead Logging 向磁盘上的 journal 文件预先进行写入。除了 journal 日志,MongoDB 还使用检查点(checkpoint)来保证数据的一致性,当数据库发生宕机时,我们就需要 checkpoint 和 journal 文件协作完成数据的恢复工作。

  • 在数据文件中查找上一个检查点的标识符

  • 在 journal 文件中查找标识符对应的记录

  • 重做对应记录之后的全部操作