与内容方向,生成的摘要如下:,记录了一名程序员对于“链动小铺”项目后端的反思与技术重建过程,最初因赶工与设计缺陷,系统频繁崩溃、接口响应超时、数据并发严重混乱,几乎陷入不可用状态,程序员坦言,自己曾轻视了代码规范与架构设计,直到线上事故频发、团队信任崩塌,才被迫直面混乱的代码库,通过重构核心数据库、引入分布式缓存、优化慢查询与异步任务处理,系统逐步恢复稳定,最终支撑了日均百万级并发请求,这段经历不仅是一次技术上的救赎,更是一次职业态度的深层忏悔——代码不仅是工具的堆砌,更是责任的延伸。
凌晨三点,我盯着屏幕上的错误日志,第37次重启了那个该死的服务,创业公司的CTO兼运维兼后端兼测试,这个头衔听起来挺唬人,实际上就是一个人干四个人的活,而此刻,我最想干的事情是把发卡网平台链动小铺的整个后端架构扔进碎纸机。

“老板,服务器又崩了。”
这不是我第一次在凌晨三点发这条消息了,老板大概已经习惯了,他上次回我:“没事,等用户骂够了我们再修复。”但这次不一样,链动小铺上线才一个月,日活从0飙到了3000,而我们的后端架构还停留在单机模式,连个负载均衡都没有,就像一个三轮车的引擎被塞进了法拉利的车身,一脚油门下去,不光冒烟,还得炸缸。
一切的开始,是从一张Excel表格开始的
做发卡网平台链动小铺的初衷很简单:让每个人都能轻松开店卖卡,音视频会员、游戏点卡、话费充值,什么都能卖,用户注册后自动生成一个小铺,可以分享给朋友,朋友下单后自动分佣,听起来是不是很牛?但最初的架构,说出来你可能不信,就是一个PHP文件加一个MySQL数据库,连Redis都没有。
那时候我们还在用最原始的方式处理并发:用户下单 -> 写数据库 -> 检查库存 -> 扣库存 -> 返回结果,一旦两个用户同时下单,一个成功率只有50%的“幸运大转盘”就开始了,更可笑的是,我们连分布式锁都没用,直接靠MySQL的行锁硬扛,你能想象吗?高峰期的时候,一张库存为100的卡,两个用户同时下单,最后我们卖了103张。
这是唯一一次老板希望用户“不要买太多”的时候。
重构,从“人肉负载均衡”开始
事情发生转机是在一个暴雨天的下午,服务器第三次被打挂后,我终于忍不住了,把全部代码push到了一个新分支,开始重构,重构这个词听起来高大上,实际上就是:我承认之前的代码是坨屎,我要重新拉一坨。
第一步,解决用户激增问题,我们用上了Nginx做反向代理,后端服务拆成多个实例,但光有实例不够,得有服务发现,当时我们人手不够,连个配置中心都没有,我就写了一个简单的Python脚本来动态更新Nginx配置,每次上线新实例,手动运行一下脚本,然后reload Nginx,这个方法被团队成员戏称为“人肉负载均衡器”。
但说实话,人肉负载均衡器虽然土,但在用户规模3000的时候完全够用,真正让我头疼的是库存扣减的问题。
库存扣减,一个看似简单的问题
链动小铺的核心逻辑是:用户下单 -> 扣库存 -> 支付 -> 发货,扣库存这一步,看起来简单,实际上坑多得能把程序员活埋,最开始我们用数据库行锁,但这玩意儿在高并发下就是灾难,锁等待、死锁、性能下降,样样齐全。
后来聪明了,引入了Redis,库存放在Redis里,用Lua脚本保证原子性,下单流程变成了:Redis扣减库存 -> 生成订单 -> 异步处理支付,这样就把数据库的压力降下来了,并发能力从几十提升到几百。
但问题又来了:Redis挂了怎么办?库存数据丢了怎么办?这就引出了一个经典问题——库存一致性,为了解决这个问题,我们设计了一个双重校验机制:先用Redis快速扣减,然后异步同步到数据库,再做一个定时任务进行对账,如果一个订单Redis扣了库存,但数据库没同步到,定时任务会发现并补偿。
听起来很美好对吧?但实际跑起来,各种边界条件能把人恶心死,比如用户支付超时,我们需要回滚库存;用户支付成功,但发货失败,也要回滚库存,每个场景都像是一个隐藏的地雷,踩到就是事故。
分佣系统的“数学题”
如果说库存扣减是技术问题,那分佣系统就是一个纯数学问题,链动小铺的分佣层级有多复杂呢?用户A邀请用户B,B再邀请用户C,C卖了一张卡,A抽佣1%,B抽佣1.5%,平台抽佣1.5%,这个看起来简单的计算,在订单量大的时候会演变成一个噩梦。
最开始我们用的是同步计算:用户下单后,立即计算所有层级的分佣,然后写入数据库,结果可想而知,一个订单的分佣计算可能要遍历几千条用户关系数据,响应时间从50ms飙升到5s,用户在下单页面卡得怀疑人生。
后来我们改成了异步计算,订单创建后,发一个消息到消息队列,由后台worker处理分佣计算,为了避免重复计算,我们给每个订单生成了一个唯一的分佣任务ID,用Redis记录任务状态。
这还没完,分佣还有一个结算周期的问题,链动小铺的分佣规则是:订单确认收货后,分佣才有效,这意味着分佣计算不能在下单时完成,而要在用户点击“确认收货”的时候触发,我们又加了一层状态机:下单 -> 库存扣减成功 -> 支付成功 -> 确认收货 -> 分佣结算,每个状态之间的转换都需要保证原子性和一致性,说到这里,我都替自己心疼。
故事的高潮:双十一那天的考验
经过三个月的重构和优化,链动小铺的后端架构总算勉强能看了,Redis集群、MySQL读写分离、RabbitMQ消息队列、Nginx + Keepalived高可用,该有的都有了,但真正的考验来得出乎意料。
双十一那天,一个平台大V在群里推广了链动小铺,瞬间涌入了8000活跃用户,要知道我们之前预估的最高并发是5000,监控面板上,QPS从500飙到2000,然后又飙到5000,我的心脏跟心电图一样,跟着波形上下跳动。
最先扛不住的是Redis,因为库存扣减太频繁,单个实例的CPU直接打满,幸亏我们提前做了Redis集群,主从切换后,流量被分散到了三个节点上,但紧接着,MySQL开始告警:连接数太多,我们开启了连接池,但数据库节点还是扛不住写压力。
紧急时刻,我们临时启用了读写分离:读请求走从库,写请求走主库,我们把不需要实时反馈的分佣计算任务全部暂停,等高峰期过了再处理,这个决定是对的,虽然大V用户的分佣到账时间从5分钟变成了2小时,但至少系统没有挂掉。
双十一结束,链动小铺的后端一共处理了3.2万笔订单,库存扣减零事故,分佣计算零差错,老板在群里发了个红包,说“今晚庆功”,我回了句:“请把服务器扩容的预算批了。”他这次没有再犹豫。
一些没写在PPT里的教训
现在链动小铺的日活已经稳定在1万左右,后端架构也从最初的“一个人一台服务器”变成了“一个团队十台服务器”,回头看看这一段重构经历,有几点教训是写在PPT里绝对看不到的:
第一,不要过度设计,初期200用户的时候,你搞Redis集群、消息队列、分布式事务,除了给自己增加复杂度外没有任何意义,用户量增长是螺旋式的,架构演进也应该是渐进的。
第二,库存扣减是所有发卡网平台的核心底线,你可以忍受页面加载慢,可以忍受分佣到账晚,但绝对不能出现库存多卖或者少卖,一旦信任崩塌,用户会说走就走。
第三,分佣系统不是技术问题,而是信任问题,用户选择链动小铺,是因为他们相信分佣规则是公平的、准确的,所以分佣计算的每一步都必须可审计、可追溯,我们为所有分佣记录都生成了不可篡改的MD5摘要,用户有任何疑问,前台就能查到完整的分佣链路。
如果不是纯技术挑战,一个人搞后端真的会猝死,我们后来招了两个后端工程师,一个专门负责库存和订单,一个专门负责分佣和结算,我之前写的那些烂代码,也终于有人接手了——不对,是有人帮忙重构了。
写在最后
链动小铺的故事还在继续,昨天老板跟我说,他打算接入海外支付,让用户可以在海外卖卡,我苦笑着打开新需求文档,发现里面写着“预计用户量增长50%”,看来,新一轮的重构,又要开始了。
但这次我不再害怕了,因为我知道,一个灵活的架构,就像一个有生命力的机体,需要不断迭代、进化、优化,而链动小铺的后端,大概就是我写过的最有灵魂的代码了——虽然它经历过的崩溃比我写过的bug还多。
如果你也是发卡网平台的开发者,或者在创业公司独自扛着后端,欢迎来聊聊技术问题,毕竟,在这个领域,我们都是战友,至少,我们都有一个梦想:下次双十一,千万别再崩了。
本文链接:https://www.ncwmj.com/news/10489.html
