<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Redis秒殺實(shí)戰(zhàn)-微信搶紅包-秒殺庫(kù)存,附案例源碼(Jmeter壓測(cè))

          共 5692字,需瀏覽 12分鐘

           ·

          2020-09-02 03:03

          原文鏈接:https://www.cnblogs.com/chenyanbin/p/13587508.html

          已原創(chuàng)授權(quán)


          為啥寫(xiě)這個(gè)微信搶紅包項(xiàng)目呢,公司 0202 年 08 月 22 日,公司周年慶,搶了100多紅包?,O(∩_∩)O哈哈~

          業(yè)務(wù)流程分析


          功能拆解

          新建紅包

          在 DB、Redis 分別新增一條記錄

          搶紅包(并發(fā))

          「使用技術(shù)」

          Redis 中數(shù)據(jù)類型的 String 特性的原子遞減(DECR key)和減少指定值(DECRBY key decrement)

          「業(yè)務(wù)」

          1. 請(qǐng)求 Redis ,當(dāng)剩余紅包個(gè)數(shù)大于 0,紅包個(gè)數(shù)原子遞減,隨機(jī)獲取紅包
          2. 計(jì)算金額,當(dāng)最后一個(gè)紅包時(shí),最后一個(gè)紅包金額=總金額-總已搶紅包金額
          3. 更新數(shù)據(jù)庫(kù)

          「查詢紅包記錄」

          查詢 DB 即可

          數(shù)據(jù)庫(kù)設(shè)計(jì)

          紅包流水表

          CREATE TABLE `red_packet_info` (
          `id` int(11) NOT NULL AUTO_INCREMENT,
          `red_packet_id` bigint(11) NOT NULL DEFAULT 0 COMMENT '紅包id,采?
          timestamp+5位隨機(jī)數(shù)',
          `total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '紅包總?額,單位分',
          `total_packet` int(11) NOT NULL DEFAULT 0 COMMENT '紅包總個(gè)數(shù)',
          `remaining_amount` int(11) NOT NULL DEFAULT 0 COMMENT '剩余紅包?額,單位
          分',
          `remaining_packet` int(11) NOT NULL DEFAULT 0 COMMENT '剩余紅包個(gè)數(shù)',
          `uid` int(20) NOT NULL DEFAULT 0 COMMENT '新建紅包?戶的?戶標(biāo)識(shí)',
          `create_time` timestamp COMMENT '創(chuàng)建時(shí)間',
          `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
          CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='紅包信息
          表,新建?個(gè)紅包插??條記錄';

          紅包記錄表

          CREATE TABLE `red_packet_record` (
          `id` int(11) NOT NULL AUTO_INCREMENT,
          `amount` int(11) NOT NULL DEFAULT '0' COMMENT '搶到紅包的?額',
          `nick_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '搶到紅包的?戶的?戶
          名',
          `img_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '搶到紅包的?戶的頭像',
          `uid` int(20) NOT NULL DEFAULT '0' COMMENT '搶到紅包?戶的?戶標(biāo)識(shí)',
          `red_packet_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '紅包id,采?
          timestamp+5位隨機(jī)數(shù)',
          `create_time` timestamp COMMENT '創(chuàng)建時(shí)間',
          `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
          CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='搶紅包記
          錄表,搶?個(gè)紅包插??條記錄';

          發(fā)紅包 API

          發(fā)紅包接口開(kāi)發(fā)

          • 新增一條紅包記錄
          • 往 mysql 里面添加一條紅包記錄
          • 往 redis 里面添加一條紅包數(shù)量記錄
          • 往redis里面添加一條紅包金額記錄
          ?

          往db中就單純存入一條記錄,Service層和Mapper層,就簡(jiǎn)單的一條sql語(yǔ)句,主要是提供思路,下面會(huì)附案例源碼,不要慌

          ?

          搶紅包 API

          • 搶紅包功能屬于原子減操作
          • 當(dāng)大小小于 0 時(shí)原子減失敗
          • 當(dāng)紅包個(gè)數(shù)為0時(shí),后面進(jìn)來(lái)的用戶全部搶紅包失敗,并不會(huì)進(jìn)入拆紅包環(huán)節(jié)
          • 搶紅包功能設(shè)計(jì)
            • 將紅包ID的請(qǐng)求放入請(qǐng)求隊(duì)列中,如果發(fā)現(xiàn)超過(guò)紅包的個(gè)數(shù),直接返回
          • 注意事項(xiàng)
            • 搶到紅包不一定能拆成功

          搶紅包算法拆解

          img

          通過(guò)上圖算法得出,靠前面的人,手氣最佳幾率小,手氣最佳,往往在后面

          1. 發(fā) 100 元,共 10 個(gè)紅包,那么平均值是 10 元一個(gè),那么發(fā)出來(lái)的紅包金額在 0.01~20 元之間波動(dòng)
          2. 當(dāng)前面 4 個(gè)紅包總共被領(lǐng)了 30 元時(shí),剩下 70 元,總共 6 個(gè)紅包,那么這 6 個(gè)紅包的金額在 0.01~23.3 元之間波動(dòng)

          搶紅包接口開(kāi)發(fā)

          「測(cè)試」

          「發(fā)紅包」


          模擬高并發(fā)搶紅包(Jmeter壓測(cè)工具)

          因?yàn)槲野l(fā)了 10 個(gè)紅包,金額是 20000,使用壓測(cè)工具,模擬50個(gè)請(qǐng)求,只允許前10個(gè)請(qǐng)求能搶到紅包,并且金額等于20000。

          布隆過(guò)濾器

          介紹

          布隆過(guò)濾器是1970年由布隆提出的。它實(shí)際上是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)。布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。它的優(yōu)點(diǎn)是空間效率和查詢時(shí)間都遠(yuǎn)遠(yuǎn)超過(guò)一般的算法,缺點(diǎn)是有一定的誤識(shí)別率和刪除困難。

          優(yōu)點(diǎn)

          相比于其他的數(shù)據(jù)結(jié)構(gòu),布隆過(guò)濾器在空間和時(shí)間方面都有巨大的優(yōu)勢(shì)。布隆過(guò)濾器存儲(chǔ)空間和插入/查詢時(shí)間都是常數(shù)。另外三列函數(shù)相互之間沒(méi)有關(guān)系,方便由硬件并行實(shí)現(xiàn)。布隆過(guò)濾器不需要存儲(chǔ)元素本身,在某些對(duì)保密要求非常嚴(yán)格的場(chǎng)合有優(yōu)勢(shì)。

          缺點(diǎn)

          但是布隆過(guò)濾器的缺點(diǎn)和有點(diǎn)一樣明顯。誤算率是其中之一。隨著存入的元素?cái)?shù)量增加,誤算率隨之增加。但是如果元素?cái)?shù)量太少,則使用散列表足矣。

          布隆過(guò)濾器有什么用

          1. 黑客流量攻擊:故意訪問(wèn)不存在的數(shù)據(jù),導(dǎo)致查程序不斷訪問(wèn)DB的數(shù)據(jù)
          2. 黑客安全阻截:當(dāng)黑客訪問(wèn)不存在的緩存時(shí)迅速返回避免緩存及DB掛掉
          3. 網(wǎng)頁(yè)爬蟲(chóng)對(duì) URL 的去重,避免爬取相同的URL地址
          4. 反垃圾郵件,從數(shù)十億個(gè)垃圾郵件列表中判斷某郵件是否垃圾郵件(同理,垃圾短信)
          5. 緩存擊穿,將已存在的緩存放到布隆中,當(dāng)黑客訪問(wèn)不存在的緩存時(shí)迅速返回避免緩存及 DB 掛掉

          布隆過(guò)濾器實(shí)現(xiàn)會(huì)員轉(zhuǎn)盤(pán)抽獎(jiǎng)

          需求

          一個(gè)抽獎(jiǎng)程序,只針對(duì)會(huì)員用戶有效

          通過(guò)google布隆過(guò)濾器存儲(chǔ)會(huì)員數(shù)據(jù)

          1. 程序啟動(dòng)時(shí)將數(shù)據(jù)放入內(nèi)存中
          2. google自動(dòng)創(chuàng)建布隆過(guò)濾器
          3. 用戶ID進(jìn)來(lái)之后判斷是否是會(huì)員

          代碼實(shí)現(xiàn)

          引入依賴


          ??com.google.guava
          ??guava
          ??29.0-jre

          數(shù)據(jù)庫(kù)會(huì)員表

          CREATE TABLE `sys_user` (
          `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
          `user_name` varchar(11) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '?戶名',
          `image` varchar(11) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '?戶頭像',
          PRIMARY KEY (`id`)
          ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

          初始化布隆過(guò)濾器

          dao 層和 dao 映射文件,就單純的一個(gè) sql 查詢,看核心方法,下面會(huì)附源碼滴,不要慌好嘛

          控制層

          測(cè)試

          缺點(diǎn)

          1. 內(nèi)存級(jí)別產(chǎn)部
          2. 重啟即失效
          3. 本地內(nèi)存無(wú)法用在分布式場(chǎng)景
          4. 不支持大數(shù)據(jù)量存儲(chǔ)

          Redis布隆過(guò)濾器

          優(yōu)點(diǎn)

          1. 可擴(kuò)展性 Bloom 過(guò)濾器
          2. 不存在重啟即失效或定時(shí)任務(wù)維護(hù)的成本

          缺點(diǎn)

          1. 需要網(wǎng)絡(luò)IO,性能比基于內(nèi)存的過(guò)濾器低

          布隆過(guò)濾器安裝

          「下載」

          github:https://github.com/RedisBloom/RedisBloom

          鏈接:?https://pan.baidu.com/s/16DlKLm8WGFzGkoPpy8y4Aw??密碼:?25w1

          「編譯」

          make

          「將 Rebloom 加載到 Redis 中」

          先把 Redis 給停掉!!!在 redis.conf 里面添加一行命令->加載模塊

          loadmodule?/usr/soft/RedisBloom-2.2.4/redisbloom.so

          「測(cè)試布隆過(guò)濾器」

          SpringBoot 整合 Redis 布隆過(guò)濾器

          編寫(xiě)兩個(gè)lua腳本

          1. 添加數(shù)據(jù)到指定名稱的布隆過(guò)濾器
          2. 從指定名稱的布隆過(guò)濾器獲取key是否存在的腳本

          local?bloomName?=?KEYS[1]
          local?value?=?KEYS[2]
          --bloomFilter
          local?result_1?=?redis.call('BF.ADD',bloomName,value)
          return?result_1

          local?bloomName?=?KEYS[1]
          local?value?=?KEYS[2]
          --bloomFilter
          local?result_1?=?redis.call('BF.EXISTS',bloomName,value)
          return?result_1

          在 RedisService.java 中添加 2 個(gè)方法

          驗(yàn)證

          秒殺

          秒殺業(yè)務(wù)流程圖

          數(shù)據(jù)落地存儲(chǔ)方案

          1. 通過(guò)分布式redis減庫(kù)存
          2. DB存最終訂單信息數(shù)據(jù)

          API性能調(diào)優(yōu)

          1. 性能瓶頸在高并發(fā)秒殺
          2. 技術(shù)難題在于超賣(mài)問(wèn)題

          實(shí)現(xiàn)步驟

          提前將秒殺數(shù)據(jù)緩存到 redis

          set?skuId_start_1?0_1554045087?--秒殺標(biāo)識(shí)
          set?skuId_access_1?12000?--允許搶購(gòu)數(shù)
          set?skuId_count_1?0?--搶購(gòu)計(jì)數(shù)
          set?skuId_booked_1?0?--真實(shí)秒殺數(shù)
          1. 秒殺開(kāi)始前,skuId_start為0,代表活動(dòng)未開(kāi)始
          2. 當(dāng)skuId_start改為1時(shí),活動(dòng)開(kāi)始,開(kāi)始秒殺叭
          3. 當(dāng)接受下單數(shù)達(dá)到sku_count*1.2后,繼續(xù)攔截所有請(qǐng)求,商品剩余數(shù)量為0(為啥接受搶購(gòu)數(shù)為1萬(wàn)2呢,看業(yè)務(wù)流程圖,涉及到“校驗(yàn)訂單信息”,一般設(shè)置的值要比總數(shù)多一點(diǎn),多多少自己定)

          利用 Redis 緩存加速增庫(kù)存數(shù)

          "skuId_booked":10000?//從0開(kāi)始累加,秒殺的個(gè)數(shù)只能加到1萬(wàn)

          將用戶訂單數(shù)據(jù)寫(xiě)入 MQ(異步方式)。

          另外一臺(tái)服務(wù)器監(jiān)聽(tīng) mq,將訂單信息寫(xiě)入到 DB。

          好了,以上就是完整的開(kāi)發(fā)步驟,下面我們開(kāi)始編寫(xiě)代碼

          代碼實(shí)戰(zhàn)

          網(wǎng)關(guān)瀏覽攔截層

          1、先判斷秒殺是否已經(jīng)開(kāi)始

          2、利用 Redis 緩存 incr 攔截流量

          • 用 incr 方法原子加
          • 通過(guò)原子加帕努單當(dāng)前 skuId_access 是否達(dá)到最大值

          訂單信息校驗(yàn)層

          1、校驗(yàn)當(dāng)前用戶是否已經(jīng)買(mǎi)過(guò)這個(gè)商品

          • 需要存儲(chǔ)用戶的uid
          • 存數(shù)據(jù)庫(kù)效率太低
          • 存Redis value方式數(shù)據(jù)太大
          • 存布隆過(guò)濾器性能高且數(shù)據(jù)量小(推薦)

          2、校驗(yàn)通過(guò)直接返回?fù)屬?gòu)成功

          開(kāi)發(fā)lua腳本實(shí)現(xiàn)庫(kù)存扣除

          1、庫(kù)存扣除成功,獲取當(dāng)前最新庫(kù)存

          2、如果庫(kù)存大于0,即馬上進(jìn)行庫(kù)存扣除,并且訪問(wèn)搶購(gòu)成功給用戶

          3、考慮原子性問(wèn)題

          • 保證原子性的方式,采用 lua 腳本
          • 采用lua腳本方式保證原子性帶來(lái)缺點(diǎn),性能有所下降
          • 不保證原子性缺點(diǎn),放入請(qǐng)求量可能大于預(yù)期
          • 當(dāng)前扣除庫(kù)存場(chǎng)景必須保證原子性,否則會(huì)導(dǎo)致超賣(mài)

          4、返回?fù)屬?gòu)結(jié)果

          • 搶購(gòu)成功
          • 庫(kù)存沒(méi)了,搶購(gòu)失敗

          控制層

          Service 層

          布隆過(guò)濾器

          初始化redis緩存

          set?skuId_start_1?0_1554045087?--秒殺標(biāo)識(shí)
          set?skuId_access_1?12000?--允許搶購(gòu)數(shù)
          set?skuId_count_1?0?--搶購(gòu)計(jì)數(shù)
          set?skuId_booked_1?0?--真實(shí)秒殺數(shù)

          秒殺驗(yàn)證

          jmeter 配置

          壓測(cè)秒殺驗(yàn)證原子性

          項(xiàng)目下載

          鏈接(奶牛?快傳):?https://cowtransfer.com/s/74998eaf64da44??取件碼:?rqzbyj

          尾聲

          演示的時(shí)候,我使用的 Redis 單機(jī)的,吞吐量不是很大,感興趣的,可以自己搭建個(gè) Redis 主從復(fù)制+哨兵+集群,然后再測(cè)試。

          最近比較忙,沒(méi)時(shí)間完善微信搶紅包秒殺的原子性。下面那個(gè)完整案例搶庫(kù)存的,親自使用 Jmeter 壓測(cè)幾次,是原子性的,可以拿來(lái)借鑒,感興趣的同學(xué),可以借鑒下面搶庫(kù)存的代碼,把微信搶紅包的功能在完善下,我就不修改啦。

          閑聊

          蘇州之行,被敖丙、帥地、3y、小林這幾個(gè)狗賊叫“小胖墩”。我能忍不?那肯定不能!回去就減肥。而且,我真的也不胖的嘛!

          這是昨晚的晚餐,不過(guò),晚上沒(méi)抗住,還是去吃了夜宵。。。


          我整理的4本PDF文檔,公眾號(hào)“后端技術(shù)進(jìn)階”后臺(tái)回復(fù)“面試突擊”即可免費(fèi)獲取。


          文章有幫助可以點(diǎn)個(gè)「在看」或「分享」,都是支持,我都喜歡!

          我是Guide哥,Java后端開(kāi)發(fā),會(huì)一點(diǎn)前端知識(shí),喜歡烹飪,自由的少年。一個(gè)三觀比主角還正的技術(shù)人。我們下期再見(jiàn)!


          往期推薦



          想要搭建個(gè)人博客?我調(diào)研了100來(lái)個(gè) Java 開(kāi)源博客系統(tǒng),發(fā)現(xiàn)這 5 個(gè)最好用!

          我是如何通過(guò)廚藝征服女朋友的?

          面試被問(wèn)Tomcat整體架構(gòu)設(shè)計(jì),我哭的像個(gè)孩子

          我參與了兩個(gè)接近100k+star的開(kāi)源項(xiàng)目!聊聊開(kāi)源項(xiàng)目貢獻(xiàn)指南

          涼了!張三同學(xué)沒(méi)答好「進(jìn)程間通信」,被面試官掛了....

          大白話帶你認(rèn)識(shí) ZooKeeper !重要概念一網(wǎng)打盡!

          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线观看的三级AWW | 激情五月天色 | 成人性爱免费 | 成人7777 | 国产精品久久久久久久久久久久午夜片 |