今天简单聊聊订单系统每秒处理10万订单数据的架构,首先说明,是峰值10w,不是持续性的啊,其次我不曾有写过或者设计过这么大请求的系统,所以下面写的都是的一点简单看法吧,先看下物理架构图。谈谈架构_分库分表
记得前些天公司评审我这边写的一个后端框架,当时确实有点惊讶,都是10年以上经验的高开和技术总监,评审的内容不是底层原理、扩展性等,居然是对象之间怎么调用。下面简单说明下上面的物理架构吧,10w订单数据,姑且估算7w检索信息,3w订单写入信息。设计层面这里我保守一点,做了个nginx/lvs的负载集群和高可用集群,负载集群通过dns-server实现,高可用集群通过keepalived组件实现,这样单台nginx的流量峰值就是5w,我这里是保守哈,因为我没做过订单系统,不了解业务,有那么一句话,脱离业务谈qps实属装逼耍流氓,同时在代理层面做了布隆过滤器,通过lua脚本实现,这里主要是在入口层做一次过滤减压,静态资源做了cdn网络加速。再往后就是k8s集群,这里主要是借助k8s强大的服务生命周期管理和简单服务治理能力,k8s集群对外通过nginx-ingress网关接入,网关后面跑的就是一个个真实的应用服务,服务于服务之间通信通过rpc过程调用,当然这里面会有很多细节,不讨论。后面还有一个普罗米修斯,这里我把它画出来主要是体现结合HPA做pod的动态扩缩容,直白点就是当现有pod压力过大,超过一定阈值,由k8s集群自动根据负载扩缩容pod,好了,从入口到web服务,10w每秒的处理能力应该是能满足了,即便是有更高的负载100w请求压过来,代理和网关都可以做粗细管道和限流,当然前提还有带宽,一般的企业没有这么大请求量。
接下来是最麻烦的一环数据持久化问题,这也是系统最大的瓶颈。10w每秒的数据库操作,应该说在任何一个独立的关系数据库都无法完成,所以这里个人采用的是分库分表+读写分离。
水平分库分表,分库采用hash取模+1的算法并且以2的倍数进行扩容,如1-2-4-8-16以此类推,分表采用个位数%10的方式(每个库的分表为10个,编号0-9),这种分库分表方式在后续进行动态扩容时只需要进行表级数据迁移,不需要行级数据迁移。如现在订单库有1个db1,需要扩容到2个db1、db2,以id10001为例,原来这个id在db1(table1),现在扩容到两个库,10001id被落库到了db2(table1),所以我只需要迁移table1的所有数据到db2即可。
id生成,建议两个方案吧,snowflake分布式id,或者redis原子生成,两个方案都可以,redis支持一次区间取,snowflake一毫秒可以生成4096个id,所以不管哪种方案,都是满足的。
读写分离,数据一致性问题,对于实时性要求并不是那么严苛的话,这部分读请求流量全部打入到读库,对于实时性要求极高的需求,这小部分流量还是需要打到mysql写库。
数据同步,两种方案吧,1.mysql备份库,通过mysql同步机制实现,这里有个细节,如果没有做库表分片,千万上亿级数据还是压力蛮大的。2.elasticsearch,es天生分布式并且自带hash分片算法,高吞吐,随意动态扩缩容。实现方案canal+rabmq,canal是阿里开源中间件,原理跟mysql主从同步差不多,监听binlog日志文件,但是canal开发人员可以写业务逻辑代码,实现es库和mysql的同步操作。当然这里最好是用mq中间件解耦,canal只需要往mq写,其他业务只需订阅mq消息即可。
大概的设计就写这么多吧,整体主要是围绕10w每秒的数据处理,当然业务处理还有很多细节,也很复杂,我简单看了下一个订单的创建需要交互N个服务,商品服务、运营、会员、仓储、物流等等。技术细节同样也非常复杂,如垂直分表、数据一致性、全局分布式锁、缓存、接口幂等、分布式事务等等,暂时就写这么多吧,真实的架构是需要做数据计算的,而不是简单的我要10wqps或者tps给我一个架构,架构没有银弹,需要不断试错重构。