您好,登錄后才能下訂單哦!
博文結(jié)構(gòu)
Memcache簡(jiǎn)介
Memcache工作流程
Memcache調(diào)度算法
Memcache實(shí)現(xiàn)原理
安裝Memcache
Memcache是一套自由、開源、高性能、分布式的高速緩存系統(tǒng)。由于Memcache通過在內(nèi)存中緩存數(shù)據(jù)和對(duì)象來減少讀取數(shù)據(jù)庫的次數(shù)。目前被許多網(wǎng)站使用以提升網(wǎng)站的訪問速度,尤其對(duì)于一些大型的、需要頻繁訪問數(shù)據(jù)庫的網(wǎng)站訪問速度提升效果十分顯著。
memcache是一套分布式的高速緩存系統(tǒng),可以提高網(wǎng)站訪問的速度,尤其是對(duì)于一些大型的公司或者頻繁訪問數(shù)據(jù)庫的網(wǎng)站訪問速度提升效果十分顯著,memecache是一個(gè)開源免費(fèi)的軟件,memcache通過鍵值對(duì)的方式將數(shù)據(jù)緩存在內(nèi)存當(dāng)中,減少?gòu)暮蠖藬?shù)據(jù)庫讀取數(shù)據(jù)的次數(shù)。
MemCache 雖然被稱為”分布式緩存”,但是 MemCache 本身完全不具備 分布式的功能,MemCache 集群之間不會(huì)相互通信(與之形成對(duì)比的,比如 JBoss Cache,某 臺(tái)服務(wù)器有緩存數(shù)據(jù)更新時(shí),會(huì)通知集群中其他機(jī)器更新緩存或清除緩存數(shù)據(jù)),所謂的” 分布式”,完全依賴于客戶端程序的實(shí)現(xiàn),就像上面這張圖的流程一樣。 同時(shí)基于這張圖,理一下 MemCache 一次寫緩存的流程:
簡(jiǎn)單的路由算法可以使用余數(shù) Hash:用服務(wù)器數(shù)目和緩存數(shù)據(jù) KEY 的 hash 值相除,余數(shù)為服 務(wù)器列表下標(biāo)編號(hào),假如某個(gè) str 對(duì)應(yīng)的 HashCode 是 52、服務(wù)器的數(shù)目是 3,取余數(shù)得到 1, str 對(duì)應(yīng)節(jié)點(diǎn) Node1,所以路由算法把 str 路由到 Node1 服務(wù)器上。由于 HashCode 隨機(jī)性比 較強(qiáng),所以使用余數(shù) Hash 路由算法就可以保證緩存數(shù)據(jù)在整個(gè) MemCache 服務(wù)器集群中有 比較均衡的分布。
比如Memcache服務(wù)器集群由3臺(tái)變成4臺(tái),假設(shè)有HashCode為0~19的20個(gè)數(shù)據(jù),如圖:
現(xiàn)在擴(kuò)容到 4 臺(tái),加粗標(biāo)紅的表示命中:
在網(wǎng)站業(yè)務(wù)中,大部分的業(yè)務(wù)數(shù)據(jù)度操作請(qǐng)求上事實(shí)上是 通過緩存獲取的,只有少量讀操作會(huì)訪問數(shù)據(jù)庫,因此數(shù)據(jù)庫的負(fù)載能力是以有緩存為前提 而設(shè)計(jì)的。當(dāng)大部分被緩存了的數(shù)據(jù)因?yàn)榉?wù)器擴(kuò)容而不能正確讀取時(shí),這些數(shù)據(jù)訪問的壓 力就落在了數(shù)據(jù)庫的身上,這將大大超過數(shù)據(jù)庫的負(fù)載能力,嚴(yán)重的可能會(huì)導(dǎo)致數(shù)據(jù)庫宕機(jī)。
這個(gè)問題有解決方案,解決步驟為:
(1)在網(wǎng)站訪問量低谷,通常是深夜,技術(shù)團(tuán)隊(duì)加班,擴(kuò)容、重啟服務(wù)器
(2)通過模擬請(qǐng)求的方式逐漸預(yù)熱緩存,使緩存服務(wù)器中的數(shù)據(jù)重新分布
一致性 Hash 算法通過一個(gè)叫做一致性 Hash 環(huán)的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn) Key 到緩存服務(wù)器的 Hash 映射。
簡(jiǎn)單地說,一致性哈希將整個(gè)哈希值空間組織成一個(gè)虛擬的圓環(huán)(這個(gè)環(huán)被稱為一致性 Hash 環(huán)),如假設(shè)某空間哈希函數(shù) H 的值空間是 0~2^32-1(即哈希值是一個(gè) 32 位無符號(hào)),整個(gè)哈??臻g如下:
然后將各個(gè)服務(wù)器使用 H 進(jìn)行一個(gè)哈希計(jì)算,具體可以使用服務(wù)器的 IP 地址或者主機(jī)名 作為關(guān)鍵字,這樣每臺(tái)機(jī)器能確定其在上面的哈希環(huán)上的位置了,并且是按照順時(shí)針排列, 這里我們假設(shè)三臺(tái)節(jié)點(diǎn) memcache 經(jīng)計(jì)算后位置如下 :
接下來使用相同算法計(jì)算出數(shù)據(jù)的哈希值 h,并由此確定數(shù)據(jù)在此哈希環(huán)上的位置 假如我們有數(shù)據(jù) A、B、C、D、4 個(gè)對(duì)象,經(jīng)過哈希計(jì)算后位置如下:
根據(jù)一致性哈希算法,數(shù)據(jù) A 就被綁定到了 server01 上,D 被綁定到了 server02 上,B、C 在 server03 上,是按照順時(shí)針找最近服務(wù)節(jié)點(diǎn)方法 。
這樣得到的哈希環(huán)調(diào)度方法,有很高的容錯(cuò)性和可擴(kuò)展性:
假設(shè) server03 當(dāng)機(jī):
可以看到此時(shí) C、B 會(huì)受到影響,將 B、C 節(jié)點(diǎn)被重定位到 Server01。一般的,在一致性哈希 算法中,如果一臺(tái)服務(wù)器不可用,則受影響的數(shù)據(jù)僅僅是此服務(wù)器到其環(huán)空間中前一臺(tái)服務(wù) 器(即順著逆時(shí)針方向行走遇到的第一臺(tái)服務(wù)器)之間數(shù)據(jù),其它不會(huì)受到影響。 考慮另外一種情況,如果我們?cè)谙到y(tǒng)中增加一臺(tái)服務(wù)器 Memcached Server 04:
此時(shí) A、D、C 不受影響,只有 B 需要重定位到新的 Server04。
一致性哈希算法對(duì)于節(jié)點(diǎn)的增減都只需重定位環(huán)空間中的一小部分?jǐn)?shù)據(jù),
具有較好的容錯(cuò)性和可擴(kuò)展性
一致性哈希的缺點(diǎn):在服務(wù)節(jié)點(diǎn)太少時(shí),容易因?yàn)楣?jié)點(diǎn)分部不均勻而造成數(shù)據(jù)傾斜問題。我 們可以采用增加虛擬節(jié)點(diǎn)的方式解決。 更重要的是,集群中緩存服務(wù)器節(jié)點(diǎn)越多,增加/減少節(jié)點(diǎn)帶來的影響越小,很好理解。換 句話說,隨著集群規(guī)模的增大,繼續(xù)命中原有緩存數(shù)據(jù)的概率會(huì)越來越大,雖然仍然有小部 分?jǐn)?shù)據(jù)緩存在服務(wù)器中不能被讀到,但是這個(gè)比例足夠小,即使訪問數(shù)據(jù)庫,也不會(huì)對(duì)數(shù)據(jù) 庫造成致命的負(fù)載壓力。
Memcache的數(shù)據(jù)存放在內(nèi)存中,32位機(jī)器最多只能使用2GB的內(nèi)存弓箭,64位機(jī)器可以認(rèn)為沒有顯示
如圖:Memcache的數(shù)據(jù)存儲(chǔ)機(jī)制(內(nèi)存分配機(jī)制): 固定空間分配
(1)Memcache將內(nèi)存空間分為一組slab;
(2)每個(gè)slab下又有若干個(gè)page,每個(gè)page默認(rèn)是1M,如果一個(gè)slab占用100M內(nèi)存的話,那么這個(gè)slab下應(yīng)該有100個(gè)page;
(3)每個(gè)page里面包含一組chunk,chunk是真正存放數(shù)據(jù)的地方,同一個(gè)slab里面的chunk的大小是固定的;
(4)有相同大小chunk的slab被組織在一起,稱為slab_class;
Memcache內(nèi)存分配的方式稱為allocator(分配運(yùn)算),slab的數(shù)量是有限的,幾個(gè)、十幾個(gè)或者幾十個(gè),這個(gè)和啟動(dòng)參數(shù)的配置相關(guān);
Memcache中的value存放的地方是由value的大小決定的,value總是會(huì)存放到與chunk大小最接近的一個(gè)slab中。
1、檢查客戶端的請(qǐng)求數(shù)據(jù)是否在 memcached 中,如果有,直接把請(qǐng)求數(shù)據(jù)返回,不再對(duì)數(shù)據(jù)庫進(jìn)行任何操作,路徑操作為①②③⑦;
2、如果請(qǐng)求的數(shù)據(jù)不在 memcached 中,就去查數(shù)據(jù)庫,把從數(shù)據(jù)庫中獲取的數(shù)據(jù)返回給客戶端,同時(shí)把數(shù)據(jù)緩存一份到 memcached 中(memcached 客戶端不負(fù)責(zé),需要程序明確實(shí)現(xiàn)),路徑操作為①②④⑤⑦⑥;
3、每次更新數(shù)據(jù)庫的同時(shí)更新 memcached 中的數(shù)據(jù),保證一致性;
4、當(dāng)分配給 memcached 內(nèi)存空間用完之后,會(huì)使用 LRU(Least Recently Used,最近最少使用)策略加上到期失效策略,失效數(shù)據(jù)首先被替換,然后再替換掉最近未使用的數(shù)據(jù);
基于文本行協(xié)議:常見的協(xié)議http、ftp、smtp都是基于文本行的,所謂基于文本行,指的是信息以文本傳遞
基于libevent事件處理:libevent是利用c語言開發(fā)的程序庫,bsd系統(tǒng)的kqueue(BSD是unix的衍生版本),linux系統(tǒng)的epoll等事件,所有數(shù)據(jù)都保存在內(nèi)存中,數(shù)據(jù)訪問速度塊
分布式
各個(gè)memcached服務(wù)器之間互不通信,各自獨(dú)立存取數(shù)據(jù),不共享任何信息,服務(wù)器并不具備分布式功能,分布式部署取決于memcache客戶端,Memcache的安裝分為兩個(gè)過程,memcache服務(wù)器端的安裝和memcached客戶端的安裝
Memcache的搭建需要借助于LAMP或LNMP,本篇博文采用LNMP結(jié)構(gòu)
192.168.148.129 Memcache
192.168.148.130 php
192.168.148.131 nginx
192.168.148.136 mysql
下載nginx軟件包
[root@bogon ~]# useradd -M -s /sbin/nologin nginx
[root@bogon ~]# yum -y install openssl-devel
[root@bogon ~]# tar zxf pcre-8.39.tar.gz -C /usr/src
[root@bogon ~]# tar zxf zlib-1.2.8.tar.gz -C /usr/src
[root@bogon ~]# tar zxf nginx-1.14.0.tar.gz -C /usr/src
[root@bogon ~]# cd /usr/src/nginx-1.14.0/
[root@bogon nginx-1.14.0]# ./configure --prefix=/usr/local/nginx \
--user=nginx --group=nginx --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=/usr/src/pcre-8.39 --with-zlib=/usr/src/zlib-1.2.8 \
--with-http_ssl_module --with-http_gzip_static_module && make && make install
[root@bogon ~]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin
[root@bogon ~]# nginx
[root@bogon ~]# netstat -anpt | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 8460/nginx: master
下載php軟件包
[root@localhost ~]# yum -y install libxml2-devel lzip2-devel libcurl-devel libmcrypt-devel openssl-devel bzip2-devel
[root@localhost ~]# tar zxf libmcrypt-2.5.7.tar.gz
[root@localhost ~]# cd libmcrypt-2.5.7/
[root@localhost libmcrypt-2.5.7]# ./configure --prefix=/usr/local/libmcrypt && make && make install
[root@localhost ~]# tar zxf php-5.6.27.tar.gz
[root@localhost ~]# cd php-5.6.27/
[root@localhost 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 && make && make install
[root@localhost php-5.6.27]# cp php.ini-production /etc/php.ini
[root@localhost php-5.6.27]# cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
[root@localhost php-5.6.27]# chmod +x /etc/init.d/php-fpm
[root@localhost php-5.6.27]# chkconfig --add php-fpm
[root@localhost php-5.6.27]# chkconfig php-fpm on
[root@localhost php-5.6.27]# cp /usr/local/php5.6/etc/php-fpm.conf.default /usr/local/php5.6/etc/php-fpm.conf
[root@localhost php-5.6.27]# vim /usr/local/php5.6/etc/php-fpm.conf
修改內(nèi)容如下:
pid = run/php-fpm.pid
listen = 192.168.31.141:9000 \\本地ip地址(千萬不能用127.0.0.1)
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
[root@localhost php-5.6.27]# service php-fpm start
Starting php-fpm done
[root@localhost php-5.6.27]# netstat -anpt | grep php-fpm
tcp 0 0 192.168.148.130:9000 0.0.0.0:* LISTEN 130988/php-fpm: mas
下載mysql腳本一鍵安裝
[root@bogon ~]# mysql -u root -p123
mysql> create database testdb1;
mysql> use testdb1;
mysql> grant all on *.* to xws@'192.168.148.%' identified by '123456';
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;
mysql> insert into test1(name) values ('tom1'),('tom2'),('tom3'),('tom4'),('tom5');
mysql> select * from test1;
+----+------+
| id | name |
+----+------+
| 1 | tom1 |
| 2 | tom2 |
| 3 | tom3 |
| 4 | tom4 |
| 5 | tom5 |
+----+------+
5 rows in set (0.00 sec)
[root@bogon nginx-1.14.0]# vim /usr/local/nginx/conf/nginx.conf //修改如下(大約在43行)
location / {
root html;
index index.php index.html index.htm;
}
location ~ \.php$ {
root /var/www/html;
fastcgi_pass 192.168.148.130:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi.conf;
}
[root@bogon nginx-1.14.0]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@bogon nginx-1.14.0]# nginx -s reload
php操作如下:
[root@bogon ~]# mkdir -p /var/www/html
[root@bogon ~]# vim /var/www/html/test.php
<?php
phpinfo();
?>
[root@bogon ~]# vim /var/www/html/test1.php
<?php
$link=mysqli_connect('192.168.148.136','xws','123456');
if($link) echo "恭喜你,數(shù)據(jù)庫連接成功?。。?; else echo "connect shibai";
mysqli_close($link);
?>
客戶端訪問如下:
下載memcache
[root@bogon ~]# tar zxf libevent-2.0.22-stable.tar.gz -C /usr/src/
[root@bogon ~]# cd /usr/src/libevent-2.0.22-stable/
[root@bogon libevent-2.0.22-stable]# ./configure && make && make install
[root@bogon ~]# tar zxf memcached-1.4.33.tar.gz -C /usr/src/
[root@bogon ~]# cd /usr/src/memcached-1.4.33/
[root@bogon memcached-1.4.33]# ./configure --prefix=/usr/local/memcached \
> --with-libevent=/usr/local/ && make && make install
[root@bogon ~]# memcached -d -m 2048 -l 192.168.1.7 -p 11211 -c 10240 -P /usr/local/memcached/memcached.pid -u root
[root@bogon ~]# netstat -anpt | grep 11211
[root@bogon ~]# tar zxf memcache-3.0.8.tgz -C /usr/src/
[root@bogon ~]# cd /usr/src/memcache-3.0.8/
[root@bogon memcache-3.0.8]# /usr/local/php5.6/bin/phpize
[root@PHP memcache-3.0.8]# ./configure --enable-memcache \
--with-php-config=/usr/local/php/bin/php-config && make && make install
//執(zhí)行后會(huì)顯示memcache.so存放的路徑
[root@PHP ~]# echo "extension = /usr/local/php/lib/php/extensions/no-debug-zts-20131226/memcache.so" >> /etc/php.ini
//在PHP主配置文件中填寫memcache.so模塊存放的路徑
[root@PHP ~]# systemctl restart php-fpm
[root@PHP ~]# vim /var/www/html/test2.php
<?php
$memcache = new Memcache;
$memcache->connect('192.168.1.129', 11211) or die ("Could not connect");
$version = $memcache->getVersion();
echo "Server's version: ".$version."<br/>";
$tmp_object = new stdClass;
$tmp_object->str_attr = 'test';
$tmp_object->int_attr = 123;
$memcache->set('key', $tmp_object, false, 600) or die ("Failed to save data at the server");
echo "Store data in the cache (data will expire in 600 seconds)<br/>";
$get_result = $memcache->get('key');
echo "Data from the cache:<br/>";
var_dump($get_result);
?>
//此測(cè)試腳本是顯示memcached的版本
//并且向里面插入了一個(gè)緩存時(shí)間為600秒的鍵值對(duì)“test=123”,其ID為“key”
客戶端訪問如下:
在PHP服務(wù)器上安裝telnet工具測(cè)試
[root@PHP ~]# yum -y install telnet
[root@PHP ~]# telnet 192.168.148.129 11211 //登陸到memcached的11211端口
Trying 192.168.148.129...
Connected to 192.168.148.129.
Escape character is '^]'.
get key //查詢ID為“key”的鍵值對(duì),可以看到我們測(cè)試腳本寫入的“test=123”
VALUE key 1 66
O:8:"stdClass":2:{s:8:"str_attr";s:4:"test";s:8:"int_attr";i:123;}
END
//在進(jìn)行上面的get驗(yàn)證時(shí),需要將test2.php文件中插入的鍵值對(duì)的保存時(shí)間值改大一些
//或者重新訪問一下,以免緩存失效,查詢不到
quit //退出當(dāng)前環(huán)境
Connection closed by foreign host.
[root@PHP ~]#
[root@PHP ~]# vim /var/www/html/test4.php
<?php
$memcachehost = '192.168.1.7'; //指定Memcache服務(wù)器地址
$memcacheport = 11211; //指定其開放的端口號(hào)
$memcachelife = 60;
$memcache = new Memcache;
$memcache->connect($memcachehost,$memcacheport) or die ("Could not connect");
$query="select * from test1 limit 10";
$key=md5($query);
if(!$memcache->get($key))
{
$conn=mysql_connect("192.168.1.8","lzj","123456"); //指定數(shù)據(jù)庫服務(wù)器的IP地址、用戶及密碼
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 "<br>";
echo "$key";
echo "<br>";
//print_r($data);
foreach($data as $a)
{
echo "number is <b><font color=#FF0000>$a[id]</font></b>";
echo "<br>";
echo "name is <b><font color=#FF0000>$a[name]</font></b>"; //突出顯示信息的字體顏色
echo "<br>";
}
?>
//經(jīng)常需要修改的地方已經(jīng)標(biāo)注了!而且這個(gè)測(cè)試腳本在Memcache軟件中也有
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。