速览
本文将从以下方面介绍数据库 MongoDB 4.4、5.0、6.0、7.0版本:
- MongoDB4.4 新特性
- 隐藏索引(Hidden Indexes)
- 重定义分片键(Refinable Shard Keys)
- 复合哈希分片键(Compound Hashed Shard Keys)
- 对冲读(Hedged Reads)
- 同步建索引(Simultaneous Indexing)
- 复制读请求(Mirrored Reads)
- 基于时间保留Oplog(Time-Based Oplog Retention)
- MongoDB5.0 新特性
- 原生时间序列平台
- 在线数据重新分片
- Write Concern默认Majority级别
- 连接管理优化
- 长时间运行的快照查询
- 新版MongoDB Shell
- 可恢复的索引创建任务
- MongoDB6.0 新特性
- 可查询加密(Queryable Encryption)
- 集群同步(Cluster-to-Cluster Sync)
- 时序集合(Time Series Collection)
- 变更流(Change Streams)
- 聚合(Aggregation)
- 查询(Query)
- 弹性
- 安全性
- MongoDB7.0 新特性
- 支持分片元数据一致性校验
- 支持采样查询与分析分片键(analyzeShardKey)
- 自动合并(AutoMerger)
- MongoDB7.0 优化项
- 其他优化
- 安全性
- 聚合(Aggregation)
- 时序集合(Time-Series Collection)
- 分片(Sharding)
引言
在数据库技术日新月异的今天,MongoDB作为领先的NoSQL数据库之一,持续地推出新版本以满足不断变化的企业需求和技术挑战。从MongoDB 4.4至7.0,每一版都融入了创新特性,旨在提升性能、扩展性、安全性和易用性,同时也反映了行业趋势和用户反馈。本文旨在全面剖析这些版本中的关键新特性,不仅是为了记录技术演进的历史,更是为了赋能数据库管理员、开发者和架构师,使他们能够充分理解并利用这些新功能,从而优化数据管理和应用性能。
MongoDB 4.4版本新特性
隐藏索引
MongoDB 4.4版本中新增了隐藏索引,众所周知数据库维护太多的索引会导致写性能下降,但是往往业务上的复杂性导致了运维人员不敢轻易去删除一个潜在的低效率索引,担心误删除会带来业务性能的抖动,而重建索引的代价也非常大。
为了解决上述问题。该功能支持通过collMod命令隐藏现有的索引,保证该索引在后续的查询中不会被使用。在观察一段时间后,确定业务没有异常即可以放心删除该索引。
db.runCommand( {
collMod: 'testcoll',
index: {
keyPattern: 'key_1',
hidden: false
}
} )
重定义分片键
MongoDB4.4版本中,您可以通过refineCollectionShardKey命令给现有的Shard Key增加一个或多个Suffix Field来改善现有的文档在Chunk上的分布问题。例如在上面描述的订单业务场景中,通过refineCollectionShardKey命令把Shard key更改为{customer_id:1, order_id:1},即可避免单一分片上的访问热点问题。
并且,refineCollectionShardKey命令的性能开销非常低,仅更改Config Server节点上的元数据,不需要任何形式的数据迁移,数据的打散仍然在后续正常的Chunk自动分裂和迁移的流程中逐步进行。此外,Shard Key需要有对应的Index来支撑,因此refineCollectionShardKey命令要求提前创建新Shard Key所对应的Index。
由于并不是所有的文档都存在新增的Suffix Field,因此在4.4版本中隐式支持了Missing Shard Key功能,即新插入的文档可以不包含指定的Shard Key Field。但是由于很容易产生Jumbo Chunk,因此并不建议使用。
db.adminCommand( {
refineCollectionShardKey: "test.orders",
key: { customer_id: 1, order_id: 1 }
} )
复合哈希分片键
在最新的4.4版本中加入了复合哈希索引,即您可以在复合索引中指定单个哈希字段,位置不限,可以作为前缀,也可以作为后缀,进而也就提供了对复合哈希片键的支持。
sh.shardCollection(
"examples.compoundHashedCollection",
{ "region_id" : 1, "city_id": 1, field1" : "hashed" }
)
sh.shardCollection(
"examples.compoundHashedCollection",
{ "_id" : "hashed", "fieldA" : 1}
)
对冲读
页面的响应速度和经济损失直接挂钩。Google有一个研究报告表明,如果网页的加载时间超过3秒,用户的跳出率会增加50%。针对这个问题,MongoDB在4.4版本中提供了Hedged Reads功能,即在分片集群场景下,mongos节点会把一个读请求同时发送给某个分片的两个副本集成员,然后选择最快的返回结果回复客户端,来减少业务上的P95(指过去十秒内95%的请求延迟均在规定范围内)和P99延迟(指过去十秒内99%的请求延迟均在规定范围内)。
Hedged Reads功能作为Read Preference参数的一部分来提供, 因此可以在Operation粒度上进行配置,当Read Preference指定为nearest时,系统默认启用Hedged Reads功能,当指定为primary时,不支持Hedged Reads功能,当指定为其他时,需要显式地指定hedgeOptions才可以启用Hedged Reads。如下所示:
db.collection.find({ }).readPref(
"secondary", // mode
[ { "datacenter": "B" }, { } ], // tag set
{ enabled: true } // hedge options
)
此外,Hedged Reads也需要mongos开启支持,配置readHedgingMode参数为on,使mongos开启该功能支持。
参考代码:
db.adminCommand( { setParameter: 1, readHedgingMode: "on" } )
同步建索引
4.4 之前的版本中,索引的创建需要在主库中完成之后,才会到备库上执行。备库上的创建动作在不同的版本中,因为创建机制和创建方式的不同,对备库Oplog的影响也大有不同。
但即使在4.2版本中统一了前后台索引创建机制,使用了相当细粒度的加锁机制(只在索引创建的开始和结束阶段对集合加独占锁),也会因为索引创建本身的CPU、IO性能开销导致复制延迟,或是因为一些特殊操作,例如使用collMod命令修改集合元信息,而导致Oplog的应用阻塞,甚至会因为主库历史Oplog被覆盖而进入Recovering状态。
在4.4版本中,主库和备库上的索引创建操作是同时进行的,这样可以大幅减少上述情况所带来的主备延迟,即使在索引创建过程中,也可以保证备库访问到最新的数据。
此外,新的索引创建机制规定,只有在大多数具备投票权限节点返回成功后,索引才会真正生效。所以,也可以减轻在读写分离场景下因为索引不同而导致的性能差异。
复制读请求
在4.4版本中,MongoDB针对上述问题实现了Mirrored Reads功能,即主节点会按一定的比例把读流量复制到备库上执行,来帮助备库预热缓存。这是一个非阻塞执行(Fire and Forgot)的行为,不会对主库的性能产生任何实质性的影响,但是备库负载会有一定程度的上升。
流量复制的比例是可动态配置的,通过mirrorReads参数设置,默认复制1%的流量。
db.adminCommand( { setParameter: 1, mirrorReads: { samplingRate: 0.10 } } )
此外,还可以通过db.serverStatus( { mirroredReads: 1 } )来查看Mirrored Reads相关的统计信息,如下所示:
SECONDARY> db.serverStatus( { mirroredReads: 1 } ).mirroredReads
{ "seen" : NumberLong(2), "sent" : NumberLong(0) }
基于时间保留Oplog
在4.4版本中,MongoDB支持通过storage.oplogMinRetentionHours参数定义需要保留的Oplog时长,也可以通过replSetResizeOplog命令在线修改这个值,参考代码如下:
// First, show current configured value
db.getSiblingDB("admin").serverStatus().oplogTruncation.oplogMinRetentionHours
// Modify
db.adminCommand({
"replSetResizeOplog" : 1,
"minRetentionHours" : 2
})
MongoDB 5.0版本新特性
原生时间序列平台
- 时间序列集合:MongoDB 5.0允许创建高度优化和压缩的时间序列集合,自动存储带有时间戳的数据,减少存储需求和I/O操作,提升性能和可扩展性。
- 自动管理:时间序列集合能够自动处理数据的动态时间分区,适应不同的采集频率,并能处理无序到达的测量值。
- 简化开发:时间序列集合的创建和管理简化了开发流程,使开发者能够快速构建针对时间序列特性的高性能应用模型。
- 集成与流处理:通过MongoDB Connector for Apache Kafka,可以直接从Kafka主题中创建时间序列集合,实现数据的实时处理、聚合和写入。
- 智能索引与查询:时间序列集合自动创建时间排序的聚集索引,减少查询延迟,同时扩展了查询API,支持窗口函数和时间运算符,如移动平均、累积总和、指数移动平均等。
- 统一的数据平台:时间序列集合可以与常规MongoDB集合共存于同一数据库中,无需使用专用的时间序列数据库,降低了维护多个数据库的成本和复杂性。
创建时间序列数据集合的命令示例:
db.createCollection("collection_name",{ timeseries: { timeField: "timestamp" } } )
在线数据重新分片
数据库版本 | 特点 | 实现方法 |
MongoDB 5.0以前 | 重新分片过程复杂且需要手动分片。 | 方法一:先dump整个集合,然后用新的分片键把数据库重新加载到一个新的集合中。 由于这是一个需要离线处理的过程,因此您的应用程序在重新加载完成之前需要中断停服较长时间。例如:在一个三分片的集群上dump和重新加载一个10 TB以上的集合可能需要几天时间。 方法二:新建一个分片集群并重新设定集合的分片键,然后通过定制迁移方式,将旧分片集群中需要重新分片的集合,按新的分片键写入到新的分片集群中。 该过程中需要您自行处理查询路由和迁移逻辑、不断检查迁移进度,以确保所有数据迁移成功。 定制迁移是高度复杂的、劳动密集型的、有风险的任务,而且耗时很长。例如:某个MongoDB用户花了三个月才完成100亿个document的迁移。 |
MongoDB 5.0开始 | 运行reshardCollection命令即可启动重新分片。 重新分片的过程高效。 并不是简单地重新平衡数据,而是在后台将所有当前集合的数据复制并重新写入新集合,同时与应用程序新的写入保持同步。 重新分片是完全自动化的。 将重新分片花费的时间从几周或几个月压缩到几分钟或几小时,避免了冗长繁杂的手动数据迁移。 通过使用在线重新分片,可以方便地在开发或测试环境中评估不同分片键的效果,也可以在您需要时修改分片键。 | 您可以在业务运行(数据不断增长)的情况下,按需改变集合的分片键(Shard key),而不需要数据库停机或在数据集合中进行复杂的迁移。您只需要在MongoDB Shell中运行reshardCollection命令,选择您需要重新分片的数据库和集合,指定新的分片键即可。 reshardCollection: "<database>.<collection>", key: <shardkey> 说明 <database>:需要重新分片的数据库名称。 <collection>:需要重新分片的集合名称。 <shardkey>:分片键的名称。 当您调用reshardCollection命令时,MongoDB会克隆现有集合,然后将现有集合中所有oplog应用到新集合中,当所有oplog被使用后,MongoDB会自动切换到新集合,并在后台删除旧集合。 |
Write Concern默认Majority级别
从MongoDB 5.0开始,Write Concern默认级别为majority,仅当写入操作被应用到Primary节点(主节点)且被持久化到大多数副本节点的日志中的时候,才会提交并返回成功,“开箱即用”地提供了更强的数据可靠性保障。
说明
Write Concern是完全可调的,您可以自定义配置Write Concern,以平衡应用程序对数据库性能和数据持久性的要求
连接管理优化
默认情况下,一个客户端连接对应后端MongoDB服务器上的一个线程(net.serviceExecutor配置为synchronous)。创建、切换和销毁线程都是消耗较大的操作,当连接数过多时,线程会占用MongoDB服务器较多的资源。
连接数较多或创建连接失控的情况称为“连接风暴”,产生该情况的原因可能是多方面的,且经常是在服务已经受到影响的情况下发生。
针对这些情况,MongoDB 5.0采取了以下措施:
- 限制在任何时候驱动程序尝试创建的连接数量,以简单有效的方式防止数据库服务器过载。
- 减少驱动程序监控连接池时的检查频率,给无响应或过载的服务器节点一个缓冲和恢复的机会。
- 驱动程序将工作负载导向具有最健康连接池的更快的服务器,而不是从可用的服务器中随机选择。
以上措施,加上之前版本在mongos查询路由层的改进,进一步提升了MongoDB承受高并发负载的能力。
长时间运行的快照查询
长时间运行的快照查询增加了应用程序的通用性和弹性。您可以通过该功能运行默认时间为5分钟的查询(或将其调整为自定义持续时间),同时保持与实时事务性数据库一致的快照隔离,也可以在Secondary节点(从节点)上进行快照查询,从而在单个集群中运行不同的工作负载,并将其扩展到不同的分片上。
MongoDB通过底层存储引擎中一个名为Durable history的项目实现了长期运行的快照查询,该项目早在MongoDB 4.4中就已实现。Durable history将存储自查询开始以来所有变化的字段值的快照。通过使用Durable history,查询可以保持快照隔离,即使在数据发生变化的情况下,Durable history也有助于降低存储引擎的缓存压力,使得业务可以在高写入负载的场景下实现更高的查询吞吐量。
新版MongoDB Shell
为了提供更好的用户体验,MongoDB 5.0从头开始重新设计了MongoDB Shell(mongosh),以提供一个更现代化的命令行体验,以及增强可用性的功能和强大的脚本环境。新版MongoDB Shell已经成为MongoDB平台的默认Shell。新版MongoDB Shell引入了语法高亮、智能自动完成、上下文帮助和有用的错误信息,为您创造一个直观、互动的体验。
可恢复的索引创建任务
MongoDB 5.0支持将正在进行中的索引创建任务在节点重新启动后自动会恢复至原来的位置,减少计划中维护动作对业务的影响。例如:重新启动或升级数据库节点时,您不需要担心当前正在进行的大集合索引创建任务失效。
MongoDB 6.0版本新特性
可查询加密
重要
可查询加密功能目前是预览(Preview)版本,不建议直接在生产环境使用。预览(Preview)版本的更多信息,请参见MongoDB Releases Queryable Encryption Preview。
MongoDB 6.0新推出可查询加密功能,允许用户从客户端加密敏感数据,将其作为完全随机的加密数据存储在数据库服务器端,并对加密数据进行丰富的查询。
可查询加密只允许在客户端查看敏感数据的明文,在查询到达服务器端时会同时包含从KMS获取的加密密钥,然后在服务器端以密文进行查询并返回,最后在客户端利用密钥解密后以明文呈现。
可查询加密的特点如下:
- 从客户端加密敏感数据,只有客户端拥有加密密钥。
- 数据在整个生命周期(传输、存储、使用、审计和备份)中都是加密的。
- 客户端可以直接对加密数据进行丰富的查询(包括等值匹配、范围、前后缀或子字符串等查询类型)。
- 强大的数据隐私保护能力,只有能访问客户端的应用程序和加密密钥的授权用户才能看到明文数据。
- 更轻量化的应用程序开发,涉及敏感数据的开发者无需考虑太多安全、合规的事情,数据库会直接提供综合加密解决方案。
- 降低敏感数据上云的安全顾虑。
集群同步
无论是数据的同构同步(Mongo-to-Mongo)还是异构同步(Others-to-Mongo & Mongo-to-Others)都是MongoDB生态中的一部分,开源MongoDB推出了多种工具,比如mongoimport、mongoexport、mongodump和mongorestore等,但是这些工具并没有很好的规划数据同步。
MongoDB 6.0推出了新的同步工具mongosync,它能支持跨实例数据同步(两个MongoDB实例间连续且单向的数据同步)。用户还可以实时控制和监控整个同步过程,按需启动、停止、恢复甚至反转同步。
时序集合
分别从索引、查询以及排序多个方面增强了时序集合。
- 引入二级和复合索引,以改善读取性能。
- 引入针对时空数据的地理位置索引(Geo-Indexing),将地理信息添加到时序数据,有助于更好地分析涉及距离和位置的场景。
场景示例:跟踪夏日冷链运输车的温度变化情况、监测特定航线上的货运船燃料消耗。
- 优化对时序数据的last point查询,不再需要扫描整个集合后才能查询到最后一个数据点。
- 优化对时序数据的排序,通过时间以及元数据字段上的聚簇索引(Clustered Index)和二级索引(Secondary Index)更高效地完成排序操作。
变更流
- 支持查看变更前的视图(Pre-image)。
说明
MongoDB 6.0之前的版本仅支持查看变更后的视图,从MongoDB 6.0版本开始,支持查看变更前后的视图。前后视图的更多信息,请参见Change Streams with Document Pre- and Post-Images。
- 支持create、createIndexes、modify和shardCollection等DDL语句,更多信息,请参见Change Events。
- Change Events新增wallTime字段,时间戳支持多种转换和展示算子(包括$toDate、$tsSeconds和tsIncrement)以方便业务消费。
聚合
聚合功能允许用户处理多个文档并返回计算结果。通过将多个操作符组合到聚合管道中,用户可以构建出足够复杂的数据处理管道以提取数据并进行分析。MongoDB 6.0在原有聚合功能的基础上,推出了如下新特性以及优化项:
- 分片集群实例支持$lookup和$graphLookup。
- 改进$lookup对JOINS的支持。
- 改进$graphLookup对图遍历的支持。
- 提升$lookup性能,部分场景中性能提升可达百倍。
查询
新增$maxN、$topN、$minN、$bottomN、$lastN和$sortArray等操作符。通过操作符可以将更多的计算从业务层下沉到数据库中,使得业务层更加轻量化。
弹性
MongoDB 6.0在原有弹性的基础上,推出了如下新特性以及优化项:
- 将数据块(Chunk)规格的默认值从64 MB调整为128 MB,有效降低了数据迁移频率以及网络和路由层的开销。
- 支持configureCollectionBalancing命令,此命令支持的功能如下:
- 支持为不同的分片表设置不同的数据块规格。
示例:数据规模特别大的分片表,将数据块规格调整到256 MB。数据规模相对较小但希望在分片上分布更均匀的分片表,将数据块规格调整到64 MB或32 MB。
- 支持自动整理分片集合的磁盘空间碎片。
您可以通过configureCollectionBalancing命令设置自动整理磁盘碎片,设置自动整理后您无需再主动使用compact命令来整理磁盘空间碎片,即可有效控制磁盘空间使用率
安全性
MongoDB 6.0在原有安全性的基础上,对客户端字段级加密(CSFLE, Client-Side Field-Level Encryption)功能进行了优化。CSFLE将支持任何符合密钥管理互通协议(KMIP,Key Management Interoperability Protocol)的密钥管理提供商,即除了基于KeyFile的本地密钥管理外,MongoDB支持通过KMIP与第三方密钥管理设备集成,为用户提供更安全的保障。
MongoDB 7.0版本新特性
支持分片元数据一致性校验
MongoDB 7.0版本中新增了checkMetadataConsistency命令,以检查不同分片中元数据不一致的情况,示例如下。
示例1:
// Command
db.runCommand( {
checkMetadataConsistency: 1,
checkIndexes: true
} )
示例2:
//mongosh
db.checkMetadataConsistency()
db.collection.checkMetadataConsistency()
sh.checkMetadataConsistency()
您可以在日常运维中增加该巡检项,尽早发现可能不一致的风险。更多信息请参见checkMetadataConsistency。
支持采样查询与分析分片键
支持基于采样查询(Sampled queries)的结果来分析集合的分片键是否合理,可以帮助您更好地设计Schema以及分片键、更合理使用分片架构。核心命令analyzeShardKey的语法如下:
db.adminCommand(
{
analyzeShardKey: <string>,
key: <shardKey>,
keyCharacteristics: <bool>,
readWriteDistribution: <bool>,
sampleRate: <double>,
sampleSize: <int>
}
)
说明
尽管该命令并不会阻塞集合上的读写操作,但为了降低对业务的影响,建议配套使用secondary或secondaryPreferred模式的Read preference执行。mongos默认为secondaryPreferred模式。
更多信息请参考analyShardKey与configureQueryAnalyzer。
自动合并
MongoDB 7.0为自动均衡器(Balancer)实现了一个新的自动合并器(AutoMerger),当数据或索引分布不均衡、存在过多分片或进行数据迁移时,自动合并器会合并Chunks,以均衡数据并提高性能。MongoDB 7.0默认开启该功能,您也可以通过Balancer活动窗口的控制该功能。
之前版本引入用于单个集合数据均衡的configureCollectionBalancing命令也支持了AutoMerge:
db.adminCommand(
{
configureCollectionBalancing: "<db>.<collection>",
chunkSize: <num>,
defragmentCollection: <bool>,
enableAutoMerger: <bool>
}
)
除此之外,您也可以通过mergeAllChunksOnShard命令将一个分片上所有可合并的Chunk(或Range)进行手动合并:
db.adminCommand( { mergeAllChunksOnShard: "db.coll", shard: "Shard0" } )
分片
除了上文提到过的AutoMerger以外,还有以下优化项:
- 支持通过参数rangeDeleterHighPriority设置删除孤儿文档是否具备更高的优先级。通常业务的删除往往具有更高的优先级, 所以该参数默认为false。
- 不再支持用于记录目录缓存刷新行为的operationsBlockedByRefresh监控指标,原因为基于mongos每个利用集合路由信息的操作都会增加该计数器的次数。
- 新增关于Resharding的监控指标。
- addShard命令不再支持maxSize选项。
- 调整config.settings集合的chunksize时,增加了合理性校验,将值限制在[1,1024]。
- MongoDB在6.0.3版本后对Balancer策略进行若干调整:
- Balancer不再依据分片间Chunks数量的差异,而是依据分片间数据量的差异进行均衡。
- 将Chunk的逻辑概念转为Range。
- 自动分裂(auto-splitting)只会在跨分片移动时发生。
- 查看集合分片信息
下面的SQL将"myDB"更换为所需要查询的DB名称即可 var dbName = "myDB";
db.getSiblingDB(dbName).getCollectionNames().forEach(function(collName) { print("————————————————————————"); print("Collection: " + collName); db.getSiblingDB(dbName).getCollection(collName).getShardDistribution();});
安全性
- 支持KMIP 1.0和1.1。
- 支持OpenSSL 3.0以及OpenSSL FIPS。
聚合
新增了以下操作符,支持位计算和百分位数:
字段名 | 描述 |
$bitAnd | 返回Int或Long类型数值的按位与运算的结果。 |
$bitNot | 返回Int或Long类型数值的按位取反运算结果。 |
$bitOr | 返回Int或Long类型数值的按位或运算的结果。 |
$bitXor | 返回Int或Long类型数值的按位异或运算的结果。 |
$median | 返回近似中位数,相当于50百分位数。 |
$percentile | 返回指定的百分位数。 |
时序集合
- 移除了之前版本对于时序集合DELETE命令的操作限制。除了不能在多文档事务中使用相关删除命令外,当前DELETE命令再无其他限制。
- COMPACT命令支持时序集合。
其他优化
- 慢日志新增了catalogCacheIndexLookupDurationMillis等字段,更多信息请参见log messages for slow queries。
- 支持动态调整存储引擎事务并发度,之前默认是128,从MongoDB 7.0起,MongoDB会自动动态调整该并发度,更多信息请参见Concurrent Storage Engine Transactions。
- currentOp新增了关于query sampling的全新字段,更多信息请参见currentop-metrics。
- 支持复合通配符索引,更多信息请参见Compound Wildcard Indexes。
- 新增$changeStreamSplitLargeEvent算子支持对超过16 MB的超大变更事件(change events)进行切分,更多信息请参见Large Change Stream Events。
- 优化基于Slot查询执行引擎的性能。
- 新增Chunk迁移的指标,更多信息请参见New Sharding Statistics for Chunk Migrations。
- 支持通过USER_ROLES系统变量来获取当前User的角色。
- 新增analyzeShardKey、balancer、queryAnalyzers相关的全局参数。
- serverStatus的返回结果新增更多字段,更多信息请参见serverStatus Output Change。