ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MongoDB分片集群三组件详解:Config Server、Mongos与Shard架构原理

MongoDB分片集群三组件详解:Config Server、Mongos与Shard架构原理 1. 先搞清楚三个组件各管什么为什么不能少一个聊到MongoDB的分片集群Sharded Cluster很多从单机或者副本集起步的朋友第一反应是“把数据拆开放到多台机器上”。这么理解大方向没错但如果真的上手搭过你会发现事情没那么简单数据到底按什么规则拆客户端连哪台机器某个数据在哪个分片上这些信息谁来记录谁负责告诉客户端“你的这条数据在哪”这一连串问题就是分片集群三个核心组件——Config Server、Mongos、Shard——存在的意义也是这三个角色分工的本质。把这套东西彻底搞明白你后面无论是部署、调优、排障还是设计容量方案心里都会非常有底。至少你不会再犯“把config server和数据节点混在一台机器上还抱怨集群不稳定”这种新手错误。这篇文章我尽可能把每个组件背后“为什么这么设计”讲透不只是教你怎么敲命令。适合谁看准备上分片集群但还在犹豫架构的DBA、被公司安排搭建大数据量存储的后端开发、以及看完官方文档还是觉得云里雾里的运维同学。看完你至少能回答三个问题Config Server崩溃会发生什么Mongos为什么要多部署几个数据迁移时那一堆chunk在忙什么先说一个最简单的请求链路你就能直观感受三个组件的协作方式。假设应用发来一条查询请求这条请求第一步到的是Mongos路由节点Mongos自己手上没有任何业务数据它是一个纯粹的“转发调度员”。它收到查询后会先去Config Server查元数据确认这条数据落在哪个Shard上然后把请求转发给对应的Shard去执行最后把Shard返回的结果拿回来还给客户端。这个过程很像你去火车站人工窗口买票你应用把需求告诉窗口服务员Mongos服务员看一眼大屏幕上的余票列表Config Server上的元数据然后告诉你在几号窗口取票Shard你才能拿到票。如果没有窗口服务员你得自己跑去问“余票在哪查”那是灾难如果大屏幕信息丢了整个车站都会瘫痪如果窗口本身不售票那一切都是空谈。所以我把三个组件用一个类比总结给你组件角色类比核心职责Config Server大屏幕调度台账保存集群所有元数据哪些集合做了分片、每个chunk范围落在哪个分片、分片键定义、集群配置Mongos窗口服务员接收所有客户端请求查询元数据路由请求到正确分片聚合跨分片结果对应用透明Shard实际售票窗口真正存储业务数据的节点每个分片是一个独立副本集负责自己的数据读写和均衡你会发现这三个角色各管一摊谁也没法替代谁。没有Config ServerMongos不知道数据在哪整个集群等于失明没有Mongos客户端就得自己知道数据分布规则这等于把分布式系统的复杂度全部抛给业务方完全违背了“数据库对应用透明”的设计初衷没有Shard那更不用说数据没地方放。2. Config Server集群的“大脑”存的可不只是几行配置2.1 Config Server到底存了什么config数据库全解析Config Server在MongoDB里的角色是一个特殊的副本集。它的所有数据都保存在名为“config”的数据库中这个库里的每一张表都对应一类元数据。我刚接触分片集群时最困惑的就是“元数据”到底具体是什么直到我手动连上config server去查了一遍才彻底明白。我建议你部署完集群后也执行一下use config再show collections看看那种直观感受比看100篇文档都强。config库里比较核心的几张表shards记录了集群中每一个分片节点的地址和状态。相当于一份“分片花名册”Mongos启动后会加载这张表知道当前集群有哪些成员能提供数据存储。databases记录哪些数据库已经开启了分片以及这些库的主分片primary shard是哪个。这里特别容易踩坑一个数据库开启了分片不代表这个库里的每个集合都自动分片了只是说这个库有了被分片的能力集合本身还需要单独调用命令开启分片并指定分片键。collections记录了具体哪些集合已经分片、用的什么分片键、chunk的拆分粒度等信息。这张表是Mongos做路由跳转时最核心的参考依据。chunks这是最核心的数据表它记录了每一个数据块chunk的分布位置和键值范围。比如“用户ID从1到1000的数据块在Shard A上从1001到2000的数据块在Shard B上”。当数据写入量增大chunk会分裂这张表也会相应更新。mongos记录了当前有多少个Mongos节点连接到这个集群。你会发现Mongos启动后会自动向config server注册自己这样config server可以对整个集群的路由层有个全局认识。我见过不少团队踩过一个很深的坑觉得config server不重要备份策略里根本不包含它。结果某个分片节点磁盘损坏恢复数据后发现整个集群的元数据对不上chunk分布表丢失大半最后只能靠操作日志手工重建元数据那滋味比任何备份恢复演练都要酸爽。所以请你务必把config server的备份优先级提到和业务数据一样高甚至更高。数据还能从shard里捞元数据丢了你连数据在哪都找不着。2.2 4.4版本后的硬性要求为什么必须是副本集MongoDB 4.4版本是一个分水岭。在这之前config server可以部署成单节点模式也可以是一个副本集但从4.4版本开始官方彻底移除了对单节点config server的支持也就是说config server必须是副本集而且至少三个节点。这个变化背后的逻辑非常清楚config server是整个集群的控制面单节点一旦宕机整个集群的所有路由请求都会失败。你会看到Mongos报Unable to reach a config server之类的错误客户端所有读写直接雪崩。如果config server是副本集即使主节点挂了还有从节点能通过选举顶上来Mongos会自动感知新的主节点继续提供服务。这里必须强调一个很多人忽略的细节config server副本集内部的选举用的也是标准的Raft协议所以同样存在“投票”、“心跳”、“主从延迟”这些概念。与普通副本集唯一的区别是config server副本集不对外提供业务数据的读写它只服务集群自身的元数据读写所以它的负载通常很低但低负载不代表可以降低它的可用性等级。生产环境我会建议给config server单独分配三台机器或者至少是三台独立的虚拟机。最忌讳的就是图省事把三个config server节点和三个shard节点混布在同一批物理机上。表面上看“反正都是三台机器每台跑两个服务嘛”但一旦物理机宕机你可能是config server和shard同时挂掉两个这对分片集群来说是“双重打击”。我经历过一次机房断电因为config server和某分片部署在同一批宿主机上来电后config server先起来了但那个分片一直没有恢复整个集群因为这个分片拖尾服务恢复花了比预期多一倍的时间。2.3 Config Server运维经验备份、监控与常见误解运维config server有三点我特别想啰嗦一下。第一备份必须做全量加定期的oplog同步。config server的体积通常不大即使集群规模很大config库也就是几十MB到几百MB备份成本很低。用mongodump直接全量导出即可但要注意config server也支持mongodump的一致性问题最稳妥的是在从节点上执行备份减少对主节点的影响。恢复流程建议先在测试环境演练一遍确保备份文件能正常mongorestore回去。第二监控指标不要只看CPU和内存。config server最重要的监控项是oplog的延迟时间lag。因为config server其实也是一个副本集它的oplog是用来同步元数据变更的如果config server的从节点oplog延迟过大一旦主节点挂掉数据丢失的窗口就会变大。另一个监控项是系统负载与磁盘IO延迟因为Mongos每次路由都要实时去读config server的chunk信息如果config server响应变慢整个集群的请求延迟会连带升高。第三千万注意不要在config server上执行业务查询。我见过有同事图方便直接用Compass连上config server地址去查询业务数据发现查不到然后来问我“集群是不是坏了”。实际上config server的config库只保存元数据业务数据一条都不在上面。查询业务数据必须通过Mongos或者直连对应的Shard。把config server当普通数据库使用既会产生额外负载也会让你在排障时产生大量误判。3. Mongos无状态路由层支撑海量并发的关键3.1 Mongos怎么工作请求转发、合并与广播Mongos这个组件很多从副本集时代过来的同学会把它类比为“代理Proxy”这个类比很贴切。客户端在连接字符串里指定mongos的地址就像连了一个普通的MongoDB实例一样业务代码完全无感知。但实际上mongos内部做的事情远不止“转发”两个字。对于路由到一个分片就能完成的请求mongos的任务是解析查询条件根据分片键的范围定位到对应的chunk再定位到chunk所在的shard然后把查询请求转发过去拿到结果后原样返回给客户端。这个过程最考验的是查询条件里是否带了分片键。我见过无数人刚开始使用分片集群时写查询条件只带了一个普通字段比如按用户名查但分片键是用户ID。这种情况下mongos没法定位到具体chunk它只能把查询广播到所有shard上让每个shard各自查询一遍再汇总结果。这种“广播查询”在跨分片数据量大的时候性能会非常差。对于跨分片聚合的请求mongos要做的事情就更复杂了。比如你要做一个group by聚合数据分布在三个shard上mongos会先把聚合请求下发到每个shard让每个shard先做一次本地聚合然后mongos自己再把各个shard返回的中间结果做第二轮聚合最终返回给客户端。这个机制和MapReduce的“分而治之”思想是一模一样的。这里有个优化点如果你的聚合条件里带了分片键的前缀mongos可以将聚合请求只下发到特定的shard大幅减少网络传输和合并工作量。Mongos还有一个容易被忽略的功能它会对来自客户端的写入请求做顺序控制。如果一条写入语句是批量写入update带multi: true并且更新条件横跨了多个分片mongos需要将这条更新拆分成多个针对单分片的更新命令分别下发。在这种场景下mongos会尽力保证这些子操作的整体性但要注意它不提供跨分片的分布式事务能力除非你显式使用事务。3.2 部署数量怎么算无状态带来的横向扩展能力Mongos最爽的地方是它本身是无状态的不需要存储任何业务数据也没有自己的数据库文件。这意味着你可以随便起多少个mongos实例前面对接一个负载均衡器比如Nginx或者云厂商的SLB就可以水平扩展整个集群的路由吞吐能力。这也是为什么分片集群能够承载海量并发的原因之一每个shard的存储容量有限但你可以通过增加mongos节点把并发请求分散到多个路由入口。那到底起多少个mongos合适我个人的经验是先看应用的并发连接数需求。每一个客户端连接都会在mongos上占一个连接如果应用连接池配置了200个连接三个应用实例就是600个连接。一个双核4GB内存的mongos节点扛住几千个空闲连接问题不大但如果每个连接都有持续读写操作CPU会先被打满。所以我的建议是先按“每500到1000并发连接规划一个mongos节点”来估算然后观察mongos的CPU和连接数指标逐步增加或缩减。这里有一个实操细节mongos并不需要高配磁盘因为它不落盘数据但内存和CPU要给足因为路由计算和结果合并都吃这两样资源。另外一个很容易被忽略的点是mongos最好和应用部署在同一网络区域内。为什么因为Mongos和应用之间是高频的、低延迟的通话如果中间跨了公网或者跨了地域网络延迟会直接叠加到每一次数据库操作上。比如应用在北京mongos在上海那么每一次查询至少多几十毫秒的往返延迟对于高并发业务来说这个代价非常惊人。尽量把mongos和应用服务器放在同一个可用区AZ或者同机房内网让 mongos到shard的链路短一点整体延迟会更可控。3.3 安全必做Mongos上的鉴权与网络安全设置Mongos是集群对外的唯一入口所以安全策略必须重点部署在mongos这一层。这里我提几个你搭建完集群后立刻就要检查的点不是建议是必做项。第一开启鉴权。MongoDB分片集群的鉴权是全局的需要生成一个keyFile这个keyFile要被所有config server、所有shard、所有mongos共享它们之间互相认证就靠这个keyFile。创建管理用户的步骤也有讲究必须先在开启鉴权之前连接mongos在admin库下创建好管理员账号然后再重启所有节点并启用鉴权配置。如果顺序搞反了会出现一个非常尴尬的局面你想创建用户但服务器要求你先登录你没有用户所以登不进去。虽然可以通过本地绕过的方式解决但操作繁琐且容易留下安全隐患。第二绑定内网IP。mongos、shard、config server之间的通信默认走27017端口生产环境不要把这个端口暴露到公网。客户端访问也要限制在内网VPC内。我见过一些团队为了图省事直接把mongod、mongos的bindIp设成0.0.0.0安全组又只做了粗略限制结果导致数据被扫描工具发现后勒索。分片集群的规模已经不小了数据安全这块必须从一开始就做对。第三mongos上的连接限制也要设置。因为mongos是无状态的很容易被客户端异常连接打满。在生产环境我会给mongos配置maxIncomingConnections参数比如设为1000同时设置--setParameter maxConcurrentConnections来限制并发连接操作的阈值。这些限制能防止应用连接泄露、连接池配置过猛时把mongos拖垮。4. Shard真正的“数据仓库”存储、分裂与平衡4.1 主分片与非分片集合容易被忽略的概念很多人以为开启分片之后所有的集合都会均匀分布到各个shard上。其实不是这样。在你显式对某个集合执行sh.shardCollection()之前这个集合还不会进入分片状态。这里就引出一个非常重要的概念主分片Primary Shard。一个数据库在sh.enableSharding(db)之后MongoDB会从现有的shard中选一个作为这个数据库的“主分片”。未被显式分片的集合数据会全部存储在这个主分片上。什么意思呢假设你有三个shard你给“用户库”开分了分片但只对“订单表”执行了分片操作而“日志表”没有分片那么这个“日志表”的数据就会全部落在用户库的主分片上另外两个shard上完全不会有日志表的数据。这个设计会让负载出现明显的倾斜。我见过有团队把一个大库开了分片后只分片了其中一两张大表其他几十张小表全留在主分片上结果主分片的磁盘容量和IO飙升而另外两个shard却很空闲。解决思路其实很简单要么对这些表也做分片要么手动把不需要分片的集合迁移到其他shard。具体的迁移命令是db.runCommand({ movePrimary: dbname, to: shardName })但要注意这个操作会搬迁大量数据尽量在业务低峰期执行。4.2 Chunk分裂与迁移数据为什么会分布不均分片的核心存储单元不是“表”而是“chunk”。默认情况下一个集合的数据在开启分片后会先被划分成一个个chunk每个chunk对应分片键的一个范围区间比如“用户ID在1到1000之间的所有数据是一个chunk”。chunk分布在各个shard上MongoDB通过balancer平衡器进程来保证数据比较均匀地散布在各shard之间。你需要理解chunk分裂的原理才能解释为什么有时候分片之后数据还是歪的。当一个chunk的数据量超过设定的分片块大小默认64MB时MongoDB会自动把这个chunk一分为二。所以大表的数据会随着写入量增长chunk数量不断变多。然后balancer会检查各shard上的chunk数量差距如果某个shard的chunk数比其他shard多了8个以上默认阈值它就会把一部分chunk迁移到较空的shard上。这个机制看起来挺智能但恰恰因为它是“按chunk数量”而不是“按数据真实容量”做均衡所以巴伦刺会有“看起来分布很均匀实际上某几个chunk大得离谱”的情况。例如一个chunk里是一批大文档比如包含大数组、大字符串每个可能需要几百MB而另一个chunk里全是几字节的小文档十六个chunk加起来才几MB。这种情况下按chunk数分配策略就会导致某个shard的存储空间暴涨。解决思路是调整分片键让数据分布更均匀或者手动手动拆分散落的大chunk也可以用moveChunk命令定向调整但这是非常精细的运维操作必须对业务数据分布有充分的把握。这里我要特别强调一个高频踩坑点分片键是不可变的。你一旦对集合执行了sh.shardCollection()并指定了分片键后续就不能再改分片键了。如果你最初选错了分片键比如选了一个取值极不均匀的字段做分片键后期只能通过转储数据重建集合的方式变更分片键。有一个补救命令是MongoDB 4.4后的refineCollectionShardKey但它的限制很大只能在原有分片键基础上增加后缀字段不能彻底更换。所以选分片键时宁可多花几个晚上思考也不要草率上线后再后悔。4.3 三种分片策略对比范围、哈希、ZoneMongoDB分片支持三种典型的分片策略实际设计中常常组合使用。范围分片Ranged Sharding是最直观的分片键的取值会被划分成连续区间每个区间对应一个chunk。它的优点是做范围查询特别高效比如查询“用户ID在100到200之间”的数据mongos能直接定位到几个chunk把查询精准路由过去。但它的致命弱点是如果分片键是单调递增的比如自增ID、时间戳那么新写入的数据大概率都落在最后一个chunk上造成所谓“热点问题”——所有写入都集中在一个shard上其他shard闲着没事干。哈希分片Hashed Sharding就是为了解决热点写入问题。MongoDB对分片键的值算一个MD5哈希然后用哈希值做范围分区。这样的话编号相邻的数据会被分散到不同shard写入压力能比较均匀地散开。但有得必有失哈希分片让范围查询失去了原有的优势——想查“从1到100递增的连续ID”mongos必须广播到所有shard上再合并。所以哈希分片适合高并发写入的日志、流水类数据不适合以范围查询为主要访问模式的业务。Zone分片又称tag-aware sharding则是对数据位置的一种“硬性约束”它允许你给某些chunk范围打上标签并指定某个shard只接收某类标签的数据。比如把上海的用户数据放在上海机房的shard上把北京的用户数据放在北京机房的shard上实现数据本地化、合规化。它的配置方式是给shard添加标签给分片键范围添加标签映射。注意zone设置会覆盖自动均衡的部分行为配置不当可能导致数据长期停在一个shard上。实际搭配中最常用的是“哈希分片均匀写 Zone约束数据位置”的组合适合跨机房多活或者合规要求明确的场景。三种策略的对比我整理成下面这张表策略适用场景优势劣势范围分片分片键取值分布均匀、按范围查询多范围查询路由精准、性能高单调递增键会产生热点写哈希分片高并发写入、数据量增长快写压力分布均匀、天然避免热点范围查询需要全分片广播、性能差Zone分片数据本地化、合规要求可控制数据物理位置、满足多机房需求配置复杂、会限制自动均衡5. 实操记录从零搭建一个完整分片集群5.1 整体架构规划与目录设计纸上谈兵没意思我来给你还原一个最小可用集群的完整搭建过程。生产环境要求至少三个shard、每个shard三副本但单机模拟搭建可以用简化方式一台机器上启动3个config server节点作为副本集、2个shard节点每个shard可以先用单节点模式、2个mongos节点。如果你的机器内存足够用Docker起也行用原生的mongod进程裸启动也行本质一样。我的习惯是先把目录规划好不然一个服务一个路径后期维护会乱成一锅粥。规划如下数据目录分别放在/data/config1、/data/config2、/data/config3shard数据目录放在/data/shard1、/data/shard2mongos不需要数据目录但需要日志目录/data/logs所有节点统一使用配置文件方式启动不要用命令行参数拼接一大串配置一是可读性差二是占满shell历史记录三是出了问题不好排查。配置文件用YAML格式每个目录里放一个对应的conf文件。5.2 依次启动Config Server、Shard、Mongos并初始化先启动三个config server节点。配置文件长这样# /data/config1/mongod.conf sharding: clusterRole: configsvr replication: replSetName: cfgrs net: bindIp: 0.0.0.0 port: 27019 storage: dbPath: /data/config1 systemLog: destination: file path: /data/logs/config1.log logAppend: true processManagement: fork: true注意config server端口通常用27019而不是27017这只是一个约定俗成的做法不代表强制但团队运维时一目了然。启动命令依次执行mongod -f /data/config1/mongod.conf mongod -f /data/config2/mongod.conf mongod -f /data/config3/mongod.conf然后连接任意一个config server节点初始化副本集rs.initiate({ _id: cfgrs, configsvr: true, members: [ { _id: 0, host: ip1:27019 }, { _id: 1, host: ip2:27019 }, { _id: 2, host: ip3:27019 } ] });注意configsvr: true这个字段它标识这个副本集是config server专用的和普通副本集有区别。等到三个节点都显示 normal 状态后config server这一步就算完成了。接着启动shard。每个shard本质就是一个独立的副本集生产环境每个shard内部至少三个节点。这里为了演示我用两个单节点shard# /data/shard1/mongod.conf sharding: clusterRole: shardsvr replication: replSetName: shard1rs net: bindIp: 0.0.0.0 port: 27018 storage: dbPath: /data/shard1 systemLog: destination: file path: /data/logs/shard1.log logAppend: true processManagement: fork: true启动后初始化副本集rs.initiate({ _id: shard1rs, members: [{ _id: 0, host: ip1:27018 }] }); rs.initiate({ _id: shard2rs, members: [{ _id: 0, host: ip2:27018 }] });最后启动mongos。注意mongos启动方式和mongod不同它用mongos这个二进制文件并且配置文件里必须指定config server地址# /data/mongos1/mongos.conf sharding: configDB: cfgrs/ip1:27019,ip2:27019,ip3:27019 net: bindIp: 0.0.0.0 port: 27017 systemLog: destination: file path: /data/logs/mongos1.log logAppend: true processManagement: fork: true启动命令mongos -f /data/mongos1/mongos.conf启动完成后通过mongos连接进去把两个shard加入集群sh.addShard(shard1rs/ip1:27018); sh.addShard(shard2rs/ip2:27018);然后用sh.status()查看集群状态看到两个shard状态正常且config server显示 connected集群就搭起来了。5.3 开启分片、设置分片键、验证数据分布集群搭好之后下一步才是“表演时间”如何让一个集合真正进入分片状态。这一步要非常小心因为一旦设置分片键后续变更成本极高。首先对目标数据库开启分片能力sh.enableSharding(shopdb);然后对集合执行分片。假设我们的业务场景是电商订单需求是“高并发写入订单ID生成是单调递增的同时要避免单点写入热点”那么最合适的策略就是哈希分片用订单ID字段做分片键db.shopdb.orders.createIndex({ orderId: hashed }); sh.shardCollection(shopdb.orders, { orderId: hashed });执行完sh.status()后你应该能看到orders集合的状态是sharded并且sharding strategy一栏是Hashed。此时可以插入一批测试数据验证for (let i 0; i 100000; i) { db.orders.insertOne({ orderId: i, uid: i % 1000, amount: Math.random() * 100 }); }数据量达到一定程度后默认64MB触发chunk分裂你可以先把chunk大小调小来加速观察注意看sh.status()里的chunk分布。如果你发现两个shard的chunk数相当说明集群正常工作如果某个shard一直没分到chunk检查一下balancer是否被关了以及负载均衡窗口是否被限制了。5.4 集群健康检查命令汇总搭完集群还不算完日常运维中有一组命令是我的标配几乎每次排查都会用一遍。汇总如下检查项目命令目的查看shard状态sh.status()看chunk分布、balancer状态、集群总体健康度查看config节点复制状态rs.status()连config server确认config副本集主从正常、lag不明显查看shard内部状态rs.status()连对应shard确认每个分片内部复制、选举正常查看mongos路由连接数db.serverStatus().connections检查连接是否达到上限、是否异常泄漏查看数据分布db.orders.getShardDistribution()精确看每个shard上的数据量、chunk数、平均文档大小查看balancer运行状态sh.getBalancerState()确认平衡器开启状态6. 常见问题排查实录这些坑我替你踩过了6.1 分片后数据还是集中在一个Shard这个问题几乎每个分片集群新手都遇到过而且排查思路非常固定。我遇到过一个案例开发团队对一个集合执行了分片分片键是“createTime”创建时间结果跑了半个月后发现第一个shard磁盘快满了第二个shard几乎为空。查看分布之后发现所有新写入的时间戳都在最近几天对应chunk都在一个新数据区间内而MongoDB的balancer虽然尽职尽责地想把chunk搬到空shard上但每次搬完没过多久新的写入热点又集中生成了新的chunk。这个问题的根源就是典型的“单调递增分片键”导致的写入热点。解决思路有三个层次能接受数据分布算法的场景直接改成哈希分片这个是最彻底的如果业务必须用时间范围做高频范围查询那么考虑用“组合分片键”比如createTime userId的组合让时间戳相同的记录也能散开同时保留时间范围查询的能力如果实在没法改分片键那就只能依赖balancer不停迁移chunk并把迁移窗口放到业务低峰期但要接受写入吞吐始终被热点分片限制的现实。所以再次重复分片键的选择真的是分片集群设计最重要的一个环节没有之一。6.2 连接串配置不对导致应用连接失败分片集群的连接串和普通副本集不一样这个细节虽然简单但确实是我在排查“mongodb安装失败”“应用连不上MongoDB”等问题时发现频率最高的原因之一。普通副本集的连接串是mongodb://ip1:27017,ip2:27017,ip3:27017/dbname但在分片集群下正确写法是客户端只需要连接mongos不需要也不能直接配置所有shard地址。连接串长这样mongodb://user:passwordmongos1:27017,mongos2:27017,mongos3:27017/shopdb?replicaSetnone注意几个细节第一driver会通过mongos探测集群所以在应用侧不需要指定shard地址哪怕指定了也会被mongos忽略第二驱动如果检测到mongos列表里有多个地址会自动做客户端侧的负载均衡和故障切换所以应用配置多个mongos地址是有意义的第三如果开启了鉴权连接串里的用户名密码要能通过mongos的校验而且用户必须是在admin库或对应业务库下创建的用户权限要匹配。我见过有人把config server地址也填进应用连接串结果应用连上了config server但没法执行业务查询报错信息还贼隐晦。这种情况只要把连接串改回mongos地址即可。一般来说检查应用连接配置时先确认连接的是不是mongos、是不是标准27017端口、有没有填对用户名密码这三个点八成问题都能解决。6.3 数据库安全三个基础配置不能省热搜里“MongoDB数据库安全”反复出现我就重点说说分片集群环境下的安全配置顺序。第一是网络层面mongos、shard、config server都要绑定内网IP通过安全组或防火墙限制来源IP这一步比任何密码都更重要因为即使被扫到端口没有来源权限也无法访问。第二是启用keyFile鉴权让集群内部节点互相认证。第三是管理用户与业务用户分离管理员在admin库创建业务用户只授予目标库的读写权限绝不使用root权限连接业务库。顺序上务必先创建用户再开启鉴权重启所有节点而不是先开启鉴权再创建用户。原因我在前面已经说过顺序反了会造成“无法登录但急需登录”的死锁。你可以在第一次启动所有节点时先不配置authorization然后用mongos连进去创好用户再统一在配置里加上security.keyFile配置并逐个重启节点。实际生产里我有一次因为偷懒在已经关停了所有节点才想起加keyFile重启后发现所有节点因为keyFile不匹配互相认证失败集群直接不可用那个教训记忆犹新。keyFile生成后要确保内容一致且权限是600否则启动会直接失败。6.4 嵌套List的查询在分片集群下的表现热搜里有“mongodb怎么查list嵌套list”这其实是MongoDB查询的一个经典场景。在分片集群里这个问题需要多考虑一层你的查询条件里带没带分片键假设有一个集合的分片键是userId里面每篇文档长这样{ userId: 123, friends: [ { name: A, hobbies: [骑行, 摄影] }, { name: B, hobbies: [篮球, 跑步] } ] }如果你的查询条件是“找到某个user的朋友列表里爱好包含‘摄影’的人”那么即使查询条件不直接包含userId只要你的业务是先按userId定位文档再深入查询那查询就会命中单分片性能很好但如果你的查询条件不包含userId而是直接按嵌套list内部字段去全集合搜索mongos无法定位具体chunk只能做全分片广播。所以这个场景的最佳实践是确保业务查询模式总是带着分片键前缀或者在集合设计时就考虑把嵌套list里的高频查询字段提取成单独字段再建索引避免全分片广播带来的性能雪崩。7. 我的一点实操体会最后说点私货。MongoDB的分片集群从原理到落地其实没那么神也没那么难。三组件架构的核心价值是让数据能水平扩展的同时对业务保持完全透明。但这份透明的背后是config server的元数据一致性、mongos的路由准确性、shard的chunk平衡机制在默默支撑。很多人在搭建过程中问题频出究其根本并不是命令记错了而是对“数据到底如何存储、元数据到底记录了什么、路由请求到底怎么走”这三件事的理解还停留在表面。我个人的习惯是每搭建一个分片集群都会花半小时连接config server把config.chunks、config.collections这些表翻一遍想象一下如果真的有一条真实的业务查询从这个链路走一遍每个环节会发生什么。这个习惯帮我提前排掉了不少隐患也让每次故障排查都更快。希望你也能在项目上线前静下心把这三个组件的每一环都过一遍这比急着压测调优有意义得多。
返回列表