<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>分布式 on 追风笔记</title><link>https://f91dba72.wangpeng.pages.dev/tags/%E5%88%86%E5%B8%83%E5%BC%8F/</link><description>Recent content in 分布式 on 追风笔记</description><generator>Hugo</generator><language>zh</language><lastBuildDate>Mon, 30 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://f91dba72.wangpeng.pages.dev/tags/%E5%88%86%E5%B8%83%E5%BC%8F/index.xml" rel="self" type="application/rss+xml"/><item><title>分库分表策略与热点治理</title><link>https://f91dba72.wangpeng.pages.dev/columns/data/sharding-strategy/</link><pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/columns/data/sharding-strategy/</guid><description>&lt;p&gt;当单表涨到几亿行、单库连接打满，分库分表就从「可选项」变成「生存项」。但分片一旦定错键，后期重构的代价接近重写。所以策略要在一开始就想清楚。&lt;/p&gt;&#10;&lt;h2 id="分片键是第一步也是最重要的一步"&gt;分片键是第一步，也是最重要的一步&#10;&lt;/h2&gt;&#10;&lt;p&gt;分片键决定数据怎么散、请求怎么走。选错键，要么数据倾斜，要么跨分片查询泛滥。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;选高频等值查询字段&lt;/strong&gt;：比如账户号、客户号，保证大多数请求落单分片。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;避免低基数或单调递增&lt;/strong&gt;：性别这种分片键会制造大分片；自增 ID 做键会集中在最新分片，形成写入热点。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;兼顾关联查询&lt;/strong&gt;：同一客户的账户、交易最好同片，减少跨片 JOIN。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-c3928bd6-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="sql" data-td-line-count="3"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-c3928bd6-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 按客户号哈希分 1024 片&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trans_$&lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cust_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cust_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;trans_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="扩容预分片优于临时拆"&gt;扩容：预分片优于临时拆&#10;&lt;/h2&gt;&#10;&lt;p&gt;一开始就按「未来三年规模」定足够多的逻辑分片（如 1024、4096），物理上先少后多。扩容只是把逻辑分片映射到新物理库，&lt;strong&gt;数据迁移量远小于重新分片&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;div class="td-table-scroll td-table-scroll--static"&gt;&#10;&lt;table&gt;&#10; &lt;thead&gt;&#10; &lt;tr&gt;&#10; &lt;th scope="col"&gt;策略&lt;/th&gt;&#10; &lt;th scope="col"&gt;优点&lt;/th&gt;&#10; &lt;th scope="col"&gt;缺点&lt;/th&gt;&#10; &lt;/tr&gt;&#10; &lt;/thead&gt;&#10; &lt;tbody&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;预分片&lt;/td&gt;&#10; &lt;td&gt;扩容平滑&lt;/td&gt;&#10; &lt;td&gt;初期略冗余&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;范围分片&lt;/td&gt;&#10; &lt;td&gt;易理解&lt;/td&gt;&#10; &lt;td&gt;易热点&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;tr&gt;&#10; &lt;td&gt;哈希分片&lt;/td&gt;&#10; &lt;td&gt;均衡&lt;/td&gt;&#10; &lt;td&gt;范围查询跨片&lt;/td&gt;&#10; &lt;/tr&gt;&#10; &lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;div class="td-code td-code--untitled" id="td-code-c3928bd6-fence-1" data-td-code data-td-code-auto-id&#10; data-td-language="java" data-td-line-count="3"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-c3928bd6-fence-1-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 逻辑分片到物理库的映射可配置&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;trans_&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;custId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;phys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;routeTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;映射到具体物理库&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;h2 id="热点治理"&gt;热点治理&#10;&lt;/h2&gt;&#10;&lt;p&gt;即使哈希均匀，业务上仍会有「明星账户」「大商户」成为单点热点。&lt;/p&gt;</description></item><item><title>单元化架构：银行核心的容灾与扩展底座</title><link>https://f91dba72.wangpeng.pages.dev/blog/unitization-deploy/</link><pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate><guid>https://f91dba72.wangpeng.pages.dev/blog/unitization-deploy/</guid><description>&lt;p&gt;单元化被讲成&amp;quot;异地多活的银弹&amp;quot;，但它真正解决的问题只有两个：把故障爆炸半径收进一个单元，以及让容量可以按单元横向叠加。理解不了这两点，做出来的往往只是一套更复杂的分库分表。&lt;/p&gt;&#10;&lt;h2 id="为什么核心要做单元化"&gt;为什么核心要做单元化&#10;&lt;/h2&gt;&#10;&lt;p&gt;传统主备架构的困境是：备机平时不承载流量，切换时没人敢按那个按钮。因为没人验证过备机在真实压力下能不能扛住。&lt;/p&gt;&#10;&lt;p&gt;单元化把这件事变成常态：每个单元都在真实处理流量，每个单元都是完整的业务闭环——接入层、应用层、数据库全套齐备。某个单元挂了，把它的流量按客户维度切到其他单元，其他单元本来就在跑同样的代码和同样的负载模型。&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;容灾能力不是靠架构图证明的，是靠每月切一次流量证明的。单元化的最大价值是让切换变成一件平常事。&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h2 id="单元怎么切"&gt;单元怎么切&#10;&lt;/h2&gt;&#10;&lt;p&gt;切分维度只有一个原则：&lt;strong&gt;保证绝大多数交易在单元内闭环&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;对零售银行来说，客户号是最合适的切分键。同一个客户的账户、协议、流水都在一个单元，查余额、转账（同行同单元）这些高频操作不跨单元。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;按客户号切&lt;/strong&gt;：适合零售，单元内闭环率通常能到 90% 以上&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;按机构切&lt;/strong&gt;：适合对公，但容易出现大行倾斜&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;按账号切&lt;/strong&gt;：看着均匀，实际会把同一客户的账户打散，跨单元查询暴增&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;要留一类特殊单元：全局单元。存放客户号生成、产品参数、机构信息这些必须全局唯一或全局一致的数据。全局单元只读为主，写入频率极低，靠单向复制分发到各业务单元。&lt;/p&gt;&#10;&lt;h2 id="路由与数据一致性"&gt;路由与数据一致性&#10;&lt;/h2&gt;&#10;&lt;p&gt;路由要在尽可能靠前的位置完成。我们的做法是接入层解析出客户号后直接算单元，错误路由的请求由应用层做二次校验并拒绝，绝不允许&amp;quot;就近处理&amp;quot;。&lt;/p&gt;&#10;&lt;div class="td-code td-code--untitled" id="td-code-e106a432-fence-0" data-td-code data-td-code-auto-id&#10; data-td-language="yaml" data-td-line-count="11"&gt;&#10; &lt;div class="td-code__viewport" id="td-code-e106a432-fence-0-viewport" data-td-code-viewport&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;routing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shard_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;customer_no&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mod &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 客户号后四位取模，共 64 个逻辑单元&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;logical_units&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;unit-sh-01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0-15&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;unit-sh-02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;16-31&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;unit-sz-01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;32-47&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;unit-sz-02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;48-63&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;global_unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;unit-global&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;strict_check&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 应用层校验不通过直接拒绝，不做转发&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;逻辑单元数要一次性定足（我们定了 64），物理单元可以少。扩容时把逻辑单元区间搬到新物理单元，不用改路由算法，也不用重新哈希全量数据。&lt;/p&gt;&#10;&lt;p&gt;跨单元的交易（比如两个客户不在同一单元的转账）不要试图做跨单元强一致事务。正确做法是拆成两段本地事务加异步通知，中间状态用&amp;quot;在途&amp;quot;余额表达，配日终对账兜底。&lt;/p&gt;&#10;&lt;h2 id="踩过的坑"&gt;踩过的坑&#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;路由键泄漏到业务逻辑里&lt;/strong&gt;。有个模块直接按账号查库，没带客户号，单元化后查不到数据。所有 DAO 必须强制要求分片键，缺失就报错，不要默认广播查询。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;广播查询滥用&lt;/strong&gt;。运营后台要查&amp;quot;全行某状态的账户&amp;quot;，图省事写成遍历所有单元。单元多了之后这类查询直接拖垮全部单元。这类需求应该走离线数仓。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;忘了单元内的容灾&lt;/strong&gt;。单元化解决的是单元级故障，单元内部的数据库高可用还得单独做，两者不能互相替代。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;单元化的复杂度是实打实的，它换来的是可验证的容灾能力和线性的扩容路径。如果业务量还撑不起这份复杂度，主备加读写分离活得更舒服。&lt;/p&gt;</description></item></channel></rss>