日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转:Memcached详解

發布時間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转:Memcached详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Memcached絕對稱得上是NoSQL老兵!可惜隨著時間的推移,Redis等后起之秀羽翼漸豐,Memcached相比之下已呈頹勢。那我們還用不用學習它?答案是肯定的!畢竟仍然有很多項目依賴著它,如果忽視它,一旦出了問題就只有干瞪眼的份兒了。

網絡上關于Memcached的資料可以說是浩如煙海,其中不乏一些精彩之作,比如說由愛好者翻譯的「Memcached全面剖析」系列文章,在中文社區廣為流傳,雖然已經是幾年前的文章了,但是即便現在讀起來,依然感覺收獲良多,推薦大家多看幾遍:

  • Memcached的基礎
  • 理解Memcached的內存存儲
  • Memcached的刪除機制和發展方向
  • Memcached的分布式算法
  • Memcached的應用和兼容程序
  • 當然,官方Wiki永遠是最權威的資料,即便是里面的ReleaseNotes也不要放過。

    實際應用Memcached時,我們遇到的很多問題都是因為不了解其內存分配機制所致,下面就讓我們以此為開端來開始Memcached之旅吧!

    為了規避內存碎片問題,Memcached采用了名為SlabAllocator的內存分配機制。內存以Page為單位來分配,每個Page分給一 個特定長度的Slab來使用,每個Slab包含若干個特定長度的Chunk。實際保存數據時,會根據數據的大小選擇一個最貼切的Slab,并把數據保存在 對應的Chunk中。如果某個Slab沒有剩余的Chunk了,系統便會給這個Slab分配一個新的Page以供使用,如果沒有Page可用,系統就會觸 發LRU機制,通過刪除冷數據來為新數據騰出空間,這里有一點需要注意的是:LRU不是全局的,而是針對Slab而言的。

    一個Slab可以有多個Page,這就好比在古代一個男人可以娶多個女人;一旦一個Page被分給某個Slab后,它便對Slab至死不渝,猶如古 代那些貞潔的女人。但是女人的數量畢竟是有限的,所以一旦一些男人娶得多了,必然另一些男人就只剩下咽口水的份兒,這在很大程度上增加了社會的不穩定因 素,于是乎我們要解放女性。

    好在Memcached已經意識到解放女性的重要性,新版本中Page可以調配給其它的Slab:

    shell> memcached -o slab_reassign,slab_automove

    換句話說:女人可以改嫁了!這方面,其實Memcached的兒子Twemcache革命得更徹底,他甚至寫了一篇大字報,以事實為依據,痛斥老子的無能,有興趣的可以繼續閱讀:Random Eviciton vs Slab Automove。

    了解Memcached內存使用情況的最佳工具是:Memcached-tool。如果我們發現某個Slab的Evicted不為零,則說明這個Slab已經出現了LRU的情況,這通常是個危險的信號,但也不能一概而論,需要結合Evict_Time來做進一步判斷。

    在Memcached的使用過程中,除了會遇到內存分配機制相關的問題,還有很多稀奇古怪的問題等著你呢,下面我選出幾個有代表性的問題來逐一說明:

    Cache失效后的擁堵問題

    通常我們會為兩種數據做Cache,一種是熱數據,也就是說短時間內有很多人訪問的數據;另一種是高成本的數據,也就說查詢很很耗時的數據。當這些 數據過期的瞬間,如果大量請求同時到達,那么它們會一起請求后端重建Cache,造成擁堵問題,就好象在北京上班做地鐵似的,英文稱之為:stampeding herd,老外這里的用詞還是很形象的。

    一般有如下幾種解決思路可供選擇:

    首先,我們可以主動更新Cache。前端程序里不涉及重建Cache的職責,所有相關邏輯都由后端獨立的程序(比如CRON腳本)來完成,但此方法并不適應所有的需求。

    其次,我們可以通過加鎖來解決問題。以PHP為例,偽代碼大致如下:

    <?phpfunction query() {$data = $cache->get($key);if ($cache->getResultCode() == Memcached::RES_NOTFOUND) {if ($cache->add($lockKey, $lockData, $lockExpiration)) {$data = $db->query();$cache->set($key, $data, $expiration);$cache->delete($lockKey);} else {sleep($interval);$data = query();}}return $data; }?>

    不過這里有一個問題,代碼里用到了sleep,也就是說客戶端會卡住一段時間,就拿PHP來說吧,即便這段時間非常短暫,也有可能堵塞所有的FPM 進程,從而使服務中斷。于是又有人想出了柔性過期的解決方案,所謂柔性過期,指的是設置一個相對較長的過期時間,或者干脆不再直接設置數據的過期時間,取 而代之的是把真正的過期時間嵌入到數據中去,查詢時再判斷,如果數據過期就加鎖重建,如果加鎖失敗,不再sleep,而是直接返回舊數據,以PHP為例, 偽代碼大致如下:

    <?phpfunction query() {$data = $cache->get($key);if (isset($data['expiration']) && $data['expiration'] < $now) {if ($cache->add($lockKey, $lockData, $lockExpiration)) {$data = $db->query();$data['expiration'] = $expiration;$cache->set($key, $data);$cache->delete($lockKey);}}return $data; }?>

    問題到這里似乎已經圓滿解決了,且慢!還有一些特殊情況沒有考慮到:設想一下服務重啟;或者某個Cache里原本沒有的冷數據因為某些情況突然轉換 成熱數據;又或者由于LRU機制導致某些鍵被意外刪除,等等,這些情況都可能會讓上面的方法失效,因為在這些情況里就不存在所謂的舊數據,等待用戶的將是 一個空頁面。

    好在我們還有Gearman這根救命稻草。當需要更新Cache的時候,我們不再直接查詢數據庫,而是把任務拋給Gearman來處理,當并發量比較大的時候,Gearman內部的優化可以保證相同的請求只查詢一次后端數據庫,以PHP為例,偽代碼大致如下:

    <?phpfunction query() {$data = $cache->get($key);if ($cache->getResultCode() == Memcached::RES_NOTFOUND) {$data = $gearman->do($function, $workload, $unique);$cache->set($key, $data, $expiration);}return $data; }?>

    說明:如果多個并發請求的$unique參數一樣,那么實際上Gearman只會請求一次。

    Multiget的無底洞問題

    Facebook在Memcached的實際應用中,發現了Multiget無底洞問 題,具體表現為:出于效率的考慮,很多Memcached應用都已Multiget操作為主,隨著訪問量的增加,系統負載捉襟見肘,遇到此類問題,直覺通 常都是通過增加服務器來提升系統性能,但是在實際操作中卻發現問題并不簡單,新加的服務器好像被扔到了無底洞里一樣毫無效果。

    為什么會這樣?讓我們來模擬一下案發經過,看看到底發生了什么:

    我們使用Multiget一次性獲取100個鍵對應的數據,系統最初只有一臺Memcached服務器,隨著訪問量的增加,系統負載捉襟見肘,于是 我們又增加了一臺Memcached服務器,數據散列到兩臺服務器上,開始那100個鍵在兩臺服務器上各有50個,問題就在這里:原本只要訪問一臺服務器 就能獲取的數據,現在要訪問兩臺服務器才能獲取,服務器加的越多,需要訪問的服務器就越多,所以問題不會改善,甚至還會惡化。

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

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

    如何解決這個棘手的問題呢?只要保證Multiget中的鍵只出現在一臺服務器上即可!比如說用戶名字(user:foo:name),用戶年齡 (user:foo:age)等數據在散列到多臺服務器上時,不應按照完整的鍵名(user:foo:name和user:foo:age)來散列的,而 應按照特殊的鍵(foo)來散列的,這樣就保證了相關的鍵只出現在一臺服務器上。以PHP的 Memcached客戶端為例,有getMultiByKey和setMultiByKey可供使用。

    Nagle和DelayedAcknowledgment的延遲問題

    老實說,這個問題和Memcached沒有半毛錢關系,任何網絡應用都有可能會碰到這個問題,但是鑒于很多人在寫Memcached程序的時候會遇到這個問題,所以還是拿出來聊一聊,在這之前我們先來看看Nagle和DelayedAcknowledgment的含義:

    先看看Nagle:

    假如需要頻繁的發送一些小包數據,比如說1個字節,以IPv4為例的話,則每個包都要附帶40字節的頭,也就是說,總計41個字節的數據里,其中只有1個字節是我們需要的數據。為了解決這個問題,出現了Nagle算法。它規定:如果包的大小滿足MSS,那么可以立即發送,否則數據會被放到緩沖區,等到已經發送的包被確認了之后才能繼續發送。通過這樣的規定,可以降低網絡里小包的數量,從而提升網絡性能。

    再看看DelayedAcknowledgment:

    假如需要單獨確認每一個包的話,那么網絡中將會充斥著無數的ACK,從而降低了網絡性能。為了解決這個問 題,DelayedAcknowledgment規定:不再針對單個包發送ACK,而是一次確認兩個包,或者在發送響應數據的同時捎帶著發送ACK,又或 者觸發超時時間后再發送ACK。通過這樣的規定,可以降低網絡里ACK的數量,從而提升網絡性能。

    Nagle和DelayedAcknowledgment雖然都是好心,但是它們在一起的時候卻會辦壞事。下面我們舉例說說Nagle和DelayedAcknowledgment是如何產生延遲問題的:

    Nagle和DelayedAcknowledgment的延遲問題

    客戶端需要向服務端傳輸數據,傳輸前數據被分為ABCD四個包,其中ABC三個包的大小都是MSS,而D的大小則小于MSS,交互過程如下:

    首先,因為客戶端的ABC三個包的大小都是MSS,所以它們可以耗無障礙的發送,服務端由于DelayedAcknowledgment的存在,會把AB兩個包放在一起來發送ACK,但是卻不會單獨為C包發送ACK。

    接著,因為客戶端的D包小于MSS,并且C包尚未被確認,所以D包不會立即發送,而被放到緩沖區里延遲發送。

    最后,服務端觸發了超時閾值,終于為C包發送了ACK,因為不存在未被確認的包了,所以即便D包小于MSS,也總算熬出頭了,可以發送了,服務端在收到了所有的包之后就可以發送響應數據了。

    說到這里,假如你認為自己已經理解了這個問題的來龍去脈,那么我們嘗試改變一下前提條件:傳輸前數據被分為ABCDE五個包,其中ABCD四個包的 大小都是MSS,而E的大小則小于MSS。換句話說,滿足MSS的完整包的個數是偶數個,而不是前面所說的奇數個,此時又會出現什么情況呢?答案我就不說 了,留給大家自己思考。

    知道了問題的原委,解決起來就簡單了:我們只要設置socket選項為TCP_NODELAY即可,這樣就可以禁用Nagle,以PHP為例:

    <?php$memcached->setOption(Memcached::OPT_TCP_NODELAY, true);?>

    如果大家意猶未盡,可以繼續瀏覽:TCP Performance problems caused by interaction between Nagle’s Algorithm and Delayed ACK。

    希望本文能讓大家在使用Memcached的過程中少走一些彎路。相對于Memcached,其實我更喜歡Redis,從功能上看,Redis可以說是Memcached的超集,不過Memcached自有它存在的價值,即便已呈頹勢,但是:老兵永遠不死,只是慢慢凋零。

    轉載于:https://www.cnblogs.com/dasn/articles/3203233.html

    總結

    以上是生活随笔為你收集整理的转:Memcached详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。