第十章 国企 / 金融场景专题(第209-228题)
本章导览(元问题与表面主题逐字取自 《核心考点与元理论》§六)
- 本章真正考的那一个问题: 在资金不变量与合规约束下,可用性如何「降级但不降正确性」?
- 表面主题: 登录、转账、对账、合规
- 题号与题量: 第 209–228 题,共 20 题;刷题时不要按知识点记,要按上面那一个元问题记。
209. 手机银行登录系统要高可用,怎么设计(高可用登录架构)
【考察内容】高可用架构设计 + 认证体系 + 金融可用性要求
【题目】行里要求手机银行登录系统全年可用率不低于 99.99%(全年停机不超过 53 分钟),日活 3000 万,早高峰 8:00–9:00 登录 QPS 峰值 3 万。请设计这套登录系统,并说明如何做到高可用。
【参考答案】按“先保可用、再谈体验”的思路:
分层设计:接入层用多机房 DNS 智能调度 + 四层/七层负载(LVS + Nginx),客户端 SDK 内置多 IP 兜底与自动重试;应用层登录服务无状态化(Session 外置到 Redis 集群),支持水平扩容;数据层用户认证库主从 + 读写分离,凭证校验走缓存;
认证链路:设备指纹 + 图形/短信验证码(前置风控)→ 密码/生物识别校验 → 颁发短时效 AccessToken + 长时效 RefreshToken,敏感操作二次认证;
高可用手段:同城双活(两机房同时承载,任一故障秒级切流);依赖降级(先把依赖拆开再谈降级:第 2 条里图形验证码与短信验证码同属「验证码服务」,所以「验证码服务挂」时拿短信当备用因子是走不通的(同来源一起挂)。分两种情况答:① 只有图形/风控验证码组件挂 → 降级为「密码+设备指纹+短信验证码」,保持认证强度;② 短信网关也挂(或与短信链路同源故障)→ 降级为「密码+设备指纹+人脸/UKey/口令」等不依赖短信的因子,并临时限制敏感操作、二次认证改用可用因子。两条路径都要留可审计记录。风控服务不可用时降级为“放行但加强事后审计”);限流熔断(按用户 / IP / 设备维度,防撞库打满);灰度与回滚(新版本按用户分桶灰度,异常自动回滚);
监控与演练:登录成功率、P99 耗时、验证码下发成功率、各依赖可用率;定期做故障演练(机房切换、依赖宕机)。
容量估算: 手机银行登录峰值经验:早高峰/还款日可达日常 3–5 倍;登录接口实例数 = 峰值 QPS ÷ 单机 QPS 能力 × 1.5(是「除」不是「乘」:峰值 3 万、单机 2000 → 15 台,再 ×1.5 ≈ 23 台;若按【关键数字】保守取单实例 1000 QPS,则是 30 台 ×1.5 ≈ 45 台)。Session/Token Redis:活跃会话数×Token 大小,千万级会话数 GB 级内存。认证库读 QPS≈登录 QPS×(1+重试)。可用性目标:登录 99.99%(全年停机 <53 分钟)。
失败与降级(口径与第 3 条一致:可用性可以降,认证强度不能无痕地降): 只有图形/风控验证码组件挂→密码+设备指纹+短信验证码;短信同源也挂→密码+设备指纹+人脸/UKey/口令(别答成「密码+设备指纹」两因子,那是第 3 条明确禁止的强度降级);风控挂→限额放行+事后审计;Redis 会话挂→短 AccessToken 在验签有效期内继续可用、超窗强制重新登录;认证库主挂→从库提升+只接受降级登录(限制敏感操作)。每次降级都要有监管可接受的记录。
【原理溯源】
为什么登录系统必须无状态化? 有状态 Session 绑在单机进程里,实例挂掉该机上的所有在线用户全部掉线,故障半径等于实例数的倒数。把 Session 外置到共享存储(Redis 集群)后,任意实例可处理任意请求,扩容/缩容/故障切换都不影响会话连续性——这是水平扩展的前提,也是 99.99% 的地基。
为什么金融登录不能用“纯可用性优先”的降级? 互联网业务验证码挂了可以直接放行;银行不行——登录是资金安全的第一道门。放行等于把撞库/盗号风险放大到全量流量。所以金融降级必须是认证因子降级(换备用因子),而不是认证强度降级(去掉认证)。验证码不可用 → 降为“密码 + 设备指纹 + 短信验证码”,但要先看第 2 条的依赖划分:图形与短信验证码同属「验证码服务」,该服务整体挂时短信不可用,只能换成人脸/UKey/口令这类不依赖短信的因子;风控不可用 → 可放行但必须事后加强审计。可用性可以降,认证强度不能无痕地降。
为什么 99.99% 意味着切流必须秒级? 全年 8760 小时 × 0.01% ≈ 52.6 分钟。单次机房故障若切流耗时 10 分钟,一年只允许发生 5 次;若切流 1 分钟,允许 50 次。所以架构目标不是“永不故障”,而是故障检测 + 流量切换的总时间压缩到 1 分钟内。客户端多 IP 兜底把 DNS 切换的传播延迟也吃掉一部分。
为什么 Session 要用短 AccessToken + 长 RefreshToken? 长时效 Token 一旦泄露,攻击窗口等于 Token 有效期。短时效 AccessToken(如 15–30 分钟)把泄露后的可利用时间压到很短;RefreshToken 存在更安全的位置(HttpOnly Cookie / 安全存储)且可撤销,用于无感续期。敏感操作(转账、改密)再强制二次认证——即使 AccessToken 被盗,也转不走钱。
为什么撞库要三维限流而不是只限 IP? 只限 IP:攻击者换代理池即可绕过;只限账号:对单账号低速撞库无效。按 IP / 设备 / 账号三维限流,再叠加风控评分(异地、新设备、高频失败),才能在“误伤正常用户”和“放过攻击者”之间取得平衡。
【选型判断树】
登录依赖故障时,降级路径怎么选?
├─ 验证码服务不可用
│ → 先分清挂的是哪个组件:只有图形/风控验证码挂 → 备用因子=密码+设备指纹+短信验证码;
│ 短信网关也挂(与验证码服务同源故障时)→ 改用人脸/UKey/口令等不依赖短信的因子,并限制敏感操作
│ → 禁止:直接放行(认证强度不能无痕降低)
├─ 风控服务不可用
│ → 可降级放行,但必须:① 记全量登录日志;② 事后回溯审计;③ 敏感操作仍强制二次认证
├─ 认证库主库不可用
│ → 读从库校验(凭证类可容忍短暂旧数据)
│ → 但禁用、改密、踢人必须读主或走 Redis 黑名单,否则冻结指令在复制延迟窗内失效、被盗账号仍可登录
│ → 同城另一机房主库接管(同步复制/组复制多数派保证 RPO=0;MySQL 里没有叫「强同步」的模式开关)
└─ Redis(Session)集群不可用
→ 本地缓存兜底:短期 Session 在验签有效期内仍可用,超窗强制重新登录(与第 6 条同一口径)
→ 不可:跳过 Session 校验直接放行
口诀:可用性可降,认证强度不可无痕地降;每次降级都要留审计。【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “登录是资金安全第一道门,设计原则是可用性优先但认证强度不妥协” |
| 0:30–1:30 | 分层架构 | 接入层多机房 DNS + 负载;应用层无状态 + Session 外置;数据层主从 + 缓存 |
| 1:30–2:30 | 认证链路 | 设备指纹/验证码前置 → 密码/生物识别 → 短 Token + 长 Refresh → 敏感操作二次认证 |
| 2:30–3:30 | 高可用四件套(本题专用清单) | 同城双活秒级切流;依赖降级(换因子不换强度);三维限流防撞库;灰度回滚 |
| 3:30–4:30 | 指标与演练 | 99.99%≈53 分钟;监控登录成功率/P99/依赖可用率;定期真实切流演练 |
| 4:30–5:00 | 收尾 | “一句话:金融登录的高可用,是把故障恢复压到秒级,同时保证降级不降安全” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 可用率 99.99% | 全年停机 ≤ 53 分钟 | 8760h × 0.01% |
| 切流目标 | 检测 + 切换 < 60 秒 | 否则故障预算不够用 |
| 日活 3000 万 / 峰值 QPS 3 万 | 单实例 1000–2000 QPS(压测定档) | 23–45 实例(×1.5 冗余),双机房均摊 |
| AccessToken 有效期 | 15–30 分钟 | 泄露后可利用窗口短 |
| RefreshToken 有效期 | 7–30 天,可撤销 | 存安全存储,支持踢人 |
| 同城机房延迟 | < 2ms | 强同步复制可接受 |
| 撞库限流 | 单账号失败 5–10 次锁 15 分钟 | 三维:IP/设备/账号 |
| 敏感操作二次认证 | 转账/改密/大额支付 | 即使 Token 被盗也转不走钱 |
【追问链】(三层)
L1|“99.99% 是怎么算出来的?你们系统达标吗?” → 8760 小时 × 0.01% ≈ 52.6 分钟/年。拆解到组件:任一机房故障切流 <1 分钟、单实例故障影响 <10 秒(健康检查摘除)、依赖降级开关秒级生效。达标验收靠真实切流演练的实测 RTO,不是纸面计算。
L2|“验证码服务挂了,直接放行不是更快恢复可用吗?” → 快,但不能这么做。登录是资金入口,直接放行=把撞库/盗号风险放大到全量。正确做法是认证因子降级:换备用因子(图形验证码挂→密码+设备指纹+短信;短信网关也挂→密码+设备指纹+人脸/UKey),保持认证强度;若必须放行,只放低风险场景(查询),转账类仍强制二次认证,并全量留审计事后回溯。
L3|“同城双活,数据库怎么保证两边数据一致?” → 应用层无状态天然双活;数据库用强同步复制保证任一机房故障时 RPO=0。注意口径:只有两个机房各一副本时,"多数派"就等于全部 ack,实质是强制全同步——任一链路抖动即无法定档,所以生产要引入第三仲裁点(2+1 Witness/日志节点)或三副本跨机房,代价是写延迟增加约一次同城 RTT(<2ms)与一个故障域。流量调度用 DNS/GSLB 健康检查 + 客户端多 IP 兜底,把 DNS 生效延迟也覆盖掉。切流演练必须真做,不能纸上推演。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 能说出集群、缓存、主从、负载均衡 |
| 80 分 | 分层清晰;无状态化 + 同城双活 + 依赖降级 + 限流四件套;能拆 99.99%≈53 分钟 |
| 95 分 | 主动指出“金融登录可用性可降、认证强度不可无痕地降”;短 Token + 长 Refresh + 敏感操作二次认证;降级路径具体到因子替换;强调真实切流演练;提及撞库三维限流 |
【关联题】
- 互联网版原型: 第 130 题(扫码登录)、第 155 题(SSO 单点登录)——本题是金融口径改造
- 上游理论: 第 222 题(同城双活与两地三中心)——登录双活的底层依赖
- 安全相邻: 第 214 题(数据脱敏)、第 220 题(审计留痕)——认证与审计是一套体系
- 降级专题: 第 217 题(金融级服务降级)——依赖降级的通用方法论
【自测】
- 99.99% 可用率对应的全年停机预算是多少分钟?这对切流速度有什么要求? 参考答案:约 53 分钟。单次机房故障的检测+切流必须压到 1 分钟内,否则一年只允许几次故障。
- 验证码服务不可用时,金融登录能否直接放行?正确做法是什么? 参考答案:不能。登录是资金入口,直接放行会放大撞库风险。应降为备用认证因子(密码+设备指纹+短信),保持认证强度;若部分放行,敏感操作仍强制二次认证并全量审计。
- 为什么 AccessToken 要短时效、RefreshToken 要长时效且可撤销? 参考答案:短时效压缩 Token 被盗后的可利用窗口;长 Refresh 支持无感续期且可随时撤销(踢人)。敏感操作再强制二次认证,即使 Access 被盗也转不走钱。
210. 亿级账户流水表翻页越来越慢(亿级数据分页查询)
【考察内容】深分页优化 + 亿级数据查询设计
【题目】账户流水表有 8 亿行数据,客户在 App 上翻看历史流水,翻到后面几十页时接口要 5 秒以上,客户投诉。请给出优化方案,并说明如果要设计一个“亿级数据的分页查询方案”,你的整体思路是什么。
【参考答案】
先定位:
EXPLAIN看是否全表扫描;深分页LIMIT 1000000, 20的本质是扫描并丢弃前 100 万行,代价随页数线性增长——但题干是「翻到后面几十页」,20 条/页时 offset 只有约千级(几十×20≈1000,与 100 万差约 1000 倍),千级 offset 本不该 5 秒,所以第一步要按「offset 不大却仍慢」这个分支定位:ORDER BY字段无索引/无覆盖索引导致全表 filesort、SELECT *宽行回表放大、分库分表下每片都要取offset+n条再跨片归并、页面另外还发了COUNT(*)全量统计。游标分页只治「offset 大」那一支,不先 EXPLAIN 就答游标分页属于没对症;优化手段(按性价比排序):
- 游标分页(推荐):改用“上一页最后一条的 id/时间”作为游标,
WHERE id < :last_id ORDER BY id DESC LIMIT 20,走主键索引,页数无关,恒定 O(log n); - 延迟关联:
SELECT * FROM t JOIN (SELECT id FROM t ORDER BY id LIMIT 1000000, 20) x USING(id),先在覆盖索引上分页拿到主键,再回表,减少回表次数; - 业务限流:限制最大可翻页数(如只允许查最近 6 个月),引导用户用条件筛选(时间范围 + 卡号)缩小结果集;
- 冷热分离:近 3 个月热数据在线上库,历史数据归档到历史库 / 数仓,查询路由到不同库;
- 覆盖索引:把常用查询字段(流水号、时间、金额、对手方)做成联合索引,避免回表;
- 游标分页(推荐):改用“上一页最后一条的 id/时间”作为游标,
分页查询方案的通用骨架:明确查询维度(按时间 / 按卡号 / 按类型)→ 选择分页方式(游标分页优先,跳页需求才用 offset)→ 设计索引(查询字段顺序 = 索引顺序,最左前缀)→ 控制结果集大小 → 冷热分层 → 加缓存(首页 / 热门页)。
容量估算: 亿级流水表:单行按 1KB → 1 亿行 ≈ 100GB;宽表按 3~8KB/行 → 300GB~800GB;若已到 8 亿行 ×1KB ≈ 800GB 才谈得上 TB 级——「数百 GB~TB」要说清按几 KB/行、多少行。深分页
LIMIT 1000000,20会扫过百万行,必须改写。优化:① 游标/seek(WHERE id/时间 > last);② 延迟关联先查主键再回表;③ 按用户+时间分区/分表;④ 热数据近线存储。翻页深度产品上限(如 100 页)从源头限制。查询 P99 目标金融柜面常 <2s。失败与降级: 历史查询走离线/归档库异步导出,核心在线库只服务近期;归档库不可用时提示稍后重试而不是拖垮主库。分析型需求强制走数仓。
【原理溯源】
- 为什么
LIMIT offset, n慢在“扫描丢弃”? InnoDB 的 B+ 树索引只能告诉你“下一条在哪”,不能告诉你“第 100 万条在哪”。执行LIMIT 1000000, 20时引擎必须沿索引扫过前 100 万条再丢弃,只留最后 20 条。页数越深扫描越多,RT 随页数线性增长——这是算法层面的必然,加索引救不了。 - 为什么游标分页能与页数无关? 游标(上一页最后一条的 id/时间)直接定位到 B+ 树中该键的位置,从那里顺序取 20 条。定位是 O(log n),取 20 条是 O(20),总代价恒定。代价是放弃了“随机跳到第 N 页”的能力——而流水浏览的用户行为本就是“往下刷”,跳页需求极低。
- 为什么金融流水要冷热分离? 银行流水有天然的时间衰减:客户 95% 的查询集中在近 3 个月。把历史数据归档到历史库/数仓,线上库体积缩到原来的 1/10,索引更小、缓存命中更高、备份窗口更短。同时归档不是删除——监管要求流水可追溯多年,历史库必须可查,只是查询走另一条链路(可接受秒级延迟)。
- 为什么金融场景不能随便加缓存? 流水是账务数据,缓存旧数据会让客户看到“余额不对/少了笔交易”,引发投诉甚至监管问询。可以缓存的是首页摘要、热点产品页这类非实时账务;逐笔流水必须走库(或走从库+写后读主),宁可慢一点,不能错一点。
- 为什么延迟关联有效? 先在覆盖索引(只含 id)上做深分页——覆盖索引体积小、扫描快;拿到 20 个 id 后再回主表取完整行。把“扫描 100 万行完整数据”变成“扫描 100 万行窄索引 + 回表 20 次”,IO 降一个数量级。是游标分页不可用时的次优解。
【选型判断树】
亿级流水查询,先问“用户要不要跳页”?
├─ 不要跳页(绝大多数 App 流水浏览)
│ → 游标分页:WHERE id < last_id ORDER BY id DESC LIMIT 20
│ → 恒定 O(log n),页数无关
├─ 必须跳页(客服后台按页码定位)
│ → 延迟关联 + 覆盖索引,且限制最大页数(如 100 页)
│ → 超出则强制改条件检索
└─ 进一步分层
├─ 近 3 个月 → 线上库(主/从)
├─ 3 个月–10 年 → 历史库(可接受秒级延迟;交易信息按反洗钱法至少十年)
└─ 更久远 → 数仓/归档库(T+1 批量可接受)
额外约束(金融):账务字段不缓存;查询走从库时对刚发生的交易写后读主。【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “先按 EXPLAIN 判是哪一类:题干几十页=offset 千级,这类慢多半不是扫描丢弃,而是无覆盖索引/filesort/回表/跨片归并/COUNT;offset 十几万才轮到『扫描丢弃』” |
| 0:30–1:30 | 机制 | LIMIT offset,n 要扫过前 offset 行再丢弃;页数越深代价线性涨 |
| 1:30–2:30 | 对症 | offset 大 → 游标分页(上页最后 id 定位,与页数无关,代价是不能跳页);*offset 不大却慢 → 补覆盖索引/去 SELECT /分片内先取后归并/去掉全量 COUNT |
| 2:30–3:30 | 补充手段 | 延迟关联、覆盖索引、业务限页、冷热分离(近 3 月在线上库) |
| 3:30–4:30 | 金融约束 | 账务数据不缓存;历史归档仍可追溯;写后读主 |
| 4:30–5:00 | 收尾 | “一句话:流水查询优先游标分页 + 冷热分层,金融约束是正确性优先于缓存命中” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 流水表规模 | 8 亿行 | 单表已需分库分表或归档 |
| 深分页阈值 | offset > 10 万时明显变慢 | 超过考虑换游标;但题干那种 offset 千级仍 5s 的,要先查索引/filesort/回表/跨片归并/COUNT(*) |
| 游标分页 RT | 恒定 < 50ms | 与页数无关 |
| 冷热分界 | 近 3 个月为热数据 | 按业务查询分布调整 |
| 历史流水留存 | 交易信息自交易结束后至少 10 年(反洗钱法 34 条) | 归档不删除 |
| 覆盖索引扫描 | 比回表快一个数量级 | 窄索引体积小 |
| 从库延迟容忍 | 历史查询可秒级;刚发生交易写后读主 | 账务一致性约束 |
【追问链】(三层)
L1|“业务说必须支持跳页怎么办?” → 用延迟关联+覆盖索引缓解,同时限制最大可翻页数(如 100 页)。真正的跳页体验应改为按条件检索(时间范围+卡号+金额区间)——金融流水的用户真实需求是“找到那笔交易”,不是“翻到第 87 页”。
L2|“分库分表后怎么分页?” → 各分片各取前 N 条,中间层归并排序后取全局前 N。深分页时每片都要取回前 offset+n 条再归并截段——不能只取 offset/分片数(全局 offset 之前的行可能全落在同一片,那样是漏记录,不只是变慢);offset 越大每片扫描量线性上升——所以分库分表场景更应强制游标分页。跨片统计/检索走 ES 或数仓,不在线上库广播。
L3|“缓存流水首页行不行?” → 首页摘要可以(账户余额快照+最近 5 笔,短 TTL),但逐笔明细不建议——账务数据缓存旧值会引发“钱对不上”的投诉。若必须缓存,要与对账联动:缓存只做展示加速,以库为准,缓存与库不一致时以库覆盖。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道加索引、分页要优化 |
| 80 分 | 先按 EXPLAIN 判类别(offset 大=扫描丢弃;offset 不大却慢=filesort/回表/跨片归并/COUNT),再对症给游标分页或索引方案;知道延迟关联、冷热分离 |
| 95 分 | 能论证游标分页的 O(log n);主动提金融约束(账务不缓存、历史可追溯);分库分表后的归并方案;写后读主;按业务查询模式设计索引与分层 |
【关联题】
- 互联网版原型: 第 56 题(深分页优化)——本题是金融口径改造
- 分片专题: 第 215 题(金融数据分片)——分库分表后的分页
- 读写分离: 第 216 题(主从延迟)——查询走从库的一致性问题
- 对账兜底: 第 219 题(日终对账)——归档/缓存出错时的最终防线
【自测】
LIMIT 1000000, 20为什么慢?加索引能解决吗? 参考答案:引擎必须扫描并丢弃前 100 万行,页数越深代价线性增长。加索引救不了扫描丢弃本身,应改游标分页。- 游标分页的代价是什么?金融流水场景能否接受? 参考答案:不能随机跳页。流水浏览用户行为本就是往下刷,跳页需求极低,完全可接受;真要跳页改为条件检索。
- 历史流水为什么归档而不是删除?归档后查询怎么办? 参考答案:监管要求流水可追溯多年——客户身份资料在业务关系结束后、客户交易信息在交易结束后至少保存十年(《反洗钱法》2024 修订第三十四条,2025-01-01 施行;旧法口径才是五年,别再说五年)。归档到历史库/数仓,查询路由到另一条链路,可接受秒级延迟,但必须可查。
211. 理财开售瞬间被抢空(金融秒杀场景)
【考察内容】金融场景下的高并发抢购 + 合规与公平性
【题目】一款年化 4.5% 的理财产品今日 10:00 开售,额度 5 亿元,预计 100 万客户同时抢购。要求:不超卖、不重复购买、系统不崩,且监管要求“销售过程可追溯、不得有内部插队”。请设计方案。
【参考答案】
先与互联网秒杀区分:金融抢购多两条硬约束——额度是钱(不能超卖)、过程要可追溯(监管审计);
削峰:开售前页面静态化 + 倒计时本地渲染;开售瞬间用答题 / 验证码 / 预约资格做前置拦截,把并发打散到几十秒;
库存控制:额度预扣在 Redis(按份数用
DECR、按金额用DECRBY,或 Lua 脚本保证原子性),扣减成功才进入下单;Redis 与 DB 最终一致靠 MQ 异步落库 + 定时对账补偿,DB 层再用UPDATE ... WHERE 剩余额度 >= :amount条件更新兜底,双重防超卖;每个客户限购 1 份用唯一索引(客户号 + 产品号)挡重复,或 Redis SETNX 预占;公平性与可追溯:全流程流水落库(谁、何时、以什么请求号、扣了多少额度),请求号全局唯一便于审计;拒绝任何“内部通道”,所有流量走同一入口,防止插队质疑;
兜底:排队机制(超阈值直接返回“排队中”)、降级(只保下单主链路)、限流(网关 + 用户维度)。
容量估算: 理财秒杀:额度 N 份、预约用户 M 人,峰值 QPS 可按“M×瞬时点击率”粗估,常见远高于日常百倍。库存扣减必须原子(DB 乐观锁/Redis Lua/预扣号段)。单机 Redis 扣减 10 万+ QPS 可扛热点;DB 最终落账异步化。超卖零容忍;少卖可接受。带宽与风控也要算。
失败与降级: 系统过载时:排队页/资格抽签,而不是让用户请求打死核心账务。扣减成功但下单失败必须有自动退回额度任务。支付通道失败保留额度锁定时限(如 15 分钟)后自动释放。
【原理溯源】
- 为什么金融抢购比互联网秒杀多了两条硬约束? 电商超卖可以补发优惠券、延迟发货;理财超卖是资金差错——超出登记/报备规模销售属违规销售(涉及资金归集与投资者权益处置,须走差错与退款流程);未达募集下限则是产品不成立、按发行失败处理。且这类要求是可回溯与公平销售——《理财公司理财产品销售管理暂行办法》(2021-06-27 施行)第二十六条要求录音录像等资料满足回溯检查与核查取证需要、第二十三条要求销售资料保管年限不得低于 20 年;下面这句是通俗说法而非逐字条文:“销售过程可追溯、不得有内部插队”,这不只是技术指标,是合规义务。所以金融抢购的正确性权重远高于吞吐量。
- 为什么 Redis 预扣 + DB 条件更新要双重防超卖? Redis
DECR原子且快,但 Redis 是缓存不是账本——宕机丢数据、主从切换丢写都会导致额度虚高。DB 的UPDATE ... WHERE 剩余 >= :amount是最后的账本防线:条件不满足则更新 0 行,从 SQL 层面杜绝超卖。两者一个管性能(挡并发),一个管正确性(守底线)。 - 为什么唯一索引比 Redis SETNX 更可靠? SETNX 依赖 Redis 可用性;唯一索引依赖数据库的 ACID 保证。客户+产品的唯一约束是防重复购买的最终防线,即使 Redis 全挂,数据库仍能挡住第二笔。两者叠加:Redis 挡在前面减少 DB 压力,唯一索引兜在后面保证正确。
- 为什么“可追溯”必须做到请求级留痕? 监管检查时要能回答:“某客户为何没抢到?额度被谁买走了?是否存在内部人员优先?”每笔请求的进入时间、请求号、来源 IP/渠道、扣减结果全部落库,才能用数据自证公平。没有留痕,客户投诉“有内幕”时你无法自证清白。
- 为什么削峰用“预约资格/答题”而不是简单限流? 简单限流会把真实客户和脚本一视同仁地挡掉,引发投诉。预约资格/答题把“有资格的真人”和“黄牛脚本”区分开:脚本答不了题、没有预约码。打散并发的同时兼顾公平性——这是金融场景比电商更讲究的地方。
【选型判断树】
金融抢购方案,按约束优先级决策:
1. 正确性(不可妥协)
├─ 额度不超卖 → Redis 预扣 + DB 条件更新双保险
└─ 不重复购买 → Redis SETNX + DB 唯一索引(客户号+产品号)
2. 合规(不可妥协)
├─ 过程可追溯 → 全链路请求号 + 流水落库
└─ 不得内部插队 → 统一入口,禁止旁路通道
3. 性能(在前两者约束下优化)
├─ 削峰 → 静态化 + 验证码/预约资格打散
├─ 异步 → 扣减成功才 MQ 落库
└─ 兜底 → 排队 + 限流 + 只保下单主链路
口诀:先守正确与合规,再谈性能;超卖是资金差错,不是体验问题。【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “金融抢购与电商秒杀同形不同核:额度是钱、过程要可审计” |
| 0:30–1:30 | 正确性 | Redis 预扣挡并发 + DB 条件更新守底线;唯一索引防重复 |
| 1:30–2:30 | 合规 | 全链路请求号留痕;统一入口防插队;拒绝内部通道 |
| 2:30–3:30 | 削峰 | 静态化 + 验证码/预约资格打散;MQ 异步落库 + 对账补偿 |
| 3:30–4:30 | 兜底 | 排队、限流、降级只保下单主链路 |
| 4:30–5:00 | 收尾 | “一句话:金融抢购的优先级是不超卖 > 可审计 > 高吞吐” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 额度 | 5 亿元 | 超卖即资金差错 |
| 并发 | 100 万客户同时 | 峰值 QPS 10–30 万(系数要自己给:按开售 1 秒内到达 10%–30% 算=1e6×0.1~0.3) |
| Redis 预扣 | 毫秒级 | DECR/Lua 保证原子 |
| DB 条件更新 | WHERE 剩余 >= :amt | 更新 0 行=额度不够 |
| 唯一索引 | 客户号 + 产品号 | 防重复购买最终防线 |
| 削峰窗口 | 把 1 秒并发打散到 30–60 秒 | 打散后进核心链路约 0.2~1 万 QPS(10~30 万 ÷ 30~60s);验证码/预约资格就是这道闸 |
| 对账周期 | 开售期间准实时 + 日终 | 发现 Redis 与 DB 不一致 |
| 限购 | 每客户 1 份 | 唯一索引强制 |
【追问链】(三层)
L1|“Redis 扣减成功但落库失败怎么办?” → MQ 重试 + 本地消息表保证投递;定时对账比对 Redis 与 DB 额度,不一致以 DB 为准修正 Redis。客户侧若已看到“购买成功”但落库失败,走差错处理:补偿购入或退回并致歉。关键是最终一致 + 对账兜底,不是依赖 Redis 永不失败。
L2|“客户投诉没抢到但怀疑有内部人插队,怎么自证?” → 全链路请求号留痕:每笔请求的进入时间、来源渠道/IP、扣减结果可查。用数据证明“所有流量走同一入口、无旁路通道、按时间序处理”。这也是为什么禁止“内部预留额度走特殊接口”——一旦有旁路,自证公平就不可能。
L3|“额度只剩 1 万,两个人同时买 5 千,会不会都成功?” → 会,两个都成功,而且这不是超卖。Redis 的 DECRBY(按金额扣用 DECRBY,DECR 只能减 1)是原子操作:第一个扣 5 千后剩 5 千,第二个再扣 5 千后剩 0,两次都拿到成功结果,合计正好等于剩余额度,一分没多。真正会出事的是「先查再扣」——两个请求都读到 1 万、都认为够,各扣一次就超卖 5 千,所以第 3 条要求必须原子扣减(DECR 或 Lua)。换成两人各买 8 千:第一个成功后剩 2 千,第二个扣完是 -6 千,Lua 判负即回滚并返回失败;DB 侧再走 UPDATE ... WHERE 剩余额度 >= :amount,条件不满足更新 0 行,这是原理溯源说的账本防线——超卖零容忍。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 答出 Redis 扣减、MQ 削峰、限流 |
| 80 分 | 指出金融比电商多“额度是钱”和“可审计”;双重防超卖;唯一索引防重复 |
| 95 分 | 主动对比金融与电商的本质差异;请求级留痕自证公平;预约资格兼顾削峰与防黄牛;对账补偿闭环;拒绝内部通道的合规意识 |
【关联题】
- 互联网版原型: 第 6 题(秒杀系统)——本题是金融口径改造
- 幂等专题: 第 213 题(金融级接口幂等)——防重复购买的通用机制
- 对账兜底: 第 219 题(日终对账)——Redis 与 DB 不一致的最终防线
- 审计留痕: 第 220 题(审计与等保)——可追溯的合规要求
【自测】
- 金融抢购与电商秒杀的两条本质差异是什么? 参考答案:额度是钱(超卖即资金差错,不能补发解决);过程必须可回溯(理财销售办法 26 条双录回溯、23 条资料保存不少于 20 年),不得内部插队属公平销售的合规要求(通俗表述,无逐字条文)。
- 为什么要 Redis 预扣 + DB 条件更新双重防超卖? 参考答案:Redis 管性能挡并发,但不是账本;DB 条件更新从 SQL 层面杜绝超卖,是正确性底线。
- 客户怀疑有内部插队,如何自证清白? 参考答案:全链路请求号留痕,每笔请求的进入时间、来源、结果可查;证明所有流量走同一入口、无旁路通道。
212. 核心账务与营销系统的 CAP 怎么取舍(CAP 在金融落地)
【考察内容】分布式理论在金融场景的实际选型
【题目】面试官问:CAP 理论在银行系统里怎么取舍?核心账务系统和营销活动系统应该选 CP 还是 AP?为什么?
【参考答案】
先纠一个常见误解:CAP 的“三选二”是在发生网络分区(P)时才需要取舍;正常情况下 C 和 A 都能满足。所以真正的问题是——分区发生时,你要保一致性还是保可用性;
核心账务系统 → CP:钱的账不能错。分区时宁可拒绝服务(返回“系统繁忙,请稍后重试”),也不能两边各自记账导致账不平。落地手段:同城双活用强同步复制(两副本场景下"多数派"即全部确认,需再配第三仲裁点才是真 quorum)、分布式事务(TCC / 本地消息表)、日终对账兜底;
营销活动系统 → 可选 AP:领券、积分、活动页这类短暂不一致可接受(用户刷新一下就对了),可用性优先。落地手段:异步复制、最终一致、MQ 补偿;
更进一步的答法:实际系统是按业务分级混用——同一个银行里,账务 CP、支付准 CP、营销 AP、查询类 AP(走从库);并用 BASE 思想对非核心链路做柔性事务。
容量估算与取舍落地: 核心账务选 CP(一致性优先):宁可短暂不可用,不可错账;营销/浏览选 AP。同步复制延迟与吞吐需压测:核心库集群节点数、半同步超时。账务日切窗口与批量任务资源隔离。监管可用性与时效要求写进 SLO。
失败与降级: 一致性组件(强同步)故障时:核心转账可暂停或排队,不允许“先成功后对不齐”;营销系统可降级 AP 读旧数据。故障恢复后以账务系统为准做对账修复。
【原理溯源】
为什么账务不能选最终一致? 最终一致意味着分区/故障窗口内两边各自记账,恢复后靠对账/补偿收敛。对互联网业务(积分、点赞)这没问题;对账务,不一致窗口内的每一笔错误记账都是资金差错——可能已扣款未入账、可能重复入账。资金差错进监管投诉、影响账务平衡,事后补偿的代价(对账、冲正、客户沟通、监管报告)远大于分区期暂时拒绝服务。所以核心账务宁可 CP——分区时拒绝,不制造错误数据。
为什么营销可以 AP? 领券、积分的不一致窗口用户几乎无感(刷新一下就对了),且错了可以补偿(补发一张券)。错误代价低、可修复,用短暂不一致换高可用是划算的。这与账务形成鲜明对比:错误代价决定一致性强度。
为什么实际是“分级混用”而不是一刀切? 同一家银行里,转账扣款错 1 分钱都要上报;活动页多展示一个推荐位毫无影响。一刀切 CP 会让营销活动在分区时白白不可用;一刀切 AP 会让账务冒资金风险。正确做法是按业务的数据价值分级:账务 CP、支付准 CP(关键路径同步+异步补偿)、营销 AP、查询 AP。
为什么账务 CP 还要对账兜底? CP 保证的是“分区时不制造不一致”,但不能保证“所有时候都不出错”——应用 bug、消息丢失、人工误操作都可能造成账务差错。对账是独立于运行时协议的最终防线:不管底层怎么保证,日终总账+明细核对能发现一切差异。金融系统的可靠性哲学是分层设防,不是单点信任。
为什么查询类可以 AP? 查询走从库,短暂读到旧数据不影响资金安全(不会因为读旧数据而多扣钱)。写路径 CP 保证正确,读路径 AP 保证吞吐——这是“读写分治”的经典应用。刚发生的交易用“写后读主”弥补读己之写问题。
【选型判断树】
银行系统 CAP 选型,按数据价值分级:
├─ 核心账务(余额、流水、清算)
│ → CP:分区时拒绝服务,不制造错误账务
│ → 手段:强同步复制、TCC/本地消息表、日终对账
├─ 支付链路(准 CP)
│ → 关键路径同步强一致 + 异步补偿 + 对账
├─ 营销活动(券、积分、活动页)
│ → AP:短暂不一致可接受,可用性优先
│ → 手段:异步复制、最终一致、MQ 补偿
└─ 查询展示(余额查询、流水浏览)
→ AP:走从库,短暂旧数据可接受
→ 刚发生的交易:写后读主
前提:CAP 只在分区时取舍;正常时 C 和 A 都能满足。【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “先澄清:CAP 是分区时的取舍,不是平时三选二” |
| 0:30–1:30 | 账务选 CP | 钱的账不能错;分区时宁可拒绝服务;强同步复制 + TCC + 对账 |
| 1:30–2:30 | 营销选 AP | 券/积分不一致用户无感、可补偿;可用性优先 |
| 2:30–3:30 | 分级混用 | 账务 CP、支付准 CP、营销 AP、查询 AP;按数据价值分级 |
| 3:30–4:30 | 对账兜底 | CP 不等于永不出错;日终对账是独立于协议的最终防线 |
| 4:30–5:00 | 收尾 | “一句话:错误代价决定一致性强度——账务错不起所以 CP,营销错得起所以 AP” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 账务一致性 | 强一致(CP) | 不一致窗口内不允许错误记账 |
| 强同步复制延迟 | 同城 < 2ms | 多数派提交——两副本时「多数派」就是全部确认,真 quorum 需第三仲裁点(见第 2 条) |
| 营销最终一致 SLA | 秒级 | 刷新即可见 |
| 对账周期 | 资金 T+0 准实时 + T+1 日终 | 双保险 |
| 账务差错处理 | 超阈值人工双人复核 | 调账本身留痕 |
| 查询从库延迟容忍 | 秒级 | 写后读主弥补 |
【追问链】(三层)
L1|“核心账务选 CP,高峰期拒绝服务客户不会投诉吗?” → 账务写操作可短暂排队/拒绝,但查询走从库保证可读。关键是“钱不错”比“立刻成功”更重要。可用性用同城双活+快速切流补:正常时双活都可用,只有真正的分区/故障时才触发 CP 行为,而那种时候本来也难以正常服务。
L2|“最终一致怎么保证不丢?” → 本地消息表保证业务与消息同事务提交;MQ 重试 + 死信兜底投递;定时对账发现漏单。三重机制缺一不可。对账是最后一道防线——即使消息全丢,日终对账也能发现差异并补处理。
L3|“能不能做个既强一致又高可用的系统?” → CAP 说的是分区场景。常态下 Raft/ZAB 多数派存活即可既一致又可用。跨地域时:单地域 Raft 强一致,跨地域异步复制+最终一致,或单元化把交易锁在单地域。不能在跨分区场景同时要 C 和 A,但可以在无分区时两者兼得。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道 CAP 三选二,账务选 CP |
| 80 分 | 先澄清“分区时才取舍”;账务 CP、营销 AP 分级;能落到强同步、TCC、对账 |
| 95 分 | 论证“错误代价决定一致性强度”;分级混用整体观;对账是独立于协议的最终防线;能答“为何账务不能最终一致”的金融约束 |
【关联题】
- 互联网理论版: 第 97 题(CAP)、第 98 题(BASE)——本题是金融落地改造
- 事务专题: 第 218 题(金融分布式事务)——TCC/状态机的具体实现
- 对账兜底: 第 219 题(日终对账)——CP 之后的最终防线
- 读写分离: 第 216 题(主从延迟)——查询 AP 的具体问题
【自测】
- 为什么账务不能选最终一致? 参考答案:不一致窗口内的错误记账是资金差错,进监管投诉、影响账务平衡;事后补偿代价远大于分区期暂时拒绝服务。
- 营销系统为什么可以 AP? 参考答案:券/积分短暂不一致用户无感,错了可补偿,错误代价低;用不一致换可用性划算。
- 为什么 CP 了还要做对账? 参考答案:CP 只保证分区时不制造不一致,不能防应用 bug、消息丢失、人工误操作。对账是独立于运行时协议的最终防线,分层设防。
213. 转账接口被重复调用导致重复扣款(金融级接口幂等)
【考察内容】金融场景的幂等设计
【题目】客户点了一次“转账”,但因为网络超时客户端自动重试,后端收到两次请求,客户被扣了两次钱。请说明如何设计幂等来根治,并解释为什么这个场景比普通电商的幂等要求更严。
【参考答案】
幂等键设计:客户端生成全局唯一请求流水号(UUID 或 业务号+时间戳+随机数),服务端以该流水号为幂等键;
存储与判定:用唯一索引(或 Redis SETNX)挡重复——建表或
ALTER TABLE txn_log ADD UNIQUE KEY uk(request_id);运行时就是普通INSERT,重复才报 1062——INSERT语句里没有UNIQUE KEY子句(照抄是 1064 语法错),插入失败即为重复请求,直接返回首次结果;状态机:转账单有明确状态流转(待处理 → 处理中 → 成功 / 失败),幂等判定要结合状态:同流水号 + 同状态 → 返回原结果;同流水号 + 不同参数 → 拒绝并告警(防篡改);
与业务的一致性:幂等记录与业务扣款必须在同一个本地事务里提交(或“先落幂等记录,再走业务,最后更新状态”的本地消息表模式),否则会出现“幂等记录有了但钱没扣”或“钱扣了但幂等记录没落”;
为什么比电商严:电商重复下单可取消、钱能退;银行重复扣款是资金差错,会进监管投诉、影响账务平衡,且客户感知极强。因此必须做到“宁可拒绝,不可重复”,并配套对账与差错处理。
容量估算: 幂等键存储:按日交易量×保留期(金融常 1–7 年)。如日 1000 万笔×1 年≈36 亿条,需分库分表或归档。幂等键长度与索引要考虑。重复请求命中率监控。接口超时重试策略与幂等配合(客户端 1 次+服务端去重)。
失败与降级: 幂等存储不可用:金融资金接口 fail-closed(拒绝交易)而不是放行重复;非资金查询可放行但打日志。补偿任务与人工调账通道必须存在。
【原理溯源】
为什么网络超时会导致重复扣款? 客户端超时后不知道服务端是否已处理,只能重试;服务端若无幂等机制,会把重试当成新请求再扣一次。这不是客户端的错——分布式系统中“至少一次”投递是常态,幂等必须在服务端保证,不能指望客户端不重试。
为什么唯一索引比 Redis SETNX 更可靠? SETNX 依赖 Redis:主从切换可能丢写、宕机可能丢数据。唯一索引依赖数据库 ACID:只要事务提交,约束一定生效。金融场景必须有不依赖缓存的最终防线——Redis 只是前置拦截减压,唯一索引才是正确性保证。
为什么幂等要“结合状态机”而不是只看 key? 只看 key:同一流水号第二次请求直接返回首次结果。但若第一次还在“处理中”(未终态),返回“处理中”给客户端可能导致客户端继续重试或展示错误状态。结合状态机:处理中→返回处理中(可轮询);成功→返回成功结果;失败→返回失败原因。更关键的是:同流水号但金额/收款方不同——这不是重试,是篡改或 bug,必须拒绝+告警,不能当成重复请求静默处理。
为什么幂等记录必须与业务扣款同事务? 若分两步:先写幂等记录再扣款,中间宕机→有幂等记录但钱没扣,客户重试被幂等拦截,转账永远失败;先扣款再写幂等记录,中间宕机→钱扣了但无幂等记录,重试会再扣一次。同事务保证两者要么都在要么都不在——这是本地 ACID 事务在金融幂等中的核心价值。
为什么金融幂等是“宁可拒绝,不可重复”? 重复扣款=资金差错,要走差错处理、可能进监管投诉、客户信任受损。拒绝服务=客户重试或稍后再来,代价可控。所以拿不准时(比如幂等存储不可用)应该拒绝请求而不是放行——这是与互联网“尽量服务”的根本差异。
【选型判断树】
金融接口幂等,按防线分层:
1. 前置拦截(性能层)
└─ Redis SETNX:毫秒级挡重复,减少 DB 压力
2. 最终防线(正确性层)
└─ DB 唯一索引(request_id):不依赖缓存,ACID 保证
3. 语义判定(业务层)
├─ 同流水号 + 同参数 + 处理中 → 返回处理中
├─ 同流水号 + 同参数 + 终态 → 返回首次结果
└─ 同流水号 + 不同参数 → 拒绝 + 告警(防篡改)
4. 事务一致性
└─ 幂等记录与业务扣款同一本地事务
5. 不确定时
└─ 拒绝服务,不放行(宁可拒绝不可重复)
口诀:Redis 挡在前,唯一索引兜在底,状态机判语义,同事务保一致。【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “分布式至少一次投递是常态,幂等必须服务端保证” |
| 0:30–1:30 | 双层防线 | Redis SETNX 前置拦截;DB 唯一索引最终防线 |
| 1:30–2:30 | 状态机判定 | 处理中/成功/失败分别返回什么;不同参数=篡改要拒绝 |
| 2:30–3:30 | 事务一致性 | 幂等记录与扣款同事务,避免半吊子状态 |
| 3:30–4:30 | 与电商对比 | 电商重复可退;银行重复=资金差错;宁可拒绝不可重复 |
| 4:30–5:00 | 收尾 | “一句话:金融幂等是双层防线 + 状态语义 + 同事务,拿不准时拒绝” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 幂等键 | 全局唯一请求流水号 | UUID 或 业务号+时间戳+随机 |
| Redis SETNX TTL | 覆盖最大重试窗口,通常 ≥ 24h | 资金类建议长期 |
| 唯一索引 | request_id | DB 层最终防线 |
| 幂等有效期 | 热层 1–7 年可查,终态归档长期留存 | 审计与对账需要;"永久"指归档层,不是热库 |
| 重试策略 | 与幂等配合:客户端最多 1~3 次指数退避(第 6 条的超时与 DB 峰值预算是按 1 次算的),服务端必须去重 | 减少无效重试;重试上限直接把打到 DB 的峰值流量乘成 2~4 倍 |
| 状态机终态 | 成功/失败/已退回 | 处理中可轮询 |
【追问链】(三层)
L1|“Redis 挂了怎么办?” → 唯一索引是最终防线。Redis 只是前置拦截,挂了流量直达 DB,DB 唯一索引仍能挡重复。代价是 DB 压力增大,需要限流保护。正确性不依赖 Redis,这是金融幂等的设计原则。
L2|“同流水号不同金额怎么处理?” → 这不是重试,是篡改或客户端 bug。必须拒绝请求并告警,不能当成重复请求返回首次结果——否则攻击者可能用同一流水号、不同金额反复试探。金融安全要求语义严格匹配。
L3|“幂等记录和业务扣款为什么要同事务?分两步不行吗?” → 分两步会出现半吊子:先写幂等后扣款,中间宕机→有幂等无扣款,重试被拦截,转账永远失败;先扣款后写幂等,中间宕机→有钱无幂等,重试再扣一次。同事务保证要么都在要么都不在——这是本地 ACID 在金融幂等中的核心价值。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道用 Redis 或唯一索引防重复 |
| 80 分 | 双层防线;结合状态机判定;幂等与业务同事务 |
| 95 分 | 论证“宁可拒绝不可重复”的金融约束;同流水号不同参数=篡改要拒绝;幂等有效期覆盖审计需求;能对比电商幂等的严格程度差异 |
【关联题】
- 互联网版原型: 第 110 题(接口幂等)——本题是金融口径改造
- 分布式事务: 第 218 题(金融分布式事务)——TCC 中的幂等
- 对账兜底: 第 219 题(日终对账)——幂等失效后的最终防线
- 秒杀防重: 第 211 题(金融抢购)——唯一索引防重复购买
【自测】
- 为什么金融幂等必须有 DB 唯一索引,不能只用 Redis? 参考答案:Redis 主从切换可能丢写、宕机可能丢数据;唯一索引依赖 DB ACID 是最终防线。Redis 只是前置拦截减压。
- 同流水号但金额不同,应该怎么处理? 参考答案:这不是重试而是篡改或 bug,必须拒绝+告警,不能静默当成重复请求。
- 为什么说金融幂等是“宁可拒绝,不可重复”? 参考答案:重复扣款是资金差错(监管投诉、账务不平、客户信任);拒绝服务代价可控。拿不准时拒绝。
214. 客户信息要脱敏,但客服还要能查到(银行级数据安全与脱敏)
【考察内容】数据分级分类 + 动态脱敏 + 受控解密 + 审计留痕
【题目】监管要求客户手机号、身份证号、银行卡号在系统里必须脱敏展示;但客服在核身通过后又需要看到完整信息。请设计一套方案,兼顾合规与业务可用。
【参考答案】
分级分类:先给数据定级——L1 公开、L2 内部、L3 敏感(手机号 / 身份证 / 卡号)、L4 核心(密码 / 密钥)。不同级别不同策略;
存储层:敏感字段加密存储(如 AES + KMS 托管密钥),密码类用加盐哈希(BCrypt / Argon2),不可逆;需要检索的字段(如手机号)存“加密值 + 哈希索引”两列,用哈希列做等值查询;
展示层(动态脱敏):默认接口返回脱敏值(
138****8888),在网关 / 序列化层统一做,避免每个业务代码各写一遍;脱敏规则配置化(不同角色不同规则),不硬编码;明文获取(受控解密):独立“解密服务”,需二次授权(业务系统申请 + 权限校验 + 事由填写);解密操作全量审计(谁、何时、查了谁的什么字段、什么理由),审计日志不可篡改、独立存储;支持按需授权(如客服核身后限时 5 分钟可见)、次数限制、异常行为告警;
其他:数据传输全程 HTTPS / 国密;日志与埋点中禁止出现明文(日志脱敏中间件);测试环境用假数据。
容量估算: 脱敏规则与性能:展示层脱敏 CPU 开销低;密文检索用盲索引/保序加密要评估安全与性能。客服查询审计日志按查询次数全量保存。权限模型:角色数×字段策略,避免组合爆炸。国密算法改造时接口加解密 RT 增量压测。
失败与降级: 脱敏服务故障:默认不展示明文(fail-closed),提供工单紧急授权通道。密钥服务不可用时暂停需要解密的查询,核心交易用缓存 DEK 限时。
【原理溯源】
- 为什么脱敏要在网关/序列化层统一做? 业务代码分散在几十上百个服务里,让每个开发者记得脱敏,必然漏。漏一处=一处泄露面。在网关/序列化层统一拦截,所有出口流量走同一套规则——把安全控制从业务逻辑中抽离,用架构保证而不是靠人的自觉。这也是“默认不可见”原则的工程落地。
- 为什么“客服核身后可见”不破坏脱敏的价值? 脱敏的意义是把“默认可见”反转成“默认不可见”。原本所有接口、所有日志、所有导出都可能带明文;改后明文获取变成需要二次授权、事由填写、全量留痕的例外操作。暴露面从“无处不在”收窄到“受控通道”,且通道内每一次访问都可追溯——这是合规检查的核心逻辑。
- 为什么密码用哈希而手机号用加密? 密码只需要验证对错,不需要还原——加盐哈希(BCrypt/Argon2)不可逆,库被拖也无法直接得到明文。手机号/身份证需要支持查询和核对——加密可逆,配合密钥管理;再存一列哈希做等值索引。选可逆还是不可逆,取决于业务是否需要还原。
- 为什么审计日志必须独立存储且不可篡改? 若审计日志与业务库同权限,拿到业务库权限的人可以直接改日志抹掉“谁看过什么”的痕迹——审计就失去“不可否认”的价值。独立存储+独立权限+append-only,是审计可信的前提。金融行业“谁看了客户信息”和“谁改了客户信息”同等重要。
- 为什么密钥要 KMS 托管、与数据分离? 密钥和密文放在一起,等于没加密。KMS 托管实现密钥与数据分离存储、权限控制、使用审计、定期轮换。即使数据库被拖,没有 KMS 权限也解不开——这是纵深防御的一层。
【选型判断树】
敏感数据方案,按数据分级决策:
1. 先分级
├─ L4 核心:用户口令 → 加盐慢哈希(Argon2id/bcrypt)不可逆;密钥/DEK → KMS·HSM 加密托管+轮换(哈希了密钥自身就无法使用)
├─ L3 敏感(手机/身份证/卡号)→ 加密存储 + 哈希索引 + 动态脱敏
├─ L2 内部 → 按角色可见,关键操作审计
└─ L1 公开 → 无需特殊处理
2. 展示层
└─ 默认脱敏(网关/序列化层统一),角色可配置
3. 明文获取
├─ 独立解密服务
├─ 二次授权 + 事由填写
├─ 限时 + 限次
└─ 全量审计(独立存储、不可篡改)
4. 配套
├─ 日志/埋点禁止明文
├─ 密钥 KMS 托管、定期轮换
└─ 测试环境用假数据
口诀:默认不可见,明文是例外;例外必须留痕。【口述骨架】(5 分钟;框架 A,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “兼顾合规与业务,核心是把默认可见反转成默认不可见” |
| 0:30–1:30 | 分级存储 | L1–L4 分级;密码哈希不可逆;敏感字段加密+哈希索引 |
| 1:30–2:30 | 动态脱敏 | 网关/序列化层统一做;规则配置化;不靠业务代码自觉 |
| 2:30–3:30 | 受控解密 | 独立服务;二次授权+事由;限时限次;全量审计 |
| 3:30–4:30 | 审计独立 | 独立存储、不可篡改;“谁看了”与“谁改了”同等重要 |
| 4:30–5:00 | 收尾 | “一句话:脱敏把明文从默认变成例外,例外全部留痕可追溯” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 数据分级 | L1 公开 / L2 内部 / L3 敏感 / L4 核心 | 级别决定策略 |
| 客服明文可见时长 | 核身后 5–15 分钟 | 限时授权 |
| 明文查询次数限制 | 日上限(如 50–200 次) | 防批量倒卖 |
| 加密算法 | AES-256;国密 SM4 | 传输 HTTPS/国密 |
| 密码哈希 | BCrypt / Argon2 | 加盐,不可逆 |
| 审计留存 | 法定底线:网络日志 ≥6 个月(网安法第二十三条三);客户身份资料与交易信息 ≥10 年(反洗钱法第三十四条);访问/操作审计按机构制度常取 3–5 年 | 独立存储 |
| 密钥轮换 | 90–180 天 | KMS 托管 |
| 日志脱敏 | 禁止明文写入 L3/L4 字段 | 日志中间件统一 |
【追问链】(三层)
L1|“客服要看完整信息,那脱敏还有意义吗?” → 有。意义在于把“默认可见”反转成“默认不可见”,把明文获取变成需要留痕的例外操作,大幅收窄暴露面。原本所有接口/日志/导出都可能带明文;改后只有受控通道,且每次访问可追溯。
L2|“加密后还能按手机号查询吗?” → 存“加密值 + 哈希索引”两列:查询时对输入手机号做同样哈希后在哈希列上等值匹配。范围查询需另建辅助索引或用可搜索加密。关键不是“确定性哈希”而是带密钥的确定性:裸哈希(如 SHA256(手机号))可枚举反推——有效号段约 7×10⁹(13x–19x),比 10¹¹ 的表观空间小一个量级但仍可穷举,必须用 HMAC(密钥放 KMS)或本题【容量估算】提到的盲索引,并按租户加盐。
L3|“密钥怎么管?万一泄露怎么办?” → KMS 托管,密钥与数据分离存储;定期轮换(90–180 天);密钥使用本身有审计。泄露应急:立即轮换密钥→用新密钥重加密数据→排查泄露范围→按监管要求上报。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道接口返回时打星号 |
| 80 分 | 有分级分类;区分加密与哈希;默认脱敏+受控解密双轨 |
| 95 分 | 主动提审计留痕独立存储;网关层统一脱敏的架构理由;密码不可逆vs手机号可逆的选型依据;密钥KMS托管;“默认不可见”的合规逻辑 |
【关联题】
- 互联网安全版: 第 193 题(敏感数据加密)、第 196 题(日志合规)
- 审计专题: 第 220 题(审计留痕与等保)——受控解密的下游审计
- 情景题: 第 227 题(明文密码)——发现不合规时的推动
- 登录认证: 第 209 题(高可用登录)——认证体系的数据安全面
【自测】
- 为什么密码用哈希、手机号用加密? 参考答案:密码只需验证对错不需要还原→加盐哈希不可逆;手机号需支持查询和核对→加密可逆+哈希索引。
- 脱敏在业务代码里做还是网关层做?为什么? 参考答案:网关/序列化层统一做。业务代码分散必然漏,架构保证比人的自觉可靠。
- 审计日志为什么要独立存储? 参考答案:同库同权限时,拿业务权限就能改日志抹痕,审计失去不可否认价值。
215. 全国 1 亿用户数据单库扛不住(金融数据分片)
【考察内容】分片策略设计 + 金融场景的分片键选择
【题目】全国 1 亿宽带用户信息存在一张表里,单库单表已经扛不住写入和查询压力。请设计分片方案,并说明分片键怎么选。
【参考答案】
分片键选择(最关键):候选有用户 ID、手机号、省份(
province_id)、地市。按省份 / 地市分片天然贴合电信业务的查询习惯(大部分查询带地域维度),且各省数据量相对均衡,运维可按省隔离、按省扩容;若业务查询以用户维度为主,则用用户 ID 哈希分片,保证单用户查询落在单分片;实践中常见两级分片——先按省(业务维度,便于隔离与合规),省内再按用户 ID 哈希(数据均衡);但两级分片成立的前提是「查询能带上第一级键」:题干若用户点查不带省份(或用户 ID 里没编码省份),路由层就只能对 31 个省各查一遍再归并,反而比单层按 user_id 哈希慢一个数量级。所以先问两件事:① 查询是否总带地域维度(报表/按省稽核→带;App 查自己的账单→通常不带);② 能否让 user_id 本身编码省份段(或维护一张 user→province 路由表并在写入时固化)。两者都不成立就别硬套两级分片,直接按 user_id 哈希+异构出「按省」的汇总表;分片方式:范围分片(易扩容、易热点)vs 哈希分片(均衡、扩容需迁移);金融场景更看重稳定,常用“预分片 + 一致性哈希 / 双倍扩容”降低迁移成本;
路由与中间件:用 ShardingSphere / 自研路由,SQL 解析后按分片键路由;分片键必须出现在查询条件里,否则广播查询;
跨分片查询:跨省统计走数仓 / 分布式 SQL 引擎(如 Presto / ClickHouse),不在线上库做全分片扫描;
配套:全局唯一 ID(雪花算法,注意时钟回拨)、分布式事务(分片后跨库事务)、分片扩容方案(双写迁移 + 校验 + 切流)。
容量估算: 1 亿用户:账户主数据单行 KB 级约 100GB+,单库难扛扩展与DDL。分片键选择:用户号取模/范围;热点产品账户可能倾斜需二次散列。每分片目标 <5000 万行、磁盘水位 <60%。全局唯一 ID、跨片查询走索引表/ES。迁移期双写与校验抽样比例。
失败与降级: 单分片故障:影响该分片用户,需快速切换/重建;跨分片事务用最终一致+补偿,不阻塞全局。路由服务要做多级缓存,防止路由表故障全局不可用。
【原理溯源】
- 为什么分片键选择比索引优化重要得多? 索引解决“单库内怎么查快”,分片键决定“数据落在哪、查询路由到哪”。分片键选错:查询带不上分片键→广播全部分片;数据倾斜→热点分片打满。分片键一旦定下,迁移成本极高——所以是分片设计的第一决策。
- 为什么金融/电信场景常用“按省分片”? ①业务查询天然带地域(按省出报表、按省监管报送);②运维可按省隔离——某省故障不影响他省,符合国企“局部故障不扩散”的稳定性要求;③合规上数据可按省留存。这是业务维度优先于技术维度的选型思路。
- 为什么还要省内再哈希? 按省分片时人口大省数据量是小省的数倍,直接按省到库会造成倾斜。省内再按用户 ID 哈希拆子片,既保留“按省路由、按省运维”的优势,又保证数据均衡——两级分片 = 业务隔离 + 技术均衡。
- 为什么跨分片查询要走数仓? 线上库的职责是点查/小范围查;跨省统计、全量报表是分析型负载——扫描量大、延迟容忍度高。把分析负载扔到线上库会拖垮交易。在线与分析分离,是亿级数据架构的基本原则。
- 为什么金融场景偏好预分片? 取模分片扩容时模数变化→几乎所有数据要重新分布。预分片(逻辑分片数远大于物理分片数)扩容时只迁移部分逻辑片,迁移量是 1/N。金融系统重视稳定,宁可初期多分几片,也不要扩容时全量搬迁。
【选型判断树】
分片键怎么选?先看查询模式:
├─ 查询 90%+ 带地域维度(电信/政务)
│ → 按省分片;大省再哈希拆子片(两级分片;要求查询能带第一级键,否则退化为广播)
├─ 查询以用户维度为主(转账/账单)
│ → 用户 ID 哈希分片;保证单用户落单分片
├─ 既要地域隔离又要用户均衡
│ → 两级:先按省路由,省内按用户 ID 哈希(前提:查询带省或 user_id 已编码省;都不带时只能广播 31 省,别用两级)
└─ 分片方式
├─ 金融偏好 → 预分片(逻辑片 >> 物理库)+ 扩容平滑
└─ 热点明显 → 范围分片 + 大热点单独隔离
硬约束:分片键必须进查询条件,否则广播;跨片分析走数仓。【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “分片键选择决定数据布局与查询路由,是第一决策” |
| 0:30–1:30 | 选型 | 先问「查询带不带省」:带 → 按省分片贴合业务与运维隔离、省内再哈希保均衡;不带 → 直接按 user_id 哈希,另出按省汇总表 |
| 1:30–2:30 | 分片方式 | 范围 vs 哈希;预分片降低扩容迁移成本 |
| 2:30–3:30 | 路由与跨片 | 分片键必须进条件;跨省统计走数仓不走线上 |
| 3:30–4:30 | 配套 | 全局 ID(雪花);分布式事务;双写迁移+校验+切流 |
| 4:30–5:00 | 收尾 | “一句话:先按业务查询模式选分片键,再用预分片保扩容平滑” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 数据规模 | 1 亿用户 | 单表必须分片 |
| 单分片容量 | 千万级行 | 再大索引/备份都吃力 |
| 预分片逻辑片 | 物理库数 × 64–1024 | 扩容时只迁 1/N |
| 两级分片 | 省 → 省内用户 ID 哈希 | 隔离 + 均衡;成立前提=查询带第一级键(或 ID 编码省份) |
| 跨片查询 | 走数仓/Presto | 不在线上广播 |
| 全局 ID | 雪花算法 | 注意时钟回拨 |
| 扩容方式 | 双写迁移 + 校验 + 切流 | 金融偏好可回滚 |
| 单用户查询 | 必须落单分片 | 分片键=用户 ID |
【追问链】(三层)
L1|“扩容时取模变了怎么办?” → 取模分片扩容需数据迁移。改用预分片(逻辑分片数远大于物理库数)或一致性哈希:扩容时只迁移部分逻辑片到新库,迁移量是 1/N。金融场景偏好预分片。
L2|“按省分片会不会数据倾斜?” → 会,人口大省数据多。对大省再按用户 ID 哈希拆子片(两级分片);或按“省+哈希”混合。关键是保留“按省路由、按省运维”的业务优势,同时消除技术倾斜。
L3|“跨省查询怎么做?” → 离线/报表走数仓(Presto/ClickHouse);在线需跨省的场景建异构索引表或 ES。不在线上库做全分片广播扫描。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道按 ID 取模分片 |
| 80 分 | 结合业务查询模式选分片键;分片键必须进条件;预分片 |
| 95 分 | 两级分片(省+哈希)的隔离与均衡论证,并能说出它要求查询带第一级键、否则只能广播;跨片分析走数仓;扩容双写迁移可回滚;全局 ID 与分布式事务的配套意识 |
【关联题】
- 分页专题: 第 210 题(亿级流水分页)——分片后的分页
- 分布式事务: 第 218 题(金融分布式事务)——分片后跨库事务
- 读写分离: 第 216 题(主从延迟)——分片+读写的组合
- 互联网版: 分库分表通用知识——本题强调金融/电信业务维度选型
【自测】
- 为什么金融/电信场景常按省分片而不是按用户 ID 哈希? 参考答案:业务查询天然带地域;运维可按省隔离;合规可按省留存。大省再哈希拆子片解决倾斜。
- 预分片为什么能降低扩容成本? 参考答案:逻辑分片数远大于物理库数,扩容时只迁移部分逻辑片到新库,迁移量是 1/N。
- 跨省统计应该怎么做? 参考答案:走数仓/OLAP 引擎,不在线上库广播扫描;在线跨省需求建异构索引或 ES。
216. 核心库读压力大,读写分离后主从延迟(金融读写分离)
【考察内容】读写分离架构 + 主从延迟的业务处理
【题目】核心账务库读压力大,做了读写分离(写主库、读从库)后,出现“客户刚转账成功,刷新却查不到这笔记录”。请说明原因和解决方案。
【参考答案】
原因:MySQL 主从复制是异步(或半同步)的,从库回放有延迟(网络 + 单线程/多线程回放),秒级到分钟级都可能。写主库后立刻读从库,就会读到旧数据(读己之写问题);
解决方案(按业务影响面排序):
- 写后强制读主:写操作后对该用户的短时间窗口(如 3–5 秒)内的读请求路由到主库,窗口过后回从库;实现方式:写时在 Redis / 本地缓存打一个“用户级标记”,读时判断;
- 按业务分流:账务类“刚发生的交易”必须读主;历史流水查询、统计类读从库;
- 会话粘性:同一用户在同一会话内的读写都走主库;
- 半同步复制 / 组复制(MGR):至少保证从库收到 binlog 才返回成功,把延迟压到最低(代价是写入变慢);
- GTID + 位点校验:读从库前校验位点是否已追平,未追平则等待或降级读主;
兜底:对“读不到”的场景给出明确提示(如“交易处理中,请稍后查看”),并在前端做乐观展示。
容量估算: 读写分离:主库写 QPS 与从库读 QPS 分开压测。延迟预算:核心“读己之写”路径强制主库;报表读从库。金融日终批量时延迟可能激增,要隔离。从库数量按读峰值/单从能力。连接路由中间件本身单点风险要双活。
失败与降级: 延迟超过阈值时只把「该用户这笔写后读」切回主库,不做「关键读整体切主」:题干前提就是核心账务库读压力大才做读写分离,整体切主恰恰让读写分离失去意义(全部读主=没做分离),再靠主库限流会把客户从「查不到」变成「被拒」;正解是动态把写后窗口拉长到当前延迟 P99、只对该用户该笔走主库+异步补推与前端提示,从库确实不可用才从连接池摘除。从库挂了从连接池摘除。日终对账期间暂停非核心读。
【原理溯源】
- 为什么异步复制必然有延迟? 主库提交事务后立即返回,binlog 异步发送到从库并回放。延迟 = 网络传输 + 从库回放耗时。回放若是单线程,大事务会堵住后续所有事务。这是可用性和性能换一致性的典型取舍。
- 为什么金融账务不能接受“读到旧数据”? 客户刚转账成功,刷新却看不到——这不只是体验问题,会引发“钱丢了”的恐慌、客服投诉、甚至监管问询。金融查询的语义是“账务事实”,不是“可能的近似值”。所以账务类刚发生的交易必须读主。
- 为什么“写后读主”比“全部读主”好? 全部读主:读流量回到主库,读写分离白做。写后读主:只把该用户写后的短窗口(覆盖复制延迟 P99)内的读路由到主库,其余仍读从。用最小的主库增量换取读己之写的正确性——精准升级,而不是一刀切。
- 为什么半同步不是万能的? 半同步要求至少一个从库收到 binlog 才返回成功,延迟增加约一次网络 RTT。且超时后会退化为异步(保证可用性优先)。所以半同步是“把延迟压到最低”而非“消灭延迟”;配合写后读主,双保险。
- 为什么前端要做乐观展示? 即使后端做了写后读主,网络抖动、缓存过期等仍可能短暂查不到。前端在用户完成转账后本地先展示“待确认/处理中”,后台确认后刷新为“成功”——用户感知是连续的。
【选型判断树】
读写分离后读到旧数据,按业务分级处理:
├─ 账务刚发生的交易(转账、扣款)
│ → 写后读主(用户级标记,窗口=复制延迟 P99)
│ → 或会话粘性:同会话读写都走主
├─ 历史流水、统计报表
│ → 读从库,容忍秒级延迟
├─ 复制延迟压不下来
│ → 半同步 / MGR(代价:写变慢)
│ → GTID 位点校验,未追平则等待或降级读主
└─ 兜底
└─ 前端乐观展示 + “处理中”提示
口诀:账务读主,历史读从;写后窗口精准升级,不一刀切。【口述骨架】(5 分钟;框架 C,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “读己之写问题:异步复制导致从库落后” |
| 0:30–1:30 | 机制 | 主库提交即返回,binlog 异步回放;延迟=网络+回放 |
| 1:30–2:30 | 分层方案 | 写后读主(精准窗口);账务读主/历史读从;会话粘性 |
| 2:30–3:30 | 压延迟 | 半同步/MGR;GTID 位点校验 |
| 3:30–4:30 | 金融约束 | 账务查询是“事实”不是“近似”;前端乐观展示 |
| 4:30–5:00 | 收尾 | “一句话:按业务分级路由,账务写后读主,历史读从” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 异步复制延迟 | 正常 <1s,异常可达分钟级 | 网络/大事务 |
| 写后读主窗口 | 3–5 秒(取延迟 P99) | 可配置 |
| 半同步延迟增加 | 约 1 次网络 RTT | 同城 <2ms |
| 半同步超时退化 | 超时后降为异步 | 可用性优先 |
| 会话粘性 | 同用户同会话走主 | 实现简单但主库压力大 |
| 前端乐观展示 | 转账后本地“处理中” | 避免感知断裂 |
【追问链】(三层)
L1|“写后读主的窗口设多久?” → 取复制延迟的 P99(本题按 3–5 秒),窗口内读主,窗口外读从。窗口本身要可配置,根据监控的延迟分布动态调整。
L2|“半同步会不会拖慢写入?” → 会,增加约一次网络 RTT。所以通常只在核心账务库用,且设置超时降级为异步。或用 MGR/并行复制缓解。金融场景为正确性付这点延迟是值得的。
L3|“读不到时直接报错行不行?” → 不行,客户会恐慌“钱丢了”。应给明确提示“交易处理中,请稍后查看”,前端做乐观展示。关键是不制造焦虑。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道主从延迟导致读旧数据 |
| 80 分 | 说清读己之写;写后读主+业务分流+半同步组合 |
| 95 分 | 按业务分级路由(账务读主/历史读从);窗口取延迟 P99;前端乐观展示的体验意识;金融账务“事实而非近似”的语义约束 |
【关联题】
- 互联网版原型: 第 59 题(读写分离延迟)——本题是金融口径改造
- CAP 落地: 第 212 题(CAP 金融取舍)——查询 AP 的具体问题
- 分页查询: 第 210 题(亿级流水分页)——历史查询读从
- 容灾: 第 222 题(同城双活)——强同步复制的另一个应用
【自测】
- 什么是读己之写问题? 参考答案:写主库后立刻读从库,因复制延迟读到旧数据。刚转账成功刷新却查不到。
- 为什么不用“全部读主”解决? 参考答案:读流量回到主库,读写分离白做。应写后读主:只把写后短窗口的读路由到主,其余读从。
- 账务查询和历史查询的路由策略有什么不同? 参考答案:账务刚发生的交易必须读主(事实语义);历史流水/统计读从(容忍秒级延迟)。
217. 大促式流量下核心系统不能停(金融级服务降级)
【考察内容】降级设计 + 业务影响面控制
【题目】某银行做营销活动,流量是平时的 10 倍,核心账务系统压力剧增。要求在流量峰值时“保证核心业务可用、非核心功能可牺牲”。请设计降级方案。
【参考答案】
先做业务分级(降级的前提):P0 不可降——转账、扣款、账务记账、登录;P1 可延迟——账单查询、历史流水、消息推送、积分更新;P2 可关闭——活动页个性化推荐、排行榜、弹窗广告、非必要的埋点上报;
降级手段:读降级(非核心查询直接返回缓存 / 默认值 / “系统繁忙,请稍后”);写降级(非核心写如积分、埋点、日志异步化或本地落盘后补);功能降级(关闭个性化推荐、简化页面、关闭非必要风控规则需审批);依赖降级(下游超时立即熔断,返回兜底值,不阻塞主链路);
触发机制:自动(错误率 / RT / 线程池水位超阈值自动降级)+ 手动(开关在配置中心,值班可一键切换);必须有开关、有灰度、有回滚;
前置准备:容量压测确认阈值、降级预案演练、开关默认值评审、降级期间的客户提示话术;
事后:降级期间记录明细,恢复后补数据(如积分补发),并复盘是否要永久优化。
容量估算: 金融降级分级:L1 营销/推荐可弃;L2 非核心查询只读/缓存;L3 核心转账排队但不丢。流量按“必须服务的最小功能集”反推机器与 DB 容量。限流阈值分层:网关全局、服务、用户、渠道。演练分两件事:预案切换演练每季度至少 1 次(验证降级/切流真能生效),全链路压测每年至少 1 次(验证容量水位),别把两者合成一句“每年一次”。
失败与降级: 预案必须可一键执行:开关中心化、有权限、有审计。降级期间 SLA 口径要提前定义(排队成功率、最长等待)。恢复时按反序慢慢放量,防止二次冲击。
【原理溯源】
为什么降级的前提是“业务分级”而不是先上熔断器? 熔断器只回答“依赖挂了怎么办”,不回答“该牺牲谁”。没有业务分级,降级变成随机丢弃——可能把转账也降了。P0/P1/P2 分级的本质是在资源紧张时明确资源分配的优先级。这是业务决策,不是技术决策。
为什么金融降级不能“关掉风控”? 风控是资金安全的防线。关掉风控=打开欺诈窗口,活动期间正是攻击者活跃期。若非核心风控规则可降,需审批+白名单+事后加强审计;核心风控(转账反欺诈)绝不可降。降级降的是体验,不是安全。
为什么开关必须“有灰度、有回滚”? 降级开关配错可能把 P0 关了——比不降级更糟。灰度:先对 1% 流量生效,观察无异常再全量;回滚:一键恢复;白名单:核心功能的开关在代码层面硬编码为不可降。这是用发布工程的严谨对待降级操作。
为什么降级后要“补数据”? 写降级时积分/埋点落本地或丢弃,恢复后必须补发——否则客户积分少了是投诉,埋点丢了是数据缺口。降级是临时手段,业务语义必须最终完整。这也符合金融“对账闭环”思维。
为什么自动降级和手动降级都要有? 自动降级反应快(错误率超阈值秒级触发),但可能误判;手动降级准确(值班结合业务判断),但反应慢。两者互补:自动兜住突发故障,手动处理复杂场景。
【选型判断树】
降级方案设计,按步骤:
1. 业务分级(前提)
├─ P0 不可降:转账、扣款、记账、登录、核心风控
├─ P1 可延迟:账单查询、历史流水、推送、积分
└─ P2 可关闭:推荐、排行榜、弹窗、非必要埋点
2. 降级手段
├─ 读降级:非核心查询返回缓存/默认值
├─ 写降级:积分/埋点异步化或落盘后补
├─ 功能降级:关闭个性化(核心风控除外)
└─ 依赖降级:下游超时熔断,返回兜底值
3. 触发与控制
├─ 自动:错误率/RT/线程池水位
├─ 手动:配置中心一键切换
└─ 硬约束:开关可灰度、可回滚、核心功能白名单
4. 事后
└─ 补数据(积分补发)+ 复盘
口诀:先分级,再降非核心;降体验不降安全;降完要补。【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “降级的前提是业务分级,核心是影响面控制” |
| 0:30–1:30 | P0/P1/P2 | 转账记账登录不可降;查询推送可延迟;推荐弹窗可关 |
| 1:30–2:30 | 四类手段 | 读降级、写降级、功能降级、依赖降级 |
| 2:30–3:30 | 触发控制 | 自动+手动;开关灰度回滚;核心功能白名单硬编码 |
| 3:30–4:30 | 金融约束 | 风控不可随意降;降完补数据;压测与演练前置 |
| 4:30–5:00 | 收尾 | “一句话:降级是资源再分配,降体验不降安全,降完要闭环” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 流量倍数 | 平时 10 倍 | 活动峰值 |
| 自动降级触发 | 错误率 >1% 或 RT P99 >1s | 可配置 |
| 开关生效时间 | 秒级(配置中心推送) | 必须预置 |
| 灰度比例 | 先 1% 观察再全量 | 防开关配错 |
| 核心功能白名单 | 转账/扣款/记账/登录/核心风控 | 代码层不可降 |
| 降级补数据 | 恢复后 24h 内 | 积分/优惠券补发 |
| 演练频率 | 每季度至少 1 次 | 预案有效性验证 |
【追问链】(三层)
L1|“降级开关配错了把核心功能关了怎么办?” → 核心功能开关在代码层硬编码为不可降,配置中心改不了;或需双人审批+灰度发布+自动校验(配置变更后比对白名单)。
L2|“降级期间客户投诉怎么办?” → 前端明确提示(“系统繁忙,推荐暂时关闭”);客服话术预案;事后主动补偿(积分/优惠券)。关键是不静默降级——用户要知道发生了什么。
L3|“能不能把风控也降了换性能?” → 不能。风控是资金安全防线,活动期正是攻击者活跃期。非核心风控规则可降但需审批+事后加强审计;核心风控(转账反欺诈)绝不可降。降级降的是体验,不是安全。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道用熔断器、限流 |
| 80 分 | 先业务分级;四类降级手段;开关灰度回滚 |
| 95 分 | 论证“降体验不降安全”(风控不可随意降);核心功能白名单硬编码;降完补数据的闭环;压测演练前置;自动+手动互补 |
【关联题】
- 互联网版原型: 第 22 题(降级兜底)——本题是金融口径改造
- 高可用登录: 第 209 题——依赖降级的具体场景
- CAP 落地: 第 212 题——分级混用的理论基础
- 对账闭环: 第 219 题——降级后补数据的最终保障
【自测】
- 为什么降级前必须先做业务分级? 参考答案:没有分级,降级变成随机丢弃,可能把转账也降了。P0/P1/P2 是资源紧张时的优先级决策。
- 风控能不能降级? 参考答案:核心风控不可降;非核心风控可降但需审批+白名单+事后加强审计。降级降体验不降安全。
- 降级后为什么要补数据? 参考答案:积分少了是投诉,埋点丢了是数据缺口。降级是临时手段,业务语义必须最终完整。
218. 跨行转账跨 5 个系统,怎么保证钱不错(金融分布式事务)
【考察内容】金融分布式事务方案选型
【题目】一笔跨行转账要经过“本行账户扣款 → 本行记账 → 人行清算系统 → 他行入账 → 结果回执”5 个环节,涉及 5 个系统。请设计事务方案,保证钱不错。
【参考答案】
先明确约束:跨机构转账不能要求全局强一致事务(对方系统不受你控制),只能用最终一致 + 对账;
本行内部(可控部分):主路径要按题干的「5 个系统」来答——扣款与记账分属两个系统,跨服务只能 TCC;「用本地事务保证原子」只是特例(当且仅当两者同库同事务时最省,成本最低),答题要先说明这个前提再提它,别把它当通用方案;跨服务用 TCC——Try(冻结额度)→ Confirm(实际扣减 + 记账)→ Cancel(解冻)。资金类场景 TCC 比 Saga 更合适,因为冻结机制天然防止“钱花了但没转出去”;或用本地消息表 + MQ;
跨机构(不可控部分):状态机驱动——转账单有明确状态(已受理 → 已发出 → 对方已入账 → 已完成 / 已退回),每个环节幂等推进;超时未收到回执 → 进入查询-重试-冲正流程;长时间未确认 → 挂起并进人工处理队列;
最终兜底:日终对账——与人行清算系统、他行逐笔核对;不平的进差错处理流程;
关键细节:全链路唯一业务流水号贯穿 5 个系统;每步操作幂等;资金冻结有超时自动解冻。
容量估算: 跨行转账跨系统:典型状态机(受理→人行/网联→账务→通知)。超时与对账窗口:人行支付系统运行时段外只能预约。分布式事务不选 2PC 打满所有系统,金融常用本地事务+消息+对账+差错处理。峰值按代发工资日、节假日前后估算。
失败与降级: 中间机构失败:转账挂起进“处理中”,禁止自动重复扣款;超时未明确结果的查询状态接口+人工/自动查询查复。账务系统以“最终账”为准,渠道结果靠对账补齐。客户侧要展示明确处理中状态,避免投诉升级。
【原理溯源】
为什么跨机构不能用 2PC/TCC 这类强一致事务? 强一致事务要求所有参与方在同一事务管理器协调下。跨机构时对方系统不受你控制:你无法让对方“执行但不提交”,也无法在对方超时时强制回滚。技术上不可行,组织上也不允许——跨机构只能约定接口协议+对账。
为什么资金场景 TCC 比 Saga 更合适? Saga 用补偿事务回滚:钱已经扣了,失败时再转回来——中间窗口内客户余额少了,若补偿失败则资金悬空。TCC 的 Try 阶段只冻结不扣减:余额显示“冻结中”,Confirm 才真正扣,Cancel 解冻。冻结期间钱还在客户账上(只是不可用),Cancel 失败可重试,超时自动解冻——资金不会凭空消失。
为什么状态机是跨机构协作的正确模型? 跨机构没有分布式锁、没有全局事务,只能靠“双方各自推进状态+异步通知”。明确的状态机让每个参与方知道“当前在哪一步、下一步该干什么、超时了怎么处理”。每步幂等,重复通知不会导致重复入账。
为什么日终对账是最终兜底而非可选项? 运行时的所有机制(TCC、状态机、重试)都可能失效:网络分区丢消息、对方系统 bug、人工误操作。对账是独立于运行时的最终防线。金融系统的可靠性哲学是分层设防,对账是最外层。
为什么全链路唯一流水号至关重要? 5 个系统各自记账,没有统一标识就无法关联“这 5 条记录是同一笔转账”。对账时要靠流水号匹配;排查时要靠流水号串联;冲正时要靠流水号定位。流水号是跨系统的“主键”。
【选型判断树】
跨行转账事务方案,按可控性分层:
├─ 本行单库(扣款+记账)
│ → 本地 ACID 事务
├─ 本行跨服务
│ → TCC(Try冻结→Confirm扣减→Cancel解冻)
│ → 或本地消息表 + MQ
│ → 资金场景优先 TCC:冻结防资金悬空
├─ 跨机构(不可控)
│ → 状态机驱动:已受理→已发出→对方入账→完成/退回
│ → 超时:查询-重试-冲正
│ → 长时间未确认:挂起+人工队列
└─ 最终兜底
└─ 日终对账 + 差错处理(自动冲正/人工双人复核)
硬约束:全链路唯一流水号;每步幂等;冻结超时自动解冻。【口述骨架】(5 分钟;框架 B,骨架共性见文首第二节,本题只写特定内容)
| 时间 | 任务 | 内容 |
|---|---|---|
| 0:00–0:30 | 定性 | “跨机构无法强一致,只能最终一致+对账” |
| 0:30–1:30 | 本行内部 | 单库本地事务;跨服务 TCC(冻结防悬空)或本地消息表 |
| 1:30–2:30 | 跨机构 | 状态机驱动;超时查询重试冲正;挂起人工 |
| 2:30–3:30 | 对账兜底 | 日终与人行/他行逐笔核对;差错处理闭环 |
| 3:30–4:30 | 关键细节 | 全链路流水号;每步幂等;冻结超时解冻 |
| 4:30–5:00 | 收尾 | “一句话:可控部分 TCC,不可控部分状态机,最终靠对账” |
【关键数字】
| 参数 | 经验值 | 说明 |
|---|---|---|
| 环节数 | 5 个系统 | 扣款→记账→人行→他行→回执 |
| TCC 超时解冻 | 15–30 分钟 | 自动 Cancel 兜底 |
| 状态机超时查询 | 发出后 30s–5min 无回执则查询 | 可配置 |
| 挂起人工阈值 | 24h 未终态 | 进人工队列 |
| 对账周期 | T+0 准实时 + T+1 日终 | 双保险 |
| 幂等键 | 全链路唯一业务流水号 | 贯穿 5 系统 |
| 差错人工复核 | 超阈值(如 5 万)双人复核 | 调账留痕;更细的三档分流(≤1000 自动、1000–5 万规则+抽检、>5 万双人)见第 219 题【关键数字】 |
【追问链】(三层)
L1|“TCC 的 Cancel 失败怎么办?” → Cancel 必须幂等且可重试;重试仍失败则告警进人工;冻结额度有超时自动解冻兜底——即使一切失败,钱最终回到客户账上。这正是 TCC 优于 Saga 的地方:资金不会悬空。
L2|“对方入账了但我方没收到回执怎么办?” → 状态机停在“已发出”,超时后主动查询对方状态;日终对账最终确认。若对方已入账而我方未知,对账时发现后补记“已完成”;重复入账由对方幂等键拦截。
L3|“为什么不用 Seata AT 模式?” → AT 依赖代理数据源自动解析 SQL 生成回滚日志,只能在同一种数据库、同一事务管理器下工作。跨机构时对方系统不受控,AT 根本接不进去。本行内部同构库可用 AT,但资金场景更推荐 TCC——冻结机制语义更清晰。
【评分标准】
| 档位 | 答案特征 |
|---|---|
| 60 分 | 知道用分布式事务、消息队列 |
| 80 分 | 识别“跨机构无法强一致”;本行 TCC/本地消息表,跨机构状态机+对账 |
| 95 分 | 论证 TCC 冻结优于 Saga 补偿(防资金悬空);日终对账是独立最终防线;全链路流水号贯穿;每步幂等;超时自动解冻 |
【关联题】
- 互联网版原型: 第 99–103 题(事务选型/2PC·3PC/TCC/Seata AT/Saga),锁类见第 104–105 题(分布式锁/RedLock)——本题是金融口径改造
- CAP 落地: 第 212 题——账务 CP 的具体实现
- 对账专题: 第 219 题(日终对账)——最终兜底的详细设计
- 幂等: 第 213 题(金融级幂等)——状态机每步幂等
【自测】
- 为什么跨机构不能用强一致事务? 参考答案:对方系统不受控,无法协调提交/回滚;技术上不可行,组织上也不允许。只能接口协议+对账。
- 为什么资金场景 TCC 比 Saga 好? 参考答案:TCC 的 Try 只冻结不扣减,Cancel 失败可重试+超时自动解冻,资金不悬空;Saga 先扣再补偿,补偿失败则资金悬空。
- 日终对账为什么是必须的? 参考答案:运行时机制(TCC/状态机/重试)都可能失效;对账是独立于运行时的最终防线,发现一切差异并走差错处理。