利用MongoDB的SplitVector命令实现并发数据迁移

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 数据迁移是数据库运维中一个很常见的场景。数据迁移分为全量和增量。为了追求速度,通常我们会采用并发的方式对数据进行全量迁移。在全量导出数据时,通常都会选择做到记录级的并发,因此通常会涉及到对需要导出的某个表(集合)按照并发度进行切分(分区)的过程。现有常用做法是通过若干个skip加limit来找到一些分区点,然后就可以并发同时导出多个分区。事实上MongoDB还有一个SplitVector命令特别适合用来做集合的分区。本文将介绍一下如何利用这个命令来对集合做分区,实现并发数据迁移。

背景

数据迁移是数据库运维中一个很常见的场景。数据迁移分为全量和增量。为了追求速度,通常我们会采用并发的方式对数据进行全量迁移。在全量导出数据时,通常都会选择做到记录级的并发,因此通常会涉及到对需要导出的某个表(集合)按照并发度进行切分(分区)的过程。现有常用做法是通过若干个skip加limit来找到一些分区点,然后就可以并发同时导出多个分区。事实上MongoDB还有一个SplitVector命令特别适合用来做集合的分区。本文将介绍一下如何利用这个命令来对集合做分区,实现并发数据迁移。

命令简介

SplitVector命令原是在sharding中chunk分裂时需要用的一个内部命令,是mongos在准备分裂某个chunk前发给这个chunk所在shard以计算分裂点(SplitPoint)时使用的。但是这个命令也可以用于普通的副本集,我们可以把副本集中的集合看作一个唯一的chunk,利用这个命令来为这个chunk计算分裂点,从而达到为某个集合进行分区的目的。

SplitVector命令的使用在官方文档中没有介绍,只说明了其实一个内部命令,但是使用命令的Help却可以看到:

db.runCommand({splitVector:"test.test", help:1})
{
 "help" : "help for: splitVector Internal command.\nexamples:\n  { splitVector : \"blog.post\" , keyPattern:{x:1} , min:{x:10} , max:{x:20}, maxChunkSize:200 }\n  maxChunkSize unit in MBs\n  May optionally specify 'maxSplitPoints' and 'maxChunkObjects' to avoid traversing the whole chunk\n  \n  { splitVector : \"blog.post\" , keyPattern:{x:1} , min:{x:10} , max:{x:20}, force: true }\n  'force' will produce one split point even if data is small; defaults to false\nNOTE: This command may take a while to run",
 "lockType" : 0,
 "ok" : 1
}

从帮助文档中可以大致看到,这个命令大致是这么使用的:

db.runCommand({splitVector:"blog.post", keyPattern:{x:1}, min{x:10}, max:{x:20}, maxChunkSize:200})

接下来介绍一下各个参数及其含义。

字段 类型 描述
splitVector string splitVector的操作对象集合名
keyPattern document chunk分裂使用的分区键,必须拥有索引,在sharding中就是shard key,在副本集中通常就指定成主键_id索引
min document 可选参数,分区数据集的最小值,如果没有指定,那么使用MinKey
max document 可选参数,分区数据集的最大值,如果没有指定,那么使用MaxKey
maxChunkSize integer 可选参数,和『force』参数二者必须指定一个。分区后每个chunk的最大大小
maxSplitPoints integer 可选参数,分裂点个数上限
maxChunkObjects integer 可选参数,分区后每个chunk最大包含的记录数,默认为250000
force boolean 可选参数,和『maxChunkSize』参数二者必须指定一个。默认情况下如果当前chunk的数据大小小于maxChunkSize则不会进行分裂。如果指定了『force』为true,那么会强制在当前chunk的中位点进行分裂,返回一个分裂点。默认为false。

这么多参数到底怎么用呢?我怎么知道出来的结果是怎样的?没有更详细的文档,只有啃源码了。​

原理

SplitVector的原理是遍历指定的『keyPattern』索引,根据指定的『maxChunkSize』找到满足以下条件的n个分裂点:分裂后的每个新的chunk的大小约为『maxChunkSize』的一半。如果集合当前大小比『maxChunkSize』小或者集合记录数为空,那么返回一个空的分裂点集合。如果指定了『force: true』,那么会忽略传入的『maxChunkSize』参数,并强制在集合的中位点进行分片,这时候只会产生一个分裂点。
在寻找分裂点时首先会根据集合的平均文档大小计算一个分裂后每个chunk所包含的文档数:

​​keyCount = maxChunkSize / (2 * avgObjSize)

​​如果指定了『maxChunkObjects』参数,并且『maxChunkObjects』比keyCount小,会使用『maxChunkObjects』作为keyCount。接下来就是遍历索引,每遍历keyCount个key,就得到一个分裂点(第keyCount+1个key),直到达到『maxSplitPoints』(若有指定)或遍历结束。因此其实每个partition会包含keyCount个key,最终得到的分裂点个数:

​​partitionCount = keyTotalCount / (keyCount + 1)
splitPointCount = partitionCount - 1

​​其中keyTotalCount为索引的key总数。

使用案例

知道了原理后,就知道如何去传参数了,如果要精确控制得到的分裂点个数(以便控制并发数),这里可以给出一个公式及推导过程。现在我们有以下公式:

​1. splitPointCount = partitionCount - 1
2. splitPointCount = keyTotalCount / (keyCount + 1)
3. keyCount = maxChunkSize / (2 * avgObjSize)

由上述公式可以推导出

maxChunkSize = (keyTotalCount / partitionCount - 1) * 2 * avgObjSize

由于所有集合都有_id字段上的唯一索引,并且每个文档都有_id字段,因此我们可以直接利用集合文档的个数docCount作为索引key的个数。文档个数和avgObjSize都可以通过collStats命令得到。注意参数中的『maxChunkSize』是以MB为单位的,最终传到命令的时候需要转换一下,并且在服务端中事实上会将『maxChunkSize』做个向下取整,因此最终计算出来的keyCount可能比我们设想的要小,这样就会导致最终得到的分裂点个数比我们想要的多。为了达到我们的需求,最好加上『maxSplitPoints』这个可选参数对分裂点进行限制,这样我们允许最后一个分区比其他分区包含更多的文档数。

接下来举个具体的例子,假设现在需要将某个集合分成10个分区以支持10个并发同时对外导出数据,这个集合共有10240条文档,avgObjSize是1024,那么根据上述公式可以计算得到:

​maxChunkSize = (10240 / 10 - 1) * 2 * 1024 = 2MB

这样我们执行如下命令:

db.runCommand({splitVector:"test.test", keyPattern:{_id:1}, maxChunkSize:2, maxSplitPoints:9})

​这样就会只得到9个分裂点。

目录
相关文章
|
7月前
|
NoSQL MongoDB 微服务
微服务——MongoDB常用命令——文档的分页查询
本文介绍了文档分页查询的相关内容,包括统计查询、分页列表查询和排序查询。统计查询使用 `count()` 方法获取记录总数或按条件统计;分页查询通过 `limit()` 和 `skip()` 方法实现,控制返回和跳过的数据量;排序查询利用 `sort()` 方法,按指定字段升序(1)或降序(-1)排列。同时提示,`skip()`、`limit()` 和 `sort()` 的执行顺序与编写顺序无关,优先级为 `sort()` > `skip()` > `limit()`。
251 1
|
7月前
|
JSON NoSQL MongoDB
微服务——MongoDB常用命令——文档基本CRUD
本文介绍了MongoDB中文档的基本操作,包括插入、查询、更新和删除。单个文档插入使用`insert()`或`save()`方法,批量插入用`insertMany()`。查询所有文档用`find()`,条件查询可在`find()`中添加参数,投影查询控制返回字段。更新文档通过`update()`实现,支持覆盖修改、局部修改(使用`$set`)和批量修改。列值增长可用`$inc`实现。删除文档用`remove()`,需谨慎操作以免误删数据。此外,文档键值对有序,区分大小写,不能有重复键。
136 1
|
7月前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令——MongoDB索引知识概述
本文介绍MongoDB索引相关知识,包括其在查询中的重要作用。索引可避免全集合扫描,显著提升查询效率,尤其在处理海量数据时。通过B树数据结构存储字段值并排序,支持相等匹配、范围查询及排序操作。文中还提供了官方文档链接以供深入学习。
99 0
|
7月前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令——MongoDB索引的类型
本节介绍了MongoDB中索引的几种类型及其特点。包括单字段索引,支持升序/降序排序,索引顺序对操作无影响;复合索引,字段顺序重要,可实现多级排序;地理空间索引,支持平面与球面几何查询;文本索引,用于字符串搜索并存储词根;哈希索引,基于字段值散列,适合等值匹配但不支持范围查询。
180 1
微服务——MongoDB常用命令——MongoDB索引的类型
|
7月前
|
存储 JSON NoSQL
MongoDB常用命令
本文介绍了将文章评论数据存储到MongoDB中的操作方法,包括数据库和集合的基本操作。主要内容涵盖:选择与创建数据库(如`articledb`)、数据库删除、集合的显式与隐式创建及删除、文档的CRUD操作(插入、查询、更新、删除)。此外,还详细说明了分页查询、排序查询以及统计查询的方法,例如使用`limit()`、`skip()`实现分页,`sort()`进行排序,`count()`统计记录数。通过实例展示了如何高效管理MongoDB中的数据。
|
7月前
|
NoSQL 关系型数据库 MongoDB
微服务——MongoDB常用命令——集合操作
本节主要介绍MongoDB中的集合操作,包括显式与隐式创建集合的方法。显式创建使用`db.createCollection(name)`,需遵循命名规范(如不能以"system."开头或包含`\0`字符)。隐式创建则通过直接向不存在的集合插入文档实现,更为常用。此外,还介绍了集合删除方法`db.collection.drop()`及其返回值规则,帮助用户管理数据库中的集合资源。
241 0
|
7月前
|
存储 NoSQL MongoDB
微服务——MongoDB常用命令1——数据库操作
本节介绍了 MongoDB 中数据库的选择、创建与删除操作。使用 `use 数据库名称` 可选择或创建数据库,若数据库不存在则自动创建。通过 `show dbs` 或 `show databases` 查看所有可访问的数据库,用 `db` 命令查看当前数据库。注意,集合仅在插入数据后才会真正创建。数据库命名需遵循 UTF-8 格式,避免特殊字符,长度不超过 64 字节,且部分名称如 `admin`、`local` 和 `config` 为系统保留。删除数据库可通过 `db.dropDatabase()` 实现,主要用于移除已持久化的数据库。
441 0
|
Oracle NoSQL 关系型数据库
实时计算 Flink版产品使用合集之MongoDB CDC connector的全量快照功能可以并发读取吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
199 2
|
11月前
|
存储 JSON NoSQL
MongoDB常用命令
MongoDB常用命令
101 1
MongoDB常用命令
|
12月前
|
NoSQL MongoDB 数据库
使用NimoShake将数据从AWS DynamoDB迁移至阿里云MongoDB
使用NimoShake将数据从AWS DynamoDB迁移至阿里云MongoDB

相关产品

  • 云数据库 MongoDB 版
  • 推荐镜像

    更多