跳转到主要内容

标签: 架构

  • 银行核心系统建模随笔

    发布于 博客 327 字 2 分钟

    银行核心领域建模架构账务

    核心系统的模型看起来很朴素:账户、余额、交易、分录。真正做进去才发现,每一个概念背后都有一堆不能妥协的约束,而这些约束在需求文档里往往一个字都没写。这篇是零散的建模笔记,按我认为重要性排的顺序。 核心系统到底在建模什么 先把定位说清楚。核心系统建模的对象不是"业务流程",而是会计事实。 客户在手机上点一下转账,这是渠道行为;反洗钱要不要拦、限额够不够,这是风控与协议判断;而核心真正负责的只有一件事:在正确的会计日,把一组借贷平衡的分录准确地记下来,并保证余额永远等于分录累计的结果。 想清楚这一点 …

    核心系统的模型看起来很朴素:账户、余额、交易、分录。真正做进去才发现,每一个概念背后都有一堆不能妥协的约束,而这些约束在需求文档里往往一个字都没写。这篇是零散的建模笔记,按我认为重要性排的顺序。 核心系统到底在建模什么 先把定位说清楚。核心系统建模的对象不是"业务流程",而是会计事实。 客户在手机上点一下转账,这是渠道行为;反洗钱要不要拦、限额够不够,这是风控与协议判断;而核心真正负责的只有一件事:在正确的会计日,把一组借贷平衡的分录准确地记下来,并保证余额永远等于分录累计的结果。 想清楚这一点 …

  • 缓存设计的那些坑

    发布于 博客 132 字 1 分钟

    Redis缓存高并发架构

    缓存是最容易加的优化,也是最容易埋雷的优化。加的时候只要几行代码,出问题时往往是深夜的数据不一致或者数据库被打穿。 一致性:先更库还是先删缓存 这个问题的正确答案是先更新数据库,再删除缓存(Cache Aside)。但要理解它为什么仍然不完美。 先删缓存再更库的问题很直接:删完缓存到更库完成之间,另一个请求读到旧数据并把它写回缓存,之后缓存里就一直是脏数据。 先更库再删缓存也有极小概率出问题:读请求恰好在缓存失效后读到旧库值,且回写发生在删除动作之后。概率很低,但在高并发下不等于零。 …

    缓存是最容易加的优化,也是最容易埋雷的优化。加的时候只要几行代码,出问题时往往是深夜的数据不一致或者数据库被打穿。 一致性:先更库还是先删缓存 这个问题的正确答案是先更新数据库,再删除缓存(Cache Aside)。但要理解它为什么仍然不完美。 先删缓存再更库的问题很直接:删完缓存到更库完成之间,另一个请求读到旧数据并把它写回缓存,之后缓存里就一直是脏数据。 先更库再删缓存也有极小概率出问题:读请求恰好在缓存失效后读到旧库值,且回写发生在删除动作之后。概率很低,但在高并发下不等于零。 …

  • 分库分表不是银弹

    发布于 博客 72 字 1 分钟

    MySQL分库分表数据库架构

    分库分表在很多团队是一种默认操作:数据量一大就分。但它换来的收益很窄,付出的代价很宽,而且代价大多在上线半年后才开始显现。 先问要不要分 分库分表真正解决的问题只有两个:单表数据量导致的索引效率下降,以及单库写入的 IOPS 瓶颈。除此之外的问题它都解决不了,甚至会恶化。 在决定分之前,这几件事的性价比更高: 加索引或改查询。慢查询里相当大一部分是索引缺失或者写法导致索引失效,跟数据量无关。 冷热分离。交易流水表里九成查询集中在最近三个月。把历史数据归档到历史库,主表立刻瘦身,成本远低于分表。 …

    分库分表在很多团队是一种默认操作:数据量一大就分。但它换来的收益很窄,付出的代价很宽,而且代价大多在上线半年后才开始显现。 先问要不要分 分库分表真正解决的问题只有两个:单表数据量导致的索引效率下降,以及单库写入的 IOPS 瓶颈。除此之外的问题它都解决不了,甚至会恶化。 在决定分之前,这几件事的性价比更高: 加索引或改查询。慢查询里相当大一部分是索引缺失或者写法导致索引失效,跟数据量无关。 冷热分离。交易流水表里九成查询集中在最近三个月。把历史数据归档到历史库,主表立刻瘦身,成本远低于分表。 …

  • CQRS 与 Saga:复杂业务的读写分离与最终一致

    发布于 架构专栏

    CQRSSaga最终一致架构

    很多团队把 CQRS 当成「加个 MQ 同步数据」就完事,结果读模型对了、写模型乱了,长事务更无从下手。CQRS 解决「读写诉求不同」,Saga 解决「业务跨多个聚合却不能开大事务」,两者常一起出现但职责不同。 读写为什么要分家 写模型关心一致性与不变量,是一组小而强的聚合;读模型关心展示与组装,最好能直接查出前端要的 DTO。诉求冲突时强行共用一个模型,两边都不讨好。 account.withdraw(money); // 写侧:只做业务 repository.save(account); …

    很多团队把 CQRS 当成「加个 MQ 同步数据」就完事,结果读模型对了、写模型乱了,长事务更无从下手。CQRS 解决「读写诉求不同」,Saga 解决「业务跨多个聚合却不能开大事务」,两者常一起出现但职责不同。 读写为什么要分家 写模型关心一致性与不变量,是一组小而强的聚合;读模型关心展示与组装,最好能直接查出前端要的 DTO。诉求冲突时强行共用一个模型,两边都不讨好。 account.withdraw(money); // 写侧:只做业务 repository.save(account); …

  • Kafka 在交易系统的削峰与解耦

    发布于 博客 83 字 1 分钟

    Kafka消息队列交易系统架构

    交易系统引入 Kafka 通常出于两个动机:扛住流量尖峰,以及把非核心逻辑从主链路上摘下来。这两件事它都能做,但做法和注意点完全不同。 削峰:把洪峰变成队列 代发工资、批量还款、营销活动这些场景的流量特征是极不均匀——平时每秒几百笔,活动开始瞬间冲到几万笔。数据库扛不住的不是总量,是瞬时并发。 削峰的本质是用延迟换稳定:请求先落 Kafka,下游按自己的处理能力匀速消费。关键是消费端要限速,不能拿到消息就火力全开打数据库。 spring: kafka: consumer: group-id: …

    交易系统引入 Kafka 通常出于两个动机:扛住流量尖峰,以及把非核心逻辑从主链路上摘下来。这两件事它都能做,但做法和注意点完全不同。 削峰:把洪峰变成队列 代发工资、批量还款、营销活动这些场景的流量特征是极不均匀——平时每秒几百笔,活动开始瞬间冲到几万笔。数据库扛不住的不是总量,是瞬时并发。 削峰的本质是用延迟换稳定:请求先落 Kafka,下游按自己的处理能力匀速消费。关键是消费端要限速,不能拿到消息就火力全开打数据库。 spring: kafka: consumer: group-id: …

  • 分布式事务在金融场景的取舍

    发布于 博客 92 字 1 分钟

    分布式事务一致性金融架构

    金融系统聊分布式事务,很容易陷入"选哪个框架"的讨论。但真实的约束是:钱不能错,且必须能查清为什么错。这两条决定了方案选择的顺序和别的行业不太一样。 金融场景的真实约束 不允许静默不一致。可以暂时不一致,但必须有机制发现并修复,不能出现谁也不知道差在哪的状态。 必须能人工干预。任何自动补偿机制都有失效的可能,最终要留一条人工处理通道,且要有审计留痕。 联机响应时间有硬指标。核心交易通常要求 100ms 内,多阶段协调的额外网络往返基本吃不下。 补偿不总是可行。已经出账到人民银行的报文没法回滚,只 …

    金融系统聊分布式事务,很容易陷入"选哪个框架"的讨论。但真实的约束是:钱不能错,且必须能查清为什么错。这两条决定了方案选择的顺序和别的行业不太一样。 金融场景的真实约束 不允许静默不一致。可以暂时不一致,但必须有机制发现并修复,不能出现谁也不知道差在哪的状态。 必须能人工干预。任何自动补偿机制都有失效的可能,最终要留一条人工处理通道,且要有审计留痕。 联机响应时间有硬指标。核心交易通常要求 100ms 内,多阶段协调的额外网络往返基本吃不下。 补偿不总是可行。已经出账到人民银行的报文没法回滚,只 …

  • DDD 在银行核心系统的落地实践

    发布于 博客 373 字 2 分钟

    DDD银行核心架构领域建模

    银行核心系统是一类很特殊的软件:它的业务规则几十年没怎么变,但承载它的代码每隔七八年就要重写一次。我参与过一次核心的部分重构,也旁观过一次彻底失败的重写。DDD 在这两次里都被提过,但只有一次真的起了作用。这篇把我认为有效的部分和纯属自我感动的部分分开写。 为什么银行核心需要 DDD 老核心的问题从来不是技术栈老。COBOL 写的联机交易照样能扛住每秒几千笔。真正的问题是:业务知识只存在于少数几个人的脑子里,代码里找不到对应的表达。 一段典型的老代码长这样:把交易码、账户类型、产品编号、机构号混 …

    银行核心系统是一类很特殊的软件:它的业务规则几十年没怎么变,但承载它的代码每隔七八年就要重写一次。我参与过一次核心的部分重构,也旁观过一次彻底失败的重写。DDD 在这两次里都被提过,但只有一次真的起了作用。这篇把我认为有效的部分和纯属自我感动的部分分开写。 为什么银行核心需要 DDD 老核心的问题从来不是技术栈老。COBOL 写的联机交易照样能扛住每秒几千笔。真正的问题是:业务知识只存在于少数几个人的脑子里,代码里找不到对应的表达。 一段典型的老代码长这样:把交易码、账户类型、产品编号、机构号混 …