天天看点

Memcached - In Action Memcached

标签 : Java与NoSQL

比较知名的Java Memcached客户端有三款:Java-Memcached-Client、XMemcached以及Spymemcached, 其中以XMemcached性能最好, 且维护较稳定/版本较新:

任何技术都有其最适用的场景,只有在合适的场景下,才能发挥最好的效果.Memcached使用内存读写数据,速度比DB和文件系统快得多, 因此,Memcached的常用场景有:

缓存DB查询数据: 作为缓存“保护”数据库, 防止频繁的读写带给DB过大的压力;

中继MySQL主从延迟: 利用其“读写快”特点实现主从数据库的消息同步.

通过Memcached缓存数据库查询结果,减少DB访问次数,以提高动态Web应用响应速度:

JDBC模拟Memcached缓存DB数据:

MySQL在做<code>replication</code>时,主从复制时会由一段时间延迟,尤其是主从服务器分处于异地机房时,这种情况更加明显.FaceBook官方的一篇技术文章提到:其加州的数据中心到弗吉尼亚州数据中心的主从同步延迟达到70MS. 考虑以下场景:

用户U购买电子书B:<code>insert into Master (U,B)</code>;

用户U观看电子书B:<code>select 购买记录 [user='A',book='B'] from Slave</code>.

由于主从延迟的存在,第②步中无记录,用户无权观看该书.

此时可以利用Memcached在Master与Slave之间做过渡:

用户U购买电子书B:<code>memcached-&gt;add('U:B',true)</code>;

主数据库: <code>insert into Master (U,B)</code>;

用户U观看电子书B: <code>select 购买记录 [user='U',book='B'] from Slave</code>;

如果没查询到,则<code>memcached-&gt;get('U:B')</code>,查到则说明已购买但有主从延迟.

如果Memcached中也没查询到,用户无权观看该书.

目标:

key的分布尽量均匀;

增/减服务器节点对于其他节点的影响尽量小.

首先开辟一块非常大的空间(如图中:0~232),然后将所有的数据使用<code>hash</code>函数(如MD5、Ketama等)映射到这个空间内,形成一个Hash环. 当有数据需要存储时,先得到一个hash值对应到hash环上的具体位置(如k1),然后沿顺时针方向找到一台机器(如B),将k1存储到B这个节点中:

如果B节点宕机,则B上的所有负载就会落到C节点上:

这样,只会影响C节点,对其他的节点如A、D的数据都不会造成影响. 然而,这样又会带来一定的风险,由于B节点的负载全部由C节点承担,C节点的负载会变得很高,因此C节点又会很容易宕机,依次下去会造成整个集群的不稳定.

理想的情况下是当B节点宕机时,将原先B节点上的负载平均的分担到其他的各个节点上. 为此,又引入了“虚拟节点”的概念: 想象在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点,每个虚拟节点都会关联到一个真实节点,但一个真实节点会对应多个虚拟节点,且不同真实节点的多个虚拟节点是交差分布的:

图中A1、A2、B1、B2、C1、C2、D1、D2 都是“虚拟节点”,机器A负责存储A1、A2的数据, 机器B负责存储B1、B2的数据… 只要虚拟节点数量足够多且分布均匀,当其中一台机器宕机之后,原先机器上的负载就会平均分配到其他所有机器上(如图中节点B宕机,其负载会分担到节点A和节点D上).

测试

经过实际测试: 当有十台真实节点,而每个真实节点有50个虚拟节点时,在发生一台实际节点宕机/新增一台节点的情况时,命中率仍然能够达到90%左右.对比简单取模Hash算法:

当节点从N到N-1时,缓存的命中率直线下降为1/N(N越大,命中率越低);一致性Hash的表现就优秀多了:

命中率只下降为原先的 (N-1)/N ,且服务器节点越多,性能越好.因此一致性Hash算法可以最大限度地减小服务器增减时的缓存重新分布带来的压力.

实际上XMemcached客户端自身实现了很多一致性Hash算法(<code>KetamaMemcachedSessionLocator</code>/<code>PHPMemcacheSessionLocator</code>), 因此在开发中没有必要自己去实现:

示例: 支持分布式的MemcachedFilter:

以上代码最好有Nginx的如下配置支持: Nginx以前端请求的<code>"URI+Args"</code>作为key去请求Memcached,如果key命中,则直接由Nginx从缓存中取出数据响应前端;未命中,则产生404异常,Nginx捕获之并将request提交后端服务器.在后端服务器中,request被<code>MemcachedFilter</code>拦截, 待业务逻辑执行完, 该<code>Filter</code>会将Response的数据拿到并写入Memcached, 以备下次直接响应.

<dl></dl>

<dt>参考:</dt>