Java-分布式框架-redis-4

redis分布式鎖redisson

分布式框架中,普通鎖是滿足不了業(yè)務需求的是晨,分布式鎖在分布式框架中不可缺失肚菠;比如互聯(lián)網(wǎng)秒殺、搶優(yōu)惠券罩缴、接口冪等性校驗蚊逢。redis中存在redisson工具包專門處理redis在分布式鎖的應用。

java中redisson的實現(xiàn)

<!--添加依賴-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>
@Bean
 public Redisson redisson() {
    // 此為單機模式
    Config config = new Config();
    config.useSingleServer().setAddress("redis://192.168.0.60:6379").setDatabase(0);
   // 此為集群模式
    /*config.useClusterServers()
                .addNodeAddress("redis://192.168.0.61:8001")
                .addNodeAddress("redis://192.168.0.62:8002")
                .addNodeAddress("redis://192.168.0.63:8003")
                .addNodeAddress("redis://192.168.0.61:8004")
                .addNodeAddress("redis://192.168.0.62:8005")
                .addNodeAddress("redis://192.168.0.63:8006");*/
  //還有哨兵模式
    return (Redisson) Redisson.create(config);
}
@Autowired
private Redisson redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;

@RequestMapping("/deduct_stock")
public String deductStock() throws InterruptedException {
    String lockKey = "product_001";
    RLock redissonLock = redisson.getLock(lockKey);
    try {
        // 加鎖箫章,實現(xiàn)鎖續(xù)命功能
        redissonLock.lock();
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
        if (stock > 0) {
            int realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
            System.out.println("扣減成功烙荷,剩余庫存:" + realStock + "");
        } else {
            System.out.println("扣減失敗,庫存不足");
        }
    }finally {
        redissonLock.unlock();
    }
    return "end";
}
  1. 使用了jedis.setnx方法加鎖檬寂,只允許設置一次终抽;刪除對應的key進行解鎖。
  2. 每個線程設置的key對應的value值具有唯一性桶至,最后解鎖刪除value值判斷是否為當前加鎖的線程拿诸,防止了因為當前線程處理時間過長redis本身刪除解鎖后其它線程進來而導致當前線程解了其他線程的鎖,保證了自己加鎖自己解鎖塞茅。
  3. 增加了鎖續(xù)命的功能。
image.png
Redis Lua腳本

Redis在2.6推出了腳本功能季率,允許開發(fā)者使用Lua語言編寫腳本傳到Redis中執(zhí)行野瘦。使用腳本的好處如下:

  1. 減少網(wǎng)絡開銷:本來5次網(wǎng)絡請求的操作,可以用一個請求完成,原先5次請求的邏輯放在redis服務器上完成鞭光。使用腳本吏廉,減少了網(wǎng)絡往返時延。這點跟管道類似惰许。
  2. 原子操作:Redis會將整個腳本作為一個整體執(zhí)行席覆,中間不會被其他命令插入。管道不是原子的汹买,不過 redis的批量操作命令(類似mset)是原子的佩伤。
  3. 替代redis的事務功能:redis自帶的事務功能很雞肋,報錯不支持回滾晦毙,而redis的lua腳本幾乎實現(xiàn)了常規(guī)的事務功能生巡,支持報錯回滾操作,官方推薦如果要使用redis的事務功能可以用redis lua替代见妒。

從Redis2.6.0版本開始孤荣,通過內(nèi)置的Lua解釋器,可以使用EVAL命令對Lua腳本進行求值须揣。EVAL命令的格式如下:

EVAL script numkeys key [key ...] arg [arg ...] 

script:lua腳本盐股。
numkeys:key的數(shù)量。
key:key值耻卡,這里為list疯汁。
arg:value值,這里為list劲赠,與key相對應涛目。

redis中使用lua腳本

//******* lua腳本示例 ********
//模擬一個商品減庫存的原子操作
//lua腳本命令執(zhí)行方式:redis-cli --eval /tmp/test.lua , 10
jedis.set("product_stock_10016", "15");  //初始化商品10016的庫存
String script = " local count = redis.call('get', KEYS[1]) " +
                    " local a = tonumber(count) " +
                    " local b = tonumber(ARGV[1]) " +
                    " if a >= b then " +
                    "   redis.call('set', KEYS[1], count-b) " +
                    //模擬語法報錯回滾操作"   bb == 0 " +
                    "   return 1 " +
                    " end " +
                    " return 0 ";
Object obj = jedis.eval(script, Arrays.asList("product_stock_10016"), Arrays.asList("10"));
System.out.println(obj);

注意1:多線程在執(zhí)行l(wèi)ua腳本的時候,是不存在并發(fā)問題的凛澎,原因是所有的命令在redis中都會以單線程的形式執(zhí)行霹肝。
注意2:key與value傳參都是以list的類型傳入,同時下標從1開始而不是0塑煎。

lua腳本缺點

redis在高并發(fā)執(zhí)行指令都是串行化沫换,單線程的,如果lua腳本業(yè)務邏輯比較復雜執(zhí)行時間比較長最铁,這會影響redis的性能讯赏;如果lua腳本中出現(xiàn)死循環(huán)的現(xiàn)象,執(zhí)行該腳本的redis集群基本上癱瘓了冷尉。

注意1:redisson加的鎖為可重入鎖漱挎,前提是同一個線程。
注意2:redisson在加鎖后會存在主節(jié)點重新選舉的情況雀哨,這期間可能會導致鎖數(shù)據(jù)丟失磕谅,從而出現(xiàn)鎖失效的現(xiàn)象私爷。

redis RedLock

為解決主節(jié)點重新選舉的情況,這期間可能會導致鎖數(shù)據(jù)丟失問題膊夹,引入RedLock可解決問題衬浑。但是RedLock性能比較差,而且有存在Bug放刨。不推薦使用工秩,建議容忍該問題,若不能容忍进统,推薦使用zookeeper助币。

@RequestMapping("/redlock")
public String redlock() throws InterruptedException {
    String lockKey = "product_001";
    //這里需要自己實例化不同redis實例的redisson客戶端連接,這里只是偽代碼用一個redisson客戶端簡化了
    RLock lock1 = redisson.getLock(lockKey);
    RLock lock2 = redisson.getLock(lockKey);
    RLock lock3 = redisson.getLock(lockKey);

    /**
     * 根據(jù)多個 RLock 對象構(gòu)建 RedissonRedLock (最核心的差別就在這里)
     */
    RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
    try {
        /**
         * 4.嘗試獲取鎖
         * waitTimeout 嘗試獲取鎖的最大等待時間麻昼,超過這個值奠支,則認為獲取鎖失敗
         * leaseTime   鎖的持有時間,超過這個時間鎖會自動失效(值應設置為大于業(yè)務處理的時間,確保在鎖有效期內(nèi)業(yè)務能處理完)
         */
        boolean res = redLock.tryLock(10, 30, TimeUnit.SECONDS);
        if (res) {
            //成功獲得鎖抚芦,在這里處理業(yè)務
        }
    } catch (Exception e) {
        throw new RuntimeException("lock fail");
    } finally {
        //無論如何, 最后都要解鎖
        redLock.unlock();
    }
    return "end";
}
高并發(fā)場景分布式鎖性能提升

把資源庫存分成多份倍谜,別分存儲在不同的集群內(nèi),把鎖分段分發(fā)叉抡。比如一個商品庫存數(shù)量1000個尔崔,把庫存分為3份,應用key的hash算法把3份庫存分發(fā)的不同的redis子集群內(nèi)褥民。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末季春,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子消返,更是在濱河造成了極大的恐慌载弄,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵颊,死亡現(xiàn)場離奇詭異宇攻,居然都是意外死亡,警方通過查閱死者的電腦和手機倡勇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門逞刷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妻熊,你說我怎么就攤上這事夸浅。” “怎么了扔役?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵帆喇,是天一觀的道長。 經(jīng)常有香客問我亿胸,道長坯钦,這世上最難降的妖魔是什么法严? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮葫笼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拗馒。我一直安慰自己路星,他們只是感情好,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布诱桂。 她就那樣靜靜地躺著洋丐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挥等。 梳的紋絲不亂的頭發(fā)上友绝,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天,我揣著相機與錄音肝劲,去河邊找鬼迁客。 笑死,一個胖子當著我的面吹牛辞槐,可吹牛的內(nèi)容都是我干的掷漱。 我是一名探鬼主播,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼榄檬,長吁一口氣:“原來是場噩夢啊……” “哼卜范!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹿榜,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤海雪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舱殿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奥裸,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年怀薛,在試婚紗的時候發(fā)現(xiàn)自己被綠了刺彩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡枝恋,死狀恐怖创倔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焚碌,我是刑警寧澤畦攘,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布胖缤,位于F島的核電站菠劝,受9級特大地震影響谆棺,放射性物質(zhì)發(fā)生泄漏魄宏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一台盯、第九天 我趴在偏房一處隱蔽的房頂上張望罢绽。 院中可真熱鬧,春花似錦静盅、人聲如沸良价。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽明垢。三九已至,卻和暖如春市咽,著一層夾襖步出監(jiān)牢的瞬間痊银,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工施绎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留溯革,地道東北人。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓粘姜,卻偏偏與公主長得像鬓照,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子孤紧,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容