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

python實(shí)現(xiàn)redis三種cas事務(wù)操作

 更新時(shí)間:2017年12月19日 10:46:44   作者:辰辰沉沉沉  
本篇文章主要介紹了python實(shí)現(xiàn)redis三種cas事務(wù)操作,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

cas全稱是compare and set,是一種典型的事務(wù)操作。

簡(jiǎn)單的說(shuō),事務(wù)就是為了存取數(shù)據(jù)庫(kù)中同一數(shù)據(jù)時(shí)不破壞操作的隔離性和原子性,從而保證數(shù)據(jù)的一致性。

一般數(shù)據(jù)庫(kù),比如MySql是如何保證數(shù)據(jù)一致性的呢,主要是加鎖,悲觀鎖。比如在訪問(wèn)數(shù)據(jù)庫(kù)某條數(shù)據(jù)的時(shí)候,會(huì)用SELECT FOR UPDATE ,這MySql就會(huì)對(duì)這條數(shù)據(jù)進(jìn)行加鎖,直到事務(wù)被提交(COMMIT),或者回滾(ROLLBACK)。如果此時(shí),有其他事務(wù)對(duì)被加鎖的數(shù)據(jù)進(jìn)行寫入,那么該事務(wù)將會(huì)被阻塞,直到第一個(gè)事務(wù)完成為止。它的缺點(diǎn)在于:持有鎖的事務(wù)運(yùn)行越慢,等待解鎖的事務(wù)阻塞時(shí)間就越長(zhǎng)。并且容易產(chǎn)生死鎖(前面有篇文章有講解死鎖)!

本文會(huì)介紹三種redis實(shí)現(xiàn)cas事務(wù)的方法,并會(huì)解決下面的虛擬問(wèn)題:
維護(hù)一個(gè)值,如果這個(gè)值小于當(dāng)前時(shí)間,則設(shè)置為當(dāng)前時(shí)間;如果這個(gè)值大于當(dāng)前時(shí)間,則設(shè)置為當(dāng)前時(shí)間+30。簡(jiǎn)單的單線程環(huán)境下代碼如下:

# 初始化
r = redis.Redis()
if not r.exists("key_test"):
  r.set("key_test", 0)

def inc():
  count = int(r.get('key_test')) + 30 #1
  # 如果值比當(dāng)前時(shí)間小,則設(shè)置為當(dāng)前時(shí)間
  count = max(count, int(time.time())) #2
  r.set('key_test', count) #3
  return count

很簡(jiǎn)單的一段代碼,在單線程環(huán)境下可以跑的很歡,但顯然,是無(wú)法移植到多線程或者是多進(jìn)程環(huán)境的(進(jìn)程A和B同時(shí)運(yùn)行到#1,獲取了相同的count值,然后運(yùn)行#2#3,會(huì)導(dǎo)致count值總共只增加了30)。而為了能在多進(jìn)程環(huán)境下運(yùn)行,我們需要引入一些其他的東西。

py-redis本身自帶的事務(wù)操作

redis有這么幾個(gè)和事務(wù)相關(guān)的命令,multi,exec,watch。通過(guò)這幾個(gè)命令,可以實(shí)現(xiàn)‘將多個(gè)命令打包,然后一次性、按順序執(zhí)行,且不會(huì)被終端'。事務(wù)會(huì)從MULTI開始,執(zhí)行EXEC后觸發(fā)事件。另外,我們還需要WATCH,watch可以監(jiān)視任意數(shù)量的鍵,當(dāng)在調(diào)用EXEC執(zhí)行事務(wù)時(shí),如果任意一個(gè)鍵被修改了,整個(gè)事務(wù)不會(huì)執(zhí)行。

下邊是使用redis本身的事務(wù)解決cas問(wèn)題的代碼。

class CasNormal(object):
  def __init__(self, host, key):
    self.r = redis.Redis(host)
    self.key = key
    if not self.r.exists(self.key):
      self.r.set(self.key, 0)

  def inc(self):
    with self.r.pipeline() as pipe:
      while True:
        try:
          #監(jiān)視一個(gè)key,如果在執(zhí)行期間被修改了,會(huì)拋出WatchError
          pipe.watch(self.key)
          next_count = 30 + int(pipe.get(self.key))
          pipe.multi()
          if next_count < int(time.time()):
            next_count = int(time.time())
          pipe.set(self.key, next_count)
          pipe.execute()
          return next_count
        except WatchError:
          continue
        finally:
          pipe.reset()

代碼也不復(fù)雜,引入了之前說(shuō)到的multi,exec,watch,如果對(duì)事務(wù)操作比較熟悉的同學(xué),可以很容易看出來(lái),這是一個(gè)樂(lè)觀鎖的操作(咱們假設(shè)沒(méi)人競(jìng)爭(zhēng)來(lái)著,每次去拿數(shù)據(jù)的時(shí)候都不會(huì)上鎖,真有人來(lái)改了再說(shuō)。)樂(lè)觀鎖在高并發(fā)的情況下會(huì)顯得很無(wú)力,文末的性能對(duì)比會(huì)顯示這個(gè)問(wèn)題。

使用基于redis的悲觀鎖

悲觀鎖,就是很悲觀的鎖,每次拿數(shù)據(jù)都會(huì)假設(shè)別人也要拿,先給鎖起來(lái),用完再把鎖釋放掉。redis本身沒(méi)有實(shí)現(xiàn)悲觀鎖,但我們可以先用redis實(shí)現(xiàn)一個(gè)悲觀鎖。

ok,咱們現(xiàn)在有悲觀鎖了,做起事來(lái)也有底氣了,根據(jù)上邊的代碼,咱們只要加上@ synchronized注釋就能保證同一時(shí)間只有一個(gè)進(jìn)程在執(zhí)行。下邊是基于悲觀鎖的解決方案。

lock_conn = redis.Redis("localhost")

class CasLock(object):
  def __init__(self, host, key):
    self.r = redis.Redis(host)
    self.key = key
    if not self.r.exists(self.key):
      self.r.set(self.key, 0)

  @synchronized(lock_conn, "lock", 10)
  def inc(self):
    next_count = 30 + int(self.r.get(self.key))
    if next_count < int(time.time()):
      next_count = int(time.time())
    self.r.set(self.key, next_count)
    return next_count

代碼看上去少多了(因?yàn)橐肓藄ynchronized...)

基于lua腳本實(shí)現(xiàn)

上邊兩種方法都是用鎖來(lái)實(shí)現(xiàn)的,鎖的實(shí)現(xiàn)總會(huì)出現(xiàn)競(jìng)爭(zhēng)的問(wèn)題,區(qū)別無(wú)非是出現(xiàn)競(jìng)爭(zhēng)了咋辦的問(wèn)題。使用redis lua腳本的實(shí)現(xiàn),可以直接把這個(gè)cas操作當(dāng)成一個(gè)<b>原子操作</b>。

我們知道,redis本身的一系列操作,都是原子操作,且redis會(huì)按順序執(zhí)行所有收到的命令。先看代碼

class CasLua(object):
  def __init__(self, host, key):
    self.r = redis.Redis(host)
    self.key = key
    if not self.r.exists(self.key):
      self.r.set(self.key, 0)
    self._lua = self.r.register_script("""
    local next_count = redis.call('get',KEYS[1]) + ARGV[1]
    ARGV[2] = tonumber(ARGV[2])
    if next_count < ARGV[2] then
      next_count = ARGV[2]
    end
    redis.call('set',KEYS[1],next_count)
    return tostring(next_count)
        """)

  def inc(self):
    return int(self._lua([self.key], [30, int(time.time())]))

這里先注冊(cè)了這個(gè)腳本,后邊可以直接去使用他。關(guān)于redis lua腳本的文章有不少,感興趣的可以去搜搜看,這邊就不贅述了。

性能對(duì)比

這邊的測(cè)試只是一個(gè)非常簡(jiǎn)單的測(cè)試(不過(guò)還是能看出效果來(lái)的),測(cè)試換機(jī)就是自己的開發(fā)機(jī),數(shù)字看個(gè)大小就行了。

分別測(cè)了三種操作在單線程,五個(gè)線程,十個(gè)線程,五十個(gè)線程情況下,進(jìn)行1000次操作各自的表現(xiàn),時(shí)間如下

     optimistic Lock pessimistic lock  lua
1thread       0.43       0.71 0.35
5thread       5.80       3.10 0.62
10thread      17.80       5.60 1.30
50thread      245.00       29.60 6.50

依次是redis本身事務(wù)實(shí)現(xiàn)的樂(lè)觀鎖,基于redis實(shí)現(xiàn)的悲觀鎖以及l(fā)ua實(shí)現(xiàn)。

在比較悲觀鎖和樂(lè)觀鎖之前,需要先說(shuō)明一點(diǎn),這邊的測(cè)試對(duì)樂(lè)觀鎖不是很公平,樂(lè)觀鎖本身就是假設(shè)不會(huì)有很多的并發(fā)的。在單線程情況下,悲觀鎖要差一些。單線程下,不存在競(jìng)爭(zhēng)關(guān)系,悲觀鎖耗時(shí)長(zhǎng)僅因?yàn)槭嵌嗔艘淮蝦edis的網(wǎng)絡(luò)交互。隨著線程的增加,悲觀鎖的性能逐漸變好,畢竟悲觀鎖本身就是為了解決這種高并發(fā)高競(jìng)爭(zhēng)的環(huán)境而誕生的。在50線程的時(shí)候,樂(lè)觀鎖的實(shí)現(xiàn)單次操作的時(shí)間要0.245秒,非??植溃绻巧a(chǎn)環(huán)境,幾乎都不能用了。

至于lua的性能,快的不可思議,幾乎就是線性增加。(50線程的情況下,平均的1000次完成時(shí)間是6.5s,換言之,6.5秒內(nèi)執(zhí)行了50 * 1000次cas操作)。

以上測(cè)試都是本地redis,本地測(cè)試,如果redis是遠(yuǎn)端的,網(wǎng)絡(luò)交互時(shí)間會(huì)增加,lua優(yōu)勢(shì)會(huì)更加明顯。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 對(duì)python中l(wèi)ist的拷貝與numpy的array的拷貝詳解

    對(duì)python中l(wèi)ist的拷貝與numpy的array的拷貝詳解

    今天小編就為大家分享一篇對(duì)python中l(wèi)ist的拷貝與numpy的array的拷貝詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Python制表符\t的具體使用

    Python制表符\t的具體使用

    本文詳細(xì)介紹了Python中制表符(\t)的作用,包括代碼縮進(jìn)和數(shù)據(jù)對(duì)齊,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Python sort 自定義函數(shù)排序問(wèn)題

    Python sort 自定義函數(shù)排序問(wèn)題

    這篇文章主要介紹了Python sort 自定義函數(shù)排序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Flask-WTF表單的使用方法

    Flask-WTF表單的使用方法

    這篇文章主要介紹了Flask-WTF表單的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 大家都說(shuō)好用的Python命令行庫(kù)click的使用

    大家都說(shuō)好用的Python命令行庫(kù)click的使用

    這篇文章主要介紹了大家都說(shuō)好用的Python命令行庫(kù)click的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • Python實(shí)現(xiàn)對(duì)特定列表進(jìn)行從小到大排序操作示例

    Python實(shí)現(xiàn)對(duì)特定列表進(jìn)行從小到大排序操作示例

    這篇文章主要介紹了Python實(shí)現(xiàn)對(duì)特定列表進(jìn)行從小到大排序操作,涉及Python文件讀取、計(jì)算、正則匹配、排序等相關(guān)操作技巧,需要的朋友可以參考下
    2019-02-02
  • python基于C/S模式實(shí)現(xiàn)聊天室功能

    python基于C/S模式實(shí)現(xiàn)聊天室功能

    這篇文章主要為大家詳細(xì)介紹了python基于C/S模式實(shí)現(xiàn)聊天室功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • python interpret庫(kù)訓(xùn)練模型助力機(jī)器學(xué)習(xí)

    python interpret庫(kù)訓(xùn)練模型助力機(jī)器學(xué)習(xí)

    這篇文章主要為大家介紹了python interpret庫(kù)訓(xùn)練模型功能特性,為你的機(jī)器學(xué)習(xí)提供便捷的路徑,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • 基于PyQt5實(shí)現(xiàn)狀態(tài)欄(statusBar)顯示和隱藏功能

    基于PyQt5實(shí)現(xiàn)狀態(tài)欄(statusBar)顯示和隱藏功能

    這篇文章主要為大家詳細(xì)介紹了如何利用PyQt5實(shí)現(xiàn)狀態(tài)欄顯示和隱藏功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-08-08
  • win10下Python3.6安裝、配置以及pip安裝包教程

    win10下Python3.6安裝、配置以及pip安裝包教程

    下面小編就為大家?guī)?lái)一篇win10下Python3.6安裝、配置以及pip安裝包教程。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10

最新評(píng)論

光山县| 平顺县| 灵武市| 宝坻区| 东兰县| 呈贡县| 白银市| 阿巴嘎旗| 汽车| 同江市| 巴林右旗| 新乡县| 清镇市| 米林县| 惠安县| 昭苏县| 伊宁市| 新竹市| 布尔津县| 兴安盟| 阜南县| 宜昌市| 鄄城县| 墨江| 西昌市| 无为县| 文化| 九江市| 平安县| 阿勒泰市| 揭西县| 高台县| 永嘉县| 宜都市| 苍溪县| 读书| 绩溪县| 喀什市| 阿巴嘎旗| 修水县| 隆回县|