golang数据库 MongoDB 存储引擎(13)

Golang 2023/04/23 Golang, MongoDB

一 计算机中数据的存取流程

计算机的存储包括内存存储和硬盘存储:

  • 内存由半导体材料制作,容量很小,但是数据传送速度极快,架设在硬盘和高速缓存器(比内存容量更小,速度更快,位于内存和CPU之间)之间,断电后数据丢失。

  • 硬盘由磁性材料制作,容量很大,但是数据传输速度很慢,只是用来存储暂时不用的数据,数据永久保存。

因为内存才是计算机的工作,所以硬盘上的数据只有在装入内存后才能被处理。CPU与硬盘之间不发生直接的数据转换,CPU通过控制信号指挥硬盘工作,硬盘上的数据如果要使用,就得先通过IO操作,例如调用read/write函数封入内存。由此可见,存取内存的数据i存取硬盘的数据要快得多,在很多情况下(尤其是随机IO)是系统的瓶颈。

针对这样的情况,MongoDB所有数据都实际存储在硬盘,但是部分或者全部要操作的数据通过内存映射存储引擎映射到内存中。

Mongo对数据的操作:

  • 如果是读操作,直接从内存中读取数据;

  • 如果是写操作,会修改内存中的对应的数据,然后就不用管了,操作系统的虚拟内存管理器会定时把数据刷新保存到硬盘中。至于什么时候保存,就不需要用户了解与操作了。

上述设计有一个弊端,写入了Mongo数据,但是还未来得及更新到硬盘,断电了,写入数据就会丢失。针对此问题,Mongo设计了Journal模式,在意外宕机时,数据库会有重演日志的机制。该模式从1.9.2版本开始默认打开,默认情况下,Mongo每100毫秒(数据和Journal文件不在同一磁盘,则是30毫秒)往Journal文件flush一次数据,那么即使断电,也只是消失100ms数据。使用--journalCommitInterval可以修改刷新频率,频率越高,安全越高,但是消耗也越大。

二 Mongo存储引擎

2.1 Mongo存储引擎概述

存储引擎主要负责数据在硬盘和内存中的存储,以及内存的使用方式。不同的存储引擎应用场景不同,有针对高并发而设计的,有针对高压缩率而设计的。

Mongo在3.0版本之前,一直使用基于内存映射技术的MMAP存储引擎(在2.6时更名为MMAPV1)。

在3.0版本后,为了满足不同的数据需求以及扩展性,Mongo引入了可插拔存储引擎API,为64位Mongo增加了WiredTiger引擎,在3.2版本中,该引擎成为默认引擎。

目前的MongoDB引擎有:

  • MMAPV1

  • WiredTiger(默认)

  • In-Memory

2.2 MMAP1引擎

MMAP即Memory Mapped Storage Engine,该引擎使用了操作系统底层的内存映射机制,把磁盘文件的一部分或全部数据直接映射到内存,这样磁盘文件中的数据位置就会在内存中有对应的地址空间,把磁盘IO操作转换成内存操作,这时对文件的读写可以直接使用指针来操作,而不需要read/write来IO。Mongo并没有将数据直接放入物理内存,只有访问到这块数据时才会被操作系统以Page的方式交换到物理内存,MongoDB将内存管理工作交给操作系统的虚拟内存管理器来完成,MongoDB只负责做映射,什么时候存储数据到磁盘,什么时候从磁盘文件取出数据,都由操作系统来完成。

这样操作:

  • 优点:Mongo本身操作简单,无需关心数据的读写

  • 缺点:不能方便的控制Mongo占据的内存,所以推荐Mongo不和服务器放在一起

MMAP1是对MMMAP无缝升级,主要有2个改变:

  • 锁粒度由库级别锁提升为集合级别锁,进一步提升了并发性能

  • 文档空间分配方式发生了改变。

文档分配的演进:

在MMAP中,文档按照写入顺序排列存储,如果文档更新后长度变长且原油存储位置后面没有足够的空间放下增长的数据,那么文档就要移动到文件中的其他位置。这种因更新而导致的文档位置移动,会直接导致集合中所有索引都要同步修改文档新的存储位置,严重降低了写入性能。

MMAP提供了两种文档空间分配方式:

  • 基于填充因子(paddingFactor)的自适应方式(3.0之前默认方式,现已基本舍弃)。

  • 预分配方式(usePowerOf2Sizes,3.0后的默认方式)

填充因子方式基于每个集合中文档更新历史计算文档更新的平均增长长度,然后在新文档插入或旧文档插入/移动时填充一部分空间,如:当前集合paddingFactor值为1.5,那么一个大小为200字节的文档插入,会在文档后填充100字节的空间。预分配方式则是直接为文档分配2的N次方大小的存储孔家,如一个大小同样为200字节的文档插入时直接分配256个字节空间。

由于基于填充因子方式的分配会因为文档大小的不规则性,造成大小不一的剩余存储空间,难以复用,造成了空间碎片化。基于预分配的方式则更容易维护,如果某个集合上只有insert或者in-place update,用户可以设置该集合的noPadding标志位,关闭空间预分配。

2.3 WiredTiger引擎

WiredTiger引擎主要特点是高性能写入、支持压缩、支持文档级锁,与MMAP1引擎完全兼容,已经是Mongo默认引擎,较于MMAP1的优点有:

  • 支持多核CPU,可以充分利用芯片级缓存

  • 基于B-TREE和LSM算法

  • 提供文档级锁,可以大幅提升高并发下的写入负载(同一时间内,多个写操作能修改同一集合中的不同文档,写入效率被提升了,但是如果是多个写操作操作同一个文档,仍然是需要竞争和排序的)

  • 支持文件压缩(默认为Snappy方式,还可以选择不压缩、zlib压缩方式)

对于大多数读写操作,WiredTiger使用乐观并发控制,只有在Global,Database,Collection级别上使用意向锁(Intent Lock),如果WiredTiger检测到两个操作发生冲突时,会自动让其中一个操作重新执行。

由于新的压缩机制(块压缩算法、前缀压缩算法和Snappy压缩库),WiredTiger引擎存储成本只有MMAP1的10%-30%,比如存储100G数据,WiredTiger只会占用30G左右磁盘空间。

2.4 In-Memory引擎

MMAP1和WiredTiger都是持久化存储数据,即数据会被刷新保存到磁盘,断电后数据不会丢失。In-Memory引擎被启用后,Mongo基本成为了一个内存数据库。


本文地址:https://www.stayed.cn/item/394

转载请注明出处。

本站部分内容来源于网络,如侵犯到您的权益,请 联系我

我的博客

人生若只如初见,何事秋风悲画扇。