暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MongoDB 分片集群部署

悦专栏 2022-04-10
176

在生产环境中,通常情况使用副本集就够了(使用配置文件部署副本集可跳转:5.x 副本集部署,使用命令行部署副本集可参考这篇文章)。除非容量非常大,并发访问非常高,副本集已经无法正常提供服务时,才建议考虑使用分片。这一节内容就来聊聊 MongoDB 分片。

1 MongoDB 分片介绍

1.1 MongoDB 分片架构

MongoDB 分片架构图如下:

各个组件的作用:

  • shard:存储数据,为了提高可用性和数据一致性,每个分片都是一个副本集。分片和分片之间的数据不重复。

  • Router(或者mongos):与客户端相连,并将操作定向到适当的一个或多个分片。从 MongoDB 4.4 开始,mongos 开始支持 hedged reads 最大程度减少延迟。生产环境可配置多个 mongos 以实现高可用或者负载均衡。

  • config Server:存储集群的元数据。该数据包含集群数据集到分片的映射。查询路由器使用此元数据将操作定向到特定的分片。


1.2 分片键

分片键是集合中每个文档中都存在的索引字段或索引复合字段,MongoDB将分片键值划分为多个块,并将这些块均匀地分布在各个分片上。要将分片键值划分为多个块,MongoDB使用基于范围的分区或基于哈希的分区。有基于范围的分片和基于哈希的分片。

从 MongoDB 4.2 开始,可以更新文档的分片键值,除非分片键字段是不可变 id 字段。


1.3 平衡

平衡器是管理数据块迁移的后台进程。平衡器可以从群集中的任何查询路由器运行。

当分片集合在集群中分布不均匀时,平衡器进程会将块从具有最多块数的分片迁移到具有最小块数的分片中,直到集群平衡。


1.4 从集群添加和删除分片

将分片添加到集群会导致不平衡,当 MongoDB立即开始将数据迁移到新地分片时,集群平衡可能需要一段时间.

删除分片时,平衡器将所有块从一共分片迁移到其他分片。迁移所有数据并更新元数据后,可以安全地删除分片。


2 MongoDB 分片集群部署

2.1 架构介绍

这次实验架构如下:

其中:

HostnameIP
node1192.168.150.232
node2192.168.150.253
node3192.168.150.123

MongoDB 版本采用的是:5.0.3。


2.2 部署 config server

第一台机器上:

    mkdir /data/mongodb/config -p
    mongod --configsvr --replSet config --dbpath data/mongodb/config --bind_ip localhost,192.168.150.232 --logpath data/mongodb/config/mongod.log --port 27020 --fork



    第二台机器上:

      mkdir /data/mongodb/config -p
      mongod --configsvr --replSet config --dbpath data/mongodb/config --bind_ip localhost,192.168.150.253 --logpath data/mongodb/config/mongod.log --port 27020 --fork



      第三台机器上:

        mkdir /data/mongodb/config -p
        mongod --configsvr --replSet config --dbpath data/mongodb/config --bind_ip localhost,192.168.150.123 --logpath data/mongodb/config/mongod.log --port 27020 --fork


        连接到其中一台:

          mongosh --host 192.168.150.232 --port 27020


          启动副本集:

            rs.initiate(
            {
            _id: "config",
            configsvr: true,
            members: [
            { _id : 0, host : "192.168.150.232:27020" },
            { _id : 1, host : "192.168.150.253:27020" },
            { _id : 2, host : "192.168.150.123:27020" }
            ]
            }
            )


            查看副本集状态:

              rs.status()


              注意以下信息:

                ......
                members: [
                {
                _id: 0,
                name: '192.168.150.232:27020',
                health: 1,
                state: 1,
                stateStr: 'PRIMARY',
                uptime: 53,
                ......
                },
                {
                _id: 1,
                name: '192.168.150.253:27020',
                health: 1,
                state: 2,
                stateStr: 'SECONDARY',
                uptime: 17,
                ......
                },
                {
                _id: 2,
                name: '192.168.150.123:27020',
                health: 1,
                state: 2,
                stateStr: 'SECONDARY',
                uptime: 17,
                ......
                }
                ],
                ......

                1 个 PRIMARY 和 2 个 SECONDARY,并且 health 的值都为 1,说明集群创建正常。


                2.3 MongoDB 实例启动

                node1

                  mkdir /data/mongodb/shardtest01 -p
                  mkdir data/mongodb/shardtest02 -p
                  mongod --shardsvr --replSet shardtest01 --dbpath data/mongodb/shardtest01 --bind_ip localhost,192.168.150.232 --logpath data/mongodb/shardtest01/mongod.log --port 27001 --fork
                  mongod --shardsvr --replSet shardtest02 --dbpath data/mongodb/shardtest02 --bind_ip localhost,192.168.150.232 --logpath data/mongodb/shardtest02/mongod.log --port 27002 --fork


                  node2

                    mkdir /data/mongodb/shardtest01 -p
                    mkdir data/mongodb/shardtest02 -p
                    mongod --shardsvr --replSet shardtest01 --dbpath data/mongodb/shardtest01 --bind_ip localhost,192.168.150.253 --logpath data/mongodb/shardtest01/mongod.log --port 27001 --fork
                    mongod --shardsvr --replSet shardtest02 --dbpath data/mongodb/shardtest02 --bind_ip localhost,192.168.150.253 --logpath data/mongodb/shardtest02/mongod.log --port 27002 --fork


                    node3

                      mkdir /data/mongodb/shardtest01 -p
                      mkdir data/mongodb/shardtest02 -p
                      mongod --shardsvr --replSet shardtest01 --dbpath data/mongodb/shardtest01 --bind_ip localhost,192.168.150.123 --logpath data/mongodb/shardtest01/mongod.log --port 27001 --fork
                      mongod --shardsvr --replSet shardtest02 --dbpath data/mongodb/shardtest02 --bind_ip localhost,192.168.150.123 --logpath data/mongodb/shardtest02/mongod.log --port 27002 --fork


                      2.4 创建分片副本集

                      创建第一个分片副本集

                      连接到其中一台:

                        mongosh --host 192.168.150.232 --port 27001

                        启动副本集:

                          rs.initiate(
                          {
                          _id: "shardtest01",
                          members: [
                          { _id : 0, host : "192.168.150.232:27001" },
                          { _id : 1, host : "192.168.150.253:27001" },
                          { _id : 2, host : "192.168.150.123:27001" }
                          ]
                          }
                          )


                          查看副本集:

                            rs.status()

                            判断是否正常跟上面聊到的 config server 副本集判断方法一样。


                            创建第二个分片副本集

                            连接到其中一台:

                              mongosh --host 192.168.150.232 --port 27002


                              启动副本集:

                                rs.initiate(
                                {
                                _id: "shardtest02",
                                members: [
                                { _id : 0, host : "192.168.150.232:27002" },
                                { _id : 1, host : "192.168.150.253:27002" },
                                { _id : 2, host : "192.168.150.123:27002" }
                                ]
                                }
                                )


                                查看副本集:

                                  rs.status()

                                  判断是否正常跟上面聊到的 config server 副本集判读方法一样。


                                  2.5 启动 mongos

                                  在其中一台机器上(这里选择的时:192.168.150.232)启动 mongos,启动 mongos 需要指定之前部署的 config 副本集。

                                    mkdir /data/mongodb/mongos -p
                                    mongos --configdb config/192.168.150.232:27020,192.168.150.232:27020,192.168.150.232:27020 --bind_ip localhost,192.168.150.232 --logpath /data/mongodb/mongos/mongos.log --fork


                                    2.6 将分片副本集添加到集群

                                    连接到分片集群:

                                      mongosh --host 192.168.150.232 --port 27017


                                      将分片副本集添加到集群:

                                        sh.addShard( "shardtest01/192.168.150.232:27001,192.168.150.253:27001,192.168.150.123:27001")
                                        sh.addShard( "shardtest02/192.168.150.232:27002,192.168.150.253:27002,192.168.150.123:27002")


                                        2.7 创建分片表

                                        为数据库启动分片

                                          sh.enableSharding("martin")


                                          对 collection 进行分片,这里是根据"_id"字段做 hash 分片,读者可根据自己实际情况进行分片,要注意的是,如果collection  drop 掉重建了,需要重新创建 collection 分片规则。

                                            sh.shardCollection("martin.userinfo", {_id: "hashed" } )


                                            查看分片详情

                                              sh.status();


                                              可以看到如下结果:

                                                ......
                                                shards
                                                [
                                                {
                                                _id: 'shardtest01',
                                                host: 'shardtest01/192.168.150.123:27001,192.168.150.232:27001,192.168.150.253:27001',
                                                state: 1,
                                                topologyTime: Timestamp({ t: 1649497030, i: 2 })
                                                },
                                                {
                                                _id: 'shardtest02',
                                                host: 'shardtest02/192.168.150.123:27002,192.168.150.232:27002,192.168.150.253:27002',
                                                state: 1,
                                                topologyTime: Timestamp({ t: 1649502644, i: 2 })
                                                }
                                                ]
                                                ---
                                                active mongoses
                                                [ { '5.0.3': 1 } ]
                                                ......
                                                databases
                                                [
                                                {
                                                database: { _id: 'config', primary: 'config', partitioned: true },
                                                collections: {
                                                'config.system.sessions': {
                                                shardKey: { _id: 1 },
                                                unique: false,
                                                balancing: true,
                                                chunkMetadata: [
                                                { shard: 'shardtest01', nChunks: 1022 },
                                                { shard: 'shardtest02', nChunks: 2 }
                                                ],
                                                chunks: [
                                                'too many chunks to print, use verbose if you want to force print'
                                                ],
                                                tags: []
                                                }
                                                }
                                                },
                                                {
                                                database: {
                                                _id: 'martin',
                                                primary: 'shardtest01',
                                                partitioned: true,
                                                version: {
                                                uuid: UUID("b0ec23fa-2c9c-45ad-8ac1-eab27e4b24c3"),
                                                timestamp: Timestamp({ t: 1649497150, i: 1 }),
                                                lastMod: 1
                                                }
                                                },
                                                collections: {
                                                'martin.userinfo': {
                                                shardKey: { _id: 'hashed' },
                                                unique: false,
                                                balancing: true,
                                                chunkMetadata: [
                                                { shard: 'shardtest01', nChunks: 1 },
                                                { shard: 'shardtest02', nChunks: 1 }
                                                ],
                                                chunks: [
                                                { min: { _id: MinKey() }, max: { _id: Long("0") }, 'on shard': 'shardtest02', 'last modified': Timestamp({ t: 2, i: 0 }) },
                                                { min: { _id: Long("0") }, max: { _id: MaxKey() }, 'on shard': 'shardtest01', 'last modified': Timestamp({ t: 2, i: 1 }) }
                                                ],
                                                tags: []
                                                }
                                                }
                                                }
                                                ]

                                                其中:

                                                • database._id 表示开启分片的库;

                                                • database.primary 表示主 shard;

                                                • database.partitioned 表示这个库是否开启分片,true 表示开启;

                                                • collections 中表示分片的表;

                                                • collections.shardKey 表示分片健;

                                                • collections.balancing 为 true 表示平衡;

                                                • collections.chunks 数据分布情况。


                                                3 数据分布测试

                                                3.1 写入数据

                                                登录 mongos

                                                  mongo --host 192.168.150.232 --port 27017 martin


                                                  往分片表 userinfo 写入数据:

                                                    for (var i=1; i<=10; i++ ) db.userinfo.save({userid:i,username:'a'});


                                                    3.2 查看数据分布

                                                    登录 shardtest01:

                                                      mongo --host 192.168.150.232 --port 27001 martin


                                                      查看数据:

                                                        shardtest01:PRIMARY> db.userinfo.find()
                                                        { "_id" : ObjectId("62524b9da1e27b1723e77844"), "userid" : 1, "username" : "a" }
                                                        { "_id" : ObjectId("62524b9da1e27b1723e77847"), "userid" : 4, "username" : "a" }
                                                        { "_id" : ObjectId("62524b9da1e27b1723e77848"), "userid" : 5, "username" : "a" }
                                                        { "_id" : ObjectId("62524b9da1e27b1723e7784a"), "userid" : 7, "username" : "a" }
                                                        { "_id" : ObjectId("62524b9da1e27b1723e7784c"), "userid" : 9, "username" : "a" }
                                                        { "_id" : ObjectId("62524b9da1e27b1723e7784d"), "userid" : 10, "username" : "a" }


                                                        登录 shardtest02:

                                                          mongo --host 192.168.150.232 --port 27002 martin


                                                          查看数据:

                                                            shardtest02:PRIMARY> db.userinfo.find()
                                                            { "_id" : ObjectId("62524b9da1e27b1723e77845"), "userid" : 2, "username" : "a" }
                                                            { "_id" : ObjectId("62524b9da1e27b1723e77846"), "userid" : 3, "username" : "a" }
                                                            { "_id" : ObjectId("62524b9da1e27b1723e77849"), "userid" : 6, "username" : "a" }
                                                            { "_id" : ObjectId("62524b9da1e27b1723e7784b"), "userid" : 8, "username" : "a" }

                                                            以看到数据分布在两个分片中。


                                                            4 参考资料

                                                            官方文档 Deploy a Sharded Cluster:https://www.mongodb.com/docs/manual/tutorial/deploy-shard-cluster/




                                                            数据库专题内容推荐

                                                            MySQL 基础知识笔记(7 篇)

                                                            MySQL 高频面试题解析(9 篇)

                                                            MySQL 监控(3 篇)

                                                            MySQL 实战笔记(4 篇)

                                                            MySQL 高可用方案(7 篇)

                                                            MHA 源码阅读(4 篇)

                                                            Redis 运维实战(10 篇)

                                                            MongoDB 实战笔记(4 篇)

                                                            ClickHouse 实战笔记(6 篇)




                                                            欢迎加入数据库交流群讨论,入群请添加下方群秘微信,备注“进群”,等待群秘邀你入群。




                                                            LIKECOLUMN

                                                            悦专栏



                                                            悦专栏 ,从小白到大神的起点



                                                            文章转载自悦专栏,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                            评论