环境
window 10
mongodb:4.4
工具:Robo 3T
前言
由于现在的公司用的不是mongodb,但是用mongodb用了那么多年;
它的变化本人也是时刻关注的;相比以前,可能没有实战了;只能自己demo;
本篇是基于mongodb中文官网,总结其自己感觉挺实用的特性;
并非揽括官方的所有特性;
Hidden Indexes
根据名称,我们就知道其作用是隐藏索引,为什么要隐藏索引呢?
我们都知道数据库太多索引会导致性能下降。但是业务的复杂性,导致我们是不敢乱删索引的;万一删除了,又得重建索引,代价非常大;
Hidden Index 这个正是为了解决上面问题而存在的;它支持通过CollMod
命令来或者 mongo shell helper
对现有的索引进行隐藏;保证后续的查询都不会利用到该索引,通过观察一段时候,确定该索引没用之后,就可以放心的删除;
使用方法:
方法一、
db.runCommand( {
collMod: 'testcoll',
index: {
keyPattern: 'key_1',
hidden: false
}
} )
方法二、
假设我们一个名为borough_1_ratings_1
的索引,集合名:restaurants
db.restaurants.hideIndex( "borough_1_ratings_1" );
注意
索引被隐藏之后只是对 MongoDB 的执行计划器不可见,并不会改变索引本身的一些特殊行为,比如唯一键约束,TTL 淘汰等。
索引在隐藏期间,如果新的写入,也是会被更新的,所以也可以通过取消隐藏,很方便的让索引立刻变的可用。
Refinable Shard Keys
啥意思呢?以前分片键一旦创建好了是不可以修改的;
虽然在4.2的版本中可以修改Shard Key
的 Value
。但是数据的跨 Shard 迁移以及基于分布式事务的实现机制导致性能开销很大,而且并不能完全解决 Jumbo Chunk
或访问热点的问题。比如,现在有一个订单表,Shard Key
为 {customer_id:1}
,在业务初期每个客户不会有很多的订单,这样的 Shard Key
完全可以满足需求,但是随着业务的发展,某个大客户累积的订单越来越多,进而对这个客户订单的访问成为某个单一 Shard
的热点,由于订单和customer_id
天然的关联关系,修改customer_id
并不能改善访问不均的情况。
现在,4.4版本中可以通过 refineCollectionShardKey
命名来给现有的分片键增加一个或多个Suffix Field来改善现有文档在Chunk上的分布问题;比如,在上面描述的订单业务场景中,通过refineCollectionShardKey
命令把 Shard key 更改为{customer_id:1, order_id:1},即可避免单一 Shard 上的访问热点问题。
refineCollectionShardKey 命令消耗性能非常低,只是修改Config Server上的元数据;
Compound Hashed Shard Keys
看名称就知道,复合哈希分片键;
为什么4.4又支持了呢?因为4.4中支持了复合哈希索引;即可以在复合索引中指定单个哈希字段,位置不限;可以作为前缀,也可以作为后缀,进而也就提供了对复合哈希片键的支持;
sh.shardCollection(
"examples.compoundHashedCollection",
{ "region_id" : 1, "city_id": 1, field1" : "hashed" }
)
sh.shardCollection(
"examples.compoundHashedCollection",
{ "_id" : "hashed", "fieldA" : 1}
)
这个新功能带来的好处:
- 因为法律法规的要求,需要使用 MongoDB 的 zone sharding 功能,把数据尽量均匀打散在某个地域的多个分片上。
- 集合指定的片键的值是递增的,比如在上文中举的例子,{customer_id:1, order_id:1} 这个片键,如果customer_id 是递增的,而业务也总是访问最新的顾客的数据,导致的结果是大部分的流量总是访问单一分片。
在没有「复合哈希片键」支持的情况下,只能由业务对需要的字段提前计算哈希值,存储到文档中的某个特殊字段中,然后再通过「范围分片」的方式指定这个预先计算出哈希值的特殊字段及其他字段作为片键来解决上述问题。
现在可以片键设置为 {customer_id:'hashed', order_id:1}
即可;
Hedged Reads
这个名称我自己感到非常大的困惑;
为什么叫对冲读取
;一开始我以为是一边读一边写的意思;
看了官方文档后,其实不然;
在 4.4 中 MongoDB
提供了 Hedged Reads 的功能,即在分片集群场景下,mongos
会把一个读请求同时发送到某个分片的两个副本集成员,然后选择最快的返回结果回复客户端,来减少业务上的 P95
和 P99
延迟。
P99 xxx 表示过去的10秒内最慢的1%请求的平均延时为xxx秒
p95 过去的10秒内最慢的5%的请求平均延时.
这个具体用在什么场景呢?
并发很高的场景;就是把主节点上的读请求在从节点上也执行一边,保持从节点的缓存一直是热的,万一发生切换的时候,可以迅速顶上。否则还要预热缓存,预热期间效率降低影响线上体验。
降低复制延迟
在4.4之前,备库是通过不断轮询的方式来获取增量更新操作。每次轮询都是备库主动给主库发送一个getMore
命令读取其上的Oplog集合,如果有数据,返回一个最大16M的Batch,如果没有数据,备库也会通过awaitData
选项来控制备库无谓的getMore 开销,同时能够在有新的增量更新时,第一时间获取到对应的 Oplog。
拉取是由单个 OplogFetcher 线程来完成,每个 Batch 的获取都需要经历一个完整的 RTT,在副本集网络状况不好的情况下,复制的性能就严重受限于网络延迟。所以,在 4.4 中,增量的 Oplog 是不断的“流向”备库的,而不是依靠备库主动轮询,相比于之前的方式,至少在 Oplog 获取上节省了一半的 RTT。
RTT:往返时延,是指从网络的一端传送到另一端的所需的时间;一般有:发送时延、传送时延,排队时延、处理时延。
Simultaneous Indexing
同时索引
在4.4版本之前,索引的创建是在主库中完成之后,才会复制到备库上执行。
4.2中,使用细粒度的加锁机制–只在索引创建的开始和结束阶段对集合加独占锁,也会因为索引创建本身的性能开销(CPU、IO
),导致复制延迟,或者因为一些特殊操作,比如 collMod
命令修改集合元信息,而导致 Oplog
的应用阻塞,甚至会因为主库历史 Oplog
被覆盖掉而进入 Recovering
状态。
在 4.4 中,主库和备库上的索引创建操作是同时进行的,可以大幅减少因为上述情况所带来的主备延迟,尽量保证即使在索引创建过程中,备库读也可以访问到最新的数据。
Mirrored Reads
主库会按一定的比例把读流量复制到备库上执行,来帮助备库预热缓存。这个执行是一个「Fire and Forgot」的行为,不会对主库的性能产生任何实质性的影响,但是备库负载会有一定程度的上升。
Fire and Forgot 发射自寻,可以理解为自动完成的
查询能力和易用性增强
Union
新增: $unionWith stage,类似 SQL 中的「union all」
功能。
$unionWith stage 支持分片集合。当在 Aggregate Pipeline 中使用了多个 $unionWith stage 的时候,可以对多个集合数据做聚合,使用方式如下,
{ $unionWith: { coll: "<collection>", pipeline: [ <stage1>, ... ] } }
假设我们现在有这样的数据:
db.orders_april.insertMany([
{ _id:1, item: "A", quantity: 100 },
{ _id:2, item: "B", quantity: 30 },
]);
db.orders_may.insertMany([
{ _id:1, item: "C", quantity: 20 },
{ _id:2, item: "A", quantity: 50 },
]);
db.orders_june.insertMany([
{ _id:1, item: "C", quantity: 100 },
{ _id:2, item: "D", quantity: 10 },
]);
现在我想知道orders_may
和orders_june
这两个集合中,数量quantity
总和是多少?
如果是以前,只能用代码来实现;现在可以:
db.orders_april.aggregate( [
{ $unionWith: "orders_may" },
{ $unionWith: "orders_june" },
{ $group: { _id: "$item", total: { $sum: "$quantity" } } },
{ $sort: { total: -1 }}
] )
Custom Aggregation Expressions
4.4 之前的版本中可以通过 find
命令中的 $where operator
或者 MapReduce
功能来实现在 Server 端执行自定义的 JavaScript 脚本,进而提供更为复杂的查询能力,但是这两个功能并没有做到和 Aggregation Pipeline
在使用上的统一。
4.4中,MongoDB 提供了两个新的 Aggregation Pipeline Operator
,$accumulator
和 $function
用来取代 $where operator
和 MapReduce
,借助于「Server Side JavaScript」来实现自定义的 Aggregation Expression,这样做到复杂查询的功能接口都集中到 Aggregation Pipeline
中,完善接口统一性和用户体验的同时,也可以把Aggregation Pipeline
本身的执行模型利用上,实现所谓 「1+1 > 2」 的效果。
$accumulator
相当于 MapReduce
。
$function
和 $where operator
在功能上基本一致,但是强大之处是可以和其他的 Aggregation Pipeline Operator
配合使用。
参考地址: