1. 程式人生 > >應對Memcache緩存失效,導致高並發查詢DB

應對Memcache緩存失效,導致高並發查詢DB

post 過期 == 導致 寫入 標記 線程 name memcach

當Memcached緩存失效時,容易出現高並發的查詢DB,導致DB壓力驟然上升。

這篇blog主要是探討如何在緩存將要失效時,及時地更新緩存,而不是如何在緩存失效之後,如何防止高並發的DB查詢。

解決這個問題有四種思路:

比如一個key是aaa,失效時間是30s。

1.定期從DB裏查詢數據,再刷到memcached裏

這種方法有個缺點是,有些業務的key可能是變化的,不確定的。

而且不好界定哪些數據是應該查詢出來放到緩存中的,難以區分冷熱數據。

2.當緩存取到為null時,加鎖去查詢DB,只允許一個線程去查詢DB

這種方式不太靠譜,不多討論。而且如果是多個web服務器的話,還是有可能有並發的操作。

3.在向memcached寫入value時,同時寫入當前機器在時間作為過期時間

當get得到數據時,如果當前時間 - 過期時間 > 5s,則後臺啟動一個任務去查詢DB,更新緩存。

當然,這裏的後臺任務必須保證同一個key,只有一個線程在執行查詢DB的任務,不然這個還是高並發查詢DB。

缺點是要把過期時間和value合在一起序列化,取出數據後,還要反序列化。很不方便。

網上大部分文章提到的都是前面兩種方式,有少數文章提到第3種方式。下面提出一種基於兩個key的方法:

4.兩個key,一個key用來存放數據,另一個用來標記失效時間

比如key是aaa,設置失效時間為30s,則另一個key為expire_aaa,失效時間為25s。

在取數據時,用multiget,同時取出aaa和expire_aaa,如果expire_aaa的value == null,則後臺啟動一個任務去查詢DB,更新緩存。和上面類似。

對於後臺啟動一個任務去查詢DB,更新緩存,要保證一個key只有一個線程在執行,這個如何實現?

對於同一個進程,簡單加鎖即可。拿到鎖的就去更新DB,沒拿到鎖的直接返回。

對於集群式的部署的,如何實現只允許一個任務執行?

這裏就要用到memcached的add命令了。

add命令是如果不存在key,則設置成功,返回true,如果已存在key,則不存儲,返回false。

當get expired_aaa是null時,則add expired_aaa 過期時間由自己靈活處理。比如設置為3秒。

如果成功了,再去查詢DB,查到數據後,再set expired_aaa為25秒。set aaa 為30秒。

綜上所述,來梳理下流程:

比如一個key是aaa,失效時間是30s。查詢DB在1s內。

  • put數據時,設置aaa過期時間30s,設置expire_aaa過期時間25s;
  • get數據時,multiget aaa 和 expire_aaa,如果expired_aaa對應的value != null,則直接返回aaa對應的數據給用戶。如果expire_aaa返回value == null,則後臺啟動一個任務,嘗試add expire_aaa,並設置超時過間為3s。這裏設置為3s是為了防止後臺任務失敗或者阻塞,如果這個任務執行失敗,那麽3秒後,如果有另外的用戶訪問,那麽可以再次嘗試查詢DB。如果add執行成功,則查詢DB,再更新aaa的緩存,並設置expire_aaa的超時時間為25s。

總結:

我個人是傾向於第4種方式的,因為很簡單,直觀。

這種兩個key的方式,還有一個好處,就是數據是自然冷熱適應的。如果是冷數據,30秒都沒有人訪問,那麽數據會過期。

如果是熱門數據,一直有大流量訪問,那麽數據就是一直熱的,而且數據一直不會過期。

原文地址:http://lixiangfeng.com/blog/article/content/12

應對Memcache緩存失效,導致高並發查詢DB