Featured image of post MongoDB 修改表分片规则

MongoDB 修改表分片规则

MongoDB 修改表分片规则

背景

Golang 模块需要 upsert MongoDB 的一个 Collection,该表的分片键 Sharding key 规则是指定 piddid 双主键, 业务上需要改为 did 单主键,直接使用 golang mongo-driver 的 FindOneAndUpdate 操作会报错

shadow fail to Update Mongodb (ShardKeyNotFound) Query for sharded findAndModify must contain the shard key

原因是分片键的规则与查询的 filter 对不上(did 单主键

为了适配业务,需要将该表的分片规则进行修改,改为 did 单主键

测试环境:MongoDB 3.6 版本,分片两份 shard-0shard-1

以下方案需要停机操作,无法做到读写操作同时修改(需要将原表删除

原理

参照 MySQL 影子拷贝法

将待修改表 base 表 copy 一份影子 base_tmp,该影子的分片键规则进行修改,再将 base 表改为 base_bak 用于冗余保留备份,也可 dump 成文件,然后 base_tmp 改为 base 表

解决方案

RenameCollection 影子拷贝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
use admin;

// Step 1: 创建影子拷贝并设置新分片键
db.runCommand({
    shardCollection: "db_name.base",
    key: {did: 1}
});

use db_name;

// Step 2: 复制数据
db.base.find().forEach(function (doc) {
    db.base_tmp.insertOne(doc);
});

// Step 3: 重命名原始集合以保留备份
db.base.renameCollection("base_bak");

// Step 4: 重命名临时集合为原始集合
db.base_tmp.renameCollection("base");

实际操作会在 renameCollection 这一步报错

You can't rename a sharded collection

不能修改一个分片的表,因此解决方案只能将数据 dump 出来,再 restore 回新分片的表里

mongodump 和 mongorestore

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// mongodump 备份数据
mongodump --host ip:port --username ROOT --password PASSWORD --authenticationDatabase admin --db DB_NAME --collection base --out=base.bak.d

// 删除 base 表
db.base.drop()

// 创建分片需要 root 账户,admin 表中
use admin;

// 创建影子拷贝并设置新分片键
db.runCommand({
    shardCollection: "db_name.base",
    key: {did: 1}
});

sh.status() // 检查新分片规则

mongorestore --host ip:port --username ROOT --password PASSWORD --authenticationDatabase admin --db DB_NAME --collection base --dir=ota_shadow_device_base.bak.d/DB_NAME/base.bson

Failed: ota_car_online.ota_shadow_device_base: error creating indexes for ota_car_online.ota_shadow_device_base: createIndex error: { mongodb-mongodb-sharding-shard-0/mongodb-mongodb-sharding-shard0-data-0.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017, mongodb-mongodb-sharding-shard0-data-1.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017, mongodb-mongodb-sharding-shard0-data-2.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017: "request doesn't allow collection to be created implicitly", mongodb-mongodb-sharding-shard-1/mongodb-mongodb-sharding-shard1-data-0.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017, mongodb-mongodb-sharding-shard1-data-1.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017, mongodb-mongodb-sharding-shard1-data-2.mongodb-mongodb-sharding-headless.bota-basic.svc.cluster.local:27017: "cannot create unique index over { pid: 1.0, did: 1.0 } with shard key pattern { did: 1.0 }" }

使用 mongodump 指定 collection 只导出该 collection 的 bson 信息,但是还会带有额外的原始集合元数据,包括分片信息,因此旧数据的分片规则并不适用新分片集合中,所以 mongorestore 会报错

mongoexport 和 mongoimport

mongoexport 和 mongoimport 的组合,一般涉及到单表,且纯数据操作,不涉及与 MongoDB 属性绑定的场景,例如导出导入 json csv 数据,因此使用该方案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 修改 ota_shadow_device_base 分片规则

# 1. 在 /mongodb/bin 目录下,将 ota_shadow_device_base 数据备份
mongoexport --host IP:PORT --username $ROOT --password $PASSWORD --authenticationDatabase admin --db $DB_NAME --collection ota_shadow_device_base --out ota_shadow_device_base.json --readPreference primary

# 2. 删除原集合(务必先执行上述备份
mongo --host IP:PORT --username $ROOT --password $PASSWORD --authenticationDatabase admin --eval "db.ota_shadow_device_base.drop();" ota_car_online

# 3. 使用 ROOT 账户进入 mongo-cli
# 手动创建集合,建立索引
use ota_car_online;
db.createCollection('ota_shadow_device_base');
db.ota_shadow_device_base.createIndex({did: 1}, {unique: true});
db.ota_shadow_device_base.createIndex({pid: 1});
# 手动创建集合分片规则(需 ROOT 账户才能创建
use admin;
db.runCommand({shardCollection: 'ota_car_online.ota_shadow_device_base', key: {did: 1}});
# 以下命令检查分片规则是否生效
# ota_car_online.ota_shadow_device_base, shard key: { "did" : 1 }
sh.status();

# 4. 数据还原
# 退出 mongo-cli,在备份文件根目录处恢复数据,按照新分片规则导入数据
mongoimport --host IP:PORT --username $ROOT --password $PASSWORD --authenticationDatabase admin --db $DB_NAME --collection ota_shadow_device_base --file ota_shadow_device_base.json --writeConcern majority

sh.status() 查看分片是否设置成功

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计