最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解redis中的鎖以及使用場(chǎng)景

 更新時(shí)間:2020年12月17日 10:19:27   作者:ZPeng_Yan  
這篇文章主要介紹了詳解redis中的鎖以及使用場(chǎng)景,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

分布式鎖

什么是分布式鎖?

分布式鎖是控制分布式系統(tǒng)之間同步訪問(wèn)共享資源的一種方式。

為什么要使用分布式鎖?

​ 為了保證共享資源的數(shù)據(jù)一致性。

什么場(chǎng)景下使用分布式鎖?

​ 數(shù)據(jù)重要且要保證一致性

如何實(shí)現(xiàn)分布式鎖?

主要介紹使用redis來(lái)實(shí)現(xiàn)分布式鎖

redis事務(wù)

redis事務(wù)介紹:

​ 1.redis事務(wù)可以一次執(zhí)行多個(gè)命令,本質(zhì)是一組命令的集合。

​ 2.一個(gè)事務(wù)中的所有命令都會(huì)序列化,按順序串行化的執(zhí)行而不會(huì)被其他命令插入

​ **作用:**一個(gè)隊(duì)列中,一次性、順序性、排他性的執(zhí)行一系列命令

multi指令的使用

1. 下面指令演示了一個(gè)完整的事物過(guò)程,所有指令在exec前不執(zhí)行,而是緩存在服務(wù)器的一個(gè)事物隊(duì)列中

2. 服務(wù)器一旦收到exec指令才開(kāi)始執(zhí)行事物隊(duì)列,執(zhí)行完畢后一次性返回所有結(jié)果

3. 因?yàn)閞edis是單線程的,所以不必?fù)?dān)心自己在執(zhí)行隊(duì)列是被打斷,可以保證這樣的“原子性”

注:redis事物在遇到指令失敗后,后面的指令會(huì)繼續(xù)執(zhí)行

# Multi 命令用于標(biāo)記一個(gè)事務(wù)塊的開(kāi)始事務(wù)塊內(nèi)的多條命令會(huì)按照先后順序被放進(jìn)一個(gè)隊(duì)列當(dāng)中,最后由 EXEC 命令原子性( atomic )地執(zhí)行
> multi(開(kāi)始一個(gè)redis事物)
incr books
incr books
> exec (執(zhí)行事物)
> discard (丟棄事物)

注:mysql的rollback與redis的discard的區(qū)別

mysql回滾為sql全部成功才執(zhí)行,一條sql失敗則全部失敗,執(zhí)行rollback后所有語(yǔ)句造成的影響消失

redis的discard只是結(jié)束本次事務(wù),正確命令造成的影響仍然還在.

1)redis如果在一個(gè)事務(wù)中的命令出現(xiàn)錯(cuò)誤,那么所有的命令都不會(huì)執(zhí)行;
2)redis如果在一個(gè)事務(wù)中出現(xiàn)運(yùn)行錯(cuò)誤,那么正確的命令會(huì)被執(zhí)行。

watch 指令作用

實(shí)質(zhì):WATCH 只會(huì)在數(shù)據(jù)被其他客戶端搶先修改了的情況下通知執(zhí)行命令的這個(gè)客戶端(通過(guò) WatchError 異常)但不會(huì)阻止其他客戶端對(duì)數(shù)據(jù)的修改

1. watch其實(shí)就是redis提供的一種樂(lè)觀鎖,可以解決并發(fā)修改問(wèn)題

2. watch會(huì)在事物開(kāi)始前盯住一個(gè)或多個(gè)關(guān)鍵變量,當(dāng)服務(wù)器收到exec指令要順序執(zhí)行緩存中的事物隊(duì)列時(shí),redis會(huì)檢查關(guān)鍵變量自watch后是否被修改

3. WATCH 只會(huì)在數(shù)據(jù)被其他客戶端搶先修改了的情況下通知執(zhí)行命令的這個(gè)客戶端(通過(guò) WatchError 異常)但不會(huì)阻止其他客戶端對(duì)數(shù)據(jù)的修改

watch+multi實(shí)現(xiàn)樂(lè)觀鎖

setnx指令(redis的分布式鎖)

1、分布式鎖

分布式鎖本質(zhì)是占一個(gè)坑,當(dāng)別的進(jìn)程也要來(lái)占坑時(shí)發(fā)現(xiàn)已經(jīng)被占,就會(huì)放棄或者稍后重試
占坑一般使用 setnx(set if not exists)指令,只允許一個(gè)客戶端占坑
先來(lái)先占,用完了在調(diào)用del指令釋放坑

> setnx lock:codehole true
.... do something critical ....
> del lock:codehole

​但是這樣有一個(gè)問(wèn)題,如果邏輯執(zhí)行到中間出現(xiàn)異常,可能導(dǎo)致del指令沒(méi)有被調(diào)用,這樣就會(huì)陷入死鎖,鎖永遠(yuǎn)無(wú)法釋放
為了解決死鎖問(wèn)題,我們拿到鎖時(shí)可以加上一個(gè)expire過(guò)期時(shí)間,這樣即使出現(xiàn)異常,當(dāng)?shù)竭_(dá)過(guò)期時(shí)間也會(huì)自動(dòng)釋放鎖

> setnx lock:codehole true
> expire lock:codehole 5
.... do something critical ....
> del lock:codehole

這樣又有一個(gè)問(wèn)題,setnx和expire是兩條指令而不是原子指令,如果兩條指令之間進(jìn)程掛掉依然會(huì)出現(xiàn)死鎖
為了治理上面亂象,在redis 2.8中加入了set指令的擴(kuò)展參數(shù),使setnx和expire指令可以一起執(zhí)行

> set lock:codehole true ex 5 nx
''' do something '''
> del lock:codehole

redis解決超賣問(wèn)題

1、使用reids的 watch + multi 指令實(shí)現(xiàn)

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import redis
def sale(rs):
  while True:
    with rs.pipeline() as p:
      try:
        p.watch('apple')          # 監(jiān)聽(tīng)key值為apple的數(shù)據(jù)數(shù)量改變
        count = int(rs.get('apple'))
        print('拿取到了蘋果的數(shù)量: %d' % count)
        p.multi()             # 事務(wù)開(kāi)始
        if count> 0 :           # 如果此時(shí)還有庫(kù)存
          p.set('apple', count - 1)
          p.execute()          # 執(zhí)行事務(wù)
        p.unwatch()
        break               # 當(dāng)庫(kù)存成功減一或沒(méi)有庫(kù)存時(shí)跳出執(zhí)行循環(huán)
      except Exception as e:         # 當(dāng)出現(xiàn)watch監(jiān)聽(tīng)值出現(xiàn)修改時(shí),WatchError異常拋出
        print('[Error]: %s' % e)
        continue              # 繼續(xù)嘗試執(zhí)行

rs = redis.Redis(host='127.0.0.1', port=6379)   # 連接redis
rs.set('apple',1000)                # # 首先在redis中設(shè)置某商品apple 對(duì)應(yīng)數(shù)量value值為1000
sale(rs)

1)原理

1. 當(dāng)用戶購(gòu)買時(shí),通過(guò) WATCH 監(jiān)聽(tīng)用戶庫(kù)存,如果庫(kù)存在watch監(jiān)聽(tīng)后發(fā)生改變,就會(huì)捕獲異常而放棄對(duì)庫(kù)存減一操作

2. 如果庫(kù)存沒(méi)有監(jiān)聽(tīng)到變化并且數(shù)量大于1,則庫(kù)存數(shù)量減一,并執(zhí)行任務(wù)

2)弊端

1. Redis 在嘗試完成一個(gè)事務(wù)的時(shí)候,可能會(huì)因?yàn)槭聞?wù)的失敗而重復(fù)嘗試重新執(zhí)行

2. 保證商品的庫(kù)存量正確是一件很重要的事情,但是單純的使用 WATCH 這樣的機(jī)制對(duì)服務(wù)器壓力過(guò)大

2、使用reids的 watch + multi + setnx 指令實(shí)現(xiàn)

1)為什么要自己構(gòu)建鎖

然有類似的 SETNX 命令可以實(shí)現(xiàn) Redis 中的鎖的功能,但他鎖提供的機(jī)制并不完整

. 并且setnx也不具備分布式鎖的一些高級(jí)特性,還是得通過(guò)我們手動(dòng)構(gòu)建

2)創(chuàng)建一個(gè)redis鎖

在 Redis 中,可以通過(guò)使用 SETNX 命令來(lái)構(gòu)建鎖:rs.setnx(lock_name, uuid值)

. 而鎖要做的事情就是將一個(gè)隨機(jī)生成的 128 位 UUID 設(shè)置位鍵的值,防止該鎖被其他進(jìn)程獲取

3)釋放鎖

鎖的刪除操作很簡(jiǎn)單,只需要將對(duì)應(yīng)鎖的 key 值獲取到的 uuid 結(jié)果進(jìn)行判斷驗(yàn)證

. 符合條件(判斷uuid值)通過(guò) delete 在 redis 中刪除即可,pipe.delete(lockname)

3. 此外當(dāng)其他用戶持有同名鎖時(shí),由于 uuid 的不同,經(jīng)過(guò)驗(yàn)證后不會(huì)錯(cuò)誤釋放掉別人的鎖

4)解決鎖無(wú)法釋放問(wèn)題

1. 在之前的鎖中,還出現(xiàn)這樣的問(wèn)題,比如某個(gè)進(jìn)程持有鎖之后突然程序崩潰,那么會(huì)導(dǎo)致鎖無(wú)法釋放

2. 而其他進(jìn)程無(wú)法持有鎖繼續(xù)工作,為了解決這樣的問(wèn)題,可以在獲取鎖的時(shí)候加上鎖的超時(shí)功能

import redis
import uuid
import time

# 1.初始化連接函數(shù)
def get_conn(host="127.0.0.1",port=6379):
  rs = redis.Redis(host=host, port=port)
  return rs

# 2. 構(gòu)建redis鎖
def acquire_lock(rs, lock_name, expire_time=10):
  '''
  rs: 連接對(duì)象
  lock_name: 鎖標(biāo)識(shí)
  acquire_time: 過(guò)期超時(shí)時(shí)間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過(guò)有效時(shí)間,則退出循環(huán),本次取鎖失敗,返回False
    if rs.setnx(lock_name, identifier): # 嘗試取得鎖
      return identifier
    # time.sleep(.001)
    return False

# 3. 釋放鎖
def release_lock(rs, lockname, identifier):
  '''
  rs: 連接對(duì)象
  lockname: 鎖標(biāo)識(shí)
  identifier: 鎖的value值,用來(lái)校驗(yàn)
  '''


  if rs.get(lockname).decode() == identifier: # 防止其他進(jìn)程同名鎖被誤刪
    rs.delete(lockname)
    return True      # 刪除鎖
  else:
    return False      # 刪除失敗

#有過(guò)期時(shí)間的鎖
def acquire_expire_lock(rs, lock_name, expire_time=10, locked_time=10):
  '''
  rs: 連接對(duì)象
  lock_name: 鎖標(biāo)識(shí)
  acquire_time: 過(guò)期超時(shí)時(shí)間
  locked_time: 鎖的有效時(shí)間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過(guò)有效時(shí)間,則退出循環(huán),本次取鎖失敗,返回False
    if rs.setnx(lock_name, identifier): # 嘗試取得鎖
      # print('鎖已設(shè)置: %s' % identifier)
      rs.expire(lock_name, locked_time)
      return identifier
    time.sleep(.001)
  return False


'''在業(yè)務(wù)函數(shù)中使用上面的鎖'''
def sale(rs):
  start = time.time()      # 程序啟動(dòng)時(shí)間
  with rs.pipeline() as p:
    '''
    通過(guò)管道方式進(jìn)行連接
    多條命令執(zhí)行結(jié)束,一次性獲取結(jié)果
    '''

    while 1:
      lock = acquire_lock(rs, 'lock')
      if not lock: # 持鎖失敗
        continue

      #開(kāi)始監(jiān)測(cè)"lock"
      p.watch("lock")
      try:
        #開(kāi)啟事務(wù)
        p.multi()
        count = int(rs.get('apple')) # 取量
        p.set('apple', count-1)   # 減量
        # time.sleep(5)
        
        #提交事務(wù)
        p.execute()
        print('當(dāng)前庫(kù)存量: %s' % count)
        #成功則跳出循環(huán)
        break
      except:
        #事務(wù)失敗對(duì)應(yīng)處理
        print("修改數(shù)據(jù)失敗")

      #無(wú)論成功與否最終都需要釋放鎖
      finally:

        res = release_lock(rs, 'lock', lock)
        #釋放鎖成功,
        if res:
          print("刪除鎖成功")
        #釋放鎖失敗,強(qiáng)制刪除
        else:
          print("刪除鎖失敗,強(qiáng)制刪除鎖")
          res = rs.delete('lock')
          print(res)

    print('[time]: %.2f' % (time.time() - start))

rs = redis.Redis(host='127.0.0.1', port=6379)   # 連接redis
# rs.set('apple',1000)               # # 首先在redis中設(shè)置某商品apple 對(duì)應(yīng)數(shù)量value值為1000
sale(rs)

優(yōu)化鎖無(wú)法釋放的問(wèn)題,為鎖添加過(guò)期時(shí)間

def acquire_expire_lock(rs, lock_name, expire_time=10, locked_time=10):
  '''
  rs: 連接對(duì)象
  lock_name: 鎖標(biāo)識(shí)
  acquire_time: 過(guò)期超時(shí)時(shí)間
  locked_time: 鎖的有效時(shí)間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過(guò)有效時(shí)間,則退出循環(huán),本次取鎖失敗,返回False
    if rs.setnx(lock_name, identifier): # 嘗試取得鎖
      # print('鎖已設(shè)置: %s' % identifier)
      rs.expire(lock_name, locked_time)
      return identifier
    time.sleep(.001)
  return False

關(guān)于redis中的鎖

Watch:監(jiān)測(cè)一個(gè)key。如果這個(gè)key的value改變,那個(gè)接下來(lái)的事務(wù)操作全部失效

multi: 開(kāi)啟一個(gè)事務(wù)。

Setnx: 跟set一樣都往redis添加一個(gè)key。不一定的地方在于:set的時(shí)候如果這個(gè)值存在,就是修改操作。不存在就是添加操作。setnx:存在的時(shí)候不能再次添加,不存在的時(shí)候才能添加。

到此這篇關(guān)于詳解redis中的鎖以及使用場(chǎng)景的文章就介紹到這了,更多相關(guān)redis 鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis高并發(fā)緩存問(wèn)題分析及解決過(guò)程

    Redis高并發(fā)緩存問(wèn)題分析及解決過(guò)程

    文章總結(jié)了Redis緩存的六種常見(jiàn)問(wèn)題及其解決方案:緩存穿透、緩存擊穿、緩存雪崩、熱點(diǎn)key重建優(yōu)化、緩存和數(shù)據(jù)庫(kù)雙寫(xiě)不一致,以及Redis對(duì)過(guò)期key的三種清除策略,每種問(wèn)題都提供了詳細(xì)的原因分析和具體的解決方案
    2025-01-01
  • 淺談Redis主從復(fù)制以及主從復(fù)制原理

    淺談Redis主從復(fù)制以及主從復(fù)制原理

    在現(xiàn)有企業(yè)中80%公司大部分使用的是redis單機(jī)服務(wù),在實(shí)際的場(chǎng)景當(dāng)中單一節(jié)點(diǎn)的redis容易面臨風(fēng)險(xiǎn)。本文將介紹Redis主從復(fù)制以及主從復(fù)制原理。
    2021-05-05
  • 關(guān)于redigo中PubSub的一點(diǎn)小坑分析

    關(guān)于redigo中PubSub的一點(diǎn)小坑分析

    這篇文章主要給大家介紹了關(guān)于redigo中PubSub的一點(diǎn)小坑的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • Redis在windows環(huán)境下如何啟動(dòng)

    Redis在windows環(huán)境下如何啟動(dòng)

    這篇文章主要介紹了Redis在windows環(huán)境下如何啟動(dòng)的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 控制Redis的hash的field中的過(guò)期時(shí)間

    控制Redis的hash的field中的過(guò)期時(shí)間

    這篇文章主要介紹了控制Redis的hash的field中的過(guò)期時(shí)間問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Redis官方可視化工具RedisInsight的安裝使用詳細(xì)教程(功能強(qiáng)大)

    Redis官方可視化工具RedisInsight的安裝使用詳細(xì)教程(功能強(qiáng)大)

    RedisInsight是Redis官方出品的可視化管理工具,可用于設(shè)計(jì)、開(kāi)發(fā)、優(yōu)化你的Redis應(yīng)用。支持深色和淺色兩種主題,界面非常炫酷,接下來(lái)通過(guò)本文給大家介紹Redis官方可視化工具RedisInsight的安裝使用過(guò)程,需要的朋友可以參考下
    2022-04-04
  • redis慢查詢?nèi)罩镜脑L問(wèn)和管理方式

    redis慢查詢?nèi)罩镜脑L問(wèn)和管理方式

    這篇文章主要介紹了redis慢查詢?nèi)罩镜脑L問(wèn)和管理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Redis高級(jí)數(shù)據(jù)類型Hyperloglog、Bitmap的使用

    Redis高級(jí)數(shù)據(jù)類型Hyperloglog、Bitmap的使用

    很多小伙伴在面試中都會(huì)被問(wèn)道 Redis的常用數(shù)據(jù)結(jié)構(gòu)有哪些?可能很大一部分回答都是 string、hash、list、set、zset,但其實(shí)還有Hyperloglog和Bitmap,本文就來(lái)介紹一下
    2021-05-05
  • Redis+攔截器實(shí)現(xiàn)接口防刷

    Redis+攔截器實(shí)現(xiàn)接口防刷

    接口防刷有很多種實(shí)現(xiàn)思路,例如:攔截器/AOP+Redis、攔截器/AOP+本地緩存、前端限制等等很多種實(shí)現(xiàn)思路,本文主要來(lái)講一下?攔截器+Redis?的實(shí)現(xiàn)方式,需要的可以參考下
    2023-08-08
  • Redis如何一鍵部署腳本

    Redis如何一鍵部署腳本

    這篇文章主要介紹了Redis如何一鍵部署腳本,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評(píng)論

六枝特区| 龙井市| 加查县| 益阳市| 宁南县| 汉阴县| 都兰县| 横山县| 德州市| 广西| 图们市| 周口市| 瓦房店市| 定安县| 黔西县| 三穗县| 永定县| 嘉荫县| 明水县| 三门峡市| 阿尔山市| 迁西县| 祁连县| 南宁市| 博爱县| 汾阳市| 阳新县| 昌黎县| 鸡东县| 大竹县| 博乐市| 涡阳县| 白河县| 四平市| 巩义市| 磴口县| 项城市| 夏河县| 永丰县| 陆河县| 汾阳市|