redis 事務 多進程模擬秒殺 保證庫存的正確 lua腳本和watch的方法

三種方法來實現

  1. lpop的 原子操作 10個商品就 lpush 10個數據 搶購開始用lpop來判斷是否還存在庫存

  2. watch實現 下面有代碼實現
    watch庫存鍵, multi後如果該key被其他客戶端改變, 事務操作會拋出WatchError異常

  3. lua腳本 下面有代碼實現

watch方法實現

import redis
from redis import WatchError
from concurrent.futures import ProcessPoolExecutor

r = redis.Redis(host='127.0.0.1', port=6379, password='123')


# 減庫存函數, 循環直到減庫存完成
# 庫存充足, 減庫存成功, 返回True
# 庫存不足, 減庫存失敗, 返回False
def decr_stock():

    # python中redis事務是通過pipeline的封裝實現的
    with r.pipeline() as pipe:
        while True:
            try:
                # watch庫存鍵, multi後如果該key被其他客戶端改變, 事務操作會拋出WatchError異常
                pipe.watch('stock:count')
                count = int(pipe.get('stock:count'))
                if count > 0:  # 有庫存
                    # 事務開始
                    pipe.multi()
                    pipe.decr('stock:count')
                    # 把命令推送過去
                    # execute返回命令執行結果列表, 這裏只有一個decr返回當前值
                    print pipe.execute()[0]
                    return True
                else:
                    return False
            except WatchError, ex:
                # 打印WatchError異常, 觀察被watch鎖住的情況
                print ex
                pipe.unwatch()


def worker():
    while True:
        # 沒有庫存就退出
        if not decr_stock():
            print "=============="
            break


# 實驗開始
# 設置庫存爲100
r.set("stock:count", 100000)
print r.get("stock:count")
import time
testa = time.time()
# 多進程模擬多個客戶端提交
with ProcessPoolExecutor(max_workers=5) as pool:
    for _ in range(10):
        pool.submit(worker)
print time.time() - testa

lua腳本的方法實現

import redis
from redis import WatchError
from concurrent.futures import ProcessPoolExecutor

r = redis.Redis(host='127.0.0.1', port=16379, password='joyame!@#')


# 減庫存函數, 循環直到減庫存完成
# 庫存充足, 減庫存成功, 返回True
# 庫存不足, 減庫存失敗, 返回False
def decr_stock():
   lua_2 = """
        if (tonumber(redis.call("GET","stock:count")) > tonumber(0)) then
            redis.call("DECR","stock:count")
            return true
        else
            return nil
        end
        """

    # python中redis事務是通過pipeline的封裝實現的
    with r.pipeline() as pipe:
        while True:
            try:
                script_2 = r.register_script(lua_2)
                a = script_2()
                if a >= 0:
                    print a
                    return True
                else:
                    return False

            except WatchError, ex:
                # 打印WatchError異常, 觀察被watch鎖住的情況
                print ex
                pipe.unwatch()


def worker():
    while True:
        # 沒有庫存就退出
        if not decr_stock():
            print "=============="
            break


# 實驗開始
# 設置庫存爲100
r.set("stock:count", 1000)

import time
testa = time.time()
# 多進程模擬多個客戶端提交
with ProcessPoolExecutor(max_workers=5) as pool:
    for _ in range(10):
        pool.submit(worker)
print time.time() - testa
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章