但行好事,莫问前程

挖了太多坑,一点点填回来

聊聊 Memcache

cache, memcache, 缓存

什么是 Memcache

Memcached 是一个免费开源的、高性能的、具有分布式内存对象的缓存系统,它通过减轻数据库负载加速动态 Web 应用。最初版本由 LiveJournal 的 Brad Fitzpatrick 在 2003 年开发完成。目前全世界很多用户都在使用它来构建自己的大负载网站或提高自己的高访问网站的响应速度。Memcache 是这个项目的名称,而 Memcached 是服务器端的主程序文件名。

缓存一般用来保存一些经常存取的对象或数据(例如,浏览器会把经常访问的网页缓存起来),通过缓存来存取对象或数据要比磁盘存取快很多。Memcache 是一种内存缓存,把经常存取的对象或数据缓存在内存中,内存中缓存的这些数据通过 API 的方式被存取,数据就像一张大的 hash 表,以 key-value 对的方式存在。Memcache 通过缓存经常被存取的对象或数据,来减轻数据库的压力,提高网站的响应速度,构建速度更快的可扩展的Web应用。

Memcache 是一个分布式的缓存系统,既然是分布式的,那么就会碰到帽子定理(CAP theorem)。

什么是 CAP theorem?

在理论计算机科学中,CAP 定理(CAP theorem),又被称作布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

一致性(Consistency)(所有节点在同一时间具有相同的数据)

可用性(Availability)(保证每个请求不管成功或者失败都有响应)

分隔容忍(Partition tolerance)(系统中任意信息的丢失或失败不会影响系统的继续运作)

根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。所以,小伙伴们就不要冒着头发掉光的危险去想办法实现系统中的完美功能了,因为世界上就没有完美的东西。

分布式 Memcache

Memcache 在 PHP 下有两个扩展,分别是 memcachememcached,由于之前在工作中一直都是用到 memcache ,因此下面的内容将以这个扩展为准。

Memcache 的 PHP 扩展在客户端实现了分布式功能,通过很简单的代码就能添加或者减少节点,比如:

1
2
3
4
5
<?php
$memcache = new Memcache();
$memcache->addServer('memcache_host', 11211);
$memcache->addServer('memcache_host2', 11211);
// ...coding to add more memcache servers...

而 Memcache 选取节点的算法,一般情况下是采用取余算法,通过 crc32 函数将 key 处理之后的结果对当前节点数目进行取余,取余的结果就决定这条 key 将会映射到哪个节点上。不过这样一来就会有一个很明显的问题——如果在生产环境上增加或者减少节点,那么在做取余运算的时候,原有的 key 几乎都要映射到新的节点上,原有的缓存基本都生效了。在服务器遇到极大访问量的时候,压力就会直接到达数据库,啊多么痛的领悟!!!

于是就有了改进的分布式算法——一致性 hash 算法

一致性 hash 算法

分布式缓存设计核心点:在设计分布式 cache 系统的时候,我们需要让 key 的分布均衡,并且在增加 cache server 后,cache 的迁移做到最少。一致性哈希算法的做法是:选择具体的机器节点不在只依赖需要缓存数据的 key 的哈希本身了,而是机器节点本身也进行了哈希运算。

hash 机器节点

首先求出机器节点的 hash 值,然后将其分布到0~232的一个圆环上(顺时针分布)。如下图所示:

图 – 1

集群中有机器:A , B, C, D, E 五台机器,通过一定的 hash 算法我们将其分布到如上图所示的环上。

访问方式

如果有一个写入缓存的请求,其中 key 值为 K,计算其 hash 值 Hash(K), Hash(K) 对应于图 – 1环中的某一个点,如果该点对应没有映射到具体的某一个机器节点,那么顺时针查找,直到第一次找到有映射机器的节点,该节点就是确定的目标节点,如果超过了 232 仍然找不到节点,则命中第一个机器节点。比如 Hash(K) 的值介于 A ~ B 之间,那么命中的机器节点应该是 B 节点(如上图)。

增加节点的处理

如上图 – 1,在原有集群的基础上欲增加一台机器 F,增加过程如下:

计算机器节点的 hash值,将机器映射到环中的一个节点,如下图:

图 – 2

增加机器节点 F 之后,访问策略不改变,依然按照之前方式访问,此时缓存命不中的情况依然不可避免,不能命中的数据是 hash(K)在增加节点以前落在 C~F 之间的数据。尽管依然存在节点增加带来的命中问题,但是比较传统的 hash 取模的方式,一致性 hash 已经将不命中的数据降到了最低。

一致性哈希算法最大限度地抑制了 hash 键的重新分布。另外要取得比较好的负载均衡的效果,往往在服务器数量比较少的时候需要增加虚拟节点来保证服务器能均匀的分布在圆环上。因为使用一般的 hash 方法,服务器的映射地点的分布非常不均匀。使用虚拟节点的思想,为每个物理节点(服务器)在圆上分配 100~200 个点。这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。用户数据映射在虚拟节点上,就表示用户数据真正存储位置是在该虚拟节点代表的实际物理服务器上。下面有一个图描述了需要为每台物理服务器增加的虚拟节点。

图 – 3

X 轴表示的是需要为每台物理服务器扩展的虚拟节点倍数(scale),Y 轴是实际物理服务器数,可以看出,当物理服务器的数量很小时,需要更大的虚拟节点,反之则需要更少的节点。从图上可以看出,在物理服务器有 10 台时,差不多需要为每台服务器增加 100 ~ 200 个虚拟节点才能达到真正的负载均衡。

Memcache 的小知识

1、单条缓存值限制为 1 MB。

2、key 的限制为 250 Bytes。

3、cache 失效算法是 LRU 算法。

4、cache 默认失效时间和最长缓存时间都是 30 天。

参考链接

http://zh.wikipedia.org/wiki/Memcached
http://zh.wikipedia.org/wiki/CAP%E5%AE%9A%E7%90%86
http://zh.wikipedia.org/wiki/%E5%BF%AB%E5%8F%96%E6%96%87%E4%BB%B6%E7%BD%AE%E6%8F%9B%E6%A9%9F%E5%88%B6
http://zh.wikipedia.org/wiki/%E5%BE%AA%E7%8E%AF%E5%86%97%E4%BD%99%E6%A0%A1%E9%AA%8C
http://us1.php.net/manual/zh/book.memcache.php http://php.net/manual/zh/book.memcached.php

Have a nice day!