亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > Redis > 正文

詳解利用redis + lua解決搶紅包高并發的問題

2020-03-17 12:39:57
字體:
來源:轉載
供稿:網友

搶紅包的需求分析

搶紅包的場景有點像秒殺,但是要比秒殺簡單點。

因為秒殺通常要和庫存相關。而搶紅包則可以允許有些紅包沒有被搶到,因為發紅包的人不會有損失,沒搶完的錢再退回給發紅包的人即可。

另外像小米這樣的搶購也要比淘寶的要簡單,也是因為像小米這樣是一個公司的,如果有少量沒有搶到,則下次再搶,人工修復下數據是很簡單的事。而像淘寶這么多商品,要是每一個都存在著修復數據的風險,那如果出故障了則很麻煩。

基于redis/208155.html">redis的搶紅包方案

下面介紹一種基于Redis的搶紅包方案。

把原始的紅包稱為大紅包,拆分后的紅包稱為小紅包。

1.小紅包預先生成,插到數據庫里,紅包對應的用戶ID是null。

2.每個大紅包對應兩個redis隊列,一個是未消費紅包隊列,另一個是已消費紅包隊列。開始時,把未搶的小紅包全放到未消費紅包隊列里。

未消費紅包隊列里是json字符串,如{userId:'789', money:'300'}。

3.在redis中用一個map來過濾已搶到紅包的用戶。

4.搶紅包時,先判斷用戶是否搶過紅包,如果沒有,則從未消費紅包隊列中取出一個小紅包,再push到另一個已消費隊列中,最后把用戶ID放入去重的map中。

5.用一個單線程批量把已消費隊列里的紅包取出來,再批量update紅包的用戶ID到數據庫里。

上面的流程是很清楚的,但是在第4步時,如果是用戶快速點了兩次,或者開了兩個瀏覽器來搶紅包,會不會有可能用戶搶到了兩個紅包?

為了解決這個問題,采用了lua腳本方式,讓第4步整個過程是原子性地執行。

下面是在redis上執行的Lua腳本:

-- 函數:嘗試獲得紅包,如果成功,則返回json字符串,如果不成功,則返回空 -- 參數:紅包隊列名, 已消費的隊列名,去重的Map名,用戶ID -- 返回值:nil 或者 json字符串,包含用戶ID:userId,紅包ID:id,紅包金額:money  -- 如果用戶已搶過紅包,則返回nil if rediscall('hexists', KEYS[3], KEYS[4]) ~= 0 then  return nil else  -- 先取出一個小紅包  local hongBao = rediscall('rpop', KEYS[1]);  if hongBao then   local x = cjsondecode(hongBao);   -- 加入用戶ID信息   x['userId'] = KEYS[4];   local re = cjsonencode(x);   -- 把用戶ID放到去重的set里   rediscall('hset', KEYS[3], KEYS[4], KEYS[4]);   -- 把紅包放到已消費隊列里   rediscall('lpush', KEYS[2], re);   return re;  end end return nil 

下面是測試代碼:

public class TestEval {   static String host = "localhost";   static int honBaoCount = 1_0_0000;      static int threadCount = 20;      static String hongBaoList = "hongBaoList";   static String hongBaoConsumedList = "hongBaoConsumedList";   static String hongBaoConsumedMap = "hongBaoConsumedMap";      static Random random = new Random();    // -- 函數:嘗試獲得紅包,如果成功,則返回json字符串,如果不成功,則返回空 // -- 參數:紅包隊列名, 已消費的隊列名,去重的Map名,用戶ID // -- 返回值:nil 或者 json字符串,包含用戶ID:userId,紅包ID:id,紅包金額:money   static String tryGetHongBaoScript =  //     "local bConsumed = rediscall('hexists', KEYS[3], KEYS[4]);/n" //     + "print('bConsumed:' ,bConsumed);/n"       "if rediscall('hexists', KEYS[3], KEYS[4]) ~= 0 then/n"       + "return nil/n"       + "else/n"       + "local hongBao = rediscall('rpop', KEYS[1]);/n" //     + "print('hongBao:', hongBao);/n"       + "if hongBao then/n"       + "local x = cjsondecode(hongBao);/n"       + "x['userId'] = KEYS[4];/n"       + "local re = cjsonencode(x);/n"       + "rediscall('hset', KEYS[3], KEYS[4], KEYS[4]);/n"       + "rediscall('lpush', KEYS[2], re);/n"       + "return re;/n"       + "end/n"       + "end/n"       + "return nil";   static StopWatch watch = new StopWatch();      public static void main(String[] args) throws InterruptedException { //   testEval();     generateTestData();     testTryGetHongBao();   }      static public void generateTestData() throws InterruptedException {     Jedis jedis = new Jedis(host);     jedisflushAll();     final CountDownLatch latch = new CountDownLatch(threadCount);     for(int i = 0; i < threadCount; ++i) {       final int temp = i;       Thread thread = new Thread() {         public void run() {           Jedis jedis = new Jedis(host);           int per = honBaoCount/threadCount;           JSONObject object = new JSONObject();           for(int j = temp * per; j < (temp+1) * per; j++) {             objectput("id", j);             objectput("money", j);             jedislpush(hongBaoList, objecttoJSONString());           }           latchcountDown();         }       };       threadstart();     }     latchawait();   }      static public void testTryGetHongBao() throws InterruptedException {     final CountDownLatch latch = new CountDownLatch(threadCount);     Systemerrprintln("start:" + SystemcurrentTimeMillis()/1000);     watchstart();     for(int i = 0; i < threadCount; ++i) {       final int temp = i;       Thread thread = new Thread() {         public void run() {           Jedis jedis = new Jedis(host);           String sha = jedisscriptLoad(tryGetHongBaoScript);           int j = honBaoCount/threadCount * temp;           while(true) {             Object object = jediseval(tryGetHongBaoScript, 4, hongBaoList, hongBaoConsumedList, hongBaoConsumedMap, "" + j);             j++;             if (object != null) { //             Systemoutprintln("get hongBao:" + object);             }else {               //已經取完了               if(jedisllen(hongBaoList) == 0)                 break;             }           }           latchcountDown();         }       };       threadstart();     }          latchawait();     watchstop();          Systemerrprintln("time:" + watchgetTotalTimeSeconds());     Systemerrprintln("speed:" + honBaoCount/watchgetTotalTimeSeconds());     Systemerrprintln("end:" + SystemcurrentTimeMillis()/1000);   } } 

測試結果20個線程,每秒可以搶2.5萬個,足以應付絕大部分的搶紅包場景。

如果是真的應付不了,拆分到幾個redis集群里,或者改為批量搶紅包,也足夠應付。

總結:

redis的搶紅包方案,雖然在極端情況下(即redis掛掉)會丟失一秒的數據,但是卻是一個擴展性很強,足以應付高并發的搶紅包方案。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Redis頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产亚洲精品久久| 97人人模人人爽人人喊中文字| 久久精品成人一区二区三区| 欧洲美女7788成人免费视频| 国产成人拍精品视频午夜网站| 久久久成人的性感天堂| 亚洲色图美腿丝袜| 情事1991在线| 亚洲国产另类 国产精品国产免费| 91在线网站视频| 午夜精品蜜臀一区二区三区免费| 精品国产一区二区三区四区在线观看| 国产精品女视频| 日本精品va在线观看| 亚洲福利视频在线| www.日韩欧美| 丰满岳妇乱一区二区三区| 国产精品视频白浆免费视频| 日韩美女在线观看一区| 亚洲性日韩精品一区二区| 91久久久国产精品| 欧美成人免费观看| 中文字幕精品国产| 91在线视频精品| 亚洲精品999| 亚洲精品国产成人| 国产suv精品一区二区三区88区| 5252色成人免费视频| 国产精品第10页| 国产日韩精品入口| 中文字幕在线看视频国产欧美| 欧美视频不卡中文| 国产亚洲美女精品久久久| 国产亚洲视频在线观看| 91国产在线精品| 美女撒尿一区二区三区| 亚洲欧美国内爽妇网| 国产精品黄页免费高清在线观看| 国产免费一区视频观看免费| 亚洲精品视频在线播放| 92版电视剧仙鹤神针在线观看| 麻豆精品精华液| 97av在线视频免费播放| 成人有码视频在线播放| 色无极亚洲影院| 欧美夫妻性生活视频| 91色p视频在线| 国产丝袜一区二区| 国产va免费精品高清在线观看| 久久久女人电视剧免费播放下载| 亚洲精品视频在线观看视频| 欧美国产日韩在线| 欧美插天视频在线播放| 欧美极品在线视频| 日韩福利伦理影院免费| 国产亚洲欧美视频| 久久手机精品视频| 美女999久久久精品视频| 欧美韩日一区二区| 久久久国产在线视频| 国产丝袜一区二区| 国产精品激情自拍| 国产免费一区视频观看免费| 成人情趣片在线观看免费| 啪一啪鲁一鲁2019在线视频| 精品在线观看国产| 国产成人精品久久二区二区91| 欧美日韩福利在线观看| 亚洲第一区中文字幕| 久久激情视频久久| 亚洲a级在线播放观看| 欧美日韩色婷婷| 欧美另类交人妖| 性欧美xxxx视频在线观看| 久久综合网hezyo| 亚洲午夜久久久久久久| 日韩成人在线视频观看| 国产97在线|日韩| 欧美做受高潮1| 亚洲人午夜精品| 97视频免费观看| 久久精品国产亚洲一区二区| 欧美日韩国产一中文字不卡| 精品国产欧美一区二区五十路| 国产欧美一区二区三区在线| www日韩中文字幕在线看| 亚洲精品中文字幕有码专区| 中文字幕日韩视频| 午夜精品在线视频| 欧美巨乳美女视频| 97在线视频精品| 日韩欧美中文免费| 色www亚洲国产张柏芝| 成人精品一区二区三区电影黑人| 成人免费视频a| 亚洲人成在线一二| 欧美综合一区第一页| 久久久久久久久电影| 精品国产乱码久久久久久天美| 一区二区三区亚洲| 国产欧美一区二区三区在线| 日韩一区二区精品视频| 欧美性一区二区三区| 亚洲一区二区三区视频播放| 日韩在线观看av| 欧美日韩精品中文字幕| 91精品国产成人| 欧美专区第一页| 国产裸体写真av一区二区| 久久成人免费视频| 国产成人亚洲综合91| 最近中文字幕2019免费| 久久久国产影院| 国产精品激情av在线播放| 欧美精品免费在线观看| 国产午夜精品一区二区三区| 中文字幕亚洲欧美| 一区二区三区美女xx视频| 在线播放日韩专区| 一道本无吗dⅴd在线播放一区| 亚洲人成在线免费观看| 精品久久久久久久久久久久| 曰本色欧美视频在线| 亚洲国产精品一区二区三区| 国产精品久久久久久超碰| 久久久精品在线观看| 欧美国产视频一区二区| 欧美成人精品激情在线观看| 日韩精品在线观看一区二区| 亚洲精品aⅴ中文字幕乱码| 久久精品国产成人| 这里只有精品视频在线| 国产精品爽爽爽爽爽爽在线观看| 成人疯狂猛交xxx| 国产精品美女在线观看| 久久激情视频久久| 久久久久久高潮国产精品视| 亚洲第一免费网站| 日本欧美一级片| 日韩极品精品视频免费观看| 国产日韩欧美在线视频观看| 精品久久久久久中文字幕一区奶水| 日韩欧美一区视频| 日韩av日韩在线观看| 国产成人精品电影久久久| 国产成人精品免费视频| 美女精品视频一区| 国产精品丝袜白浆摸在线| 精品视频久久久久久| 亚洲人成电影在线播放| 国产精品福利无圣光在线一区| 91午夜理伦私人影院| 欧美与欧洲交xxxx免费观看| 久久综合伊人77777| 日韩经典第一页| 96pao国产成视频永久免费| 精品在线观看国产| 亚洲欧洲成视频免费观看| 91免费版网站入口| 日韩色av导航| 色狠狠久久aa北条麻妃| 欧美一区二三区| 精品自拍视频在线观看| 91精品国产综合久久香蕉922|