[{"data":1,"prerenderedAt":290},["ShallowReactive",2],{"\u002Fjava-interview\u002Fdistributed-systems":3,"java-interview-search:\u002Fjava-interview\u002Fdistributed-systems":245},{"id":4,"title":5,"body":6,"category":231,"cover":232,"date":233,"description":234,"extension":235,"group":236,"meta":237,"navigation":238,"order":239,"path":240,"seo":241,"sitemap":242,"stem":243,"__hash__":244},"javaInterview\u002Fjava-interview\u002Fdistributed-systems.md","分布式系统：CAP、ID 与分库分表",{"type":7,"value":8,"toc":217},"minimark",[9,13,18,21,24,27,30,34,37,40,43,46,50,53,61,64,67,71,78,81,88,91,95,98,101,104,107,111,118,121,124,127,131,134,137,140,143,147,150,153,156,159,162,175,178,181],[10,11,12],"p",{},"分布式题很容易变成名词接龙。建议抓住一条判断标准：在故障和并发下，系统愿意牺牲什么、又能保证到什么程度。下面每个问题都尽量落到这个取舍上。",[14,15,17],"h2",{"id":16},"_1-cap-和-base-到底在说什么","1. CAP 和 BASE 到底在说什么？",[10,19,20],{},"C 是一致性，通常指线性一致：读到的必须是最新已完成的写；A 是可用性，每个请求都能在有限时间内得到非错误响应；P 是分区容错，网络分区时系统仍能继续工作。",[10,22,23],{},"关键点是：网络分区不是「要不要容忍」，而是迟早会发生。所以真实选择是「分区发生时保 C 还是保 A」，而不是平时三选二。很多回答说「我们选了 CA」，在跨机房部署下基本不成立。",[10,25,26],{},"BASE 是对 AP 的工程化补充：基本可用、软状态、最终一致。它不是放弃正确性，而是把一致性从「时刻」放宽到「一段时间内」。",[10,28,29],{},"还有一个常被追问的 PACELC：分区时在 C 和 A 之间选，没有分区时在延迟和一致性之间选。多数系统平时也要为一致性付出延迟。",[14,31,33],{"id":32},"_2-分布式-id-有哪些方案雪花算法的坑在哪","2. 分布式 ID 有哪些方案？雪花算法的坑在哪？",[10,35,36],{},"UUID 本地生成、无依赖，但无序且较长，做主键会造成 B+ 树页分裂和索引膨胀。数据库自增简单可靠，但单点、扩展难，分库后需要步长或号段。",[10,38,39],{},"雪花算法生成 64 位 ID：1 位符号、41 位毫秒时间、10 位机器标识、12 位序列。趋势递增、性能高，但强依赖时钟，回拨可能产生重复 ID；机器标识的分配也需要统一管理。",[10,41,42],{},"常见的回拨处理：小幅回拨等待追平，大幅回拨直接报错或切换备用 workerId；也可以用「上次时间戳 + 序列」的本地状态兜底。",[10,44,45],{},"号段模式（如 Leaf）从数据库批量取一段 ID 在内存分配，减少数据库压力，但要处理号段耗尽时的预取和容灾。最终选型看是否需要趋势递增、能否接受中心依赖，以及前端是否会被 JS 精度截断。",[14,47,49],{"id":48},"_3-如何设计一个幂等接口","3. 如何设计一个幂等接口？",[10,51,52],{},"幂等的本质是「同一个业务意图执行多次，结果与执行一次相同」。先确定幂等键：客户端生成的请求号、业务单号，或「用户 + 操作 + 目标」的组合。",[10,54,55,56,60],{},"常见实现：数据库唯一索引（最可靠，但要注意捕获重复键异常）；去重表或 Redis ",[57,58,59],"code",{},"SET NX"," 记录已处理键（要设置合理 TTL）；状态机限制非法迁移；乐观锁版本号；Token 机制（先领 token，提交时消费）。",[10,62,63],{},"还要处理并发窗口：两个相同请求同时到达，都查到「未处理」，然后都执行。唯一约束或分布式锁能把窗口关掉，单纯「先查再写」不行。",[10,65,66],{},"设计时明确三点：幂等键由谁生成、保存多久、重复请求返回什么（成功结果、进行中还是冲突）。这些约定比具体技术更重要。",[14,68,70],{"id":69},"_4-分布式锁有哪些实现怎么选","4. 分布式锁有哪些实现？怎么选？",[10,72,73,74,77],{},"Redis 锁性能高、实现简单，",[57,75,76],{},"SET NX PX"," 加 Lua 释放，配合续期；但主从异步复制下存在锁丢失窗口，适合防重复而非强互斥。",[10,79,80],{},"ZooKeeper 用临时顺序节点：创建最小序号节点者获得锁，其他节点监听前一个节点。会话断开后临时节点自动删除，可靠性较好；缺点是性能低于 Redis，且依赖 ZK 集群。",[10,82,83,84,87],{},"数据库锁可以用唯一索引或 ",[57,85,86],{},"SELECT ... FOR UPDATE","，实现最简单，但性能和扩展性最差，适合低频、强一致的场景。",[10,89,90],{},"选择时看失败代价：重复发一条通知，Redis 锁足够；扣减余额，应该用数据库行锁、乐观锁或共识系统，并配合 fencing token 防止旧持有者继续写。",[14,92,94],{"id":93},"_5-为什么要分库分表会带来哪些问题","5. 为什么要分库分表？会带来哪些问题？",[10,96,97],{},"单表数据量大时，索引变深、DDL 变慢、备份恢复困难；单库连接数和写入能力也有上限。分表解决数据量，分库解决连接数和写扩展。",[10,99,100],{},"拆分方式有垂直和水平：垂直按业务或字段拆，边界清晰但可能留下大表；水平按分片键拆，扩展性好，但跨片查询变难。",[10,102,103],{},"问题清单要能说出来：跨库 join、跨片分页和聚合、分布式事务、全局唯一 ID、跨片唯一约束、路由规则变更、数据迁移与扩容、热点分片。",[10,105,106],{},"工程上通常配合中间件（如 ShardingSphere）做路由和归并，但中间件不能消灭跨片成本。设计分片键时优先选查询必带、分布均匀的字段；如果业务必须按两个维度查，考虑冗余表或搜索引擎。",[14,108,110],{"id":109},"_6-一致性哈希解决了什么问题","6. 一致性哈希解决了什么问题？",[10,112,113,114,117],{},"普通取模 ",[57,115,116],{},"hash(key) % N"," 在节点数变化时几乎全部 key 都要重新映射，缓存会大面积失效。一致性哈希把节点和 key 映射到同一个环上，key 顺时针找到第一个节点，增删节点只影响相邻区间。",[10,119,120],{},"为了解决节点分布不均，引入了虚拟节点：每个物理节点在环上放置多个副本，key 的分布更均匀，节点扩容时迁移也更平滑。",[10,122,123],{},"它仍有两个边界：一是热点 key 不会因为哈希而分散，需要额外拆分；二是节点性能不同，等权虚拟节点可能造成负载不均，要按权重调整。",[10,125,126],{},"一致性哈希常用于缓存和对象存储，数据库分片更常用范围或哈希取模，因为要兼顾范围查询和运维可控性。",[14,128,130],{"id":129},"_7-zookeeper-的典型用法和原理","7. ZooKeeper 的典型用法和原理？",[10,132,133],{},"ZooKeeper 是一个高可用的协调服务，数据组织成树形 znode，支持持久节点、临时节点和顺序节点。它通过 ZAB 协议在 leader 和 follower 之间复制日志，写请求由 leader 处理，多数派确认后提交。",[10,135,136],{},"临时节点随会话消失，因此常用于服务注册和故障感知；顺序节点配合 watch 可以实现公平锁；节点数据小、读多写少，适合存元数据和配置，不适合存大对象或高频写。",[10,138,139],{},"watch 是一次性的：触发后需要重新注册，客户端要处理「注册前发生变化」的窗口。会话超时时间决定了故障感知的快慢，设得太短容易误判，太长则恢复慢。",[10,141,142],{},"同类系统 etcd 基于 Raft，提供租约、MVCC 和更现代的 API；选型时看生态和团队经验，不要只看「谁更快」。",[14,144,146],{"id":145},"_8-系统高可用要做哪些事","8. 系统高可用要做哪些事？",[10,148,149],{},"先定义目标：RTO（多久恢复）和 RPO（能丢多少数据），不同业务差异很大。然后从冗余、故障转移、隔离和演练四方面设计。",[10,151,152],{},"冗余包括多实例、多可用区、多副本；故障转移要有健康检查、选主和流量摘除，并防止脑裂——常用多数派、租约和 fencing 机制。隔离包括线程池、连接池、机房和租户隔离，避免一个依赖拖垮全局。",[10,154,155],{},"降级和限流是「保命」手段：非核心功能可关闭，核心链路保留最小依赖。所有降级开关要提前演练，不能等到故障时才发现配置不生效。",[10,157,158],{},"最后是演练和演练后的修复：混沌工程、故障注入、备份恢复演练。没有演练过的高可用方案，通常只是文档上的高可用。",[14,160,161],{"id":161},"易错点与自测",[163,164,165,169,172],"ul",{},[166,167,168],"li",{},"CAP 不是「三选二」，分区发生时才是真正的取舍点；BASE 是最终一致，不是「不一致也行」。",[166,170,171],{},"雪花 ID 的趋势递增依赖时钟，回拨处理必须明确。",[166,173,174],{},"分库分表能解决容量，但会把查询和事务复杂度推到应用层。",[10,176,177],{},"自测：① 解释为什么 UUID 不适合做 InnoDB 主键；② 说明一致性哈希中虚拟节点解决什么问题；③ 给出一个「并发重复请求」的幂等落地方案。",[14,179,180],{"id":180},"参考资料",[163,182,183,193,201,209],{},[166,184,185,192],{},[186,187,191],"a",{"href":188,"rel":189},"https:\u002F\u002Fzookeeper.apache.org\u002Fdoc\u002Fcurrent\u002FzookeeperProgrammers.html",[190],"nofollow","ZooKeeper 官方文档：程序员指南"," — znode、watch 与会话语义",[166,194,195,200],{},[186,196,199],{"href":197,"rel":198},"https:\u002F\u002Fshardingsphere.apache.org\u002Fdocument\u002Fcurrent\u002Fen\u002Foverview\u002F",[190],"Apache ShardingSphere 文档"," — 分片、路由与分布式事务",[166,202,203,208],{},[186,204,207],{"href":205,"rel":206},"https:\u002F\u002Fetcd.io\u002Fdocs\u002F",[190],"etcd 官方文档"," — Raft、租约与 watch 的对照参考",[166,210,211,216],{},[186,212,215],{"href":213,"rel":214},"https:\u002F\u002Fsre.google\u002Fsre-book\u002Fhandling-overload\u002F",[190],"Google SRE Book：处理过载"," — 限流、降级与容量设计",{"title":218,"searchDepth":219,"depth":219,"links":220},"",2,[221,222,223,224,225,226,227,228,229,230],{"id":16,"depth":219,"text":17},{"id":32,"depth":219,"text":33},{"id":48,"depth":219,"text":49},{"id":69,"depth":219,"text":70},{"id":93,"depth":219,"text":94},{"id":109,"depth":219,"text":110},{"id":129,"depth":219,"text":130},{"id":145,"depth":219,"text":146},{"id":161,"depth":219,"text":161},{"id":180,"depth":219,"text":180},"java-interview",null,"2026-10-24","讲清 CAP\u002FBASE 的真实含义、分布式 ID 与幂等设计，并梳理分库分表、一致性哈希和高可用的工程权衡","md","分布式与工程",{},true,"120","\u002Fjava-interview\u002Fdistributed-systems",{"title":5,"description":234},{"loc":240},"java-interview\u002Fdistributed-systems","lFowmIzCC56o9oMSB8MmUA3hDHOXU_nkU9zxXjUyHnA",[246,250,254,258,262,266,270,274,278,282,286],{"id":240,"title":5,"titles":247,"content":248,"level":249},[],"讲清 CAP\u002FBASE 的真实含义、分布式 ID 与幂等设计，并梳理分库分表、一致性哈希和高可用的工程权衡 分布式题很容易变成名词接龙。建议抓住一条判断标准：在故障和并发下，系统愿意牺牲什么、又能保证到什么程度。下面每个问题都尽量落到这个取舍上。",1,{"id":251,"title":17,"titles":252,"content":253,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_1-cap-和-base-到底在说什么",[5],"C 是一致性，通常指线性一致：读到的必须是最新已完成的写；A 是可用性，每个请求都能在有限时间内得到非错误响应；P 是分区容错，网络分区时系统仍能继续工作。 关键点是：网络分区不是「要不要容忍」，而是迟早会发生。所以真实选择是「分区发生时保 C 还是保 A」，而不是平时三选二。很多回答说「我们选了 CA」，在跨机房部署下基本不成立。 BASE 是对 AP 的工程化补充：基本可用、软状态、最终一致。它不是放弃正确性，而是把一致性从「时刻」放宽到「一段时间内」。 还有一个常被追问的 PACELC：分区时在 C 和 A 之间选，没有分区时在延迟和一致性之间选。多数系统平时也要为一致性付出延迟。",{"id":255,"title":33,"titles":256,"content":257,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_2-分布式-id-有哪些方案雪花算法的坑在哪",[5],"UUID 本地生成、无依赖，但无序且较长，做主键会造成 B+ 树页分裂和索引膨胀。数据库自增简单可靠，但单点、扩展难，分库后需要步长或号段。 雪花算法生成 64 位 ID：1 位符号、41 位毫秒时间、10 位机器标识、12 位序列。趋势递增、性能高，但强依赖时钟，回拨可能产生重复 ID；机器标识的分配也需要统一管理。 常见的回拨处理：小幅回拨等待追平，大幅回拨直接报错或切换备用 workerId；也可以用「上次时间戳 + 序列」的本地状态兜底。 号段模式（如 Leaf）从数据库批量取一段 ID 在内存分配，减少数据库压力，但要处理号段耗尽时的预取和容灾。最终选型看是否需要趋势递增、能否接受中心依赖，以及前端是否会被 JS 精度截断。",{"id":259,"title":49,"titles":260,"content":261,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_3-如何设计一个幂等接口",[5],"幂等的本质是「同一个业务意图执行多次，结果与执行一次相同」。先确定幂等键：客户端生成的请求号、业务单号，或「用户 + 操作 + 目标」的组合。 常见实现：数据库唯一索引（最可靠，但要注意捕获重复键异常）；去重表或 Redis SET NX 记录已处理键（要设置合理 TTL）；状态机限制非法迁移；乐观锁版本号；Token 机制（先领 token，提交时消费）。 还要处理并发窗口：两个相同请求同时到达，都查到「未处理」，然后都执行。唯一约束或分布式锁能把窗口关掉，单纯「先查再写」不行。 设计时明确三点：幂等键由谁生成、保存多久、重复请求返回什么（成功结果、进行中还是冲突）。这些约定比具体技术更重要。",{"id":263,"title":70,"titles":264,"content":265,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_4-分布式锁有哪些实现怎么选",[5],"Redis 锁性能高、实现简单，SET NX PX 加 Lua 释放，配合续期；但主从异步复制下存在锁丢失窗口，适合防重复而非强互斥。 ZooKeeper 用临时顺序节点：创建最小序号节点者获得锁，其他节点监听前一个节点。会话断开后临时节点自动删除，可靠性较好；缺点是性能低于 Redis，且依赖 ZK 集群。 数据库锁可以用唯一索引或 SELECT ... FOR UPDATE，实现最简单，但性能和扩展性最差，适合低频、强一致的场景。 选择时看失败代价：重复发一条通知，Redis 锁足够；扣减余额，应该用数据库行锁、乐观锁或共识系统，并配合 fencing token 防止旧持有者继续写。",{"id":267,"title":94,"titles":268,"content":269,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_5-为什么要分库分表会带来哪些问题",[5],"单表数据量大时，索引变深、DDL 变慢、备份恢复困难；单库连接数和写入能力也有上限。分表解决数据量，分库解决连接数和写扩展。 拆分方式有垂直和水平：垂直按业务或字段拆，边界清晰但可能留下大表；水平按分片键拆，扩展性好，但跨片查询变难。 问题清单要能说出来：跨库 join、跨片分页和聚合、分布式事务、全局唯一 ID、跨片唯一约束、路由规则变更、数据迁移与扩容、热点分片。 工程上通常配合中间件（如 ShardingSphere）做路由和归并，但中间件不能消灭跨片成本。设计分片键时优先选查询必带、分布均匀的字段；如果业务必须按两个维度查，考虑冗余表或搜索引擎。",{"id":271,"title":110,"titles":272,"content":273,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_6-一致性哈希解决了什么问题",[5],"普通取模 hash(key) % N 在节点数变化时几乎全部 key 都要重新映射，缓存会大面积失效。一致性哈希把节点和 key 映射到同一个环上，key 顺时针找到第一个节点，增删节点只影响相邻区间。 为了解决节点分布不均，引入了虚拟节点：每个物理节点在环上放置多个副本，key 的分布更均匀，节点扩容时迁移也更平滑。 它仍有两个边界：一是热点 key 不会因为哈希而分散，需要额外拆分；二是节点性能不同，等权虚拟节点可能造成负载不均，要按权重调整。 一致性哈希常用于缓存和对象存储，数据库分片更常用范围或哈希取模，因为要兼顾范围查询和运维可控性。",{"id":275,"title":130,"titles":276,"content":277,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_7-zookeeper-的典型用法和原理",[5],"ZooKeeper 是一个高可用的协调服务，数据组织成树形 znode，支持持久节点、临时节点和顺序节点。它通过 ZAB 协议在 leader 和 follower 之间复制日志，写请求由 leader 处理，多数派确认后提交。 临时节点随会话消失，因此常用于服务注册和故障感知；顺序节点配合 watch 可以实现公平锁；节点数据小、读多写少，适合存元数据和配置，不适合存大对象或高频写。 watch 是一次性的：触发后需要重新注册，客户端要处理「注册前发生变化」的窗口。会话超时时间决定了故障感知的快慢，设得太短容易误判，太长则恢复慢。 同类系统 etcd 基于 Raft，提供租约、MVCC 和更现代的 API；选型时看生态和团队经验，不要只看「谁更快」。",{"id":279,"title":146,"titles":280,"content":281,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#_8-系统高可用要做哪些事",[5],"先定义目标：RTO（多久恢复）和 RPO（能丢多少数据），不同业务差异很大。然后从冗余、故障转移、隔离和演练四方面设计。 冗余包括多实例、多可用区、多副本；故障转移要有健康检查、选主和流量摘除，并防止脑裂——常用多数派、租约和 fencing 机制。隔离包括线程池、连接池、机房和租户隔离，避免一个依赖拖垮全局。 降级和限流是「保命」手段：非核心功能可关闭，核心链路保留最小依赖。所有降级开关要提前演练，不能等到故障时才发现配置不生效。 最后是演练和演练后的修复：混沌工程、故障注入、备份恢复演练。没有演练过的高可用方案，通常只是文档上的高可用。",{"id":283,"title":161,"titles":284,"content":285,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#易错点与自测",[5],"CAP 不是「三选二」，分区发生时才是真正的取舍点；BASE 是最终一致，不是「不一致也行」。 雪花 ID 的趋势递增依赖时钟，回拨处理必须明确。 分库分表能解决容量，但会把查询和事务复杂度推到应用层。 自测：① 解释为什么 UUID 不适合做 InnoDB 主键；② 说明一致性哈希中虚拟节点解决什么问题；③ 给出一个「并发重复请求」的幂等落地方案。",{"id":287,"title":180,"titles":288,"content":289,"level":219},"\u002Fjava-interview\u002Fdistributed-systems#参考资料",[5],"ZooKeeper 官方文档：程序员指南 — znode、watch 与会话语义 Apache ShardingSphere 文档 — 分片、路由与分布式事务 etcd 官方文档 — Raft、租约与 watch 的对照参考 Google SRE Book：处理过载 — 限流、降级与容量设计",1793263559818]