Redis逾時時的驚群現象

來源:互聯網
上載者:User
背景:

Redis的快取資料庫是為快速響應用戶端減輕資料庫壓力的有效手段之一,其中有一種功能是失效緩衝,其優點是可以不週期性釋放使用頻率低的業務空間而增加有限的記憶體,但對於同步資料庫和緩衝之間的資料來說需要面臨一個問題就是:在並發量比較大的情況下當一個快取資料失效之後會導致同時有多個並發線程去向後端資料庫發起請求去擷取同一業務資料(每次緩衝失效的時候,我們理想的話,是有1個線程去資料庫取資料,然後把這1份資料寫入redis中就可以,但是在高並發的環境下,可能會有100個線程去資料庫擷取資料,然後也會把這100份資料寫到redis中,導致資料庫壓力大,有大量的緩衝失效。),這樣如果在一段時間內同時產生了大量的緩衝,然後在另外一段時間內又有大量的緩衝失效,這樣就會導致後端資料庫的壓力陡增,這種現象就可以稱為“緩衝到期產生的驚群現象”。


處理思路:

緩衝內真實失效時間time1

  緩衝value中存放人為失效時間戳記 :time2 ( time2 永遠小於time1)

  緩衝value對應的lock鎖(就是一個與value 對應的 另一個key),主要用於判斷是第幾個線程來讀取redis的value

   當把資料庫的資料寫入緩衝後,這時有用戶端第一次來讀取緩衝,取當前系統時間:system_time 如果system_time >= time2  則認為預設緩衝已到期(如果system_time< time1 則還沒真實失效 ),這時再擷取value的lock鎖,調用redis的incr函數(單線程自增函數)判斷是第幾個擷取鎖的線程,若且唯若是第一個線程時返回1,以後都逐漸遞增。第一個訪問的線程到資料庫中擷取最新值重新放入緩衝並刪除lock鎖的key,並重新設定時間戳記;在刪除lock之前所有訪問value用戶端線程擷取lock的value都大於1,這些線程仍然讀取redis中的舊值,而不會集中訪問資料庫。


解決問題python代碼:

import jsonimport pickleimport redisimport timeimport mathclass RedisApi(redis.Redis):    def get_json(self, name):        """        如果和老的api設定的值可以用這個方法取        :param name:        :return:        """        value = self.get(name)        if value is None:            return None        try:            return json.loads(value)        except Exception:            if value.startswith(b'!'):                try:                    return json.loads(pickle.loads(value[1:]))                except pickle.PickleError:                    return None            else:                return json.loads(value.decode().replace("'", '"'))    def get_bylock(self, key):        """        避免redis逾時時的驚群現象,請必須配合 `set_bylock` 使用        調用方法與`get`一樣        """        lock_key = key + ".lock"        data = self.get(key)        current = int(time.time())        if not data:            return None        else:            real_data = json.loads(data)            # 如果人為設定的逾時時間逾時了            if real_data['expireat'] <= current:                # 如果擷取到鎖                if self.set(lock_key, "x", ex=2, nx=True):                    return None                # 如果沒擷取到鎖                else:                    return real_data['data']            else:                return real_data['data']    def set_bylock(self, key, data, expire_time):        """        避免redis逾時時的驚群現象,請必須配合`get_bylock`使用        調用方法與`setex`一樣        """        current = int(time.time())        real_data = {'data': data, 'expireat': current + expire_time - math.ceil(expire_time / 2)}        self.setex(key, json.dumps(real_data), expire_time)

RedisAPI類繼承了redis.Redis類,用法盒Redis類一樣,只是使用者在使用.get()和.setex()的方法時,對應的替換成.get_bylock()和.set_bylock()方法


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.