轉載快取

weixin_33716557發表於2018-12-10

https://kb.cnblogs.com/page/69074/) 
這裡收集了經常被問到的關於memcached的問題

叢集架構方面的問題

memcached是怎麼工作的?

Memcached的神奇來自兩階段雜湊(two-stage hash)。Memcached就像一個巨大的、儲存了很多<key,value>對的雜湊表。通過key,可以儲存或查詢任意的資料。

客戶端可以把資料儲存在多臺memcached上。當查詢資料時,客戶端首先參考節點列表計算出key的雜湊值(階段一雜湊),進而選中一個節點;客戶端將請求傳送給選中的節點,然後memcached節點通過一個內部的雜湊演算法(階段二雜湊),查詢真正的資料(item)。

舉個列子,假設有3個客戶端1, 2, 3,3臺memcached A, B, C:
  Client 1想把資料"barbaz"以key "foo"儲存。Client 1首先參考節點列表(A, B, C),計算key "foo"的雜湊值,假設memcached B被選中。接著,Client 1直接connect到memcached B,通過key "foo"把資料"barbaz"儲存進去。  Client 2使用與Client 1相同的客戶端庫(意味著階段一的雜湊演算法相同),也擁有同樣的memcached列表(A, B, C)。
  於是,經過相同的雜湊計算(階段一),Client 2計算出key "foo"在memcached B上,然後它直接請求memcached B,得到資料"barbaz"。

各種客戶端在memcached中資料的儲存形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客戶端實現的雜湊演算法也不一樣。但是,memcached伺服器端的行為總是一致的。

最後,從實現的角度看,memcached是一個非阻塞的、基於事件的伺服器程式。這種架構可以很好地解決C10K problem ,並具有極佳的可擴充套件性。

可以參考A Story of Caching ,這篇文章簡單解釋了客戶端與memcached是如何互動的。

memcached最大的優勢是什麼?

請仔細閱讀上面的問題(即memcached是如何工作的)。Memcached最大的好處就是它帶來了極佳的水平可擴充套件性,特別是在一個巨大的系統中。由於客戶端自己做了一次雜湊,那麼我們很容易增加大量memcached到叢集中。memcached之間沒有相互通訊,因此不會增加 memcached的負載;沒有多播協議,不會網路通訊量爆炸(implode)。memcached的叢集很好用。記憶體不夠了?增加幾臺memcached吧;CPU不夠用了?再增加幾臺吧;有多餘的記憶體?在增加幾臺吧,不要浪費了。

基於memcached的基本原則,可以相當輕鬆地構建出不同型別的快取架構。除了這篇FAQ,在其他地方很容易找到詳細資料的。

看看下面的幾個問題吧,它們在memcached、伺服器的local cache和MySQL的query cache之間做了比較。這幾個問題會讓您有更全面的認識。

memcached和MySQL的query cache相比,有什麼優缺點?

把memcached引入應用中,還是需要不少工作量的。MySQL有個使用方便的query cache,可以自動地快取SQL查詢的結果,被快取的SQL查詢可以被反覆地快速執行。Memcached與之相比,怎麼樣呢?MySQL的query cache是集中式的,連線到該query cache的MySQL伺服器都會受益。

  • 當您修改表時,MySQL的query cache會立刻被重新整理(flush)。儲存一個memcached item只需要很少的時間,但是當寫操作很頻繁時,MySQL的query cache會經常讓所有快取資料都失效。

  • 在多核CPU上,MySQL的query cache會遇到擴充套件問題(scalability issues)。在多核CPU上,query cache會增加一個全域性鎖(global lock), 由於需要重新整理更多的快取資料,速度會變得更慢。

  • 在MySQL的query cache中,我們是不能儲存任意的資料的(只能是SQL查詢結果)。而利用memcached,我們可以搭建出各種高效的快取。比如,可以執行多個獨立的查詢,構建出一個使用者物件(user object),然後將使用者物件快取到memcached中。而query cache是SQL語句級別的,不可能做到這一點。在小的網站中,query cache會有所幫助,但隨著網站規模的增加,query cache的弊將大於利。

  • query cache能夠利用的記憶體容量受到MySQL伺服器空閒記憶體空間的限制。給資料庫伺服器增加更多的記憶體來快取資料,固然是很好的。但是,有了memcached,只要您有空閒的記憶體,都可以用來增加memcached叢集的規模,然後您就可以快取更多的資料。

memcached和伺服器的local cache(比如PHP的APC、mmap檔案等)相比,有什麼優缺點?

首先,local cache有許多與上面(query cache)相同的問題。local cache能夠利用的記憶體容量受到(單臺)伺服器空閒記憶體空間的限制。不過,local cache有一點比memcached和query cache都要好,那就是它不但可以儲存任意的資料,而且沒有網路存取的延遲。

  • local cache的資料查詢更快。考慮把highly common的資料放在local cache中吧。如果每個頁面都需要載入一些數量較少的資料,考慮把它們放在local cached吧。

  • local cache缺少集體失效(group invalidation)的特性。在memcached叢集中,刪除或更新一個key會讓所有的觀察者覺察到。但是在local cache中, 我們只能通知所有的伺服器重新整理cache(很慢,不具擴充套件性),或者僅僅依賴快取超時失效機制。

  • local cache面臨著嚴重的記憶體限制,這一點上面已經提到。

memcached的cache機制是怎樣的?

Memcached主要的cache機制是LRU(最近最少用)演算法+超時失效。當您存資料到memcached中,可以指定該資料在快取中可以呆多久Which is forever, or some time in the future。如果memcached的記憶體不夠用了,過期的slabs會優先被替換,接著就輪到最老的未被使用的slabs。

memcached如何實現冗餘機制?
  不實現!我們對這個問題感到很驚訝。Memcached應該是應用的快取層。它的設計本身就不帶有任何冗餘機制。如果一個memcached節點失去了所有資料,您應該可以從資料來源(比如資料庫)再次獲取到資料。您應該特別注意,您的應用應該可以容忍節點的失效。不要寫一些糟糕的查詢程式碼,寄希望於memcached來保證一切!如果您擔心節點失效會大大加重資料庫的負擔,那麼您可以採取一些辦法。比如您可以增加更多的節點(來減少丟失一個節點的影響),熱備節點(在其他節點down了的時候接管IP),等等。

memcached如何處理容錯的?
  不處理!:) 在memcached節點失效的情況下,叢集沒有必要做任何容錯處理。如果發生了節點失效,應對的措施完全取決於使用者。節點失效時,下面列出幾種方案供您選擇:

  • 忽略它! 在失效節點被恢復或替換之前,還有很多其他節點可以應對節點失效帶來的影響。

  • 把失效的節點從節點列表中移除。做這個操作千萬要小心!在預設情況下(餘數式雜湊演算法),客戶端新增或移除節點,會導致所有的快取資料不可用!因為雜湊參照的節點列表變化了,大部分key會因為雜湊值的改變而被對映到(與原來)不同的節點上。

  • 啟動熱備節點,接管失效節點所佔用的IP。這樣可以防止雜湊紊亂(hashing chaos)。

  • 如果希望新增和移除節點,而不影響原先的雜湊結果,可以使用一致性雜湊演算法(consistent hashing)。您可以百度一下一致性雜湊演算法。支援一致性雜湊的客戶端已經很成熟,而且被廣泛使用。去嘗試一下吧!

  • 兩次雜湊(reshing)。當客戶端存取資料時,如果發現一個節點down了,就再做一次雜湊(雜湊演算法與前一次不同),重新選擇另一個節點(需要注意的時,客戶端並沒有把down的節點從節點列表中移除,下次還是有可能先雜湊到它)。如果某個節點時好時壞,兩次雜湊的方法就有風險了,好的節點和壞的節點上都可能存在髒資料(stale data)。

如何將memcached中item批量匯入匯出?

您不應該這樣做!Memcached是一個非阻塞的伺服器。任何可能導致memcached暫停或瞬時拒絕服務的操作都應該值得深思熟慮。向memcached中批量匯入資料往往不是您真正想要的!想象看,如果快取資料在匯出匯入之間發生了變化,您就需要處理髒資料了;如果快取資料在匯出匯入之間過期了,您又怎麼處理這些資料呢?

因此,批量匯出匯入資料並不像您想象中的那麼有用。不過在一個場景倒是很有用。如果您有大量的從不變化的資料,並且希望快取很快熱(warm)起來,批量匯入快取資料是很有幫助的。雖然這個場景並不典型,但卻經常發生,因此我們會考慮在將來實現批量匯出匯入的功能。

Steven Grimm,一如既往地,,在郵件列表中給出了另一個很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html

但是我確實需要把memcached中的item批量匯出匯入,怎麼辦??

好吧好吧。如果您需要批量匯出匯入,最可能的原因一般是重新生成快取資料需要消耗很長的時間,或者資料庫壞了讓您飽受痛苦。

如果一個memcached節點down了讓您很痛苦,那麼您還會陷入其他很多麻煩。您的系統太脆弱了。您需要做一些優化工作。比如處理"驚群"問題(比如 memcached節點都失效了,反覆的查詢讓您的資料庫不堪重負...這個問題在FAQ的其他提到過),或者優化不好的查詢。記住,Memcached 並不是您逃避優化查詢的藉口。

如果您的麻煩僅僅是重新生成快取資料需要消耗很長時間(15秒到超過5分鐘),您可以考慮重新使用資料庫。這裡給出一些提示:

  • 使用MogileFS(或者CouchDB等類似的軟體)在儲存item。把item計算出來並dump到磁碟上。MogileFS可以很方便地覆寫item,並提供快速地訪問。您甚至可以把MogileFS中的item快取在memcached中,這樣可以加快讀取速度。 MogileFS+Memcached的組合可以加快快取不命中時的響應速度,提高網站的可用性。
  • 重新使用MySQL。MySQL的InnoDB主鍵查詢的速度非常快。如果大部分快取資料都可以放到VARCHAR欄位中,那麼主鍵查詢的效能將更好。從memcached中按key查詢幾乎等價於MySQL的主鍵查詢:將key 雜湊到64-bit的整數,然後將資料儲存到MySQL中。您可以把原始(不做雜湊)的key儲存都普通的欄位中,然後建立二級索引來加快查詢...key被動地失效,批量刪除失效的key,等等。

上面的方法都可以引入memcached,在重啟memcached的時候仍然提供很好的效能。由於您不需要當心"hot"的item被memcached LRU演算法突然淘汰,使用者再也不用花幾分鐘來等待重新生成快取資料(當快取資料突然從記憶體中消失時),因此上面的方法可以全面提高效能。

關於這些方法的細節,詳見部落格:http://dormando.livejournal.com/495593.html

memcached是如何做身份驗證的?
  沒有身份認證機制!memcached是執行在應用下層的軟體(身份驗證應該是應用上層的職責)。memcached的客戶端和伺服器端之所以是輕量級的,部分原因就是完全沒有實現身份驗證機制。這樣,memcached可以很快地建立新連線,伺服器端也無需任何配置。

如果您希望限制訪問,您可以使用防火牆,或者讓memcached監聽unix domain socket。

memcached的多執行緒是什麼?如何使用它們?
  執行緒就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本擁有了多執行緒模式。多執行緒模式允許memcached能夠充分利用多個CPU,並在CPU之間共享所有的快取資料。memcached使用一種簡單的鎖機制來保證資料更新操作的互斥。相比在同一個物理機器上執行多個memcached例項,這種方式能夠更有效地處理multi gets。

如果您的系統負載並不重,也許您不需要啟用多執行緒工作模式。如果您在執行一個擁有大規模硬體的、龐大的網站,您將會看到多執行緒的好處。

更多資訊請參見:http://code.sixapart.com/svn/memcached/trunk/server/doc/threads.txt

簡單地總結一下:命令解析(memcached在這裡花了大部分時間)可以執行在多執行緒模式下。memcached內部對資料的操作是基於很多全域性鎖的(因此這部分工作不是多執行緒的)。未來對多執行緒模式的改進,將移除大量的全域性鎖,提高memcached在負載極高的場景下的效能。

memcached能接受的key的最大長度是多少?
  key的最大長度是250個字元。需要注意的是,250是memcached伺服器端內部的限制,如果您使用的客戶端支援"key的字首"或類似特性,那麼key(字首+原始key)的最大長度是可以超過250個字元的。我們推薦使用使用較短的key,因為可以節省記憶體和頻寬。

memcached對item的過期時間有什麼限制?
  過期時間最大可以達到30天。memcached把傳入的過期時間(時間段)解釋成時間點後,一旦到了這個時間點,memcached就把item置為失效狀態。這是一個簡單但obscure的機制。

memcached最大能儲存多大的單個item?
  1MB。如果你的資料大於1MB,可以考慮在客戶端壓縮或拆分到多個key中。

為什麼單個item的大小被限制在1M byte之內?
  啊...這是一個大家經常問的問題!

簡單的回答:因為記憶體分配器的演算法就是這樣的。

詳細的回答:Memcached的記憶體儲存引擎(引擎將來可插拔...),使用slabs來管理記憶體。記憶體被分成大小不等的slabs chunks(先分成大小相等的slabs,然後每個slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小依次從一個最小數開始,按某個因子增長,直到達到最大的可能值。

如果最小值為400B,最大值是1MB,因子是1.20,各個slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B ...

slab中chunk越大,它和前面的slab之間的間隙就越大。因此,最大值越大,記憶體利用率越低。Memcached必須為每個slab預先分配記憶體,因此如果設定了較小的因子和較大的最大值,會需要更多的記憶體。

還有其他原因使得您不要這樣向memcached中存取很大的資料...不要嘗試把巨大的網頁放到mencached中。把這樣大的資料結構load和unpack到記憶體中需要花費很長的時間,從而導致您的網站效能反而不好。

如果您確實需要儲存大於1MB的資料,你可以修改slabs.c:POWER_BLOCK的值,然後重新編譯memcached;或者使用低效的malloc/free。其他的建議包括資料庫、MogileFS等。

我可以在不同的memcached節點上使用大小不等的快取空間嗎?這麼做之後,memcached能夠更有效地使用記憶體嗎?
  Memcache客戶端僅根據雜湊演算法來決定將某個key儲存在哪個節點上,而不考慮節點的記憶體大小。因此,您可以在不同的節點上使用大小不等的快取。但是一般都是這樣做的:擁有較多記憶體的節點上可以執行多個memcached例項,每個例項使用的記憶體跟其他節點上的例項相同。

什麼是二進位制協議,我該關注嗎?

關於二進位制最好的資訊當然是二進位制協議規範:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol

二進位制協議嘗試為端提供一個更有效的、可靠的協議,減少客戶端/伺服器端因處理協議而產生的CPU時間。
根據Facebook的測試,解析ASCII協議是memcached中消耗CPU時間最多的環節。所以,我們為什麼不改進ASCII協議呢?

在這個郵件列表的thread中可以找到一些舊的資訊:http://lists.danga.com/pipermail/memcached/2007-July/004636.html

memcached的記憶體分配器是如何工作的?為什麼不適用malloc/free!?為何要使用slabs?
  實際上,這是一個編譯時選項。預設會使用內部的slab分配器。您確實確實應該使用內建的slab分配器。最早的時候,memcached只使用malloc/free來管理記憶體。然而,這種方式不能與OS的記憶體管理以前很好地工作。反覆地malloc/free造成了記憶體碎片,OS最終花費大量的時間去查詢連續的記憶體塊來滿足malloc的請求,而不是執行memcached程式。如果您不同意,當然可以使用malloc!只是不要在郵件列表中抱怨啊:)

slab分配器就是為了解決這個問題而生的。記憶體被分配並劃分成chunks,一直被重複使用。因為記憶體被劃分成大小不等的slabs,如果item的大小與被選擇存放它的slab不是很合適的話,就會浪費一些記憶體。Steven Grimm正在這方面已經做出了有效的改進。

郵件列表中有一些關於slab的改進(power of n 還是 power of 2)和權衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.htmlhttp://lists.danga.com/pipermail/memcached/2007-March/003753.html

如果您想使用malloc/free,看看它們工作地怎麼樣,您可以在構建過程中定義USE_SYSTEM_MALLOC。這個特性沒有經過很好的測試,所以太不可能得到開發者的支援。

更多資訊:http://code.sixapart.com/svn/memcached/trunk/server/doc/memory_management.txt

memcached是原子的嗎?
  當然!好吧,讓我們來明確一下:
  所有的被髮送到memcached的單個命令是完全原子的。如果您針對同一份資料同時傳送了一個set命令和一個get命令,它們不會影響對方。它們將被序列化、先後執行。即使在多執行緒模式,所有的命令都是原子的,除非程式有bug:)
  命令序列不是原子的。如果您通過get命令獲取了一個item,修改了它,然後想把它set回memcached,我們不保證這個item沒有被其他程式(process,未必是作業系統中的程式)操作過。在併發的情況下,您也可能覆寫了一個被其他程式set的item。

memcached 1.2.5以及更高版本,提供了gets和cas命令,它們可以解決上面的問題。如果您使用gets命令查詢某個key的item,memcached會給您返回該item當前值的唯一標識。如果您覆寫了這個item並想把它寫回到memcached中,您可以通過cas命令把那個唯一標識一起傳送給memcached。如果該item存放在memcached中的唯一標識與您提供的一致,您的寫操作將會成功。如果另一個程式在這期間也修改了這個item,那麼該item存放在memcached中的唯一標識將會改變,您的寫操作就會失敗。

通常,基於memcached中item的值來修改item,是一件棘手的事情。除非您很清楚自己在做什麼,否則請不要做這樣的事
情。


2657562-574793cea19f48b7.png
image.png

2657562-dba260e400860293.png
image.png

2657562-5eddad3e8455ffa0.png
image.png

Memcached的CAS機制的實現

轉載自:http://1.simpcl.sinaapp.com/?p=1

CAS,又稱Compare-and-Swap,代表一種原子操作。

Memcached的CAS機制解決的問題及其原理:

1. 實現了Check-and-Set原子操作功能;
2. 其使用方式為:首先使用gets指令一個key-value及key對應value的版本號;其次操作產生新的value值;最後使用cas指令重新提交key-value,並附帶剛剛獲得到的版本號;
3. 當服務端判斷cas操作中的版本號不是最新的時,則認為改key的值已經被修改,本次cas操作失敗。程式設計人員通過CAS機制可實現自增和自減的原子操作;

Memcached的CAS機制的實現:

1. Memcached CAS的核心是一個64-bit唯一的版本。服務端會為每個key生成一個64-bit唯一的整數值作為版本號,並儲存在item結構體中,具體的儲存結構見結構體item的定義:

</article>

相關文章