标签 : 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->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->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>