一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術(shù)|

服務(wù)器之家 - 數(shù)據(jù)庫 - Redis - 詳解redis中的鎖以及使用場景

詳解redis中的鎖以及使用場景

2021-02-22 17:15ZPeng_Yan Redis

這篇文章主要介紹了詳解redis中的鎖以及使用場景,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

分布式

 

什么是分布式鎖?

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

為什么要使用分布式鎖?

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

什么場景下使用分布式鎖?

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

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

主要介紹使用redis來實現(xiàn)分布式鎖

redis事務(wù)

 

redis事務(wù)介紹:

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

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

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

multi指令的使用

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

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

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

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

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

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

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

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

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

watch 指令作用

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

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

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

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

watch+multi實現(xiàn)樂觀鎖

setnx指令(redis的分布式鎖)

1、分布式鎖

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

?
1
2
3
> setnx lock:codehole true
.... do something critical ....
> del lock:codehole

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

?
1
2
3
4
> setnx lock:codehole true
> expire lock:codehole 5
.... do something critical ....
> del lock:codehole

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

?
1
2
3
> set lock:codehole true ex 5 nx
''' do something '''
> del lock:codehole

redis解決超賣問題

 

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /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)聽key值為apple的數(shù)據(jù)數(shù)量改變
        count = int(rs.get('apple'))
        print('拿取到了蘋果的數(shù)量: %d' % count)
        p.multi()             # 事務(wù)開始
        if count> 0 :           # 如果此時還有庫存
          p.set('apple', count - 1)
          p.execute()          # 執(zhí)行事務(wù)
        p.unwatch()
        break               # 當(dāng)庫存成功減一或沒有庫存時跳出執(zhí)行循環(huán)
      except Exception as e:         # 當(dāng)出現(xiàn)watch監(jiān)聽值出現(xiàn)修改時,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 對應(yīng)數(shù)量value值為1000
sale(rs)

1)原理

1. 當(dāng)用戶購買時,通過 WATCH 監(jiān)聽用戶庫存,如果庫存在watch監(jiān)聽后發(fā)生改變,就會捕獲異常而放棄對庫存減一操作

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

2)弊端

1. Redis 在嘗試完成一個事務(wù)的時候,可能會因為事務(wù)的失敗而重復(fù)嘗試重新執(zhí)行

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

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

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

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

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

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

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

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

3)釋放鎖

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

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

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

4)解決鎖無法釋放問題

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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: 連接對象
  lock_name: 鎖標(biāo)識
  acquire_time: 過期超時時間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過有效時間,則退出循環(huán),本次取鎖失敗,返回False
    if rs.setnx(lock_name, identifier): # 嘗試取得鎖
      return identifier
    # time.sleep(.001)
    return False
 
# 3. 釋放鎖
def release_lock(rs, lockname, identifier):
  '''
  rs: 連接對象
  lockname: 鎖標(biāo)識
  identifier: 鎖的value值,用來校驗
  '''
 
 
  if rs.get(lockname).decode() == identifier: # 防止其他進(jìn)程同名鎖被誤刪
    rs.delete(lockname)
    return True      # 刪除鎖
  else:
    return False      # 刪除失敗
 
#有過期時間的鎖
def acquire_expire_lock(rs, lock_name, expire_time=10, locked_time=10):
  '''
  rs: 連接對象
  lock_name: 鎖標(biāo)識
  acquire_time: 過期超時時間
  locked_time: 鎖的有效時間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過有效時間,則退出循環(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()      # 程序啟動時間
  with rs.pipeline() as p:
    '''
    通過管道方式進(jìn)行連接
    多條命令執(zhí)行結(jié)束,一次性獲取結(jié)果
    '''
 
    while 1:
      lock = acquire_lock(rs, 'lock')
      if not lock: # 持鎖失敗
        continue
 
      #開始監(jiān)測"lock"
      p.watch("lock")
      try:
        #開啟事務(wù)
        p.multi()
        count = int(rs.get('apple')) # 取量
        p.set('apple', count-1)   # 減量
        # time.sleep(5)
        
        #提交事務(wù)
        p.execute()
        print('當(dāng)前庫存量: %s' % count)
        #成功則跳出循環(huán)
        break
      except:
        #事務(wù)失敗對應(yīng)處理
        print("修改數(shù)據(jù)失敗")
 
      #無論成功與否最終都需要釋放鎖
      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 對應(yīng)數(shù)量value值為1000
sale(rs)

優(yōu)化鎖無法釋放的問題,為鎖添加過期時間

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def acquire_expire_lock(rs, lock_name, expire_time=10, locked_time=10):
  '''
  rs: 連接對象
  lock_name: 鎖標(biāo)識
  acquire_time: 過期超時時間
  locked_time: 鎖的有效時間
  return -> False 獲鎖失敗 or True 獲鎖成功
  '''
  identifier = str(uuid.uuid4())
  end = time.time() + expire_time
  while time.time() < end:
    # 當(dāng)獲取鎖的行為超過有效時間,則退出循環(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)測一個key。如果這個key的value改變,那個接下來的事務(wù)操作全部失效

multi: 開啟一個事務(wù)。

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

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

原文鏈接:https://blog.csdn.net/ZPeng_Yan/article/details/106817375

延伸 · 閱讀

精彩推薦
  • Redis詳解Redis復(fù)制原理

    詳解Redis復(fù)制原理

    與大多數(shù)db一樣,Redis也提供了復(fù)制機(jī)制,以滿足故障恢復(fù)和負(fù)載均衡等需求。復(fù)制也是Redis高可用的基礎(chǔ),哨兵和集群都是建立在復(fù)制基礎(chǔ)上實現(xiàn)高可用的...

    李留廣10222021-08-09
  • RedisRedis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis的主從架構(gòu),能幫助我們實現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關(guān)于Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離的相關(guān)資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • RedisRedis 事務(wù)知識點相關(guān)總結(jié)

    Redis 事務(wù)知識點相關(guān)總結(jié)

    這篇文章主要介紹了Redis 事務(wù)相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關(guān)于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具...

    一線碼農(nóng)5812019-11-18
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis全量復(fù)制與部分復(fù)制示例詳解

    Redis全量復(fù)制與部分復(fù)制示例詳解

    這篇文章主要給大家介紹了關(guān)于Redis全量復(fù)制與部分復(fù)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Redis爬蟲具有一定的參考學(xué)習(xí)...

    豆子先生5052019-11-27
  • Redisredis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • RedisRedis的配置、啟動、操作和關(guān)閉方法

    Redis的配置、啟動、操作和關(guān)閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關(guān)閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
主站蜘蛛池模板: 亚洲高清视频网站 | 欧美国产日产精品免费视频 | 国产欧美又粗又猛又爽老 | 九九精品视频一区二区三区 | 国产精品麻豆免费版 | 日本免费一区二区三区四区五六区 | 9丨精品国产高清自在线看 9久热这里只有精品免费 | 午夜爽喷水无码成人18禁三级 | 亚洲黄色三级视频 | 精品操 | 四虎comwww最新地址 | 放荡警察巨r麻麻出轨小说 范冰冰特黄xx大片 饭冈加奈子在线播放观看 法国老妇性xx在线播放 | 天天综合亚洲 | 深夜免费网站 | 日韩不卡一区二区三区 | 欧美vpswindows动物 | 啊啊啊好大好爽视频 | 色综合色狠狠天天综合色 | 国产精品久久亚洲一区二区 | 97久久天天综合色天天综合色hd | 欧美一区二区三区在线观看不卡 | 好奇害死猫在线观看 | 欧美亚洲一区二区三区 | 99国产精品免费观看视频 | 国产在线播放一区 | 欧美日韩第二页 | 日本高清视频网址 | 国内视频一区二区三区 | 久久内在线视频精品mp4 | 色婷婷狠狠| 日本69av| 艾秋麻豆果冻传媒老狼仙踪林 | 国产婷婷高清在线观看免费 | 99九九精品免费视频观看 | 俄罗斯处女 | 5566中文字幕亚洲精品 | 亚洲国产天堂久久综合网站 | 免费看全黄特黄毛片 | 色一情一乱一伦 | 日本三级免费网站 | 无限好资源免费观看 |