日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

redis mysql 解决超卖_Redis 分布式锁解决超卖问题

發布時間:2025/4/16 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis mysql 解决超卖_Redis 分布式锁解决超卖问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redis 分布式鎖解決超賣問題

1,Redis 事物介紹

1. Redis 事物是可以一次執行多個命令, 本質是一組命令的集合.

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

作用: 一個隊列中, 一次性, 順序性, 排他性的執行一系列命令

2,multi 指令基本使用

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

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

3. 因為 Redis 是單線程的, 所以不必擔心自己在執行隊列是被打斷, 可以保證這樣的 "原子性"

注: Redis 事物在遇到指令失敗后, 后面的指令會繼續執行# Multi 命令用于標記一個事務塊的開始事務塊內的多條命令會按照先后順序被放進一個隊列當中, 最后由 EXEC 命令原子性 ( atomic ) 地執行

>multi(開始一個Redis事物)

incr books

incr books

>exec(執行事物)

>discard(丟棄事物)

[[emailprotected]~]#Redis-cli

127.0.0.1:6379>multi

OK

127.0.0.1:6379>settest123

QUEUED

127.0.0.1:6379>exec

1)OK

127.0.0.1:6379>gettest

"123"

127.0.0.1:6379>multi

OK

127.0.0.1:6379>settest456

QUEUED

127.0.0.1:6379>discard

OK

127.0.0.1:6379>gettest

"123"

127.0.0.1:6379>

在命令行測試 Redis 事物

#! /usr/bin/env python

# -*- coding: utf-8 -*-

importRedis

r=Redis.Redis(host='127.0.0.1')

pipe=r.pipeline()

pipe.multi()#開啟事務

pipe.set('key2',4)#存儲子命令

pipe.execute()#執行事務

print(r.get('key2'))

使用 python 測試 Redis 事物

注: MySQL 的 rollback 與 Redis 的 discard 的區別

1. MySQL 回滾為 sql 全部成功才執行, 一條 sql 失敗則全部失敗, 執行 rollback 后所有語句造成的影響消失

2. Redis 的 discard 只是結束本次事務, 正確命令造成的影響仍然還在.

1)Redis 如果在一個事務中的命令出現錯誤, 那么所有的命令都不會執行;

2)Redis 如果在一個事務中出現運行錯誤, 那么正確的命令會被執行.

3,watch 指令作用

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

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

2. watch 會在事物開始前盯住一個或多個關鍵變量, 當服務器收到 exec 指令要順序執行緩存中的事物隊列時, Redis 會檢查關鍵變量自 watch 后是否被修改

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

1.2 setnx(Redis 分布式鎖)

1, 分布式鎖

1. 分布式鎖本質是占一個坑, 當別的進程也要來占坑時發現已經被占, 就會放棄或者稍后重試

2. 占坑一般使用 setnx(set if not exists)指令, 只允許一個客戶端占坑

3. 先來先占, 用完了在調用 del 指令釋放坑>setnxlock:codeholetrue

....dosomething critical....

>dellock:codehole

4. 但是這樣有一個問題, 如果邏輯執行到中間出現異常, 可能導致 del 指令沒有被調用, 這樣就會陷入死鎖, 鎖永遠無法釋放

5. 為了解決死鎖問題, 我們拿到鎖時可以加上一個 expire 過期時間, 這樣即使出現異常, 當到達過期時間也會自動釋放鎖>setnxlock:codeholetrue

>expirelock:codehole5

....dosomething critical....

>dellock:codehole

6. 這樣又有一個問題, setnx 和 expire 是兩條指令而不是原子指令, 如果兩條指令之間進程掛掉依然會出現死鎖

7. 為了治理上面亂象, 在 Redis 2.8 中加入了 set 指令的擴展參數, 使 setnx 和 expire 指令可以一起執行>setlock:codeholetrueex5nx

'''do something'''

>dellock:codehole

1.3 Redis 解決超賣問題

1, 使用 reids 的 watch + multi 指令實現

#! /usr/bin/env python

# -*- coding: utf-8 -*-

importRedis

defsale(rs):

whileTrue:

withrs.pipeline()asp:

try:

p.watch('apple')# 監聽 key 值為 apple 的數據數量改變

count=int(rs.get('apple'))

print('拿取到了蘋果的數量: %d'%count)

p.multi()# 事務開始

ifcount>0:# 如果此時還有庫存

p.set('apple',count-1)

p.execute()# 執行事務

p.unwatch()

break# 當庫存成功減一或沒有庫存時跳出執行循環

exceptExceptionase:# 當出現 watch 監聽值出現修改時, WatchError 異常拋出

print('[Error]: %s'%e)

continue# 繼續嘗試執行

rs=Redis.Redis(host='127.0.0.1',port=6379)# 連接 Redis

rs.set('apple',1000)# # 首先在 Redis 中設置某商品 apple 對應數量 value 值為 1000

sale(rs)

watch+multi 解決超賣問題

1)原理

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

2. 如果庫存沒有監聽到變化并且數量大于 1, 則庫存數量減一, 并執行任務

2)弊端

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

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

2, 使用 reids 的 watch + multi + setnx 指令實現

1)為什么要自己構建鎖

1. 雖然有類似的 SETNX 命令可以實現 Redis 中的鎖的功能, 但他鎖提供的機制并不完整

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

2)創建一個 Redis 鎖

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

2. 而鎖要做的事情就是將一個隨機生成的 128 位 UUID 設置位鍵的值, 防止該鎖被其他進程獲取

3)釋放鎖

1. 鎖的刪除操作很簡單, 只需要將對應鎖的 key 值獲取到的 uuid 結果進行判斷驗證

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

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

4)解決鎖無法釋放問題

1. 在之前的鎖中, 還出現這樣的問題, 比如某個進程持有鎖之后突然程序崩潰, 那么會導致鎖無法釋放

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

#! /usr/bin/env python

# -*- coding: utf-8 -*-

importRedis

importuuid

importtime

# 1. 初始化連接函數

defget_conn(host,port=6379):

rs=Redis.Redis(host=host,port=port)

returnrs

# 2. 構建 Redis 鎖

defacquire_lock(rs,lock_name,expire_time=10):

'''

rs: 連接對象

lock_name: 鎖標識

acquire_time: 過期超時時間

return -> False 獲鎖失敗 or True 獲鎖成功

'''

identifier=str(uuid.uuid4())

end=time.time()+expire_time

whiletime.time()

# 當獲取鎖的行為超過有效時間, 則退出循環, 本次取鎖失敗, 返回 False

ifrs.setnx(lock_name,identifier):# 嘗試取得鎖

returnidentifier

time.sleep(.001)

returnFalse

# 3. 釋放鎖

defrelease_lock(rs,lockname,identifier):

'''

rs: 連接對象

lockname: 鎖標識

identifier: 鎖的 value 值, 用來校驗

'''

pipe=rs.pipeline(True)

try:

pipe.watch(lockname)

ifrs.get(lockname).decode()==identifier:# 防止其他進程同名鎖被誤刪

pipe.multi()# 開啟事務

pipe.delete(lockname)

pipe.execute()

returnTrue# 刪除鎖

pipe.unwatch()# 取消事務

exceptExceptionase:

pass

returnFalse# 刪除失敗

'''在業務函數中使用上面的鎖'''

defsale(rs):

start=time.time()# 程序啟動時間

withrs.pipeline()asp:

'''

通過管道方式進行連接

多條命令執行結束, 一次性獲取結果

'''

whileTrue:

lock=acquire_lock(rs,'lock')

ifnotlock:# 持鎖失敗

continue

try:

count=int(rs.get('apple'))# 取量

p.set('apple',count-1)# 減量

p.execute()

print('當前庫存量: %s'%count)

break

finally:

release_lock(rs,'lock',lock)

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

rs=Redis.Redis(host='127.0.0.1',port=6379)# 連接 Redis

rs.set('apple',1000)# # 首先在 Redis 中設置某商品 apple 對應數量 value 值為 1000

sale(rs)

setnx+watch+multi 解決超賣問題

defacquire_expire_lock(rs,lock_name,expire_time=10,locked_time=10):

'''

rs: 連接對象

lock_name: 鎖標識

acquire_time: 過期超時時間

locked_time: 鎖的有效時間

return -> False 獲鎖失敗 or True 獲鎖成功

'''

identifier=str(uuid.uuid4())

end=time.time()+expire_time

whiletime.time()

# 當獲取鎖的行為超過有效時間, 則退出循環, 本次取鎖失敗, 返回 False

ifrs.setnx(lock_name,identifier):# 嘗試取得鎖

# print('鎖已設置: %s' % identifier)

rs.expire(lock_name,locked_time)

returnidentifier

time.sleep(.001)

returnFalse

優化: 給分布式鎖加超時時間防止死鎖

來源: http://www.bubuko.com/infodetail-3475503.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的redis mysql 解决超卖_Redis 分布式锁解决超卖问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。