成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第6節(jié):Redis事務

banana_pi / 1842人閱讀

摘要:上一篇文章實戰(zhàn)第四章數(shù)據(jù)安全與性能保障第節(jié)處理系統(tǒng)故障下一篇文章實戰(zhàn)第四章數(shù)據(jù)安全與性能保障第節(jié)非事務型流水線為了確保數(shù)據(jù)的正確性,我們必須認識到這一點在多個客戶端同時處理相同的數(shù)據(jù)時,不謹慎的操作很容易會導致數(shù)據(jù)出錯。

上一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第5節(jié):處理系統(tǒng)故障
下一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第7節(jié):非事務型流水線

為了確保數(shù)據(jù)的正確性,我們必須認識到這一點:在多個客戶端同時處理相同的數(shù)據(jù)時,不謹慎的操作很容易會導致數(shù)據(jù)出錯。本節(jié)將介紹使用Redis事務來防止數(shù)據(jù)出錯的方法,以及在某些情況下,使用事務來提升性能的方法。

Redis的事務和傳統(tǒng)關(guān)系數(shù)據(jù)庫的事務并不相同。在關(guān)系數(shù)據(jù)庫中,用戶首先向數(shù)據(jù)庫服務器發(fā)送begin,然后執(zhí)行各個相互一致的寫操作和讀操作,最后,用戶可以選擇發(fā)送commit來確認之前所做的修改,后者發(fā)送rollback來放棄那些修改。

在Redis里面也有簡單的方法可以處理一連串相互一致的讀操作和寫操作。正如之前介紹的那樣,Redis的事務以特殊命令multi為開始,之后跟著用戶傳入的多個命令,最后以exec為結(jié)束。但是由于這種簡單的事務在exec命令被調(diào)用之前不會執(zhí)行任何實際操作,所以用戶將沒辦法根據(jù)讀取到的數(shù)據(jù)來做決定。這個問題看上去似乎無足輕重,但實際上無法以一致的形式讀取數(shù)據(jù)將導致某一類型的問題變得難以解決,除此之外,因為在多個事務同時處理同一個對象時通常需要用到二階提交,所以如果事務不能以一致的形式讀取數(shù)據(jù),那么二階提交將無法實現(xiàn),從未導致一些原本可以成功執(zhí)行的事務淪落至失敗的地步。比如說:在市場里面購買一件商品,就是其中一個會因為無法以一致的形式讀取數(shù)據(jù)而變得難以解決的問題,本節(jié)接下來將在實際環(huán)境中對這個問題進行介紹。

延遲執(zhí)行事務有助于提升性能

因為Redis在執(zhí)行事務的過程中,會延遲執(zhí)行已入隊的命令直到客戶端發(fā)送exec命令為止。因此,包括本書使用的Python客戶端在內(nèi)的很多Redis客戶端都會等到事務包含的所有命令都出現(xiàn)了之后,才一次性地將multi命令、要在事務中執(zhí)行的一系列命令,以及exec命令全部發(fā)送給Redis,然后等待知道接受到所有命令的回復為止。這種【一次性發(fā)送多個命令,然后等待所有回復出現(xiàn)】的做法通常被成為流水線,它可以通過減少客戶端與Redis服務器之間的網(wǎng)絡通信次數(shù)來提示Redis在執(zhí)行多個命令時的性能。

最近幾個月,F(xiàn)ake Game公司發(fā)現(xiàn)他們在一個社交網(wǎng)站上推出的角色扮演網(wǎng)頁游戲正在變得越來越受歡迎。因此,關(guān)心玩家需求的Fake Game公司決定在游戲里面增加一個商品買賣市場,讓玩家們可以在市場里面銷售和購買商品。本節(jié)接下來的內(nèi)容將介紹設計和實現(xiàn)這個商品買賣市場的方法,并說明如何按需對這個商品買賣市場進行擴展。

定義用戶信息和用戶包裹

下表展示了游戲中用于表示用戶信息和用戶包裹的結(jié)構(gòu):用戶信息存儲在一個散列里面,散列的各個鍵值對分別記錄了用戶的姓名、用戶擁有的錢數(shù)等屬性。用戶包裹使用一個集合來表示,它記錄了包裹里面每件商品的唯一編號。

鍵名:user:17 存儲類型:hash
name Frank
funds 43
鍵名:inventory:17 存儲類型:set
ItemL
ItemM
ItemN
鍵名:user:27 存儲類型:hash
name Bill
funds 125
鍵名:inventory:27 存儲類型:set
ItemO
ItemP
ItemQ

商品買賣市場的需求非常簡單:一個用戶(賣家)可以將自己的商品按照給定的價格放到市場上進行銷售,當另一個用戶(買家)購買這個商品時,賣家就會收到錢。另外,本節(jié)實現(xiàn)的市場只根據(jù)商品的價格來進行排序,稍后章節(jié)將介紹如何在市場里面實現(xiàn)其他排序。

為了將被銷售商品的全部信息都存儲到市場里面,我們會將商品的ID和賣家的ID拼接起來,并將拼接的結(jié)果用作成員存儲到市場有序集合里面,而商品的售價則用作成員的分值。通過將所有數(shù)據(jù)都包含在一起,我們極大簡化了實現(xiàn)商品買賣市場所需的數(shù)據(jù)結(jié)構(gòu),并且,因為市場里面的所有商品都按照價格排序,所以針對商品的分頁功能和查找功能都可以很容易地實現(xiàn)。

下表展示了一個只包含數(shù)個商品的市場例子:

鍵名:market 存儲類型:zset
正在銷售的商品.物品的擁有者 物品的價格
ItemA.4 35
ItemC.7 48
ItemE.2 60
ItemG.3 73
上表表示的商品買賣市場,第一行數(shù)據(jù)表示:用戶4正在銷售商品Item,售價為35塊錢

既然我們已經(jīng)知道了實現(xiàn)商品買賣市場所需的數(shù)據(jù)結(jié)構(gòu),那么接下來該考慮如何實現(xiàn)市場的商品上架功能了。

將商品放到市場上銷售

為了將商品放到市場上進行銷售,程序除了要使用multi命令和exec命令之外,還需要配合使用watch命令,有時候甚至還會用到unwatch和discard命令。在用戶使用watch命令對鍵進行監(jiān)視之后,直到用戶執(zhí)行exec命令的這段時間里面,如果有其他客戶端搶先對任何被監(jiān)視的鍵進行了替換、更新或刪除等操作,那么當用戶嘗試執(zhí)行exec命令的時候,事務將失敗并返回一個錯誤(之后用戶可以選擇重試事務或者放棄事務)。通過使用watch、multi/exec、unwatch/discard等命令。程序可以在執(zhí)行某些重要操作的時候,通過確保資金正在使用的數(shù)據(jù)沒有發(fā)生變化來避免數(shù)據(jù)出錯。

什么是discard?

unwatch命令可以在watch命令執(zhí)行之后,multi命令執(zhí)行之前對連接進行重置(reset);同樣地,discard命令也可以在multi命令執(zhí)行之后、exec命令執(zhí)行之前對連接進行重置。這也就是說,用戶在使用watch監(jiān)視一個或多個鍵。接著使用multi開始一個新的事物,并將多個命令入隊到事物隊列之后,仍然可以通過發(fā)送discard命令來取消watch命令并清空所有已入隊命令。本章展示的例子都沒有用到discard,主要原因在于我們已經(jīng)清楚的知道自己是否想要執(zhí)行multi/exec或者unwatch,所以沒有必要在這些例子里面使用discard。

在將一件商品放到市場上進行銷售的時候,程序需要將被銷售的商品添加到記錄市場正在銷售商品的有序集合里面,并且在添加操作執(zhí)行的過程中,監(jiān)視賣家的包裹以確保被銷售的商品的確存在于賣家的包裹當中。

下面代碼展示了這一操作的具體實現(xiàn):

import time
import redis

def list_item(conn,itemid,sellerid,price):
    inventory="inventory:%s"%sellerid
    item="%s.%s"%(itemid,sellerid)
    end=time.time()+5
    pipe=conn.pipeline()

    while time.time()

上面函數(shù)的行為就和我們之前描述的一樣,它首先執(zhí)行一些初始化步驟,然后對賣家的包裹進行監(jiān)視,驗證賣家想要銷售的商品是否仍然存在于賣家的包裹當中,如果是的話,函數(shù)就會將被銷售的商品添加到買賣市場里面,并從賣家的包裹中移除該商品。正如函數(shù)中的while循環(huán)所示,在使用watch命令對包裹進行監(jiān)視的過程中,如果包裹被更新或者修改,那么程序?qū)⒔邮盏藉e誤并進行重試。

下表展示了當Frank(用戶ID為17)嘗試以97塊錢的價格銷售ItemM時,list_item()函數(shù)執(zhí)行的過程:

watch("inventory:17") #監(jiān)視包裹發(fā)生的任何變化
鍵名:inventory:17 類型:set
ItemL
ItemM
ItemN
sismermber("inventory:17","ItemM") #確保被銷售的物品仍然存在于Frank的包裹里面
鍵名:inventory:17 類型:set
ItemL
ItemM
ItemN
鍵名:market 類型:zset
ItemA.4:35
ItemC.7:48
ItemE.2:60
ItemG.3:73
#因為沒有一個Redis命令可以在移除集合元素的同時,將被移除的元素改名并添加到有序集合里面
#所以這里使用了zadd和srem兩個命令來實現(xiàn)這一操作
zadd("market","ItemM.17",97)
srem("inventory:17","ItemM")
鍵名:inventory:17 類型:set
ItemL
ItemN
鍵名:market 類型:zset
ItemA.4:35
ItemC.7:48
ItemE.2:60
ItemG.3:73
ItemM.17:97

因為程序會確保用戶只能銷售他們自己所擁有的,所以在一般情況下,用戶都可以順利地將自己想要銷售的商品添加到商品買賣市場上面,但是正如之前所說,如果用戶的包裹在watch執(zhí)行之后直到exec執(zhí)行之前的這段時間內(nèi)發(fā)送了變化,那么添加操作將執(zhí)行失敗并重試。

在弄懂了怎樣將商品放到市場上銷售之后,接下來讓我們來了解一下怎樣從市場上購買商品。

購買商品

下面的函數(shù)展示了從市場里面購買一件商品的具體方法:程序首先使用watch對市場以及買家的個人信息進行監(jiān)視,然后獲取買家擁有的錢數(shù)以及商品的售價,并檢查買家是否有足夠的錢來購買該商品。如果買家沒有足夠的錢,那么程序會取消事務;相反,如果買家的錢足夠,那么程序首先會將買家支付的錢轉(zhuǎn)移給賣家,然后將售出的商品移動至買家的包裹,并將該商品從市場中移除。當買家的個人信息或者商品買賣市場出現(xiàn)變化而導致WatchError移除出現(xiàn)時,程序進行重試,其中最大重試時間為10秒:

import time
import redis

def purchase_item(conn,buyerid,itemid,sellerid,lprice):
    buyer="users:%s"%buyerid
    seller="users:%s"%sellerid
    item="%s.%s"%(itemid,sellerid)

    inventory="inventory:%s"%buyerid
    end=time.time()+10
    pipe=conn.pipeline()

    while time.time()funds:
                pipe.unwatch()
                return None

            #先將買家支付的錢轉(zhuǎn)移給賣家,然后再將購買的商品移交給買家
            pipe.multi()
            pipe.hincrby(seller,"funds",int(price))
            pipe.hincrby(buyer,"funds",int(-price))
            pipe.sadd(inventory,itemid)
            pipe.zrem("market:",item)
            pipe.execute()
            return True
        except redis.exceptions.WatchError:
            #如果買家的個人信息或者商品買賣市場在交易的過程中出現(xiàn)了變化,那么進行重試。
            pass
    return False

在執(zhí)行商品購買操作定位時候,程序除了需要花費大量時間來準備相關(guān)數(shù)據(jù)之外,還需要對商品買賣市場以及買家的個人信息進行監(jiān)視:監(jiān)視商品買賣市場是為了確保買家想要購買的商品仍然有售(或者在商品已經(jīng)被其他人買走時進行提示),而監(jiān)視買家的個人信息則是為了驗證買家是否有足夠的錢來購買自己想要的商品。

當程序確認商品仍然存在并且買家有足夠的錢的時候,程序會將被購買的商品移動到買家的包裹里面,并將買家支付的錢轉(zhuǎn)移給賣家。

在觀察了市場上展示的商品之后,Bill(用戶ID為27)決定購買Frank在市場上銷售的ItemM,下圖展示了購買操作執(zhí)行期間,數(shù)據(jù)結(jié)構(gòu)的變化:

watch("market:"."users:27") #對物品買賣市場以及Bill的個人信息進行監(jiān)視
鍵名:market 類型:zset
ItemA.4:35
ItemC.7:48
ItemE.2:60
ItemG.3:73
ItemM.17:97
鍵名:users:27 類型:hash
name:Bill
funds:125
鍵名:users:17 類型:hash
name:Bill
funds:43
#驗證物品的售價是否并為改變
#以及Bill是否有足夠的錢來購買該物品
price=pipe.zscore("market:","ItemM.17")
funds=int(pipe.hget("users:27","funds"))
if price!=97 or price>funds:
pipe.sadd("inventory:27","ItemM")
pipe.zrem("market:","ItemM.17")
鍵名:market 類型:zset
ItemA.4:35
ItemC.7:48
ItemE.2:60
ItemG.3:73
鍵名:users:27 類型:hash
name:Bill
funds:28
鍵名:users:17 類型:hash
name:Bill
funds:140

如果商品買賣市場有序集合或者Bill的個人信息在watch和exec執(zhí)行之前發(fā)生了變化,那么purchase_item()將進行重試,或者在重試操作超時之后放棄此購買操作。

為什么Redis沒有實現(xiàn)典型的加鎖功能?

在訪問以寫入為目的的數(shù)據(jù)的時候關(guān)系數(shù)據(jù)會對被訪問的數(shù)據(jù)進行加鎖,知道事務被提交或者被回滾為止。如果有其他客戶端視圖對被加鎖的數(shù)據(jù)行進行寫入,那么該客戶端將被阻塞,直到第一個事務執(zhí)行完畢為止,加速在實際使用中非常有效,基本上所有關(guān)旭數(shù)據(jù)庫都實現(xiàn)了這種加鎖功能,它的缺點在于,持有鎖的客戶端運行越慢,等待解鎖的客戶端被阻塞的時間就越長。

因為加鎖有可能會造成長時間的等待,所以Redis為了盡可能減少客戶端的等待時間,并不會在執(zhí)行watch命令時對數(shù)據(jù)進行加鎖。相反的,Redis只會在數(shù)據(jù)已經(jīng)被其他客戶端搶先修改的情況下,通知執(zhí)行了watch命令的客戶端,這種做法被稱為樂觀鎖,而關(guān)系數(shù)據(jù)庫實際執(zhí)行的加鎖操作則被稱為悲觀鎖。樂觀鎖在實際使用中同樣非常有效,因為客戶端永遠不必花時間去等待第一個獲得鎖的客戶端:他們只需要在自己的事務執(zhí)行失敗時進行重試就可以了。

這一節(jié)介紹了如何組合使用watch、multi和exec命令對多種類型的數(shù)據(jù)進行操作,從而實現(xiàn)游戲中的商品買賣市場。除了目前已有的商品買賣功能之外,我們還可以為整個市場添加商品拍賣和商品限時銷售等功能,或者讓市場支持更多不同類型的商品排序方式,又或者基于后面的技術(shù),給市場添加更高級的搜索和過濾公布。

當有多個客戶端同時對相同的數(shù)據(jù)進行操作時,正確的使用事務可以有效的防止數(shù)據(jù)錯誤的發(fā)生。而接下來的一節(jié)將展示在無須擔心數(shù)據(jù)被其他客戶端修改了的情況下,如果以更快地速度執(zhí)行操作。

上一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第5節(jié):處理系統(tǒng)故障
下一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第7節(jié):非事務型流水線

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/42640.html

相關(guān)文章

  • Python--Redis實戰(zhàn)四章數(shù)據(jù)安全性能保障8節(jié):關(guān)于性能方面的注意事項

    摘要:上一篇文章實戰(zhàn)第四章數(shù)據(jù)安全與性能保障第節(jié)非事務型流水線下一篇文章實戰(zhàn)第五章使用構(gòu)建支持程序第節(jié)使用來記錄日志 上一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第7節(jié):非事務型流水線下一篇文章:Python--Redis實戰(zhàn):第五章:使用Redis構(gòu)建支持程序:第1節(jié):使用Redis來記錄日志 習慣了關(guān)系數(shù)據(jù)庫的用戶在剛開始使用Redis的時候,通常會因為Re...

    neu 評論0 收藏0
  • Python--Redis實戰(zhàn)四章數(shù)據(jù)安全性能保障1節(jié):持久化選項

    摘要:為了讓讀者做好使用構(gòu)建真實軟件的準備,本章將展示維護數(shù)據(jù)安全以及應對系統(tǒng)故障的方法。上一篇文章實戰(zhàn)第三章命令第七節(jié)其他命令下一篇文章實戰(zhàn)第四章數(shù)據(jù)安全與性能保障第節(jié)快照持久化 上一篇文章:Python--Redis實戰(zhàn):第三章:Redis命令:第七節(jié):其他命令下一篇文章:Python--Redis實戰(zhàn):第四章:數(shù)據(jù)安全與性能保障:第2節(jié):快照持久化 前面的幾章介紹了各式各樣的Redi...

    derek_334892 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<