memcache 搭建緩存服務器

一、MemCache簡介

session

MemCache是一個自由、源碼開放、高性能、分布式的分布式內存對象緩存系統,用于動態Web應用以減輕數據庫的負載。它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提高了網站訪問的速度。MemCaChe是一個存儲鍵值對的HashMap,在內存中對任意的數據(比如字符串、對象等)所使用的key-value存儲,數據可以來自數據庫調用、API調用,或者頁面渲染的結果。MemCache設計理念就是小而強大,它簡單的設計促進了快速部署、易于開發并解決面對大規模的數據緩存的許多難題,而所開放的API使得MemCache能用于Java、C/C++/C#、Perl、Python、PHP、Ruby等大部分流行的程序語言。

另外,說一下為什么會有Memcache和memcached兩種名稱?其實Memcache是這個項目的名稱,而memcached是它服務器端的主程序文件名

MemCache的官方網站為http://memcached.org/

MemCache訪問模型

為了加深對memcache的理解,以memcache為代表的分布式緩存,訪問模型如下:

特別澄清一個問題,MemCache雖然被稱為”分布式緩存”,但是MemCache本身完全不具備分布式的功能,MemCache集群之間不會相互通信(與之形成對比的,比如JBoss Cache,某臺服務器有緩存數據更新時,會通知集群中其他機器更新緩存或清除緩存數據),所謂的”分布式”,完全依賴于客戶端程序的實現,就像上面這張圖的流程一樣。

同時基于這張圖,理一下MemCache一次寫緩存的流程:

1、應用程序輸入需要寫緩存的數據

2、API將Key輸入路由算法模塊,路由算法根據Key和MemCache集群服務器列表得到一臺服務器編號

3、由服務器編號得到MemCache及其的ip地址和端口號

4、API調用通信模塊和指定編號的服務器通信,將數據寫入該服務器,完成一次分布式緩存的寫操作

讀緩存和寫緩存一樣,只要使用相同的路由算法和服務器列表,只要應用程序查詢的是相同的Key,MemCache客戶端總是訪問相同的客戶端去讀取數據,只要服務器中還緩存著該數據,就能保證緩存命中。

這種MemCache集群的方式也是從分區容錯性的方面考慮的,假如Node2宕機了,那么Node2上面存儲的數據都不可用了,此時由于集群中Node0和Node1還存在,下一次請求Node2中存儲的Key值的時候,肯定是沒有命中的,這時先從數據庫中拿到要緩存的數據,然后路由算法模塊根據Key值在Node0和Node1中選取一個節點,把對應的數據放進去,這樣下一次就又可以走緩存了,這種集群的做法很好,但是缺點是成本比較大。

一致性Hash算法

從上面的圖中,可以看出一個很重要的問題,就是對服務器集群的管理,路由算法至關重要,就和負載均衡算法一樣,路由算法決定著究竟該訪問集群中的哪臺服務器,先看一個簡單的路由算法。

1、余數Hash

簡單的路由算法可以使用余數Hash:用服務器數目和緩存數據KEY的hash值相除,余數為服務器列表下標編號,假如某個str對應的HashCode是52、服務器的數目是3,取余數得到1,str對應節點Node1,所以路由算法把str路由到Node1服務器上。由于HashCode隨機性比較強,所以使用余數Hash路由算法就可以保證緩存數據在整個MemCache服務器集群中有比較均衡的分布。

如果不考慮服務器集群的伸縮性,那么余數Hash算法幾乎可以滿足絕大多數的緩存路由需求,但是當分布式緩存集群需要擴容的時候,就難辦了。

就假設MemCache服務器集群由3臺變為4臺吧,更改服務器列表,仍然使用余數Hash,52對4的余數是0,對應Node0,但是str原來是存在Node1上的,這就導致了緩存沒有命中。再舉個例子,原來有HashCode為0~19的20個數據,那么:

那么不妨舉個例子,原來有HashCode為0~19的20個數據,那么:

現在擴容到4臺,加粗標紅的表示命中:

如果擴容到20+的臺數,只有前三個HashCode對應的Key是命中的,也就是15%。當然現實情況肯定比這個復雜得多,不過足以說明,使用余數Hash的路由算法,在擴容的時候會造成大量的數據無法正確命中(其實不僅僅是無法命中,那些大量的無法命中的數據還在原緩存中在被移除前占據著內存)。在網站業務中,大部分的業務數據度操作請求上事實上是通過緩存獲取的,只有少量讀操作會訪問數據庫,因此數據庫的負載能力是以有緩存為前提而設計的。當大部分被緩存了的數據因為服務器擴容而不能正確讀取時,這些數據訪問的壓力就落在了數據庫的身上,這將大大超過數據庫的負載能力,嚴重的可能會導致數據庫宕機。

這個問題有解決方案,解決步驟為:

(1)在網站訪問量低谷,通常是深夜,技術團隊加班,擴容、重啟服務器

(2)通過模擬請求的方式逐漸預熱緩存,使緩存服務器中的數據重新分布

2、一致性Hash算法

一致性Hash算法通過一個叫做一致性Hash環的數據結構實現Key到緩存服務器的Hash映射。簡單地說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環(這個環被稱為一致性Hash環),如假設某空間哈希函數H的值空間是0~2^32-1(即哈希值是一個32位無符號整形),整個哈希空間如下:

下一步將各個服務器使用H進行一個哈希計算,具體可以使用服務器的IP地址或者主機名作為關鍵字,這樣每臺機器能確定其在上面的哈希環上的位置了,并且是按照順時針排列,這里我們假設三臺節點memcache經計算后位置如下

接下來使用相同算法計算出數據的哈希值h,并由此確定數據在此哈希環上的位置

假如我們有數據A、B、C、D、4個對象,經過哈希計算后位置如下:

根據一致性哈希算法,數據A就被綁定到了server01上,D被綁定到了server02上,B、C在server03上,是按照順時針找最近服務節點方法

這樣得到的哈希環調度方法,有很高的容錯性和可擴展性:

假設server03宕機

可以看到此時C、B會受到影響,將B、C被重定位到Server01。一般的,在一致性哈希算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

考慮另外一種情況,如果我們在系統中增加一臺服務器Memcached Server 04:

此時A、D、C不受影響,只有B需要重定位到新的Server04。一般的,在一致性哈希算法中,如果增加一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

綜上所述,一致性哈希算法對于節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。

一致性哈希的缺點:在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜問題。我們可以采用增加虛擬節點的方式解決。

更重要的是,集群中緩存服務器節點越多,增加/減少節點帶來的影響越小,很好理解。換句話說,隨著集群規模的增大,繼續命中原有緩存數據的概率會越來越大,雖然仍然有小部分數據緩存在服務器中不能被讀到,但是這個比例足夠小,即使訪問數據庫,也不會對數據庫造成致命的負載壓力。

MemCache實現原理

首先要說明一點,MemCache的數據存放在內存中

1、訪問數據的速度比傳統的關系型數據庫要快,因為Oracle、MySQL這些傳統的關系型數據庫為了保持數據的持久性,數據存放在硬盤中,IO操作速度慢

2、MemCache的數據存放在內存中同時意味著只要MemCache重啟了,數據就會消失

3、既然MemCache的數據存放在內存中,那么勢必受到機器位數的限制,32位機器最多只能使用2GB的內存空間,64位機器可以認為沒有上限

然后我們來看一下MemCache的原理,MemCache最重要的是內存如何分配的,MemCache采用的內存分配方式是固定空間分配,如下圖所示:

這張圖片里面涉及了slab_class、slab、page、chunk四個概念,它們之間的關系是:

1、MemCache將內存空間分為一組slab

2、每個slab下又有若干個page,每個page默認是1M,如果一個slab占用100M內存的話,那么這個slab下應該有100個page

3、每個page里面包含一組chunk,chunk是真正存放數據的地方,同一個slab里面的chunk的大小是固定的

4、有相同大小chunk的slab被組織在一起,稱為slab_class

MemCache內存分配的方式稱為allocator(分配運算),slab的數量是有限的,幾個、十幾個或者幾十個,這個和啟動參數的配置相關。

MemCache中的value存放的地方是由value的大小決定的,value總是會被存放到與chunk大小最接近的一個slab中,比如slab[1]的chunk大小為80字節、slab[2]的chunk大小為100字節、slab[3]的chunk大小為125字節(相鄰slab內的chunk基本以1.25為比例進行增長,MemCache啟動時可以用-f指定這個比例),那么過來一個88字節的value,這個value將被放到2號slab中。放slab的時候,首先slab要申請內存,申請內存是以page為單位的,所以在放入第一個數據的時候,無論大小為多少,都會有1M大小的page被分配給該slab。申請到page后,slab會將這個page的內存按chunk的大小進行切分,這樣就變成了一個chunk數組,最后從這個chunk數組中選擇一個用于存儲數據。

如果這個slab中沒有chunk可以分配了怎么辦,如果MemCache啟動沒有追加-M(禁止LRU,這種情況下內存不夠會報Out Of Memory錯誤),那么MemCache會把這個slab中最近最少使用的chunk中的數據清理掉,然后放上最新的數據。

Memcache的工作流程:

1、檢查客戶端的請求數據是否在memcached中,如果有,直接把請求數據返回,不再對數據庫進行任何操作,路徑操作為①②③⑦。

2、如果請求的數據不在memcached中,就去查數據庫,把從數據庫中獲取的數據返回給客戶端,同時把數據緩存一份到memcached中(memcached客戶端不負責,需要程序明確實現),路徑操作為①②④⑤⑦⑥。

3、每次更新數據庫的同時更新memcached中的數據,保證一致性。

4、當分配給memcached內存空間用完之后,會使用LRU(Least Recently Used,最近最少使用)策略加上到期失效策略,失效數據首先被替換,然后再替換掉最近未使用的數據。

Memcached特征:

協議簡單:

它是基于文本行的協議,直接通過telnet在memcached服務器上可進行存取數據操作

注:文本行的協議:指的是信息以文本傳送,一個信息單元傳遞完畢后要傳送換行。比如對于HTTP的GET請求來說,GET /index.html HTTP/1.1是一行,接下去每個頭部信息各占一行。一個空行表示整個請求結束

基于libevent事件處理:

Libevent是一套利用C開發的程序庫,它將BSD系統的kqueue,Linux系統的epoll等事件處理功能封裝成一個接口,與傳統的select相比,提高了性能。

內置的內存管理方式:

所有數據都保存在內存中,存取數據比硬盤快,當內存滿后,通過LRU算法自動刪除不使用的緩存,但沒有考慮數據的容災問題,重啟服務,所有數據會丟失。

分布式

各個memcached服務器之間互不通信,各自獨立存取數據,不共享任何信息。服務器并不具有分布式功能,分布式部署取決于memcache客戶端。

Memcache的安裝

分為兩個過程:memcache服務器端的安裝和memcached客戶端的安裝。

所謂服務器端的安裝就是在服務器(一般都是linux系統)上安裝Memcache實現數據的存儲。

所謂客戶端的安裝就是指php(或者其他程序,Memcache還有其他不錯的api接口提供)去使用服務器端的Memcache提供的數據,需要php添加擴展。

PHP的Memcache

二、centos7.2+nginx+php+memcache+mysql

環境描述:

OS:

[root@www ~]# cat /etc/redhat-release

CentOS Linux release 7.2.1511 (Core)

nginx和php:

nginx-1.10.2.tar.gz

php-5.6.27.tar.gz

ip地址:192.168.31.141/24

memcache:

memcached-1.4.33.tar.gz

ip地址:192.168.31.250/24

mysql:

mysql-5.7.13.tar.gz

ip地址:192.168.31.225/24

1、安裝nginx(在192.168.31.141主機操作)

解壓zlib

[root@www ~]# tar zxf zlib-1.2.8.tar.gz

說明:不需要編譯,只需要解壓就行。

解壓pcre

[root@www ~]# tar zxf pcre-8.39.tar.gz

說明:不需要編譯,只需要解壓就行。

[root@www ~]# yum -y install gcc gcc-c++ make libtool openssl openssl-devel

下載nginx的源碼包:http://nginx.org/download

解壓源碼包:

[root@www ~]# tar zxf nginx-1.10.2.tar.gz

[root@www ~]# cd nginx-1.10.2/

[root@www ~]# groupadd www#添加www組

[root@www ~]# useradd -g www www -s /sbin/nologin#創建nginx運行賬戶www并加入到www組,不允許www用戶直接登錄系統

[root@www nginx-1.10.2]# ./configure --prefix=/usr/local/nginx1.10 --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module ?--with-http_flv_module --with-http_mp4_module --with-pcre=/root/pcre-8.39 --with-zlib=/root/zlib-1.2.8 --with-http_ssl_module --with-http_gzip_static_module --user=www --group=www

[root@www nginx-1.10.2]# make&& make install

注:

--with-pcre:用來設置pcre的源碼目錄。

--with-zlib:用來設置zlib的源碼目錄。

因為編譯nginx需要用到這兩個庫的源碼。

[root@www nginx-1.10.2]# ln -s /usr/local/nginx1.10/sbin/nginx /usr/local/sbin/

[root@www nginx-1.10.2]# nginx -t

啟動nginx

[root@www nginx-1.10.2]# nginx

[root@www nginx-1.10.2]# netstat -anpt | grep nginx

tcp ??0 ??00.0.0.0:800.0.0.0:* ???LISTEN ?????9834/nginx: master

[root@www nginx-1.10.2]# firewall-cmd --permanent --add-port=80/tcp

success

[root@www nginx-1.10.2]# firewall-cmd --reload

success

啟動后可以再瀏覽器中打開頁面,會顯示nginx默認頁面。

2、安裝php

安裝libmcrypt

[root@www ~]# tar zxf libmcrypt-2.5.7.tar.gz

[root@www ~]# cd libmcrypt-2.5.7/

[root@www libmcrypt-2.5.7]# ./configure --prefix=/usr/local/libmcrypt && make && make install

[root@www ~]# yum -y install libxml2-devel libcurl-devel openssl-devel bzip2-devel

[root@www ~]# tar zxf php-5.6.27.tar.gz

[root@www ~]# cd php-5.6.27/

[root@www php-5.6.27]#./configure --prefix=/usr/local/php5.6 --with-mysql=mysqlnd --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd --with-openssl --enable-fpm --enable-sockets --enable-sysvshm --enable-mbstring --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr --enable-xml --with-mhash --with-mcrypt=/usr/local/libmcrypt --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-bz2 --enable-maintainer-zts

[root@www php-5.6.27]# make&& make install

[root@www php-5.6.27]# cp php.ini-production /etc/php.ini

修改/etc/php.ini文件,將short_open_tag修改為on,修改后的內容如下:

short_open_tag = On//支持php短標簽

創建php-fpm服務啟動腳本:

[root@www php-5.6.27]# cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

[root@www php-5.6.27]# chmod +x /etc/init.d/php-fpm

[root@www php-5.6.27]# chkconfig --add php-fpm

[root@www php-5.6.27]# chkconfig php-fpm on

提供php-fpm配置文件并編輯:

#cp /usr/local/php5.6/etc/php-fpm.conf.default /usr/local/php5.6/etc/php-fpm.conf

[root@www php-5.6.27]# vi /usr/local/php5.6/etc/php-fpm.conf

修改內容如下:

pid = run/php-fpm.pid

listen =127.0.0.1:9000

pm.max_children =300

pm.start_servers =10

pm.min_spare_servers =10

pm.max_spare_servers =50

啟動php-fpm服務:

[root@phpserver ~]# service ?php-fpm start

Starting php-fpm ?done

[root@www php-5.6.27]# netstat -anpt | grep php-fpm

tcp ??0 ??0 127.0.0.1:9000 ???0.0.0.0:* ????LISTEN ?????10937/php-fpm: mast

3、安裝mysql(在192.168.31.225主機操作)

因為centos7.2默認安裝了mariadb-libs,所以先要卸載掉

查看是否安裝mariadb

#rpm -qa | grep mariadb

卸載mariadb

rpm -e?--nodeps?mariadb-libs

2、安裝依賴包

注:相關依賴包的作用

cmake:由于從MySQL5.5版本開始棄用了常規的configure編譯方法,所以需要CMake編譯器,用于設置mysql的編譯參數。如:安裝目錄、數據存放目錄、字符編碼、排序規則等。

Boost#從MySQL?5.7.5開始Boost庫是必需的,mysql源碼中用到了C++的Boost庫,要求必須安裝boost1.59.0或以上版本

GCC是Linux下的C語言編譯工具,mysql源碼編譯完全由C和C++編寫,要求必須安裝GCC

bison:Linux下C/C++語法分析器

ncurses:字符終端處理庫

1)安裝文件準備

下載cmake-3.5.tar.gzhttp://wwwNaNake.org/download/

下載ncurses-5.9.tar.gzftp://ftp.gnu.org/gnu/ncurses/

下載bison-3.0.4.tar.gzhttp://ftp.gnu.org/gnu/bison/

下載mysql-5.7.13.tar.gz

wget?http://cdn.mysql.com/Downloads/MySQL-5.7/mysql-5.7.13.tar.gz

下載Boost_1_59_0.tar.gz

wget http://nchc.dl.sourceforge.net/project/boost/boost/1.59.0/boost_1_59_0.tar.gz

2)安裝CMAKE及必要的軟件

安裝cmake

cmake –version? ---查看cmake版本

安裝ncurses

安裝bison

安裝bootst

tar zxf ?boost_1_59_0.tar.gz

mv boost_1_59_0 /usr/local/boost

3)創建mysql用戶和用戶組及目錄

# groupadd -r mysql && useradd -r -g mysql -s /bin/false -M mysql---新建msyql組和msyql用戶禁止登錄shell

#mkdir /usr/local/mysql??????? ---創建目錄

#mkdir /usr/local/mysql/data?? ?---數據庫目錄

3、編譯安裝mysql

解壓mysql源碼包:

執行cmake命令進行編譯前的配置:

cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data -DSYSCONFDIR=/etc -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DEXTRA_CHARSETS=all -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_ARCHIVE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_SYSTEMD=1 -DWITH_BOOST=/usr/local/boost

開始編譯、編譯安裝:

注1:配置解釋:

-DCMAKE_INSTALL_PREFIX=/usr/local/mysql[MySQL安裝的根目錄]-DMYSQL_DATADIR=/usr/local/mysql /data[MySQL數據庫文件存放目錄]

-DSYSCONFDIR=/etc [MySQL配置文件所在目錄]

-DWITH_MYISAM_STORAGE_ENGINE=1 [添加MYISAM引擎支持]

-DWITH_INNOBASE_STORAGE_ENGINE=1[添加InnoDB引擎支持]

-DWITH_ARCHIVE_STORAGE_ENGINE=1 ?[添加ARCHIVE引擎支持]

-DMYSQL_UNIX_ADDR=/usr/local/mysql /mysql.sock[指定mysql.sock位置]

-DWITH_PARTITION_STORAGE_ENGINE=1[安裝支持數據庫分區]

-DEXTRA_CHARSETS=all [使MySQL支持所有的擴展字符]

-DDEFAULT_CHARSET=utf8[設置MySQL的默認字符集為utf8]-DDEFAULT_COLLATION=utf8_general_ci [設置默認字符集校對規則]

-DWITH-SYSTEMD=1 ?[可以使用systemd控制mysql服務]

-DWITH_BOOST=/usr/local/boost[指向boost庫所在目錄]

更多參數執行[root@localhost mysql-5.7.13]# cmake . –LH

注2:為了加快編譯速度可以按下面的方式編譯安裝

make-j$(grep processor /proc/cpuinfo | wc –l)

-j參數表示根據CPU核數指定編譯時的線程數,可以加快編譯速度。默認為1個線程編譯。

注3:若要重新運行cmake配置,需要刪除CMakeCache.txt文件

# make clean

#rm -f CMakeCache.txt

優化Mysql的執行路徑

4、設置權限并初始化MySQL系統授權表

# cd/usr/local/mysql

#chown -R mysql:mysql.---更改所有者,屬組,注意是mysql .

#bin/mysqld?--initialize--user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

注1:以root初始化操作時要加--user=mysql參數,生成一個隨機密碼(注意保存登錄時用)

注2:MySQL 5.7.6之前的版本執行這個腳本初始化系統數據庫

/usr/local/mysql/bin/mysql_install_db --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

# 5.7.6之后版本初始系統數據庫腳本(本文使用此方式初始化)

#/usr/local/mysql/bin/mysqld --initialize-insecure--user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data

注意:如果使用–initialize參數初始化系統數據庫之后,會生成root用戶的一個臨時密碼,如上圖高亮中所示。

#chown -Rmysql:mysql.---改所有者,注意是root .

5、創建配置文件

# cd/usr/local/mysql/support-files ????---進入MySQL安裝目錄支持文件目錄

# cp my-default.cnf /etc/my.cnf ???---復制模板為新的配置文件,

修改文件中配置選項,如下圖所示,添加如下配置項

#vi ?/etc/my.cnf

6、配置mysql自動啟動

在mysqld.service,把默認的pid文件指定到了/var/run/mysqld/目錄,而并沒有事先建立該目錄,因此要手動建立該目錄并把權限賦給mysql用戶。

或者修改/usr/lib/system/system/mysqld.service,修改內容如下:

#systemctl ?daemon-reload

設置數據庫管理員用戶root的密碼

4、安裝memcached服務端(在192.168.31.250主機操作)

memcached是基于libevent的事件處理。libevent是個程序庫,它將Linux的epoll、BSD類操作系統的kqueue等事件處理功能封裝成統一的接口。即使對服務器的連接數增加,也能發揮I/O的性能。memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等操作系統上發揮其高性能。

首先先安裝memcached依賴庫libevent

[root@memcache ~]# tar zxf libevent-2.0.22-stable.tar.gz

[root@memcache ~]# cd libevent-2.0.22-stable/

[root@memcache libevent-2.0.22-stable]# ./configure

[root@memcache libevent-2.0.22-stable]# make&& make install

安裝memcached

[root@memcache ~]# tar zxf memcached-1.4.33.tar.gz

[root@memcache ~]# cd memcached-1.4.33/

[root@memcache memcached-1.4.33]# ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local

[root@memcache memcached-1.4.33]# make&& make install

檢測是否成功安裝

[root@memcache ~]# ls /usr/local/memcached/bin/memcached

/usr/local/memcached/bin/memcached

通過以上操作就很簡單的把memcached服務端編譯好了。這時候就可以打開服務端進行工作了。

配置環境變量:

進入用戶宿主目錄,編輯.bash_profile,為系統環境變量LD_LIBRARY_PATH增加新的目錄,需要增加的內容如下:

[root@memcache ~]# vi ~/.bash_profile

MEMCACHED_HOME=/usr/local/memcached

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$MEMCACHED_HOME/lib

[root@memcache ~]# /usr/local/memcached/bin/memcached -d -m 2048 -l 192.168.31.250 -p 11211 -u root -c 10240 -P /usr/local/memcached/memcached.pid

啟動參數說明:

-d  選項是啟動一個守護進程。

-m  分配給Memcache使用的內存數量,單位是MB,默認64MB。

-l  監聽的IP地址。(默認:INADDR_ANY,所有地址)

-p  設置Memcache的TCP監聽的端口,最好是1024以上的端口。

-u  運行Memcache的用戶,如果當前為root的話,需要使用此參數指定用戶。

-c  選項是最大運行的并發連接數,默認是1024。

-P  設置保存Memcache的pid文件。

-M內存耗盡時返回錯誤,而不是刪除項

-f塊大小增長因子,默認是1.25

-n最小分配空間,key+value+flags默認是48

-h顯示幫助

[root@memcache ~]# netstat -anpt |grep memcached

tcp ??0 ?0 192.168.31.250:11211 ???0.0.0.0:* ?LISTEN ?????12840/memcached

設置防火墻:

[root@memcache ~]# firewall-cmd --permanent --add-port=11211/tcp

success

[root@memcache ~]# firewall-cmd --reload

success

刷新用戶環境變量:

[root@memcache ~]# source ~/.bash_profile

編寫memcached服務啟停腳本

[root@memcache ~]# vi /etc/init.d/memcached

腳本內容如下:

[root@memcache ~]# cat /etc/init.d/memcached

#!/bin/sh

#

# pidfile: /usr/local/memcached/memcached.pid

# memcached_home: /usr/local/memcached

# chkconfig: 35 21 79

# description: Start and stop memcached Service

# Source function library

. /etc/rc.d/init.d/functions

RETVAL=0

prog="memcached"

basedir=/usr/local/memcached

cmd=${basedir}/bin/memcached

pidfile="$basedir/${prog}.pid"

#interface to listen on (default: INADDR_ANY, all addresses)

ipaddr="192.168.31.250"

#listen port

port=11211

#username for memcached

username="root"

#max memory for memcached,default is 64M

max_memory=2048

#max connections for memcached

max_simul_conn=10240

start() {

echo -n $"Starting service: $prog"

$cmd -d -m $max_memory -u $username -l $ipaddr -p $port -c $max_simul_conn -P $pidfile

RETVAL=$?

echo

[ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog

}

stop() {

echo -n $"Stopping service: $prog ?"

run_user=$(whoami)

pidlist=$(ps -ef | grep $run_user | grep memcached | grep -v grep | awk '{print($2)}')

for pid in $pidlist

do

kill -9 $pid

if [ $? -ne 0 ]; then

return 1

fi

done

RETVAL=$?

echo

[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog

}

# See how we were called.

case "$1" in

start)

start

;;

stop)

stop

;;

restart)

stop

start

;;

*)

echo "Usage: $0 {start|stop|restart|status}"

exit 1

esac

exit $RETVAL

設置腳本可被執行:

[root@memcache ~]# chmod +x /etc/init.d/memcached

[root@memcache ~]# chkconfig --add memcached

[root@memcache ~]# chkconfig memcached on

說明:

shell腳本中return的作用

1)終止一個函數.

2)return命令允許帶一個整型參數,這個整數將作為函數的"退出狀態

碼"返回給調用這個函數的腳本,并且這個整數也被賦值給變量$?.

3)命令格式:return value

5、配置nginx.conf文件(在nginx主機操作)

配置內容如下:

user www www;

worker_processes ?4;

worker_cpu_affinity 0001 0010 0100 1000;

error_log ?logs/error.log;

#error_log ?logs/error.log ?notice;

#error_log ?logs/error.log ?info;

pid ???????logs/nginx.pid;

events {

use epoll;

worker_connections ?65535;

multi_accept on;

}

http {

include ??????mime.types;

default_type ?application/octet-stream;

#log_format ?main ?'$remote_addr - $remote_user [$time_local] "$request" '

# ?????????????????'$status $body_bytes_sent "$http_referer" '

# ?????????????????'"$http_user_agent" "$http_x_forwarded_for"';

#access_log ?logs/access.log ?main;

sendfile ???????on;

tcp_nopush ????on;

keepalive_timeout ?65;

tcp_nodelay on;

client_header_buffer_size 4k;

open_file_cache max=102400 inactive=20s;

open_file_cache_valid 30s;

open_file_cache_min_uses 1;

client_header_timeout 15;

client_body_timeout 15;

reset_timedout_connection on;

send_timeout 15;

server_tokens off;

client_max_body_size 10m;

fastcgi_connect_timeout ????600;

fastcgi_send_timeout 600;

fastcgi_read_timeout 600;

fastcgi_buffer_size 64k;

fastcgi_buffers ????4 64k;

fastcgi_busy_buffers_size 128k;

fastcgi_temp_file_write_size 128k;

fastcgi_temp_path /usr/local/nginx1.10/nginx_tmp;

fastcgi_intercept_errors on;

fastcgi_cache_path /usr/local/nginx1.10/fastcgi_cache levels=1:2 keys_zone=cache_fastcgi:128m inactive=1d max_size=10g;

gzip on;

gzip_min_length ?2k;

gzip_buffers ????4 32k;

gzip_http_version 1.1;

gzip_comp_level 6;

gzip_types ?text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;

gzip_vary on;

gzip_proxied any;

server {

listen ??????80;

server_name ?www.benet.com;

#charset koi8-r;

#access_log ?logs/host.access.log ?main;

location ~* ^.+\.(jpg|gif|png|swf|flv|wma|wmv|asf|mp3|mmf|zip|rar)$ {

valid_referers none blocked ?www.benet.com benet.com;

if ($invalid_referer) {

#return 302 ?http://www.benet.com/img/nolink.jpg;

return 404;

break;

}

access_log off;

}

location / {

root ??html;

index ?index.php index.html index.htm;

}

location ~* \.(ico|jpe?g|gif|png|bmp|swf|flv)$ {

expires 30d;

#log_not_found off;

access_log off;

}

location ~* \.(js|css)$ {

expires 7d;

log_not_found off;

access_log off;

}

location = /(favicon.ico|roboots.txt) {

access_log off;

log_not_found off;

}

location /status {

stub_status on;

}

location ~ .*\.(php|php5)?$ {

root html;

fastcgi_pass 127.0.0.1:9000;

fastcgi_index index.php;

include fastcgi.conf;

fastcgi_cache cache_fastcgi;

fastcgi_cache_valid 200 302 1h;

fastcgi_cache_valid 301 1d;

fastcgi_cache_valid any 1m;

fastcgi_cache_min_uses 1;

fastcgi_cache_use_stale error timeout invalid_header http_500;

fastcgi_cache_key http://$host$request_uri;

}

#error_page ?404 ?????????????/404.html;

# redirect server error pages to the static page /50x.html

#

error_page ??500 502 503 504 ?/50x.html;

location = /50x.html {

root ??html;

}

}

}

注:注釋掉fastcgi_cache cache_fastcgi;

重啟nginx服務

生成一個php測試頁

[root@www memcache-3.0.8]# cat /usr/local/nginx1.10/html/test1.php

phpinfo();

?>

使用瀏覽器訪問test1.php測試頁

6、memcache客戶端(在php服務器操作):

memcache分為服務端和客戶端。服務端用來存放緩存,客戶端用來操作緩存。

安裝php擴展庫(phpmemcache)。

安裝PHP Memcache擴展:

可以使用php自帶的pecl安裝程序

# /usr/local/php5.6/bin/pecl install memcache

也可以從源碼安裝,他是生成php的擴展庫文件memcache.so。

安裝memcache擴展庫

[root@www ~]# tar zxf memcache-3.0.8.tgz

[root@www ~]# cd memcache-3.0.8/

[root@www memcache-3.0.8]# /usr/local/php5.6/bin/phpize

[root@wwwmemcache-3.0.8]#./configure --enable-memcache --with-php-config=/usr/local/php5.6/bin/php-config

[root@www memcache-3.0.8]# make&& make install

安裝完后會有類似這樣的提示:

Installing shared extensions: ????/usr/local/php5.6/lib/php/extensions/no-debug-zts-20131226/

把這個記住,然后修改php.ini

添加一行

extension=/usr/local/php5.6/lib/php/extensions/no-debug-zts-20131226/memcache.so

重啟php-fpm服務

[root@www memcache-3.0.8]# service ?php-fpm restart

Gracefully shutting down php-fpm .done

Starting php-fpm ?done

測試:

檢查php擴展是否正確安裝

1、[root@www html]# /usr/local/php5.6/bin/php -m

命令行執行php -m查詢結果中是否有memcache項

2、創建phpinfo()頁面,查詢session項下面的Registered save handlers值中是否有memcache項

瀏覽器訪問test1.php

測試代碼:

[root@www ~]# cat /usr/local/nginx1.10/html/test2.php


$memcache = new Memcache;

$memcache->connect('192.168.31.250', 11211) or die ("Could not connect");

$version = $memcache->getVersion();

echo "Server's version: ".$version."
";

$tmp_object = new stdClass;

$tmp_object->str_attr = 'test';

$tmp_object->int_attr = 123;

$memcache->set('key', $tmp_object, false, 10) or die ("Failed to save data at the server");

echo "Store data in the cache (data will expire in 10 seconds)
";

$get_result = $memcache->get('key');

echo "Data from the cache:
";

var_dump($get_result);

?>

瀏覽器訪問test2.php

使用memcache實現session共享

配置php.ini中的Session為memcache方式。

session.save_handler = memcache

session.save_path = "tcp://192.168.31.250:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

注:

session.save_handler:設置session的儲存方式為memcache。默認以文件方式存取session數據,如果想要使用自定義的處理來存取session數據,比如memcache方式則修為session.save_handler = memcache

session.save_path:設置session儲存的位置,多臺memcache用逗號隔開

使用多個memcached server時用逗號”,”隔開,可以帶額外的參數”persistent”、”weight”、”timeout”、”retry_interval”等等,

類似這樣的:"tcp://host:port?persistent=1&weight=2,tcp://host2:port2"。

memcache實現session共享也可以在某個一個應用中設置:

ini_set("session.save_handler", "memcache");

ini_set("session.save_path", "tcp://192.168.0.9:11211");

ini_set()只對當前php頁面有效,并且不會去修改php.ini文件本身,也不會影響其他php頁面。

測試memcache可用性

重啟php-fpm

在web服務器上新建//usr/local/nginx1.10/html/memcache.php文件。內容如


session_start();

if (!isset($_SESSION['session_time']))

{

$_SESSION['session_time'] = time();

}

echo "session_time:".$_SESSION['session_time']."
";

echo "now_time:".time()."
";

echo "session_id:".session_id()."
";

?>

訪問網址http://192.168.31.141/memcache.php可以查看session_time是否都是為memcache中的Session,同時可以在不同的服務器上修改不同的標識查看是否為不同的服務器上的。

可以直接用sessionid去memcached里查詢一下:

[root@www html]# telnet 192.168.31.250 11211

Trying 192.168.31.250...

Connected to 192.168.31.250.

Escape character is '^]'.

get ffaqe5b1oar311n3cn5q9co5g6

VALUE ffaqe5b1oar311n3cn5q9co5g6 0 26

session_time|i:1479134997;

得到session_time|i:1479134997;這樣的結果,說明session正常工作

默認memcache會監聽11221端口,如果想清空服務器上memecache的緩存,一般使用的是:

[root@memcache ~]# telnet 192.168.31.250 11211

Trying 192.168.31.250...

Connected to 192.168.31.250.

Escape character is '^]'.

flush_all

OK

同樣也可以使用:

[root@memcache ~]# echo "flush_all" | nc 192.168.31.250 11211

OK

使用flush_all后并不是刪除memcache上的key,而是置為過期

memcache安全配置

因為memcache不進行權限控制,因此需要通過iptables將memcache僅開放個web服務器。

7、測試memcache緩存數據庫數據

在Mysql服務器上創建測試表

mysql> create database testdb1;

Query OK, 1 row affected (0.00 sec)

mysql> use testdb1;

Database changed

mysql> create table test1(id int not null auto_increment,name varchar(20) default null,primary key (id)) engine=innodb auto_increment=1 default charset=utf8;

Query OK, 0 rows affected (0.03 sec)

mysql> insert into test1(name) values ('tom1'),('tom2'),('tom3'),('tom4'),('tom5');

Query OK, 5 rows affected (0.01 sec)

Records: 5 ?Duplicates: 0 ?Warnings: 0

mysql> select * from test1;

+----+------+

| id | name |

+----+------+

| ?1 | tom1 |

| ?2 | tom2 |

| ?3 | tom3 |

| ?4 | tom4 |

| ?5 | tom5 |

+----+------+

5 rows in set (0.00 sec)

測試

下面就是測試的工作了,這里有個php腳本,用于測試memcache是否緩存數據成功

需要為這個腳本添加一個只讀的數據庫用戶,命令格式

mysql> grant select on testdb1.* to user@'%' identified by '123456';

Query OK, 0 rows affected, 1 warning (0.00 sec)

在web服務器上創建測試腳本內容如下:

[root@www html]# cat /usr/local/nginx1.10/html/test_db.php


$memcachehost = '192.168.31.250';

$memcacheport =11211;

$memcachelife = 60;

$memcache = new Memcache;

$memcache->connect($memcachehost,$memcacheport) or die ("Could not connect");

$query="select * fromtest1limit 10";

$key=md5($query);

if(!$memcache->get($key))

{

$conn=mysql_connect("192.168.31.225","user","123456");

mysql_select_db(testdb1);

$result=mysql_query($query);

while ($row=mysql_fetch_assoc($result))

{

$arr[]=$row;

}

$f = 'mysql';

$memcache->add($key,serialize($arr),0,30);

$data = $arr ;

}

else{

$f = 'memcache';

$data_mem=$memcache->get($key);

$data = unserialize($data_mem);

}

echo $f;

echo "
";

echo "$key";

echo "
";

//print_r($data);

foreach($data as $a)

{

echo "number is $a[id]";

echo "
";

echo "name is $a[name]";

echo "
";

}

?>

訪問頁面測試

如果出現mysql表示memcached中沒有內容,需要memcached從數據庫中取得

再刷新頁面,如果有memcache標志表示這次的數據是從memcached中取得的。

memcached有個緩存時間默認是1分鐘,過了一分鐘后,memcached需要重新從數據庫中取得數據

查看Memcached緩存情況

我們需要使用telnet命令查看

[root@memcache ~]# telnet 192.168.31.250 11211

Trying 192.168.31.250...

Connected to 192.168.31.250.

Escape character is '^]'.

stats

STAT pid 1681//Memcached進程的ID

STAT uptime 8429//進程運行時間

STAT time 1479142306//當前時間

STAT version 1.4.33//Memcached版本

STAT libevent 2.0.22-stable

STAT pointer_size 64

STAT rusage_user 1.218430

STAT rusage_system 1.449512

STAT curr_connections 5

STAT total_connections 32

STAT connection_structures 10

STAT reserved_fds 20

STAT cmd_get 25//總共獲取數據的次數(等于get_hits + get_misses)

STAT cmd_set 19//總共設置數據的次數

STAT cmd_flush 4

STAT cmd_touch 0

STAT get_hits 15//命中了多少次數據,也就是從Memcached緩存中成功獲取數據的次數

STAT get_misses 10//沒有命中的次數

STAT get_expired 3

STAT get_flushed 1

STAT delete_misses 0

STAT delete_hits 0

STAT incr_misses 2

STAT incr_hits 2

STAT decr_misses 0

STAT decr_hits 0

STAT cas_misses 0

STAT cas_hits 0

STAT cas_badval 0

STAT touch_hits 0

STAT touch_misses 0

STAT auth_cmds 0

STAT auth_errors 0

STAT bytes_read 3370

STAT bytes_written 15710

STAT limit_maxbytes 2147483648//總的存儲大小,默認為64M

STAT accepting_conns 1

STAT listen_disabled_num 0

STAT time_in_listen_disabled_us 0

STAT threads 4

STAT conn_yields 0

STAT hash_power_level 16

STAT hash_bytes 524288

STAT hash_is_expanding 0

STAT malloc_fails 0

STAT log_worker_dropped 0

STAT log_worker_written 0

STAT log_watcher_skipped 0

STAT log_watcher_sent 0

STAT bytes 584//當前所用存儲大小

STAT curr_items 3

STAT total_items 17

STAT expired_unfetched 2

STAT evicted_unfetched 0

STAT evictions 0

STAT reclaimed 4

STAT crawler_reclaimed 0

STAT crawler_items_checked 0

STAT lrutail_reflocked 0

END

命中率=get_hits/cmd_get

本文出自 “李世龍” 博客,謝絕轉載!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,695評論 2 370

推薦閱讀更多精彩內容