(1)Memcached是什麼?php
- Memcached是一個開源的,支持高性能,高併發的分佈式內存緩存系統,由C語言編寫,總共2000多行代碼。從軟件名稱上看,前3個字符「Mem」就是內存的意思,而接下來的後面5個字符「cache」就是緩存的意思,最後一個字符d,是daemon的意思,表明是服務器端守護進程模式服務。
- Memcached服務分爲服務器端和客戶端兩部分,其中,服務器端軟件的名字形如Memcached-1.4.24.tar.gz,客戶端軟件的名字形如Memcache-2.25.tar.gz
- Memcached軟件誕生於2003年,最初由LiveJournal的Brad Fitzpatrick開發完成。Memcache是整個項目的名稱,而Memcached是服務器端的主程序名,因其協議簡單,應用部署方便,且支持高併發,所以被互聯網企業普遍使用,直到如今仍然如此。其官方網站地址:http://memcached.org/.
(2)Memcached的做用css
- 傳統場景中,多數Web應用都將數據保存到關係型數據庫中(例如:MySQL),Web服務器從中讀取數據並在瀏覽器中顯示。但隨着數據量的增大,訪問的集中,關係型數據庫的負擔就會出現加劇,響應緩慢,致使網站打開延遲等問題,影響用戶體驗。
- 這時就須要Memcached軟件出馬了。使用Memcached的主要目的是,經過在自身內存中緩存關係型數據庫的查詢結果,減小數據庫被訪問的次數,以提升動態Web應用的速度,提升網站架構的併發能力和可擴展性。
- Memcached服務的運行原理是經過在事先規劃好的系統內存空間中臨時緩存數據庫中的各種數據,以達到減小前端業務服務對數據庫的直接高併發訪問,從而提高大規模網站集羣中動態服務的併發訪問能力。
-生產場景的Memcached服務通常被用來保存網站中常常被讀取的對象或數據,就像咱們的客戶端瀏覽器也會把常常訪問的網頁緩存起來同樣,經過內存緩存來存取對象或數據要比磁盤存取快不少,由於磁盤是機械的,所以,在當今的IT企業中,Memcached的應用範圍很普遍。
Memcached是一種內存緩存軟件,在工做中常常用來緩存數據庫的查詢數據,數據被緩存在事先與分配的Memcached管理的內存中,能夠經過API或命令的方式存取內存中緩存的這些數據,Memcached服務內存中緩存的數據就像一張巨大的hash表,每條數據都是以key-value對的形式存在。html
從邏輯上來講,當程序訪問後端數據庫獲取數據時會優先訪問Memcached緩存,若是緩存中有數據就直接返回給客戶端用戶,若是沒有合適的數據(沒有命中),再去後端的數據庫讀取數據,讀取到須要的數據後,就會把數據返回給客戶端,同時還會把讀取到的數據緩存到Memcached內存中,這樣客戶端用戶再次請求相同的數據時就會直接讀取Memcached緩存的數據了,這就大大地減輕了後端數據庫的壓力,並提升了整個網站的響應速度,提高了用戶體驗。前端
展現了Memcached緩存系統和後端數據庫系統的協做流程node
如上圖所示:使用Memcached緩存查詢的數據來減小數據庫壓力的具體工做流程以下:python
(1)Web程序首先檢查客戶端請求的數據是否在Memcached緩存中存在,若是存在,直接把請求的數據返回給客戶端,此時再也不請求後端數據庫。mysql
(2)若是請求的數據在Memcached緩存中不存在,則程序會去請求數據庫服務,把從數據庫中取到的數據返回給客戶端,同時把新取到的數據緩存一份到Memcached緩存中。nginx
具體流程以下:redis
(1)當程序更新或刪除數據時,會首先處理後端數據庫中的數據。算法
(2)在處理後端數據庫中數據的同時,也會通知Memcached,告訴它對應的舊數據失效,從而保證Memcached中緩存的數據始終和數據庫中一致,這個數據一致性很是重要,也是大型網站分佈式緩存集羣最頭疼的問題所在。
(3)若是是在高併發讀寫場合,除了要程序通知Memcached過時的緩存失效外,還可能要經過相關機制,例如在數據庫上部署相關程序(如在數據庫中設置觸發器使用UDFs),實現當數據庫有更新時就把數據更新到Memcached服務中,這樣一來,客戶端在訪問新數據時,因預先把更新過的數據庫數據複製到Memcached中緩存起來了,因此能夠減小第一次查詢數據庫帶來的訪問壓力,提高Memcached中緩存的命中率,甚至新浪門戶還會把持久化存儲Redis作成MySQL數據庫的從庫,實現真正的主從複製。
下圖爲Memcached網站做爲緩存應用更新數據的流程
下圖爲Memcached服務做爲緩存應用經過相關軟件更新數據的流程
在生產工做中,網站Web服務器做爲緩存應用更新數據的方案更爲經常使用,即由網站程序負責更新Memcached緩存。
(1)完整數據緩存
例如:電商的商品分類功能不是常常變更的,所以能夠事先放到Memcached裏,而後再對外提供數據訪問。這個過程被稱之爲「數據預熱」。
此時只需讀取緩存,無需讀取數據庫就能獲得Memcached緩存裏的全部商品分類數據了,因此數據庫的訪問壓力就會大大下降。
爲何商品分類數據能夠事先放在緩存裏呢?
由於,商品分類幾乎都是由內部人員管理的,若是須要更新數據,更新數據庫後,就能夠把數據同時更新到Memcached裏。
若是把商品分類數據作成靜態化文件,而後,經過在前端Web緩存或者使用CDN加速效果更好。
(2)熱點數據緩存
熱點數據緩存通常是用於由用戶更新的商品,例如淘寶的賣家,在賣家新增商品後,網站程序就會把商品寫入後端數據庫,同時把這部分數據,放入Memcached內存中,下一次訪問這個商品的請求就直接從Memcached內存中取走了。這種方法用來緩存網站熱點的數據,即利用Memcached緩存常常被訪問的數據。
提示:
這個過程能夠經過程序實現,也能夠在數據庫上安裝相關軟件進行設置,直接由數據庫把內容更新到Memcached中,就至關於Memcached是MySQL的從庫同樣。
即把客戶端用戶請求多個前端應用服務集羣產生的session會話信息,統一存儲到一個Memcached緩存中。因爲session會話數據是存儲在內存中的,因此速度很快。
下圖爲Memcached服務在企業集羣架構中的常見工做位置:
Memcached做爲高併發,高性能的緩存服務,具備以下特色:
benet-->36,key=benet,value=36 yunjisuan-->28,key=yunjisuan,value=28 #經過benet key能夠獲取到36值,同理經過yunjisuan key能夠獲取28值
下面是利用Web端程序實現Memcached分佈式的簡單代碼:
"memcached_servers" ==>array( '10.4.4.4:11211', '10.4.4.5:11211', '10.4.4.6:11211',
下面使用Tengine反向代理負載均衡的一致性哈希算法實現分佈式Memcached的配置。
http { upstream test { consistent_hash $request_uri; server 127.0.0.1:11211 id=1001 weight=3; server 127.0.0.1:11212 id=1002 weight=10; server 127.0.0.1:11213 id=1003 weight=20; } }
提示:
Tengine是淘寶網開源的Nginx的分支,上述代碼來自:
http://tengine.taobao.org/document_cn/http_upstream_consistent_hash_cn.html
Memcached是一套相似C/S模式架構的軟件,在服務器端啓動Memcached服務守護進程,能夠指定監聽本地的IP地址,端口號,併發訪問鏈接數,以及分配了多少內存來處理客戶端請求。
Memcached軟件是由C語言來實現的,所有代碼僅有2000多行,採用的是異步epoll/kqueue非阻塞I/O網絡模型,其實現方式是基於異步的libevent事件單進程,單線程模式。使用libevent做爲事件通知機制,應用程序端經過指定服務器的IP地址及端口,就能夠鏈接Memcached服務進行通訊。
- 須要被緩存的數據以key/value鍵值對的形式保存在服務器端預分配的內存區中,每一個被緩存的數據都有惟一的標識key,操做Memcached中的數據就是經過這個惟一標識的key進行的。緩存到Memcached中的數據僅放置在Memcached服務預分配的內存中,而非存儲在Memcached服務器所在的磁盤上,所以存取速度很是快。
- 因爲Memcached服務自身沒有對緩存的數據進行持久化存儲的涉及,所以,在服務器端的Memcached服務進程重啓以後,存儲在內存中的這些數據就會丟失。且當內存中緩存的數據容量達到啓動時設定的內存值時,也會自動使用LRU算法刪除過時的數據。
- 開發Memcached的初衷僅是經過內存緩存提高訪問效率,並無過多考慮數據的永久存儲問題。所以,若是使用Memcached做爲緩存數據服務,要考慮數據丟失後帶來的問題,例如:是否能夠從新生成數據,還有,在高併發場合下緩存宕機或重啓會不會致使大量請求直接到數據庫,致使數據庫沒法承受,最終致使網站架構雪崩等。
Memcached採用了以下機制:
多線程處理時採用的是pthread(POSIX)線程模式。
若要激活多線程,可在編譯時指定:./configure --enable-threads
鎖機制不夠完善
負載太重時,能夠開啓多線程(-t 線程數爲CPU核數)
- 當須要大面積重啓Memcached時,首先要在前端控制網站入口的訪問流量,而後,重啓Memcached集羣並進行數據預熱,全部數據都預熱完畢以後,再逐步放開前端網站入口的流量。
- 爲了知足Memcached服務數據能夠持久化存儲的需求,在較早時期,新浪網基於Memcached服務開發了一款NoSQL軟件,名字爲MemcacheDB,實現了在緩存的基礎上增長了持久存儲的特性,不過目前逐步被更優秀的Redis軟件取代了。
若是因爲機房斷電或者搬遷服務器集羣到新機房,那麼啓動集羣服務器時,必定要從網站集羣的後端依次往前端開啓,特別是開啓Memcached緩存服務器時要提早預熱。
(1)Malloc內存管理機制
在講解Memcached內存管理機制前,先來了解malloc
- malloc的全稱是memory allocation,中文名稱動態內存分配,當沒法知道內存具體位置的時候,想要綁定真正的內存空間,就須要用到動態分配內存。
- 早期的Memcached內存管理是經過malloc分配的內存實現的1,使用完後經過free來回收內存。這種方式容易產生內存碎片並下降操做系統對內存的管理效率。所以,也會加劇操做系統內存管理器的負擔,最壞的狀況下,會致使操做系統比Memcached進程自己還慢,爲了解決上述問題,Slab Allocator內存分配機制就誕生了。
(2)Slab內存管理機制
如今的Memcached是利用Slab Allocation機制來分配和管理內存的,過程以下:
1)提早將大內存分配大小爲1MB的若干個slab,而後針對每一個slab再進行小對象填充,這個小對象稱爲chunk,避免大量重複的初始化和清理,減輕了內存管理器的負擔。
Slab Allocation內存分配的原理是按照預先規定的大小,將分配給Memcached服務的內存預先分割成特定長度的內存塊(chunk),再把尺寸相同的內存塊(chunk)分紅組(chunks slab class),這些內存塊不會釋放,能夠重複利用,以下圖所示。
2)新增數據對象存儲時。因Memcached服務器中保存着slab內空閒chunk的列表,他會根據該列表選擇chunk,而後將數據緩存於其中。當有數據存入時,Memcached根據接收到的數據大小,選擇最適合數據大小的slab分配一個能存下這個數據的最小內存塊(chunk)。例如:有100字節的一個數據,就會被分配存入下面112字節的一個內存塊中,這樣會有12字節被浪費,這部分空間就不能被使用了,這也是Slab Allocator機制的一個缺點。
Slab Allocator還可重複使用已分配的內存,即分配到的內存不釋放,而是重複利用。
(3)Slab Allocation的主要術語
(4)Slab 內存管理機制特色
下面對Mc的內存管理機制進行一個小結
(1)chunk存儲item浪費空間
Slab Allocator解決了當初的內存碎片問題,但新的機制也給Memcached帶來了新的問題。這個問題就是,因爲分配的是特定長度的內存,所以沒法有效利用分配的內存。例如,將100字節的數據緩存到128字節的chunk中,剩餘的28字節就浪費了,以下圖所示:
避免浪費內存的辦法是,預先計算出應用存入的數據大小,或把同一業務類型的數據存入一個Memcached服務器中,確保存入的數據大小相對均勻,這樣就能夠減小內存的浪費。
還有一種辦法是,在啓動時指定「-f」參數,能在某種程度上控制內存組之間的大小差別。在應用中使用Memcached時,一般能夠不從新設置這個參數,即便用默認值1.25進行部署便可。若是想優化Memcached對內存的使用,能夠考慮從新計算數據的預期平均長度,調整這個參數來得到合適的設置值,命令以下:
-f <factor>chunk size growth factor (default:1.25)!
(2)Slab尾部剩餘空間
- 假設在classid=40中,兩個chunk佔用了1009384byte,那麼就有1048576-1009384=39192byte會被浪費掉。解決辦法:規劃slab大小=chunk大小*n整數倍。
在啓動Memcached時指定Growth Factor因子(經過 -f 選項),就能夠在某種程度上控制每組Slab之間的差別。默認值1.25。可是,在該選項出現以前,這個因子曾經被固定爲2,稱爲2「powers of 2」策略。讓咱們用之前的設置,以verbose模式啓動Memcached試試看:
#memcached -f 2 w
下面是啓動後的verbose輸出:
slab class 1:chunk size 128 perslab 8192 slab class 2:chunk size 256 perslab 4096 slab class 3:chunk size 512 perslab 2048 slab class 4:chunk size 1024 perslab 1024 slab class 5:chunk size 2048 perslab 512 slab class 6:chunk size 4096 perslab 256 slab class 7:chunk size 8192 perslab 128 slab class 8:chunk size 16384 perslab 64 slab class 9:chunk size 32768 perslab 32 slab class 10:chunk size 65536 perslab 16 slab class 11:chunk size 131072 perslab 8 slab class 12:chunk size 262144 perslab 4 slab class 13:chunk size 524288 perslab 2
可見,從128字節的組開始,組的大小依次增大爲原來的2倍。這樣設置的問題是,Slab之間的差異比較大,有些狀況下就至關浪費內存。所以,爲儘可能減小內存浪費,兩年前追加了growth factor這個選項。
來看看如今的默認設置(f=1.25)時的輸出:
slab class 1:chunk size 88 perslab 11915 <---88*11915=1048520 slab class 2:chunk size 112 perslab 9362 slab class 3:chunk size 144 perslab 7281 slab class 4:chunk size 184 perslab 5698 slab class 5:chunk size 232 perslab 4519 slab class 6:chunk size 296 perslab 3542 slab class 7:chunk size 376 perslab 2788 slab class 8:chunk size 472 perslab 2221 slab class 9:chunk size 592 perslab 1771 slab class 10:chunk size 744 perslab 1409 <---744*1409=1048520
- 此時每一個Slab的大小是同樣的,即1048520,1MB。組間的差距比因子爲2時小得多,可見,這個值越小,Slab中chunk size的差距就越小,內存浪費也就越小。可見,默認值1.25更適合緩存幾百字節的對象。從上面的輸出結果來看,可能會以爲有些計算偏差,這些偏差是爲了保持字節數的對齊而故意設置的。
- 當使用Memcached或是直接使用默認值進行部署時,最好是從新計算一下數據的預期平均長度,調整growth factor,以得到最恰當的設置。內存是珍貴的資源,浪費就太惋惜了。
(1)Memcached懶惰檢測對象過時機制
- 首先要知道,Memcached不會主動檢測item對象是否過時,而是在進行get操做時檢查item對象是否過時以及是否應該刪除!
- 由於不會主動檢測item對象是否過時,天然也就不會釋放已分配給對象的內存空間了,除非爲添加的數據設定過時時間或內存緩存滿了,在數據過時後,客戶端不能經過key取出它的值,其存儲空間將被從新利用。
- Memcached使用的這種策略爲懶惰檢測對象過時策略,即本身不監控存入的key/value對是否過時,而是在獲取key值時查看記錄的時間戳(sed key flag exptime bytes),從而檢查key/value對空間是否過時。這種策略不會在過時檢測上浪費CPU資源。
(2)Memcached懶惰刪除對象機制
- 當刪除item對象時,通常不會釋放內存空間,而是作刪除標記,將指針放入slot回收插槽,下次分配的時候直接使用。
- Memcached在分配空間時,會優先使用已通過期的key/value對空間;若分配的內存空間佔滿,Memcached就會使用LRU算法來分配空間,刪除最近最少使用的key/value對,從而將其空間分配給新的key/value對。在某些狀況下(完整緩存),若是不想使用LRU算法,那麼能夠經過「-M」參數來啓動Memcached,這樣,Memcached在內存耗盡時,會返回一個報錯信息,以下:
-M rerurn error on memory exhausted(rather than removing items)
下面針對Memcached刪除機制進行一個小結
Memcached的安裝比較簡單,支持Memcached的平臺常見的有Linux,FreeBSD,Solaris,Windows。這裏以Centos6.5爲例進行講解。
系統安裝環境以下:
[root@cache01 ~]# cat /etc/redhat-release CentOS release 6.5 (Final) [root@cache01 ~]# uname -r 2.6.32-431.el6.x86_64 [root@cache01 ~]# uname -m x86_64
安裝Memcached前須要先安裝libevent,有關libevent的內容在前文已經介紹,此處用yum命令安裝libevent。操做命令以下:
[root@cache01 ~]# yum -y install libevent libevent-devel nc #自帶光盤裏沒有,須要公網yum源 [root@cache01 packages]# rpm -qa libevent libevent-devel nc libevent-1.4.13-4.el6.x86_64 libevent-devel-1.4.13-4.el6.x86_64 nc-1.84-24.el6.x86_64
操做命令以下:
[root@cache01 ~]# yum -y install memcached #此處可經過光盤安裝
提示:
形如「memcache-2.2.7.tgz」文件名的軟件爲客戶端源代碼軟件,而形如「memcached-1.4.24.tar.gz」的文件爲服務器端的源代碼軟件。
啓動Memcached的命令以下:
[root@cache01 ~]# which memcached #查看Memcached命令路徑 /usr/bin/memcached [root@cache01 ~]# memcached -m 16m -p 11211 -d -u root -c 8192 #啓動第一個Memcached實例 [root@cache01 ~]# netstat -antup | grep 11211 #查看啓動狀況 tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 1303/memcached tcp 0 0 :::11211 :::* LISTEN 1303/memcached udp 0 0 0.0.0.0:11211 0.0.0.0:* 1303/memcached udp 0 0 :::11211 :::* 1303/memcached [root@cache01 ~]# ps -ef | grep memcached | grep -v grep #查看Memcached進程 root 1303 1 0 08:49 ? 00:00:00 memcached -m 16m -p 11211 -d -u root -c 8192 #啓動第二個Memcached實例 [root@cache01 ~]# memcached -m 16m -p 11212 -d -u root -c 8192 [root@cache01 ~]# ps -ef | grep memcached | grep -v grep root 1303 1 0 08:49 ? 00:00:00 memcached -m 16m -p 11211 -d -u root -c 8192 root 1317 1 0 08:55 ? 00:00:00 memcached -m 16m -p 11212 -d -u root -c 8192 #可把上述兩個實例的啓動命令放入/etc/rc.local,以便下次開機能夠自啓動 [root@cache01 ~]# tail -2 /etc/rc.local memcached -m 16m -p 11211 -d -u root -c 8192 memcached -m 16m -p 11212 -d -u root -c 8192
進程與鏈接設置:
|命令參數|說明|
|--|--|
|-d|以守護進程(daemon)方式運行服務|
|-u|指定運行Memcached的用戶,若是當前用戶爲root,須要使用此參數指定用戶|
|-l|指定Memcached進程監聽的服務器IP地址,能夠不設置此參數|
|-p(小寫)|指定Memcached服務監聽TCP端口號。默認爲11211|
|-P(大寫)|設置保存Memcached的pid文件($$),保存PID到指定文件|
內存相關設置:
併發鏈接設置:
測試參數:
其餘選項可經過「memcached -h」命令來顯示。
向Memcached中添加數據時,注意添加的數據通常爲鍵值對的形式,例如:key1-->values1,key2-->values2
這裏把Memcached添加,查詢,刪除等的命令和MySQL數據庫作一個基本類比,見下表:
(1)經過printf配合nc向Memcached中寫入數據,命令以下:
[root@cache01 ~]# printf "set key1 0 0 5\r\nbenet\r\n" | nc 127.0.0.1 11211 STORED #出現STORED表示成功添加key1及對應的數據 #若是set命令的字節是6,那麼後面就要6個字符(字節)。不然插入數據就會不成功。示例以下: [root@cache01 ~]# printf "set key1 0 0 4\r\nbenet\r\n" | nc 127.0.0.1 11211 CLIENT_ERROR bad data chunk ERROR #經過printf配置nc從Memcached中讀取數據,命令以下: [root@cache01 ~]# printf "get key1\r\n" | nc 127.0.0.1 11211 VALUE key1 0 5 benet #這就是讀取到的key1對應額值 #經過printf配合nc從Memcached中刪除數據,命令以下: [root@cache01 ~]# printf "delete key1\r\n" | nc 127.0.0.1 11211 DELETED [root@cache01 ~]# printf "get key1\r\n" | nc 127.0.0.1 11211 END
提示:
推薦使用上述方法測試操做Memcached
(2)經過telnet命令寫入數據時,具體步驟以下:
1)安裝telnet工具 yum -y install telnet 2)經過telnet向Memcached中寫入數據 [root@cache01 ~]# which telnet /usr/bin/telnet [root@cache01 ~]# telnet 127.0.0.1 11211 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. set user01 0 0 7 #寫入數據 Welcome STORED get user01 #瀏覽數據 VALUE user01 0 7 Welcome END delete user01 #刪除數據 DELETED get user01 #再瀏覽數據,數據被刪除 END quit #退出 Connection closed by foreign host.
如下爲操做Memcached的相關命令基本語法:
set key1 0 0 6 \r\n benet \r\n
<command name><key><flags><exptime><bytes><datablock><string><datablock> STORED <status>
下表爲操做Memcached相關命令的詳細說明:
單實例關閉Memcached的方法以下:
[root@cache01 ~]# ps -ef | grep memcached | grep -v grep root 1473 1 0 11:11 ? 00:00:00 memcached -m 16m -p 11211 -d -u root -c 8192 [root@cache01 ~]# killall memcached或pkill memcached [root@cache01 ~]# netstat -antup | grep 11211
若啓動了多個實例Memcached,使用killall或pkill方式就會同時關閉這些實例!所以最好在啓動時增長-P參數指定固定的pid文件,這樣便於管理不一樣的實例。示例以下:
[root@cache01 ~]# memcached -m 16m -p 11211 -d -u root -c 8192 -P /var/run/11211.pid [root@cache01 ~]# memcached -m 16m -p 11212 -d -u root -c 8192 -P /var/run/11212.pid [root@cache01 ~]# ps -ef | grep memcached | grep -v grep root 1486 1 0 11:14 ? 00:00:00 memcached -m 16m -p 11211 -d -u root -c 8192 -P /var/run/11211.pid root 1493 1 0 11:14 ? 00:00:00 memcached -m 16m -p 11212 -d -u root -c 8192 -P /var/run/11212.pid #此時,便可經過kill命令關閉Memcached [root@cache01 ~]# kill `cat /var/run/11211.pid` [root@cache01 ~]# netstat -antup | grep 11211 [root@cache01 ~]# netstat -antup | grep 11212 tcp 0 0 0.0.0.0:11212 0.0.0.0:* LISTEN 1493/memcached tcp 0 0 :::11212 :::* LISTEN 1493/memcached udp 0 0 0.0.0.0:11212 0.0.0.0:* 1493/memcached udp 0 0 :::11212 :::* 1493/memcached
- 在企業實際工做中,通常是開發人員提出需求,說要部署一個Memcached數據緩存。運維人員在接到這個不肯定的需求後,須要和開發人員深刻溝通,進而肯定要將內存指定爲多大,或者和開發人員商量如何根據具體業務來指定內存緩存的大小。此外,還要肯定業務的重要性,進而決定是否採起負載均衡,分佈式緩存集羣等架構,最後肯定要使用多大的併發鏈接數等。
- 對於運維人員,部署Memcached通常就是安裝Memcached服務器端,把服務啓動起來,作好監控,配好開機自啓動,基本就OK了,客戶端的PHP程序環境通常在安裝LNMP環境時都會提早安裝Memcached客戶端插件,Java程序環境下,開發人員會用第三方的JAR包直接鏈接Memcached服務。
詳細搭建過程略,請同窗們參閱以前的LNMP章節
前面已經提過,Memcached分爲服務器端軟件和客戶端插件兩部分,這裏是Memcached客戶端PHP的擴展插件(memcache-2.2.7.tgz)在PHP環境中的安裝,用於訪問Memcached服務器端數據。
PHP的Memcached擴展插件下載地址爲:http://pecl.php.net/package/memcache
[root@LNMP ~]# wget -q http://pecl.php.net/get/memcache-2.2.7.tgz [root@LNMP ~]# ls anaconda-ks.cfg install.log install.log.syslog memcache-2.2.7.tgz [root@LNMP ~]# tar xf memcache-2.2.7.tgz -C /usr/src/ [root@LNMP ~]# cd /usr/src/memcache-2.2.7/ [root@LNMP memcache-2.2.7]# /usr/local/php/bin/phpize Configuring for: PHP Api Version: 20090626 Zend Module Api No: 20090626 Zend Extension Api No: 220090626 [root@LNMP memcache-2.2.7]# ./configure --enable-memcache --with-php-config=/usr/local/php/bin/php-config [root@LNMP memcache-2.2.7]# make && make install [root@LNMP ~]# ls -l /usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/ total 244 -rwxr-xr-x. 1 root root 246576 Aug 6 12:53 memcache.so #最後生成了memcache.so模塊就表示memcache擴展插件成功安裝
修改PHP的配置文件php.ini,加入Memcache客戶端的配置,命令以下:
[root@LNMP ~]# cd /usr/local/php/lib/ [root@LNMP lib]# vim php.ini #添加以下兩行內容到php.ini文件結尾 [root@LNMP lib]# tail -2 php.ini extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20090626/" extension=memcache.so
1)檢查php-fpm語法:
[root@LNMP lib]# /usr/local/php/sbin/php-fpm -t [06-Aug-2017 13:03:04] NOTICE: configuration file /usr/local/php5.3.28/etc/php-fpm.conf test is successful
2)重啓fpm,命令以下:
[root@LNMP lib]# pkill php-fpm [root@LNMP lib]# netstat -antup | grep 9000 [root@LNMP lib]# /usr/local/php/sbin/php-fpm [root@LNMP lib]# netstat -antup | grep 9000 tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 46848/php-fpm
3)打開瀏覽器訪問phpinfo頁面,若出現以下圖所示內容,則表示Memcache客戶端安裝成功
下面爲簡單的PHP程序鏈接Memcached測試腳本
[root@LNMP bbs]# cat op_mem.php <?php #PHP開始標識 $memcache = new Memcache; #建立一個Memcache對象 $memcache->connect('192.168.0.240','11211') or die ("Could not connect Mc server"); #鏈接Memcached服務器 $memcache->set('key','yunjisuan book'); #設置一個變量到內存中 $get=$memcache->get('key'); #從內存中取出key echo $get; #輸出key值到屏幕 ?> #PHP結束標識 [root@LNMP bbs]# ls index.html op_mem.php test_info.php test_mysql.php
Linux本地測試:
[root@LNMP bbs]# /usr/local/php/bin/php op_mem.php #調用php解析器 yunjisuan book #對應的key的值
windows訪問測試:
出現上述測試結果,就表示LNMP環境鏈接Memcached服務成功。
運維人員通常可經過在命令行執行「telnet ip port」的方式登錄到Memcached,而後執行一些管理的命令,除了telnet外,nc命令也是一個不錯的管理Memcached服務的命令!
有關「telnet ip port」命令前文已經介紹過了,下面將重點講解經過nc管理及監控Memcached的一些常見操做。
如下經過腳本模擬用過戶插入及刪除數據來監控Memcached服務是否正常的示例
腳本內容以下:
[root@cache01 scripts]# pwd /server/scripts [root@cache01 scripts]# cat mon_mc.sh #!/bin/bash export MemcachedIp=$1 export MemcachedPort=$2 export NcCmd="nc $MemcachedIp $MemcachedPort" export MD5="3fe396c01f03425cb5e2da8186eb090d" USAGE(){ echo "$0 MemcachedIp MemcachedPort" exit 3 } [ $# -ne 2 ] && USAGE printf "set $MD5 0 0 9\r\nyunjisuan\r\n" | $NcCmd >/dev/null 2>&1 if [ $? -eq 0 ];then if [ `printf "get $MD5\r\n"|$NcCmd|grep yunjisuan|wc -l` -eq 1 ];then echo "Memcached status is ok" printf "delete $MD5\r\n"|$NcCmd >/dev/null 2>&1 exit 0 else echo "Memcached status is error" exit 2 fi else echo "Could not connect Mc server" exit 2 fi
Memcached服務正常的狀況下,測試檢驗腳本
[root@cache01 scripts]# sh mon_mc.sh 127.0.0.1 11211 Memcached status is ok
關閉Memcached服務,再測試腳本
[root@cache01 scripts]# kill `cat /var/run/11211.pid` [root@cache01 scripts]# netstat -antup | grep 11211 [root@cache01 scripts]# sh mon_mc.sh 127.0.0.1 11211 Could not connect Mc server
最後開啓Memcached服務,測試檢驗腳本
[root@cache01 scripts]# memcached -m 16m -p 11211 -d -u root -c 8192 -P /var/run/11211.pid [root@cache01 scripts]# netstat -antup | grep 11211 tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 2053/memcached tcp 0 0 :::11211 :::* LISTEN 2053/memcached udp 0 0 0.0.0.0:11211 0.0.0.0:* 2053/memcached udp 0 0 :::11211 :::* 2053/memcached [root@cache01 scripts]# sh mon_mc.sh 127.0.0.1 11211 Memcached status is ok
經過上面的測試,咱們發現使用監控腳本監控Memcached服務是否異常的運行是正確的。
查看Memcached服務運行狀態信息的nc命令以下:
[root@cache01 scripts]# printf "stats\r\n"|nc 127.0.0.1 11211 STAT pid 2053 STAT uptime 225 STAT time 1502044003 STAT version 1.4.4 STAT pointer_size 64 STAT rusage_user 0.002999 STAT rusage_system 0.005999 STAT curr_connections 10 STAT total_connections 14 STAT connection_structures 11 STAT cmd_get 1 STAT cmd_set 1 STAT cmd_flush 0 STAT get_hits 1 STAT get_misses 0 STAT delete_misses 0 STAT delete_hits 1 STAT incr_misses 0 STAT incr_hits 0 STAT decr_misses 0 STAT decr_hits 0 STAT cas_misses 0 STAT cas_hits 0 STAT cas_badval 0 STAT auth_cmds 0 STAT auth_errors 0 STAT bytes_read 141 STAT bytes_written 77 STAT limit_maxbytes 16777216 STAT accepting_conns 1 STAT listen_disabled_num 0 STAT threads 4 STAT conn_yields 0 STAT bytes 0 STAT curr_items 0 STAT total_items 1 STAT evictions 0 END
除了上述輸出的所有狀態之外,Memcached還支持經過如下命令的輸入來查看Memcached的部分運行狀態信息。目前管理Memcached的命令見下表:
例如,要查看Memcached的統計信息,可先執行「telnet ip 監聽端口」命令,登錄成功以後執行stats命令,具體過程以下:
[root@LNMP ~]# which telnet /usr/bin/telnet [root@LNMP ~]# telnet 192.168.0.240 11211 Trying 192.168.0.240... Connected to 192.168.0.240. Escape character is '^]'. stats STAT pid 2053 #啓動的進程id STAT uptime 682 #到目前1位置啓動了多少秒 STAT time 1502044460 STAT version 1.4.4 #Memcached的版本信息 STAT pointer_size 64 STAT rusage_user 0.007998 STAT rusage_system 0.014997 STAT curr_connections 10 #當前的併發鏈接數 STAT total_connections 15 #總的鏈接數 STAT connection_structures 11 STAT cmd_get 1 #執行的get命令的次數 STAT cmd_set 1 #執行的set命令的次數 STAT cmd_flush 0 #執行flush命令的次數 STAT get_hits 1 #get的命中數 STAT get_misses 0 #get的非命中數 STAT delete_misses 0 STAT delete_hits 1 STAT incr_misses 0 STAT incr_hits 0 STAT decr_misses 0 STAT decr_hits 0 STAT cas_misses 0 STAT cas_hits 0 STAT cas_badval 0 STAT auth_cmds 0 STAT auth_errors 0 STAT bytes_read 148 STAT bytes_written 848 STAT limit_maxbytes 16777216 #容許使用的最大內存容量 STAT accepting_conns 1 STAT listen_disabled_num 0 STAT threads 4 STAT conn_yields 0 STAT bytes 0 STAT curr_items 0 STAT total_items 1 STAT evictions 0 END
使用printf及nc命令獲取狀態信息更佳,由於無需交互,可使用如下腳本批量操做
[root@LNMP ~]# printf "stats\r\n"|nc 192.168.0.240 11211 STAT pid 2053 STAT uptime 1270 STAT time 1502045048 STAT version 1.4.4 STAT pointer_size 64 STAT rusage_user 0.011998 STAT rusage_system 0.029995 STAT curr_connections 10 STAT total_connections 24 STAT connection_structures 11 #如下省略若干...
Memcached狀態信息的詳細說明,同窗們能夠經過該表來了解相關信息:
運維人員除了經過命令行管理Memcached之外,還可使用不少的第三方開源管理工具,例如:經過memadmin工具管理及監控Memcached狀態,軟件的名稱爲「memadmin-1.0.12.tar.gz」,這個軟件的部署十分簡單,功能很是強大,可是依賴於PHP環境,推薦同窗們初學Memcached時使用。
由於這個軟件是基於PHP程序的,所以,須要有PHP的環境才行,本章已經準備好了LNMP的環境,所以,能夠簡單地將上述「memadmin-1.0.12.tar.gz」解壓到虛擬主機站點目錄下,這裏仍是以bbs虛擬主機爲例進行講解。
部署的關鍵命令以下:
[root@LNMP ~]# ls -l memadmin-1.0.12.tar.gz -rw-r--r--. 1 root root 196734 Aug 6 14:55 memadmin-1.0.12.tar.gz [root@LNMP ~]# tar xf memadmin-1.0.12.tar.gz [root@LNMP ~]# mv memadmin /usr/local/nginx/html/bbs/
LNMP服務器的nginx.conf配置文件內容以下:
[root@LNMP nginx]# cat conf/nginx.conf worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name www.yunjisuan.com; location / { root html/www; index index.html index.htm; } } server { listen 80; server_name bbs.yunjisuan.com; location / { root html/bbs; index index.html index.htm; } location ~ .*\.(php|php5)$ { root html/bbs; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } } }
打開瀏覽器輸入域名:http://bbs.yunjisuan.com/memadmin/index.php
程序的管理頁面以下圖所示:
提示:
用戶名和密碼都是admin
登錄以後的頁面以下圖所示:
而後點擊管理,進入下圖:
memadmin對單個或少許的Memcached服務器數量的監控,維護管理仍是很直觀容易的。
剩下的功能很容易懂,同窗們本身玩。
下面給你們介紹一個Memcached服務應用優化的企業案例。用戶訪問網站打開頁面很慢,經運維人員排查後,發現MySQL數據庫負載很高,load值大概爲20~30,以下:
[root@LNMP ~]# uptime 10:41:01 up 15:02, 4 users, load average: 20, 15, 10 #登錄數據庫後,使用「show full processlist;」查看,或者在Linux命令行使用下面的命令查看: mysql -uroot -p123123 -e "show full processlist"|grep -vi sleep
發現數據庫中像「LIKE‘%杜冷丁%’」這樣的SQL語句特別多,致使數據庫負載很高,咱們都知道像LIKE‘%杜冷丁%’這樣的SQL語句對於MySQl數據庫來講,利用索引沒有太大的優化餘地。
打開該網站首頁查看,發現該首頁有一個搜索框,根據前面查看的SQL語句形式,能夠推測上述「LIKE%杜冷丁%」應該是搜索框的語句帶來的結果。過後從這個公司的數據庫維護人員處獲得了證明,請問若是是你,你該如何優化解決當前的問題呢?
大概的優化方案思路以下:
- 其中1,2,3是短時間的方案,簡單容易實施。4,5,6是長期的方案目標。
- 這個案例的最終問題是,網站前端有爬蟲爬站,經過分析Web日誌獲取到爬站的IP,臨時封掉後,MySQL數據庫負載馬上降低了不少,網站服務恢復正常。
下圖是針對方案6給出的大型網站搜索集羣架構邏輯案例圖
Memcached服務的額安裝配置簡單,幾乎不須要優化就能夠跑的很好,在特殊的大併發高訪問量的場合才須要進行一些優化。
例如:每次新增數據到數據庫的同時,就將數據寫入或者複製一份到Memcached裏,而後從業務邏輯上讓程序優先讀緩存,沒有數據再查數據庫。
當網站後端的數據庫數據量很大時,單臺Memcached就沒法存放絕大部分的數據庫內數據,致使Memcached服務的命中率很低,此時,能夠採用一致性哈希分佈式緩存集羣架構,提高網站的命中率,一致性哈希能夠由程序實現或者支持一致性哈希算法的負載均衡器實現。
當訪問量增大時,在整個網站集羣架構中最早出現瓶頸的幾乎都是後端的數據庫或用於存儲的服務器,在企業生產工做中應儘可能把用戶的訪問請求往整個網站架構的最前面推,即當用戶請求數據時,越是在靠近用戶端返回數據,效果就越好。
爲了緩解應用對數據庫的高併發訪問壓力,大型網站都會在數據庫層配置數據讀寫分離,並提供讀的數據庫作負載均衡,邏輯圖以下圖所示:其中是以Amoeba軟件做爲讀寫分離說明的。
因爲訪問數據庫讀寫的是磁盤,因此,對於高併發的業務場景,上述讀寫分離的架構仍是遠遠解決不了問題的,根據離訪問用戶越近效率越高以及用內存代替磁盤的架構思想,能夠在數據庫架構的前端部署Memcached服務做爲緩存區,最大限度地把對數據庫查詢信息保存在Memcached服務的內存中,這樣前端的應用服務就可以迅速地從Memcached中讀取到本來在數據庫中才能讀取到的數據,從而加快了網站的訪問速度。
因爲單臺Memcached服務器的內存容量是有限的,而且單臺也存在單點故障,所以,大型網站每每會將多個memcached服務器組合成集羣提供服務,那麼怎麼組合效率才更高呢?
(1)緩存服務器使用常規負載均衡模式的問題
在常規負載均衡模式中,集羣節點上的程序和數據都是同樣的,例如Web集羣。而緩存集羣設計下的全部節點緩存的數據就不能都同樣,由於這樣一來,訪問數據命中率會很是低下,低下的緣由主要有兩個:
下圖是緩存服務器使用常規負載均衡模式的問題圖解
(2)分佈式緩存集羣的優劣勢
- Memcached支持分佈式集羣,其中的方法之一就是在客戶端應用程序上進行改造。例如:能夠根據key適當進行有規律的封裝。好比以用戶爲主的網站來講,每一個用戶都有UserID,那麼能夠按照固定的ID來進行提取和存取,假設以1開頭的用戶保存在第一臺Memcached服務器上,以2開頭的用過戶的數據保存在第二臺Memcached服務器上,那麼存取數據時都會按照UserID來進行相應的轉換和存取。
- 可是這樣也有缺點,就是須要對UserID進行判斷,若是業務不一致,或者是其餘類型的應用,可能不是那麼合適,此時能夠根據本身的實際業務來進行考慮,或者去想更合適的方法
- 此外,還能夠在應用服務器上經過程序用哈希算法或一致性哈希算法調度Memcached服務器,全部Memcached服務器的地址池能夠簡單地配在每一個程序的配置文件裏。而且可在負載均衡器上使用哈希算法或一致性哈希算法,不過此時須要負載均衡器的支持。
1)每一臺Memcached服務器的內容都是不同的。這些Memcached服務器緩存的內容加起來接近整個數據庫的數據容量。
2)經過在客戶端程序或者Memcached的負載均衡器上用hash算法,讓同一數據內容都分配到一個Memcached服務器。
3)普通的hash算法對於節點宕機會帶來大量的緩存數據流動(失效),可能會引發雪崩效應。
4)一致性哈希算法(還能夠帶虛擬節點)可讓緩存節點宕機對節點的數據流動(失效)降到最低。
(1)取模計算hash
優勢:簡單,分散性優秀
缺點:添加/刪除服務器時,緩存重組代價巨大,影響命中率
例:將26個字母緩存到了3個節點,此時新增長一個節點,訪問命中率將降低23%以下表:
(2)Consistent hash(一致性哈希算法)
- 一致性哈希算法(consistent hash)是一種特殊的hash算法,簡單地說,在移除以及添加一個cache節點時,它可以儘量小地改變已存在key的映射關係,讓緩存服務器緩存的內容受到的影響最小。
- consistent hash算法原理以下圖所示,首先現象一個0~2^32-1次方的數值空間,將這個空間想象成一個首(0)尾(2^32-1)相接的圓環,而後算出不一樣Memcached服務器節點的哈希值(0~(2^32-1)之間),將這些值分散到上述的圓環上,接着用一樣的方法算出存儲不一樣數據的鍵的哈希值並映射到相同的圓環上,最後從數據映射到的位置開始順時針查找,將鍵對應的數據保存到查找到的第一個服務器上,以下圖所示:
假如咱們要添加node5服務器,以下圖所示:
- 能夠看到添加node5服務器後,緩存的數據隻影響node2到node5之間的數據範圍,即node4的一部分數據,緩存到了node5,其餘的緩存服務器沒有受到影響,當移除服務器時,原理和添加服務器時同樣,再也不贅述。
- 這就是一致性哈希算法的做用,能夠最大限度地減小鍵的從新分佈。該算法的實現方法還能夠採用更好的虛擬節點的策略思路,一致性哈希算法能夠經過在前端使用程序實現或者經過負載均衡器實現。
如下是PHP Web環境集羣的session共享存儲設置。
#默認php.ini中session的類型和配置路徑爲: [root@LNMP ~]# awk '/session.save_handler/{print NR,$0}/session.save_path/{print NR,$0}' /usr/local/php/lib/php.ini 1461 session.save_handler = files #修改本行數據 1469 ; session.save_path = "N;/path" 1485 ; session.save_path = "N;MODE;/path" 1490 ;session.save_path = "/tmp" #修改本行數據 1566 ; (see session.save_path above), then garbage collection does *not* #修改爲以下配置: [root@LNMP ~]# sed -n '1461p;1490p' /usr/local/php/lib/php.ini session.save_handler = memcache session.save_path = "tcp://192.168.0.240:11211" #重啓php-fpm服務 [root@LNMP ~]# pkill php-fpm [root@LNMP ~]# netstat -antup | grep 9000 [root@LNMP ~]# /usr/local/php/sbin/php-fpm [root@LNMP ~]# netstat -antup | grep 9000 tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 1393/php-fpm
說明:
192.168.0.240:11211爲Memcached數據庫緩存的IP及端口
上述配置適合LNMP/LAMP環境
Memcached服務器也能夠是多臺,而且可經過hash算法調度
配置完畢經過phpinfo頁面查看效果以下圖所示:
優勢:
1)讀寫速度上會比普通files速度快不少
2)能夠解決多個服務器共用session的難題
缺點:
1)session數據都保存在memory中,持久化方面有所欠缺,但對session數據來講不是問題。
2)通常是單臺,若是部署多臺,多臺之間沒法數據同步。經過hash算法分配依然有session丟失的問題。
對於上面的缺點,解決思路以下:
1)能夠用其餘的持久化系統存儲sessions,例如:redis,ttserver來替代Memcached2)高性能高併發場景,cookies效率比session要好不少,所以,大網站都會用cookies解決會話共享問題3)有的已經就業了的同窗經過犧牲LB的負載均衡的策略實現,例如:lvs-p,nginx ip_hash等,但這些不是好的方法。