再說memcache的multiget hole(無底洞)


關鍵詞:multiget hole,memcache
適用於:java,php

基礎知識背景:
1)multiget 是什么:
    multiget 指的是從 memcache(或其他分布式緩存) 一次性獲得多個鍵值,一般由 memcached client 自行實現。如 PHP-memcache-client 提供了  Memcached::getMulti 函數。調用示范如下:
<?php
$items = array(
    'key1' => 'value1',
    'key2' => 'value2',
    'key3' => 'value3'
);
$m->setMulti($items);
$result = $m->getMulti(array('key1', 'key3', 'badkey'), $cas);
var_dump($result, $cas);
?>
2)”multiget hole“詳解:
『讓我們來模擬一下案發經過,看看到底發生了什么:

我們使用 Multiget 一次性獲取100個鍵對應的數據。

系統最初只有一台 Memcached 服務器,隨着訪問量的增加,系統負載捉襟見肘,於是我們又增加了一台 Memcached 服務器,數據散列到兩台服務器上。

開始那100個鍵在兩台服務器上各有50個。

問題就在這里:原本只要訪問一台服務器就能獲取的數據,現在要訪問兩台服務器才能獲取;服務器加的越多,需要訪問的服務器就越多,所以問題不會改善,甚至還會惡化。

不過,作為被告方,Memcached官方開發人員對此進行了辯護

請求多台服務器並不是問題的症結,真正的原因在於客戶端在請求多台服務器時是並行的還是串行的!問題是很多客戶端,包括Libmemcached在內,在處理Multiget多服務器請求時,使用的是串行的方式!也就是說,先請求一台服務器,然后等待響應結果,接着請求另一台,結果導致客戶端操作時間累加,請求堆積,性能下降

如何解決這個棘手的問題呢?只要保證 Multiget 中的鍵只出現在一台服務器上即可!(注:事實上這可不容易做到。)

3)以前鄭昀在文章里說過,spymemcached 某版本又是如何實現 Multiget(即getBulk)的

  1. 給一組 key,[1,2,3,4,5]。
  2. 先算一下這些key都落在哪些節點上(通過 KetamaNodeLocator 的 public Iterator<MemcachedNode> getSequence(String k)。Now that we know how many servers it breaks down into.);
  3. 此時,得到一個map:<Node1,[1,3]>;<Node2,[2,4]>;<Node3,[5]>;
  4. 遍歷這個map,從每一個 mc node 讀出對應的 keys(即單節點的 multiget 操作);一個Node一個Node串行的;
  5. 拼成一個大map<key,value>返回。
這樣就是一個 node 復一個 node 串行檢索的,雖然做了優化,但是如果涉及的 mc nodes 數量多,線程勢必長時間阻塞在等待網絡資源返回上。
(注:
spymemcached 后來的版本不再按 node 串行輪詢,而是並行:第一步,將本次操作構造成一個針對每個 node 的 Operation 對象,加入連接對象中;第二步,在連接對象中,將所有的 node 操作放入 addedQueue 隊列,然后觸發 Selector 方式異步非阻塞的執行。)

現象:
    某中心每天很多個讀取 memcache 鍵值超時,報錯如下:
Caused by: java.util.concurrent.ExecutionException: net.spy.memcached.internal. CheckedOperationTimeoutException: Operation timed out. - failing node: mcN.domain.name

         at net.spy.memcached.internal.OperationFuture.get(OperationFuture.java:172)

         at net.spy.memcached.internal.GetFuture.get(GetFuture.java:62)

分析:
    在 memcache 集群節點較多情況下,
    特別是在一次性獲取成百上千鍵值的極端場景面前,
    服務端輕則請求超時,重則宕機
 
    無論是先計算 keys 都散列到哪些 mc nodes 上了,還是直接輪詢 memcached::get ,或者說並行提交給各個 mc nodes 然后異步等待,
    假設每個 mc get 耗時2~3毫秒,一次性取 2000 個keys,都將阻塞線程長達2~6秒之久,這是身為服務所不能容忍的。
 
    所以,必須約定,適度使用批量獲取鍵值功能,100個鍵值就到頂了,別因小失大。
    
    當然,也有業務場景繞不開 multiget,那么,一是按照 facebook 所說,此時需要的是更多的 CPU,把緩存數據復制一份到另一個 memcache 集群上,一個集群負責讀一半的 keys;二是按照火丁所說,最好保證批量查的這批鍵值都在同一個 mc node 上。
 

參考資源:
1)火丁,2012, memcache 二三事兒
3)iteye,2012, 通過NIO實現Memcached multi get

贈圖幾枚:
死鎖分析
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM