[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"consumer-news-detail-879":3,"consumer-news-interaction-879":41,"consumer-news-related-879":44},{"detail":4,"item":36},{"card":5,"schemaVersion":23,"fields":24,"content":30},{"id":6,"kind":7,"targetType":8,"targetId":9,"subtype":7,"typeLabel":10,"title":11,"subtitle":12,"summary":13,"coverUrl":14,"badgeText":15,"href":16,"sourceName":12,"meta":17,"metrics":20,"tags":21,"resolved":22},"NEWS_ARTICLE:879","news","NEWS_ARTICLE",879,"资讯","高并发下的抢红包设计：微信红包背后的算法与温情","博客园","本文拆解微信拼手气红包高并发实现，详解核心**二倍均值算法**，解决红包分配公平性问题。架构上依靠 Redis+Lua 脚本实现原子抢红包，规避超发与重复抢夺；结合 MQ 异步落库、热点 key 拆分、多层限流、定时对账兜底，支撑百万 QPS。给出 Java、Lua 核心源码，梳理超发、缓存一致性、","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F739056\u002F202609\u002F739056-20260901095345589-1940958188.png","","\u002Fnews\u002F879",[18,19],"2026","综合技术",{},[19],true,"consumer-content-detail-v1",{"sourceName":12,"authorName":25,"categoryName":19,"summary":13,"description":13,"publishTime":26,"updateTime":27,"sourceUrl":28,"language":29},"Rain的Java大神实战圈","2026-09-01T09:57","2026-09-02T20:37:52","https:\u002F\u002Fwww.cnblogs.com\u002Fzrui-xyu\u002Fp\u002F22785779","中文",{"format":31,"policy":32,"normalized":22,"html":33,"text":34,"wordCount":35,"hasBody":22},"HTML","NEWS_CONTENT_V1","\u003Ch2>高并发下的抢红包设计：微信红包背后的算法与温情\u003C\u002Fh2>\n\u003Cp>拼手气红包追求的是\u003Cstrong>随机公平\u003C\u002Fstrong>和\u003Cstrong>惊喜感\u003C\u002Fstrong>，不能简单粗暴地随机，否则会出现第一个人抢走大部分钱的尴尬。\u003C\u002Fp>\n\u003Cp>整体核心思路是「\u003Cstrong>缓存扛峰值、异步提性能、最终一致保数据\u003C\u002Fstrong>」\u003C\u002Fp>\n\u003Ch2>核心需求边界拆解\u003C\u002Fh2>\n\u003Cp>先明确场景的核心约束，避免设计偏离：\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003Cstrong>分类\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>核心要求\u003C\u002Fstrong>\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>功能需求\u003C\u002Ftd>\n   \u003Ctd>发红包、抢红包、拆红包、24h 未领自动原路退款\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>非功能需求\u003C\u002Ftd>\n   \u003Ctd>百万级 QPS 高并发支撑、零超发、毫秒级响应、防重复抢 \u002F 作弊\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2>红包核心算法：二倍均值法（微信同款）\u003C\u002Fh2>\n\u003Cp>经典的实现是 \u003Cstrong>二倍均值法\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cp>🔴 这是红包设计的灵魂，也是 “温情” 的底层逻辑，公式如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>单次抢到金额 = 随机区间 (0.01, 剩余金额 ÷ 剩余人数 × 2)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>这样既能保证金额随机波动，又能避免最后的人分到 0 元，数学上保证了所有人的期望值相等。\u003C\u002Fp>\n\u003Ch3>算法特点\u003C\u002Fh3>\n\u003Col>\n \u003Cli>保底 1 分钱，不会出现抢到 0 元的尴尬体验\u003C\u002Fli>\n \u003Cli>每一轮的均值相等，不会出现 “前面人拿大头，后面全是 1 分” 的极端情况，公平性强\u003C\u002Fli>\n \u003Cli>实现简单、计算性能极高，完全适配高并发场景\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>示例演示（100 元 10 个红包）\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003Cstrong>轮次\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>剩余人数\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>剩余金额\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>本次随机金额范围\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>单轮均值\u003C\u002Fstrong>\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>第 1 个\u003C\u002Ftd>\n   \u003Ctd>10\u003C\u002Ftd>\n   \u003Ctd>100 元\u003C\u002Ftd>\n   \u003Ctd>0.01~20 元\u003C\u002Ftd>\n   \u003Ctd>10 元\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>第 2 个\u003C\u002Ftd>\n   \u003Ctd>9\u003C\u002Ftd>\n   \u003Ctd>假设剩余 92 元\u003C\u002Ftd>\n   \u003Ctd>0.01~20.44 元\u003C\u002Ftd>\n   \u003Ctd>10.22 元\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>第 3 个\u003C\u002Ftd>\n   \u003Ctd>8\u003C\u002Ftd>\n   \u003Ctd>假设剩余 83 元\u003C\u002Ftd>\n   \u003Ctd>0.01~20.75 元\u003C\u002Ftd>\n   \u003Ctd>10.375 元\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>...\u003C\u002Ftd>\n   \u003Ctd>...\u003C\u002Ftd>\n   \u003Ctd>...\u003C\u002Ftd>\n   \u003Ctd>...\u003C\u002Ftd>\n   \u003Ctd>...\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>最后 1 个\u003C\u002Ftd>\n   \u003Ctd>1\u003C\u002Ftd>\n   \u003Ctd>剩余全额\u003C\u002Ftd>\n   \u003Ctd>固定值\u003C\u002Ftd>\n   \u003Ctd>-\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>最后一个人直接拿走剩余的钱。整个分配可以用一张流程图概括：\u003C\u002Fp>\n\u003Cp>\u003Cimg alt=\"image\" src=\"https:\u002F\u002Fi1.wp.com\u002Fimg2024.cnblogs.com\u002Fblog\u002F739056\u002F202609\u002F739056-20260901095345589-1940958188.png?w=720&amp;quality=65&amp;strip=all\">\u003C\u002Fp>\n\u003Cp>\u003Cstrong>👆 这就是拼手气的核心，简单且高效。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ch2>高并发整体架构设计: 快、准、稳\u003C\u002Fh2>\n\u003Cp>核心原则：\u003Cstrong>流量层层过滤，99% 的请求拦截在缓存层，尽量少打数据库\u003C\u002Fstrong>🛡️\u003C\u002Fp>\n\u003Cp>抢红包是极致的读多写多场景，必须保证\u003Cstrong>不超发、不少发、抢得顺滑\u003C\u002Fstrong>。核心思路：\u003Cstrong>读写分离、原子化扣减、异步持久化\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>┌─────────────── 接入层 ───────────────┐\n│  Nginx限流 + 网关鉴权 + 恶意请求拦截   │\n└───────────────┬─────────────────────┘\n┌─────────────── 服务层 ───────────────┐\n│  红包核心服务 + 账户服务 + MQ异步消费  │\n└───────────────┬─────────────────────┘\n┌─────────────── 缓存层 ───────────────┐\n│ Redis库存预扣 + 用户去重 + 热点Key拆分 │\n└───────────────┬─────────────────────┘\n┌─────────────── 数据层 ───────────────┐\n│  分库分表MySQL + 定时对账兜底任务      │\n└─────────────────────────────────────┘\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>核心流程与高并发优化点\u003C\u002Fh2>\n\u003Ch3>1. 发红包流程（低并发，侧重数据初始化）\u003C\u002Fh3>\n\u003Col>\n \u003Cli>校验红包金额、个数合法性，限制单红包最大个数 \u002F 金额\u003C\u002Fli>\n \u003Cli>生成唯一红包 ID，写入数据库生成红包主记录\u003C\u002Fli>\n \u003Cli>将红包剩余个数、剩余金额同步写入 Redis，设置 24h + 过期时间\u003C\u002Fli>\n \u003Cli>返回红包跳转链接\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch4>预分配+入缓存\u003C\u002Fh4>\n\u003Cul>\n \u003Cli>用户发起支付成功后，后端用 \u003Cstrong>二倍均值法\u003C\u002Fstrong> 提前把总金额拆分成 N 个小额。\u003C\u002Fli>\n \u003Cli>将这一组金额 \u003Cstrong>push 进 Redis 的 List\u003C\u002Fstrong>\u003Ccode>（RPUSH）\u003C\u002Fcode>，相当于红包池就绪。\u003C\u002Fli>\n \u003Cli>同时，把红包元信息（发送者、留言、总金额等）异步写入 MySQL，并缓存到 Redis Hash。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>关键\u003C\u002Fstrong>：金额预分配，抢的时候直接从 List 里 LPOP，极致轻量。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>2. 抢红包核心流程（高并发重灾区，性能核心）\u003C\u002Fh3>\n\u003Ch4>核心逻辑：同步链路只做 Redis 操作，所有 DB 操作全异步\u003C\u002Fh4>\n\u003Col>\n \u003Cli>第一层过滤：网关限流、用户黑名单校验，拦截刷接口的恶意请求\u003C\u002Fli>\n \u003Cli>第二层过滤：Redis 用SETNX做用户维度去重，同一个红包单用户只能抢 1 次\u003C\u002Fli>\n \u003Cli>第三层过滤：Redis 原子执行DECR 剩余库存，结果 &lt; 0 直接返回「手慢了」\u003C\u002Fli>\n \u003Cli>异步处理：库存扣减成功后发送 MQ 消息，异步生成红包明细、更新用户账户余额\u003C\u002Fli>\n \u003Cli>同步返回：前端直接展示抢到金额，明细数据后续懒加载\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>这是并发最高的环节，必须把所有核心判断和操作压缩到一个 \u003Cstrong>Redis Lua 脚本\u003C\u002Fstrong> 里，利用 Redis 单线程保证原子性。\u003C\u002Fp>\n\u003Cp>\u003Cimg alt=\"image\" src=\"https:\u002F\u002Fi1.wp.com\u002Fimg2024.cnblogs.com\u002Fblog\u002F739056\u002F202609\u002F739056-20260901095404339-1640235349.png?w=720&amp;quality=65&amp;strip=all\">\u003C\u002Fp>\n\u003Cp>\u003Cstrong>这个方案的精髓\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>原子性\u003C\u002Fstrong>：\u003Ccode>判断 + 扣库存 + 去重\u003C\u002Fcode> 在一个 Lua 脚本里完成，无锁无超发。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>抗并发\u003C\u002Fstrong>：直接操作内存，单机 Redis 轻松扛住 10W+ QPS，热点红包可上 Redis Cluster + 本地缓存。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>最终一致\u003C\u002Fstrong>：抢完立刻给用户反馈，写入 DB 走 MQ 削峰，保证数据不丢。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>🛠️ 扩展思考：如果红包金额极其巨大、拆包耗时，可以引入\u003Cstrong>拆包预分配的二阶段模式\u003C\u002Fstrong>，但微信普通红包场景下，实时二倍均值 + Redis List 已经足够。\u003C\u002Fp>\n\u003Ch3>3. 关键优化手段\u003C\u002Fh3>\n\u003Cul>\n \u003Cli>\u003Cstrong>原子扣库存防超发\u003C\u002Fstrong>：用 Redis 的DECR原子指令替代分布式锁，性能提升一个量级，从根源杜绝超发\u003C\u002Fli>\n \u003Cli>\u003Cstrong>热点 Key 拆分\u003C\u002Fstrong>：春晚级别的超级热点红包，把单个库存 Key 拆分为多个子 Key 分摊流量，解决单 Key 热点瓶颈\u003C\u002Fli>\n \u003Cli>\u003Cstrong>全链路异步化\u003C\u002Fstrong>：抢红包成功后的写库、余额变更、消息通知全部走 MQ，同步链路耗时控制在 10ms 以内\u003C\u002Fli>\n \u003Cli>\u003Cstrong>分库分表\u003C\u002Fstrong>：红包主表、明细表按红包 ID 哈希分库分表，支撑亿级数据存储与查询\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>数据一致性与兜底方案\u003C\u002Fh2>\n\u003Col>\n \u003Cli>\u003Cstrong>最终一致性保障\u003C\u002Fstrong>：MQ 消费失败自动重试，超过阈值进入死信队列人工干预，保证 Redis 扣减和 DB 数据最终对齐\u003C\u002Fli>\n \u003Cli>\u003Cstrong>定时对账兜底\u003C\u002Fstrong>：每日定时任务扫描过期红包，校对 Redis 与 DB 的库存、金额偏差，自动执行未领红包退款\u003C\u002Fli>\n \u003Cli>\u003Cstrong>服务降级\u003C\u002Fstrong>：峰值时降级非核心接口（比如红包排行榜、好友抢红包记录），优先保障抢红包主链路可用\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>算法里的 “温情” 设计\u003C\u002Fh2>\n\u003Cp>✨ 技术之外的用户体验细节：\u003C\u002Fp>\n\u003Col>\n \u003Cli>二倍均值算法保证公平性，不会让晚抢的人只能拿到 1 分钱，照顾所有参与者的情绪\u003C\u002Fli>\n \u003Cli>24 小时未领取的红包自动原路退回，无任何资金损耗\u003C\u002Fli>\n \u003Cli>保底 1 分钱机制，避免出现 “抢了个寂寞” 的负面体验\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>技术是骨架，情感是血肉\u003C\u002Fh3>\n\u003Cp>微信红包之所以成为现象级产品，绝不仅是代码写得好，更是把 \u003Cstrong>“钱”包装成了“情”\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>惊喜感\u003C\u002Fstrong> 🎁：拼手气金额的随机波动，激发“赌徒心理”，群聊里截屏“手气最佳”成了社交货币，系统还会给手气最佳的人自动带上👑标识，引发下一轮互动。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>仪式感\u003C\u002Fstrong> 🧨：专属红包封面、春节期间的动态封皮、输入金额时的“恭喜发财，大吉大利”默认留言，都在强化“这是心意，不是转账”的认知。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>低负担\u003C\u002Fstrong> 🤝：200元上限的设定非常巧妙，既降低了发送者的心理负担，也让抢到几分钱的人不会尴尬，反而创造“一分也是爱”的群内打趣氛围。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>即时满足\u003C\u002Fstrong> ⚡：高并发架构保证“点即开”的零等待体验，配合硬币掉落的音效和动画，把“领钱”的快乐瞬间放大。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>总结一下\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cp>这个场景设计的答案就是 \u003Cstrong>“算法保证公平，架构承载海量并发，而产品细节注入温度”\u003C\u002Fstrong>。单纯用 Redis 实现高并发不难，难的是让每一次点击都既稳定又充满人情味。\u003C\u002Fp>\n\u003Ch2>核心代码实现（技术亮点）💻\u003C\u002Fh2>\n\u003Ch3>1. 亮点 1：Redis 原子抢红包 Lua 脚本（核心中的核心）\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>技术亮点\u003C\u002Fstrong>：利用 Redis 单线程特性，将「用户抢红包去重 + 库存校验 + 金额扣减」三个操作封装为原子执行，完全摒弃分布式锁，性能提升一个量级，从根源杜绝超发、重复抢问题。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>-- 入参：KEYS[1]=红包库存Key  KEYS[2]=用户去重Key  KEYS[3]=红包剩余金额Key\n-- 入参：ARGV[1]=用户ID  ARGV[2]=最小金额(1分)  ARGV[3]=当前剩余人数\nlocal stockKey = KEYS[1]\nlocal userSetKey = KEYS[2]\nlocal remainAmountKey = KEYS[3]\nlocal userId = ARGV[1]\nlocal minAmount = tonumber(ARGV[2])\nlocal remainCount = tonumber(ARGV[3])\n\n-- 1. 校验用户是否已经抢过（去重）\nif redis.call('sismember', userSetKey, userId) == 1 then\n    return -1 -- -1代表重复抢\nend\n\n-- 2. 校验库存是否充足\nlocal stock = tonumber(redis.call('get', stockKey))\nif stock &lt;= 0 then\n    return 0 -- 0代表已抢光\nend\n\n-- 3. 二倍均值计算本次抢到的金额（最后一个直接拿剩余金额）\nlocal remainAmount = tonumber(redis.call('get', remainAmountKey))\nlocal currentAmount\nif remainCount == 1 then\n    currentAmount = remainAmount\nelse\n    -- 二倍均值公式：随机区间[1分, 剩余金额\u002F剩余人数 * 2]\n    local maxAmount = math.floor(remainAmount \u002F remainCount * 2)\n    currentAmount = math.random(minAmount, maxAmount)\nend\n\n-- 4. 原子扣减库存、扣减金额、标记用户已抢\nredis.call('decr', stockKey)\nredis.call('decrby', remainAmountKey, currentAmount)\nredis.call('sadd', userSetKey, userId)\n\n-- 返回抢到的金额（单位：分）\nreturn currentAmount\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>2. 亮点 2：二倍均值红包拆分算法 Java 实现\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>技术亮点\u003C\u002Fstrong>：统一以「分」为整数单位计算，完全规避浮点精度丢失问题，严格遵循微信红包的公平性规则。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>import java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\n\u002F**\n * 微信二倍均值红包拆分算法\n *\u002F\npublic class RedPacketAlgorithm {\n    \u002F\u002F 最小金额：1分\n    private static final int MIN_AMOUNT = 1;\n    private static final Random RANDOM = new Random();\n\n    \u002F**\n     * 拆分红包\n     * @param totalAmount 总金额（单位：分）\n     * @param totalCount 红包总个数\n     * @return 每个红包的金额列表（单位：分）\n     *\u002F\n    public static List&lt;Integer&gt; splitRedPacket(int totalAmount, int totalCount) {\n        List&lt;Integer&gt; amountList = new ArrayList&lt;&gt;(totalCount);\n        int remainAmount = totalAmount;\n        int remainCount = totalCount;\n\n        for (int i = 0; i &lt; totalCount - 1; i++) {\n            \u002F\u002F 二倍均值计算上限：剩余金额 \u002F 剩余人数 * 2\n            int max = (remainAmount \u002F remainCount) * 2;\n            \u002F\u002F 随机区间 [1, max]\n            int current = RANDOM.nextInt(max - MIN_AMOUNT + 1) + MIN_AMOUNT;\n            amountList.add(current);\n            \u002F\u002F 更新剩余值\n            remainAmount -= current;\n            remainCount--;\n        }\n        \u002F\u002F 最后一个红包直接拿剩余金额\n        amountList.add(remainAmount);\n        return amountList;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>3. 亮点 3：MQ 异步落库消费核心逻辑\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>技术亮点\u003C\u002Fstrong>：同步链路只走缓存，所有 DB 操作全异步削峰，把核心抢红包接口的 RT 控制在 10ms 以内，同时用本地消息表兜底可靠性。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F 抢红包成功后发送MQ消息\npublic void grabRedPacketSuccess(Long redPacketId, Long userId, Integer amount) {\n    \u002F\u002F 组装消息体：红包ID、用户ID、抢到金额、时间\n    RedPacketGrabMessage message = new RedPacketGrabMessage(redPacketId, userId, amount);\n    \u002F\u002F 异步发送RocketMQ，支持重试、死信队列兜底\n    rocketMQTemplate.asyncSend(\"redPacket_grab_topic\", message, new SendCallback() {\n        @Override\n        public void onSuccess(SendResult sendResult) {\n            \u002F\u002F 发送成功无额外处理\n        }\n        @Override\n        public void onException(Throwable e) {\n            \u002F\u002F 发送失败降级：本地消息表兜底重试\n            localMessageTable.save(message);\n        }\n    });\n}\n\n\u002F\u002F MQ消费者：异步落库+更新账户余额\n@RocketMQMessageListener(topic = \"redPacket_grab_topic\", consumerGroup = \"redPacket_consumer_group\")\npublic class RedPacketGrabConsumer implements RocketMQListener&lt;RedPacketGrabMessage&gt; {\n    @Override\n    public void onMessage(RedPacketGrabMessage message) {\n        \u002F\u002F 1. 写入红包明细表\n        redPacketDetailMapper.insertDetail(message);\n        \u002F\u002F 2. 更新用户账户余额（钱包服务）\n        accountService.addBalance(message.getUserId(), message.getAmount());\n        \u002F\u002F 3. 发送抢成功通知\n        notifyService.sendGrabSuccessMsg(message.getUserId(), message.getAmount());\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>核心技术难点与对应解决方案 🔧\u003C\u002Fh2>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003Cstrong>核心技术难点\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>解决方案\u003C\u002Fstrong>\u003C\u002Fth>\n   \u003Cth>\u003Cstrong>落地收益\u003C\u002Fstrong>\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>高并发下红包超发、用户重复抢的并发安全问题\u003C\u002Ftd>\n   \u003Ctd>1. Redis+Lua 脚本原子执行去重、扣库存、扣金额全流程2. 完全摒弃分布式锁，避免锁开销与死锁风险\u003C\u002Ftd>\n   \u003Ctd>从根源杜绝超发，单节点并发处理能力提升 10 倍以上\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>春晚级热点红包的单 Redis Key 性能瓶颈（单 Key QPS 上限约 10W）\u003C\u002Ftd>\n   \u003Ctd>1. 库存分片：将 1 个红包的总库存拆分为 N 个子库存 Key2. 请求按用户 ID 哈希路由到不同子 Key，流量打散3. 子库存抢光后自动迁移到其他分片兜底\u003C\u002Ftd>\n   \u003Ctd>热点 Key 承载能力线性提升，10 分片可支撑百万级 QPS\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>Redis 缓存与 MySQL 数据库的数据一致性问题\u003C\u002Ftd>\n   \u003Ctd>1. 采用最终一致性模型，缓存作为主写入口，DB 异步同步2. MQ 消费失败自动重试，超过阈值进入死信队列人工干预3. 每日定时对账任务，校对库存、金额偏差，兜底修正\u003C\u002Ftd>\n   \u003Ctd>数据一致性达 99.99%，极端异常可通过对账 100% 修复\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>峰值流量冲击导致服务雪崩、数据库被打垮\u003C\u002Ftd>\n   \u003Ctd>1. 多层限流：网关层限流、接口级限流、用户维度限流2. 全链路异步化：非核心逻辑全部 MQ 异步削峰3. 服务降级：峰值关闭排行榜、领取记录等非核心接口\u003C\u002Ftd>\n   \u003Ctd>数据库压力降低 99%，系统峰值稳定性大幅提升\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>亿级红包明细数据的存储与查询性能问题\u003C\u002Ftd>\n   \u003Ctd>1. 分库分表：按红包 ID 哈希分库分表，分散存储压力2. 冷热数据分离：超过 7 天的历史红包数据归档到冷存储3. 明细查询优先走缓存，DB 只做兜底和对账\u003C\u002Ftd>\n   \u003Ctd>单表数据量控制在千万级，查询性能稳定在毫秒级\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>归纳起来就是一句话：\u003C\u002Fp>\n\u003Cp>\u003Cstrong>用 Redis 抗住读写的瞬时洪峰，用 MQ 平滑落地，用 Lua 保证原子性，用整数运算规避精度，最后加上风控兜底。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Chr>\n\u003Cp>公众号“Rain的Java大神之路”\u003Cbr>\n  个人博客“www.javadashen.com”\u003C\u002Fp>","高并发下的抢红包设计：微信红包背后的算法与温情 拼手气红包追求的是随机公平和惊喜感，不能简单粗暴地随机，否则会出现第一个人抢走大部分钱的尴尬。 整体核心思路是「缓存扛峰值、异步提性能、最终一致保数据」 核心需求边界拆解 先明确场景的核心约束，避免设计偏离： 分类 核心要求 功能需求 发红包、抢红包、拆红包、24h 未领自动原路退款 非功能需求 百万级 QPS 高并发支撑、零超发、毫秒级响应、防重复抢 \u002F 作弊 红包核心算法：二倍均值法（微信同款） 经典的实现是 二倍均值法： 🔴 这是红包设计的灵魂，也是 “温情” 的底层逻辑，公式如下： 单次抢到金额 = 随机区间 (0.01, 剩余金额 ÷ 剩余人数 × 2) 这样既能保证金额随机波动，又能避免最后的人分到 0 元，数学上保证了所有人的期望值相等。 算法特点 保底 1 分钱，不会出现抢到 0 元的尴尬体验 每一轮的均值相等，不会出现 “前面人拿大头，后面全是 1 分” 的极端情况，公平性强 实现简单、计算性能极高，完全适配高并发场景 示例演示（100 元 10 个红包） 轮次 剩余人数 剩余金额 本次随机金额范围 单轮均值 第 1 个 10 100 元 0.01~20 元 10 元 第 2 个 9 假设剩余 92 元 0.01~20.44 元 10.22 元 第 3 个 8 假设剩余 83 元 0.01~20.75 元 10.375 元 ... ... ... ... ... 最后 1 个 1 剩余全额 固定值 - 最后一个人直接拿走剩余的钱。整个分配可以用一张流程图概括： 👆 这就是拼手气的核心，简单且高效。 高并发整体架构设计: 快、准、稳 核心原则：流量层层过滤，99% 的请求拦截在缓存层，尽量少打数据库🛡️ 抢红包是极致的读多写多场景，必须保证不超发、不少发、抢得顺滑。核心思路：读写分离、原子化扣减、异步持久化。 ┌─────────────── 接入层 ───────────────┐ │ Nginx限流 + 网关鉴权 + 恶意请求拦截 │ └───────────────┬─────────────────────┘ ┌─────────────── 服务层 ───────────────┐ │ 红包核心服务 + 账户服务 + MQ异步消费 │ └───────────────┬─────────────────────┘ ┌─────────────── 缓存层 ───────────────┐ │ Redis库存预扣 + 用户去重 + 热点Key拆分 │ └───────────────┬─────────────────────┘ ┌─────────────── 数据层 ───────────────┐ │ 分库分表MySQL + 定时对账兜底任务 │ └─────────────────────────────────────┘ 核心流程与高并发优化点 1. 发红包流程（低并发，侧重数据初始化） 校验红包金额、个数合法性，限制单红包最大个数 \u002F 金额 生成唯一红包 ID，写入数据库生成红包主记录 将红包剩余个数、剩余金额同步写入 Redis，设置 24h + 过期时间 返回红包跳转链接 预分配+入缓存 用户发起支付成功后，后端用 二倍均值法 提前把总金额拆分成 N 个小额。 将这一组金额 push 进 Redis 的 List（RPUSH），相当于红包池就绪。 同时，把红包元信息（发送者、留言、总金额等）异步写入 MySQL，并缓存到 Redis Hash。 关键：金额预分配，抢的时候直接从 List 里 LPOP，极致轻量。 2. 抢红包核心流程（高并发重灾区，性能核心） 核心逻辑：同步链路只做 Redis 操作，所有 DB 操作全异步 第一层过滤：网关限流、用户黑名单校验，拦截刷接口的恶意请求 第二层过滤：Redis 用SETNX做用户维度去重，同一个红包单用户只能抢 1 次 第三层过滤：Redis 原子执行DECR 剩余库存，结果 \u003C 0 直接返回「手慢了」 异步处理：库存扣减成功后发送 MQ 消息，异步生成红包明细、更新用户账户余额 同步返回：前端直接展示抢到金额，明细数据后续懒加载 这是并发最高的环节，必须把所有核心判断和操作压缩到一个 Redis Lua 脚本 里，利用 Redis 单线程保证原子性。 这个方案的精髓： 原子性：判断 + 扣库存 + 去重 在一个 Lua 脚本里完成，无锁无超发。 抗并发：直接操作内存，单机 Redis 轻松扛住 10W+ QPS，热点红包可上 Redis Cluster + 本地缓存。 最终一致：抢完立刻给用户反馈，写入 DB 走 MQ 削峰，保证数据不丢。 🛠️ 扩展思考：如果红包金额极其巨大、拆包耗时，可以引入拆包预分配的二阶段模式，但微信普通红包场景下，实时二倍均值 + Redis List 已经足够。 3. 关键优化手段 原子扣库存防超发：用 Redis 的DECR原子指令替代分布式锁，性能提升一个量级，从根源杜绝超发 热点 Key 拆分：春晚级别的超级热点红包，把单个库存 Key 拆分为多个子 Key 分摊流量，解决单 Key 热点瓶颈 全链路异步化：抢红包成功后的写库、余额变更、消息通知全部走 MQ，同步链路耗时控制在 10ms 以内 分库分表：红包主表、明细表按红包 ID 哈希分库分表，支撑亿级数据存储与查询 数据一致性与兜底方案 最终一致性保障：MQ 消费失败自动重试，超过阈值进入死信队列人工干预，保证 Redis 扣减和 DB 数据最终对齐 定时对账兜底：每日定时任务扫描过期红包，校对 Redis 与 DB 的库存、金额偏差，自动执行未领红包退款 服务降级：峰值时降级非核心接口（比如红包排行榜、好友抢红包记录），优先保障抢红包主链路可用 算法里的 “温情” 设计 ✨ 技术之外的用户体验细节： 二倍均值算法保证公平性，不会让晚抢的人只能拿到 1 分钱，照顾所有参与者的情绪 24 小时未领取的红包自动原路退回，无任何资金损耗 保底 1 分钱机制，避免出现 “抢了个寂寞” 的负面体验 技术是骨架，情感是血肉 微信红包之所以成为现象级产品，绝不仅是代码写得好，更是把 “钱”包装成了“情”。 惊喜感 🎁：拼手气金额的随机波动，激发“赌徒心理”，群聊里截屏“手气最佳”成了社交货币，系统还会给手气最佳的人自动带上👑标识，引发下一轮互动。 仪式感 🧨：专属红包封面、春节期间的动态封皮、输入金额时的“恭喜发财，大吉大利”默认留言，都在强化“这是心意，不是转账”的认知。 低负担 🤝：200元上限的设定非常巧妙，既降低了发送者的心理负担，也让抢到几分钱的人不会尴尬，反而创造“一分也是爱”的群内打趣氛围。 即时满足 ⚡：高并发架构保证“点即开”的零等待体验，配合硬币掉落的音效和动画，把“领钱”的快乐瞬间放大。 总结一下： 这个场景设计的答案就是 “算法保证公平，架构承载海量并发，而产品细节注入温度”。单纯用 Redis 实现高并发不难，难的是让每一次点击都既稳定又充满人情味。 核心代码实现（技术亮点）💻 1. 亮点 1：Redis 原子抢红包 Lua 脚本（核心中的核心） 技术亮点：利用 Redis 单线程特性，将「用户抢红包去重 + 库存校验 + 金额扣减」三个操作封装为原子执行，完全摒弃分布式锁，性能提升一个量级，从根源杜绝超发、重复抢问题。 -- 入参：KEYS[1]=红包库存Key KEYS[2]=用户去重Key KEYS[3]=红包剩余金额Key -- 入参：ARGV[1]=用户ID ARGV[2]=最小金额(1分) ARGV[3]=当前剩余人数 local stockKey = KEYS[1] local userSetKey = KEYS[2] local remainAmountKey = KEYS[3] local userId = ARGV[1] local minAmount = tonumber(ARGV[2]) local remainCount = tonumber(ARGV[3]) -- 1. 校验用户是否已经抢过（去重） if redis.call('sismember', userSetKey, userId) == 1 then return -1 -- -1代表重复抢 end -- 2. 校验库存是否充足 local stock = tonumber(redis.call('get', stockKey)) if stock \u003C= 0 then return 0 -- 0代表已抢光 end -- 3. 二倍均值计算本次抢到的金额（最后一个直接拿剩余金额） local remainAmount = tonumber(redis.call('get', remainAmountKey)) local currentAmount if remainCount == 1 then currentAmount = remainAmount else -- 二倍均值公式：随机区间[1分, 剩余金额\u002F剩余人数 * 2] local maxAmount = math.floor(remainAmount \u002F remainCount * 2) currentAmount = math.random(minAmount, maxAmount) end -- 4. 原子扣减库存、扣减金额、标记用户已抢 redis.call('decr', stockKey) redis.call('decrby', remainAmountKey, currentAmount) redis.call('sadd', userSetKey, userId) -- 返回抢到的金额（单位：分） return currentAmount 2. 亮点 2：二倍均值红包拆分算法 Java 实现 技术亮点：统一以「分」为整数单位计算，完全规避浮点精度丢失问题，严格遵循微信红包的公平性规则。 import java.util.ArrayList; import java.util.List; import java.util.Random; \u002F** * 微信二倍均值红包拆分算法 *\u002F public class RedPacketAlgorithm { \u002F\u002F 最小金额：1分 private static final int MIN_AMOUNT = 1; private static final Random RANDOM = new Random(); \u002F** * 拆分红包 * @param totalAmount 总金额（单位：分） * @param totalCount 红包总个数 * @return 每个红包的金额列表（单位：分） *\u002F public static List\u003CInteger> splitRedPacket(int totalAmount, int totalCount) { List\u003CInteger> amountList = new ArrayList\u003C>(totalCount); int remainAmount = totalAmount; int remainCount = totalCount; for (int i = 0; i \u003C totalCount - 1; i++) { \u002F\u002F 二倍均值计算上限：剩余金额 \u002F 剩余人数 * 2 int max = (remainAmount \u002F remainCount) * 2; \u002F\u002F 随机区间 [1, max] int current = RANDOM.nextInt(max - MIN_AMOUNT + 1) + MIN_AMOUNT; amountList.add(current); \u002F\u002F 更新剩余值 remainAmount -= current; remainCount--; } \u002F\u002F 最后一个红包直接拿剩余金额 amountList.add(remainAmount); return amountList; } } 3. 亮点 3：MQ 异步落库消费核心逻辑 技术亮点：同步链路只走缓存，所有 DB 操作全异步削峰，把核心抢红包接口的 RT 控制在 10ms 以内，同时用本地消息表兜底可靠性。 \u002F\u002F 抢红包成功后发送MQ消息 public void grabRedPacketSuccess(Long redPacketId, Long userId, Integer amount) { \u002F\u002F 组装消息体：红包ID、用户ID、抢到金额、时间 RedPacketGrabMessage message = new RedPacketGrabMessage(redPacketId, userId, amount); \u002F\u002F 异步发送RocketMQ，支持重试、死信队列兜底 rocketMQTemplate.asyncSend(\"redPacket_grab_topic\", message, new SendCallback() { @Override public void onSuccess(SendResult sendResult) { \u002F\u002F 发送成功无额外处理 } @Override public void onException(Throwable e) { \u002F\u002F 发送失败降级：本地消息表兜底重试 localMessageTable.save(message); } }); } \u002F\u002F MQ消费者：异步落库+更新账户余额 @RocketMQMessageListener(topic = \"redPacket_grab_topic\", consumerGroup = \"redPacket_consumer_group\") public class RedPacketGrabConsumer implements RocketMQListener\u003CRedPacketGrabMessage> { @Override public void onMessage(RedPacketGrabMessage message) { \u002F\u002F 1. 写入红包明细表 redPacketDetailMapper.insertDetail(message); \u002F\u002F 2. 更新用户账户余额（钱包服务） accountService.addBalance(message.getUserId(), message.getAmount()); \u002F\u002F 3. 发送抢成功通知 notifyService.sendGrabSuccessMsg(message.getUserId(), message.getAmount()); } } 核心技术难点与对应解决方案 🔧 核心技术难点 解决方案 落地收益 高并发下红包超发、用户重复抢的并发安全问题 1. Redis+Lua 脚本原子执行去重、扣库存、扣金额全流程2. 完全摒弃分布式锁，避免锁开销与死锁风险 从根源杜绝超发，单节点并发处理能力提升 10 倍以上 春晚级热点红包的单 Redis Key 性能瓶颈（单 Key QPS 上限约 10W） 1. 库存分片：将 1 个红包的总库存拆分为 N 个子库存 Key2. 请求按用户 ID 哈希路由到不同子 Key，流量打散3. 子库存抢光后自动迁移到其他分片兜底 热点 Key 承载能力线性提升，10 分片可支撑百万级 QPS Redis 缓存与 MySQL 数据库的数据一致性问题 1. 采用最终一致性模型，缓存作为主写入口，DB 异步同步2. MQ 消费失败自动重试，超过阈值进入死信队列人工干预3. 每日定时对账任务，校对库存、金额偏差，兜底修正 数据一致性达 99.99%，极端异常可通过对账 100% 修复 峰值流量冲击导致服务雪崩、数据库被打垮 1. 多层限流：网关层限流、接口级限流、用户维度限流2. 全链路异步化：非核心逻辑全部 MQ 异步削峰3. 服务降级：峰值关闭排行榜、领取记录等非核心接口 数据库压力降低 99%，系统峰值稳定性大幅提升 亿级红包明细数据的存储与查询性能问题 1. 分库分表：按红包 ID 哈希分库分表，分散存储压力2. 冷热数据分离：超过 7 天的历史红包数据归档到冷存储3. 明细查询优先走缓存，DB 只做兜底和对账 单表数据量控制在千万级，查询性能稳定在毫秒级 归纳起来就是一句话： 用 Redis 抗住读写的瞬时洪峰，用 MQ 平滑落地，用 Lua 保证原子性，用整数运算规避精度，最后加上风控兜底。 公众号“Rain的Java大神之路” 个人博客“www.javadashen.com”",6221,{"id":6,"kind":7,"title":11,"summary":13,"image":14,"href":16,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":40},"2026 · 综合技术","#2563eb","16 \u002F 10",[19],{"targetType":8,"targetId":9,"likedByMe":42,"likeCount":43,"commentCount":43,"contentLikeCount":43,"contentCommentCount":43,"sourceLikeCount":43,"sourceCommentCount":43},false,0,[45,52,59,65,72,79,85,92],{"id":46,"kind":7,"title":47,"summary":48,"image":49,"href":50,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":51},"NEWS_ARTICLE:838","AI 学习笔记：LLM 的微调实验","title: LLM 的微调实验 author: 凌杰 date: 2026-08-21 tags: LoRA, LLaMA-Factory, Qwen categories: 人工智能 [!NOTE] 笔记说明 这篇笔记对应的是《[[关于 AI 的学习路线图]]》一文中所规划的第三个学习阶段。其中","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F691082\u002F202609\u002F691082-20260902122904258-1285666483.png","\u002Fnews\u002F838",[19],{"id":53,"kind":7,"title":54,"summary":55,"image":56,"href":57,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":58},"NEWS_ARTICLE:843","介绍一下常用的Token鉴权方案","本文梳理 Session‑Cookie、JWT、OAuth2.0、SSO 主流 Token 鉴权方案，对比各方案适用场景。重点讲解生产级 JWT 双 Token 架构，给出 RS256 非对称加密、Redis 黑名单、网关统一鉴权等 Java 实战代码。剖析 JWT 注销、并发刷新、令牌泄露等落地痛","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F739056\u002F202609\u002F739056-20260902114155245-1924311389.png","\u002Fnews\u002F843",[19],{"id":60,"kind":7,"title":61,"summary":62,"image":15,"href":63,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":64},"NEWS_ARTICLE:854","分治：序列分治（CDQ）与点分治","分治：序列分治（CDQ）与点分治 一、分治思想概述 分治（Divide and Conquer）是算法设计中最核心的思想之一。它的基本策略是： 分（Divide）：将原问题划分为规模更小的子问题。 治（Conquer）：递归地求解子问题（若子问题足够小则直接求解）。 合（Combine）：将子问题的","\u002Fnews\u002F854",[19],{"id":66,"kind":7,"title":67,"summary":68,"image":69,"href":70,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":71},"NEWS_ARTICLE:863","弱模型不能裸奔：Agent Harness 凭什么真实有效","Harness 不是给弱模型贴的创可贴。它是把工程纪律——验证、门禁、不变量、路由——变成架构里一等公民的方式。模型每半年换一代，今天省钱的 Flash 明天可能就过时了，但那套'默认怀疑、机器校验、按决策密度调度'的流程会留下来，并且越跑越值钱。\n弱模型不能裸奔。给它穿上 harness，便宜才真","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F510\u002F202608\u002F510-20260830181633785-1171146572.jpg","\u002Fnews\u002F863",[19],{"id":73,"kind":7,"title":74,"summary":75,"image":76,"href":77,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":78},"NEWS_ARTICLE:865","焕新鸿蒙应用权限管理方案，应用授权体验再升级","作为用户或应用开发者，或许经历过类似的体验场景：使用应用的过程中，触发应用某些功能会需要访问你的位置、麦克风、相机等常用权限，若为了保护隐私拒绝授权后，想要使用功能时，再次打开却找不到设置入口；或开启流程繁琐，需要经过频繁跳转和设置。这一问题不仅影响用户体验，还可能造成应用功能不可用、用户流失，也制","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F2396482\u002F202609\u002F2396482-20260901170248450-744043562.png","\u002Fnews\u002F865",[19],{"id":80,"kind":7,"title":81,"summary":82,"image":15,"href":83,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":84},"NEWS_ARTICLE:875","别急着翻译 SKILL.md：我做了一个专门拆解 Skill 设计的工具","别急着翻译 SKILL.md：我做了一个专门拆解 Skill 设计的工具 第一次打开一份复杂的 SKILL.md，很多人的反应都是从头往下读。 每句话似乎都认识，连在一起却不一定明白：为什么这里用了 MUST？为什么执行前要读取这些文件？状态由谁维护？AI 做到什么程度才算完成？如果两条指令发生冲突","\u002Fnews\u002F875",[19],{"id":86,"kind":7,"title":87,"summary":88,"image":89,"href":90,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":91},"NEWS_ARTICLE:896","如何设计对外API接口","详解生产级对外 API 完整设计方案，围绕易用、安全、健壮黄金三角，覆盖 RESTful 规范、统一响应、签名验签防重放、AOP 注解实现分布式幂等、Redis 令牌桶限流、版本兼容、监控文档。附带可直接复用 Java 核心代码，梳理面试高频技术难点，搭配模拟面试场景，帮你避开线上坑点，快速掌握开放","https:\u002F\u002Fimg2024.cnblogs.com\u002Fblog\u002F739056\u002F202608\u002F739056-20260831134132446-55355388.png","\u002Fnews\u002F896",[19],{"id":93,"kind":7,"title":94,"summary":95,"image":15,"href":96,"meta":37,"badge":10,"author":12,"stats":-1,"accent":38,"coverRatio":39,"tags":97},"NEWS_ARTICLE:906","上周热点回顾（8.24-8.30）","热点随笔： &#183; 从 PostgreSQL 到 Kubernetes：开源的护城河，从来不写在代码里 (张善友) &#183; 代码都能让AI写了，我还学个屁？ (佛祖让我来巡山) &#183; Vibe Coding 月提交量 29 亿次之后：GitHub 的危机、Azure 迁移，以及","\u002Fnews\u002F906",[19]]