<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Redis隊列實現Java版秒殺系統(無腳本、可用于生產)

    VSole2022-12-06 09:11:23

    寫在前面

    需求是做一個秒殺系統,比如大家來搶100臺手機,先到先得。

    查閱了網上很多用redis實現秒殺的demo(java語言),竟然沒一個能用的!!!

    有些是php的,沒閑心研究了,現在說說為什么不能用:

    • 絕大多數的DEMO都是基于redis的watch特性的事務實現①,
    • 個別是基于redis分布式鎖實現②。
    • 當然還有些用了腳本的,我也沒仔細看是lua還是調用redis指令,哪有那個閑心去研究哇。

    照顧一下小白,分析一下為什么這幾種實現不行

    1.基于watch特性的 不靠譜 實現

    其實這兩種實現方式,完全可以理解為樂觀鎖(watch)和悲觀鎖(加分布式鎖)

    watch事務,相當于是樂觀鎖,這種方法在并發情況下極為不靠譜,假設有100個人同時嘗試秒殺,那么極端情況下,有99個人都會失敗,只有一個能修改成功。

    然而demo里甚至沒寫如果修改失敗了就重試這個功能,那顯然這失敗的99個人,已經提示失敗了,過一會回來,發現還剩了90多。那我是怎么失敗的?我替他們問問了。

    并且使用這種方式實現呢,在并發量較大的時候,過多的重試線程應該會嚴重影響服務器性能。

    2.基于用redis做個分布式鎖的 不靠譜 實現

    這種實現方式相當于一個悲觀鎖,每次執行減減操作之前,在redis中存入一個k,v鍵值對,使用特定的名稱,并且使用setNX特性,確保搶鎖沒有安全問題,并在使用完成后釋放鎖。那么問題是,在100個人秒殺時,只有一個人搶到鎖,剩下99個人怎么辦?

    demo里同樣沒寫個重試,搶不到鎖就失敗,醉了,不過就算寫重新搶鎖的機制,那么幾十個上百個線程不斷搶鎖,想想是個挺恐怖的事,更別提高并發了。

    基于腳本的實現 不靠譜 實現

    作為一個C系語言開發,我看不太懂,看不懂就是不靠譜,出了問題都不知道改哪里,你說靠不靠譜

    正題:使用spring操作redis的list隊列實現

    我用的是springboot的StringRedisTemplate,至于如何整合jedis到spring等等,去查閱其他文章吧,我就不重復寫了。

    貼工具類:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    import java.time.Duration;
    import java.util.Collection;
    
    @Service
    public class RedisServiceImpl<T> implements RedisService<T> {
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        //添加字符串并設置過期時間
        @Override
        public void addString(String key, String value, Duration duration) {
            stringRedisTemplate.opsForValue().set(key, value, duration);
        }
    
        //查找字符串
        @Override
        public String findString(String key) {
            return stringRedisTemplate.opsForValue().get(key);
        }
    
        //根據Key刪除
        @Override
        public Boolean deleteByKey(String key) {
            return stringRedisTemplate.delete(key);
        }
    
     //在隊列尾部減少一個對象
        @Override
        public String removeOneEntryOnListRight(String listName) {
            return stringRedisTemplate.opsForList().rightPop(listName);
        }
    
     //在隊列頭部新增對象
        @Override
        public Long addEntriesOnListLeft(String listName, Collection args) {
            return stringRedisTemplate.opsForList().leftPushAll(listName, args);
        }
    
    }
    

    解釋一下哈 這個類的父類是我自己寫的service層,不是提供好的

    主要使用的是最后兩個方法,最后一個方法,在隊列頭部新增對象,如果沒有這個隊列,他會創建出來這個隊列,然后將一個集合統統塞到這個redis隊列中。倒數第二個方法每調用一次,會刪除隊列中最后一個元素,然后返回這個元素的值,如果隊列中已經沒有元素了(隊列已經沒了)那么他會返回null,他們都是原子操作。

    如此,每個請求都無需經過加鎖操作,直接利用redis的單線程特性,即可實現高并發下的秒殺:請求到達redis,redis會逐個執行,每一次執行要么返回一個值,要么返回null。很顯然,返回值的就是搶到了,返回null的就是沒搶到。而且可以靈活的為這個隊列新加入一些元素(老板發話再加100臺)或者直接把這個隊列刪了(老板說不行,不賣了)都不會對代碼產生任何影響。

    其中對應的redis操作指令分別是:

    • 在隊列左側新增:lpush
    • 在隊列右側消費:rpop

    老板不賣了:del (笑)

    接下來貼出十分簡單的使用方法

    先貼在任務開始時向redis中插入一個大隊列

    List entriesList = new LinkedList<>();
       for (int i = 0; i < 100; i++){
           entriesList.add("某個商品");
       }
       redisService.addEntriesOnListLeft("隊列名",entriesList);
    

    突然想到這個實現即使秒殺100臺不同型號的手機(并且在秒到時就通知用戶秒到的是啥),也不用改代碼。

    每次秒殺執行:

    String redisResult = redisService.removeOneEntryOnListRight("隊列名");
        if (null == redisResult) {
            //說明沒搶到
        }else{
     //說明搶到了 執行搶到邏輯
    }
    

    突然發現這個實現看起來甚至比那些所謂的秒殺demo還簡單

    但他既沒有并發問題,也沒有為了解決并發問題而衍生的性能問題。

    雖然沒經過測試,不過我認為就算秒殺10萬臺,放到redis隊列里,應該也占用不了多少內存。

    趕工分布式秒殺,沒想到如此基本的內容,竟沒找到一個靠譜的實現,從上午寫到現在(周末晚8點)一頓飯沒吃,被網上過于demo的資源逼的,忍餓怒出此文。

    redisredis分布式鎖
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    寫在前面需求是做一個秒殺系統,比如大家來搶100臺手機,先到先得。查閱了網上很多用redis實現秒殺的demo,竟然沒一個能用的!!!有些是php的,沒閑心研究了,現在說說為什么不能用:絕大多數的DEMO都是基于redis的watch特性的事務實現①,個別是基于redis分布式實現②。當然還有些用了腳本的,我也沒仔細看是lua還是調用redis指令,哪有那個閑心去研究哇。并且使用這種方式實現呢,在并發量較大的時候,過多的重試線程應該會嚴重影響服務器性能。
    Set第三個參數過期時間單位是秒。然后這個Set中存在的就是網點還沒有攬收的件,這時候通過Count就會知道這個網點今天還有多少件沒有攬收。如果get,set兩次以上,建議用getall,setall。
    代表的a的二進制位的修改。對應的ASCII碼是97,轉換為二進制數據是01100001. 因為bit非常節省空間,可以用來做大數據量的統計。BITOPNOTdestkeykey ,對給定 key 求邏輯非,并將結果保存到 destkey 。獲取今天點擊最多的15條:zrevrange hotNews:20190926 0 15 withscores
    面對越來越多的高并發場景,限流顯示的尤為重要。 當然,限流有許多種實現的方式,Redis具有很強大的功能,我用Redis實踐了三種的實現方式,可以較為簡單的實現其方式。Redis不僅僅是可以做限流,還可以做數據統計,附近的人等功能,這些可能會后續寫到。
    解決方案使用mysql數據庫,使用一個字段來存儲庫存,每次扣減庫存去更新這個字段。將庫存放到redis使用redis的incrby特性來扣減庫存。基于數據庫單庫存第一種方式在有請求都會在這里等待,獲取有去扣減庫存。基于數據庫多庫存第二種方式其實是第一種方式的優化版本,在一定程度上提高了并發量,但是在還是會大量的對數據庫做更新操作大量占用數據庫資源。但是一旦緩存丟失需要考慮恢復方案。
    目前業界常見的延時消息方案
    Jumpserver 概述Jumpserver 是一款使用 Python, Django 開發的開源跳板機系統, 為互聯網企業提供了認證,授權,審計,自動化運維等功能。(LDAP 是輕量目錄訪問協議,英文全稱是 Lightweight Directory Access Protocol,一般都簡稱為 LDAP。它是基于 X.500 標準的,但是簡單多了并且可以根據需要定制。與 X.500 不同,LDAP 支持 TCP/IP,這對訪問 Internet 是必須的。LDAP 的核心規范在 RFC 中都有定義,有與 LDAP 相關的 RFC 都可以在 LDAPman RFC 網頁中找到。
    這篇文章主要收集一些常見的未授權訪問漏洞。未授權訪問漏洞可以理解為需要安全配置或權限認證的地址、授權頁面存在缺陷導致其他用戶可以直接訪問從而引發重要權限可被操作、數據庫或網站目錄等敏感信息泄露。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类