Memcached 的工作原理以及简单的demo【转】
转自:http://hzp.iteye.com/blog/1872664
Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。
Memcached有两个核心组件组成:服务端(ms)和客户端(mc),在一个memcached的查询中,mc先通过计算key的hash值来 确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以 memcached交互带给网络的影响是最小化的。
举例说明:考虑以下这个场景,有三个mc分别是X,Y,Z,还有三个ms分别是A,B,C:
设置kv对
X想设置key=”foo”,value=”seattle”
X拿到ms列表,并对key做hash转化,根据hash值确定kv对所存的ms位置
B被选中了
X连接上B,B收到请求,把(key=”foo”,value=”seattle”)存了起来
获取kv对
Z想得到key=”foo”的value
Z用相同的hash算法算出hash值,并确定key=”foo”的值存在B上
Z连接上B,并从B那边得到value=”seattle”
其他任何从X,Y,Z的想得到key=”foo”的值的请求都会发向B

Memcached服务器(ms)
内存分配
默认情况下,ms是用一个内置的叫“块分配器”的组件来分配内存的。舍弃c++标准的malloc/free的内存分配,而采用块分配器的主要目的 是为了避免内存碎片,否则操作系统要花费更多时间来查找这些逻辑上连续的内存块(实际上是断开的)。用了块分配器,ms会轮流的对内存进行大块的分配,并 不断重用。当然由于块的大小各不相同,当数据大小和块大小不太相符的情况下,还是有可能导致内存的浪费。
同时,ms对key和data都有相应的限制,key的长度不能超过250字节,data也不能超过块大小的限制 --- 1MB。
因为 mc所使用的hash算法,并不会考虑到每个ms的内存大小。理论上mc会分配概率上等量的kv对给每个ms,这样如果每个ms的内存都不太一样,那可能 会导致内存使用率的降低。所以一种替代的解决方案是,根据每个ms的内存大小,找出他们的最大公约数,然后在每个ms上开n个容量=最大公约数的 instance,这样就等于拥有了多个容量大小一样的子ms,从而提供整体的内存使用率。
缓存策略
当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。Kv对存储有效时限是在mc端由app设置并作为参数传给ms的。
同时ms采用是偷懒替代法,ms不会开额外的进程来实时监测过时的kv对并删除,而是当且仅当,新来一个插入的数据,而此时又没有多余的空间放了,才会进行清除动作。
Memcached客户端(mc)
Memcached客户端有各种语言的版本供大家使用,包括java,c,php,.net等等。
大家可以根据自己项目的需要,选择合适的客户端来集成。
缓存式的Web应用程序架构
有了缓存的支持,我们可以在传统的app层和db层之间加入cache层, 每个app服务器都可以绑定一个mc,每次数据的读取都可以从ms中取得,如果没有,再从db层读取。而当数据要进行更新时,除了要发送update的 sql给db层,同时也要将更新的数据发给mc,让mc去更新ms中的数据。

假设今后我们的数据库可以和ms进行通讯了,那可以将更新的任务统一交给db层,每次数据库更新数据的同时会自动去更新ms中的数据,这样就可以进一步减少app层的逻辑复杂度。如下图:

不过每次我们如果没有从cache读到数据,都不得不麻烦数据库。为了最小化数据库的负载压力,我们可以部署数据库复写,用slave数据库来完成读取操作,而master数据库永远只负责三件事:1.更新数据;2.同步slave数据库;3.更新cache。如下图:

以上这些缓存式web架构在实际应用中被证明是能有效并能极大地降低数据库的负载同时又能提高web的运行性能。当然这些架构还可以根据具体的应用环境进行变种,以达到不同硬件条件下性能的最优化。
二、简单使用demo
1.客户端和服务端的安装
1.1服务端的安装:
1.1.1 下载memcached的windows版本,例如:memcached-1.2.6-win32-bin.zip
1.1.2 安装&运行:打开cmd,输入命令:
安装:D:\ITsoft\Memcache\memcached\memcached.exe -d install
启动服务:D:\ITsoft\Memcache\memcached\memcached.exe -d start
1.2 客户端的安装【java语言】:下载 java_memcached-release_2.6.6 ,引入到web工程的lib目录下即可
2. 编写JAVA程序测试
package com.sam.memcached.test; import java.util.Date; import com.danga.MemCached.MemCachedClient; import com.danga.MemCached.SockIOPool; @SuppressWarnings("deprecation") public class MemcachedTest { // 创建全局的唯一实例 protected static MemCachedClient mcc = new MemCachedClient(); protected static MemcachedTest memCached = new MemcachedTest(); // 设置与缓存服务器的连接池 static { // 服务器列表和其权重 String[] servers = {"127.0.0.1:11211"}; //这个端口是memcache服务的端口号,是死的 不知道怎么修改 Integer[] weights = {3}; // 获取socke连接池的实例对象 SockIOPool pool = SockIOPool.getInstance(); // 设置服务器信息 pool.setServers( servers ); pool.setWeights( weights ); // 设置初始连接数、最小和最大连接数以及最大处理时间 pool.setInitConn( 5 ); pool.setMinConn( 5 ); pool.setMaxConn( 250 ); pool.setMaxIdle( 1000 * 60 * 60 * 6 ); // 设置主线程的睡眠时间 pool.setMaintSleep( 30 ); // 设置TCP的参数,连接超时等 pool.setNagle( false ); pool.setSocketTO( 3000 ); pool.setSocketConnectTO( 0 ); // 初始化连接池 pool.initialize(); } private MemcachedTest() { } public static MemcachedTest getInstance() { return memCached; } /** * 添加对象到缓存 */ public boolean add(String key, Object value) { return mcc.add(key, value); } /** * * @param key * @param value * @param expiry 过期时间 * @return */ public boolean add(String key, Object value, Date expiry) { return mcc.add(key, value, expiry); } public boolean replace(String key, Object value) { return mcc.replace(key, value); } public boolean replace(String key, Object value, Date expiry) { return mcc.replace(key, value, expiry); } public Object get(String key) { return mcc.get(key); } public static void main(String[] args) { MemcachedTest cache = MemcachedTest.getInstance(); cache.add("memcache", "sam"); System.out.print("memcache : " + cache.get("memcache")); } }
测试:第一次运行后,把 cache.add("memcache", "sam");这行注释掉,再运行,可以成功获取值,说明对象已经存到memcached中去了

浙公网安备 33010602011771号