Memcached使用点滴

  我对于Memcached的接触,还是在去年看了CSDN的一系列国外大型网站架构设计而开始的。最初的时候只是简单的封装了Memcached Java版的客户端,主要是对于配置的简化以及Memcached多点备份作了一些工作,然后就作为ASF的组件一部分提供给其他Team使用。其实看过Memcached Java客户端代码的人就会了解其实客户端的事情很简单,就是要有一套高性能的Socket通信框架以及对Memcached的私有协议实现的接口,自己去做这些事情也是很简单的,不过既然有可以满足自己需求的开源部分,那么就去实现自己需要的但没有实现的。这里我用的是Whalin的客户端版本,这里为什么还要提出来讲这个,后面会提到。

在对Java客户端作了简单封装和扩展以后,由于其他Team使用的没有什么特殊需求,也就没有再去做太多的修改,直到最近自己的服务集成平台需要做服务访问控制,才重新丰富了Cache组件,也就是这个过程中对于Memcached的一些特性和小的细节有了一些新的认识。

作为服务集成平台需要对服务有所监控,包括访问频率控制以及访问次数控制。频率控制其实很类似于硬件方面的频率控制,例如硬件可以对IP的高频率访问视为攻击,列入黑名单。而作为服务的访问,对于服务访问者的控制其实涉及到了业务参数,那么硬件就不是很适合去做这方面的控制,为此我也考虑了很久,最开始打算在Apache上做一个模块控制,但是最后觉得还是放在后面的业务框架上做这件事情。当然后面我说说的方案可能并不好,但是也算是一种想法。要把频繁的访问数据记录下来同时分析,那么数据库肯定是不行的,最简单的方式就是采用Cache,又因为是集群范围内的控制,那么集中式Cache就非Memcached莫数了(分布式的Cache传播本身损耗太大,集中式Cache本来的最大缺点就是单点,但作简单的备份操作就可以基本解决此类问题)。

作为解决这个问题的方法来说只需要实现两部分工作:访问计数器,定时任务。定时任务在我做日志分析框架的时候都是采用了Jdk5的Concurrent包里面的ScheduledExecutorService,这个作简单的循环任务足够用了,同时也是有很好的多线程异步支持,复杂一点么用Quartz。计数器就要靠Memcached来实现了,本来一般的Cache最大的问题就是高并发下的事务保证,如果采用Get+Set来完成计数的话,那么高并发下计数器就会出现读写不一致性的问题,幸好Memcached提供了计数累加功能,让这种累加动作能够在服务端一次做好,服务端控制并发写入,保证数据的一致性。

下面就看看以下几个方法:

boolean storeCounter(String key, long count):存储key的计数器,值为count。

long getCounter(String key):获取key的计数器,如果不存在返回-1。

long addOrDecr(String key, long decr):计数器值减去decr,如果计数器不存在,保存decr作为计数器值

long addOrIncr(String key, long inc):计数器值增加inc,如果计数器不存在,保存inc作为计数器值

long decr(String key, long decr):与addOrDecr不同的是在计数器不存在的时候不保存任何值,返回-1

long incr(String key, long inc) :与addOrIncr不同的是在计数器不存在的时候不保存任何值,返回-1

这里需要说明几点:

storeCounter和普通的set方法不同,如果通过set方式置入key:value的话,getCounter等其他四个方法都认为技术器不存在。所以Counter的存储方式是和普通内容存储不同的。

在不同的场景要慎用addOrXXXX和XXXX的方法,两者还是有比较大的区别的。

计数器没有提供移除特殊方法,使用delete方法可以移除计数器,但是频繁的delete和addOrXXXX有时候会出现一些奇怪的问题(例如同名的计数器就没有办法再次被创建,不过这个还需要进一步的去研究一下看看)。一般情况下如果计数器的key不是很多,同时也会被复用,那么可以通过置为0或者减去已经分析过的数量来复位。

有上面的一套计数器机制就可以很方便的实现Memcached的计数功能,但是又一个问题出现了,如何让定时任务去遍历计数器,分析计数器是否到了阀值,触发创建黑名单记录的工作。早先我同事希望我能够提供封装好的keySet接口,但是我自己觉得其实作为Cache来说简单就是最重要的,Cache不需要去遍历。首先使用Cache的角色就应该知道Key,然后去Cache里面找,找不到就去后台例如DB里面去搜索,然后将搜索的结果在考虑更新到Cache里面,这样才是最高效并且最可靠的,Cache靠不住阿,随时都可能会丢失或者崩溃,因此作为类似于一级缓存或者这类数据完整性要求不高,性能要求很高的场景使用最合适。当时就没有提供这样的接口,直到今天自己需要了,才考虑如何去做这件事情。

开始考虑是否能够将key都记录在另外的Cache中或者是Memcached中,首先在高并发下更新操作就是一大问题,再者Memcached的内存分配回收机制以及Value的大小限制都不能满足这样的需求,如果使用数据库,那么频繁更新操作势必不可行,采用异步缓存刷新又有一个时间间隔期,同时更新也不是很方便。最后考虑如果能够让Memcached实现Keyset那么就是最好的解决方案,网上搜索了一下,找到一种策略,然后自己优化了一下,优化后的代码如下:

至此,整个keySet的问题解决了,对于即时监控也基本都作好了,这里需要把过程中的两件小事情说一下。

1.    statsCacheDump始终不能用。

刚开始的时候statsCacheDump方法始终报错说连接超时,跟踪到了java客户端代码中发现并不是什么连接超时,只是服务端返回了错误信息,而客户端认为还没有结束一直等待,导致超时。我就顺手给java客户端的开发人员mail了信息求助(代码里面有email)。再仔细看了看出错信息,返回的是不认识该指令的错误,因此就去解压memcached的服务端,看了看它的协议说明,这个Stat方法还是有的,很奇怪,没有办法了,虽然自己对于c不是很懂,但起码大致看懂逻辑还是不难,下载了Memcached的源码一看,发现居然对于StatsCacheDump这个方法调用必须还有一个参数limit,在我手头的客户端代码里面就没有这个参数,所以错误了,本来想扩展一下那个方法,但是那个方法中实现的不是很好,都是private的不容易扩展,这时候居然收到其中一个客户端开发者的回复邮件,说我手头的代码太老了,同时不建议去实现keyset,认为这样比较低效。我去下载了一个新版本,看了看源码果然已经修复了,我就回了邮件表示感谢,同时也和他说明了这么做的原因。因此大家如果要和我一样写上面的代码,就需要它2.0.1的那个版本。这里对那些国外的开源工作者表示敬佩,对于开发者是很负责任的。

2.关于fast那个选项

这个是我加上去的,做了一下测试,例如我先执行如下代码:

Cache.set(“key1”,”value1”);

Cache.set(“key2”,”value2”);

Cache.flushAll(null);

Cache.set(“key3”,”value3”);

Cache.set(“key4”,”value4”);

Boolean fast = true;

Set keys = Cache.keySet(fast);

System.out.println(keys);

Fast = false;

keys = Cache.keySet(fast);

System.out.println(keys);

得到的结果为:

Key1,key2,key3,key4

Key3,key4

可以看到其实如果通过StatsCacheDump来获取得到的keys会参杂一些已经失效的keys,只是没有回收,本来尝试获取时间戳来做判断,不过还不如使用containsKey来的有效。

同时这里采用containsKey而不是用get,就是因为counter是不能用get获得的,即使counter存在。

这些就是今天在使用Memcached所收获的,分享一下,如果有一些理解上的偏差也希望能够被指出。

Code

@SuppressWarnings("unchecked")

public Set keySet(int limit,boolean fast)

{

Set<String> keys = new HashSet<String>();

Map<String,Integer> dumps = new HashMap<String,Integer>();

Map slabs = getCacheClient().statsItems();

if (slabs != null && slabs.keySet() != null)

{

Iterator itemsItr = slabs.keySet().iterator();

while(itemsItr.hasNext())

{

String server = itemsItr.next().toString();

Map itemNames = (Map) slabs.get(server);

Iterator itemNameItr = itemNames.keySet().iterator();

while(itemNameItr.hasNext())

{

String itemName = itemNameItr.next().toString();

// itemAtt[0] = itemname

// itemAtt[1] = number

// itemAtt[2] = field

String[] itemAtt = itemName.split(":");

if (itemAtt[2].startsWith("number"))

dumps.put(itemAtt[1], Integer.parseInt(itemAtt[1]));

}

}

if (!dumps.values().isEmpty())

{

Iterator<Integer> dumpIter = dumps.values().iterator();

while(dumpIter.hasNext())

{

int dump = dumpIter.next();

Map cacheDump = statsCacheDump(dump,limit);

Iterator entryIter = cacheDump.values().iterator();

while (entryIter.hasNext())

{

Map items = (Map)entryIter.next();

Iterator ks = items.keySet().iterator();

while(ks.hasNext())

{

String k = (String)ks.next();

try

{

k = URLDecoder.decode(k,"UTF-8");

}

catch(Exception ex)

{

Logger.error(ex);

}

if (k != null && !k.trim().equals(""))

{

if (fast)

keys.add(k);

else

if (containsKey(k))

keys.add(k);

}

}

}

}

}

}

return keys;

}

对于上面代码的了解需要从Memcached内存分配和回收机制开始,以前接触Memcached的时候只是了解,这部分代码写了以后就有些知道怎么回事了。Memcached为了提高内存的分配和回收效率,采用了slab和dump分区的概念。Memcached一大优势就是能够充分利用Memory资源,将同机器或者不同机器的Memcached服务端组合成为对客户端看似统一的存储空间,Memcached可以在一台机器上开多个端口作为服务端多个实例,也可以在多台机器上开多个服务实例,而slab就是Memcached的服务端。下面是我封装后的Cache配置:

配置

<?xml version="1.0" encoding="UTF-8"?>

<memcached>

<client name="mclient0" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool0">

<!--errorHandler></errorHandler-->

</client>

<client name="mclient1" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool1">

<!--errorHandler></errorHandler-->

</client>

<client name="mclient11" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool11">

<!--errorHandler></errorHandler-->

</client>

<socketpool name="pool0" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"

nagle="false" socketTO="3000" aliveCheck="true">

<servers>10.2.225.210:13000,10.2.225.210:13001,10.2.225.210:13002</servers>

</socketpool>

<socketpool name=

时间: 2024-10-27 19:16:00

Memcached使用点滴的相关文章

艾伟:Memcached使用点滴

我对于Memcached的接触,还是在去年看了CSDN的一系列国外大型网站架构设计而开始的.最初的时候只是简单的封装了Memcached Java版的客户端,主要是对于配置的简化以及Memcached多点备份作了一些工作,然后就作为ASF的组件一部分提供给其他Team使用.其实看过Memcached Java客户端代码的人就会了解其实客户端的事情很简单,就是要有一套高性能的Socket通信框架以及对Memcached的私有协议实现的接口,自己去做这些事情也是很简单的,不过既然有可以满足自己需求的

php下session入memcached

windows7 32位环境下所需软件 一.安装Apache2.4+PHP5.6Apache2.4配置httpd.conf 参考文章:http://jingyan.baidu.com/article/fcb5aff797ec41edaa4a71c4.html     ServerRoot "c:/Apache24"    Listen 8080    DocumentRoot "c:/Apache24/htdocs"    <Directory "c

sql点滴41—MyISAM 和 InnoDB 讲解

原文:sql点滴41-MyISAM 和 InnoDB 讲解 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定.基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持已经外部键等高级数据库功能. 以下是一些细节和具体实现的差别: 1.InnoDB不支持FULLTEXT类型的索引. 2.InnoDB 中

艾伟也谈项目管理,代码背后的点滴

有段时间没有更新技术blog了,现在有空每天都写写围脖,记录生活和工作的点滴,但是有时候发现有些技术的想法和工作总结没有像过去那么完整的写很大一篇,但是也有零零散散的不少点滴,因此想着随意的写这么一个连续的片段分享. 为什么叫做代码背后的点滴呢,其实在现在互联网应用来说,其实用什么语言,用什么平台有些场景有影响,但已经不是绝对重要的因素的,其实代码被后的设计思想才是最重要的.而用最熟悉的方式去表现最自然的想法,那才能做到游刃有余,就好比我向华黎同学申请这次内部奖励的奖品希望是手写笔,因为不论什么

【原创】memcached 中的命令行参数解析

     本文主要是以 memcached 源码为例,讲解如何在 linux 下解析命令行参数.  安装 memcached 后,查看其可用选项:  ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [root@Be

实战Memcached缓存系统

 http://blog.csdn.net/column/details/memcached.html

分布式缓存系统之Memcached

简单介绍: Memcache缓存数据库:减少数据库压力.高性能的分布式内存缓存服务器,用于动态Web应用,减少I/O,提高web访问速度以减轻数据库负载,以提高动态Web应用的速度.提高可扩展性.利用 Memcached 处理实时数据读写:MySQL是影响性能的最大瓶颈,可以用一台MySQL主库(只写)+多台MySQL辅库(只读)的主辅库集群来解决.另外,访问计数等实时性很强的东西用Memcache做缓存.基于c#语言写的调用libevent库事件来实现. 1. hash表结构:key.valu

Memcached进程挂掉后的自动重启脚本

由于一台WEB服务器的Memcached死掉而导致在访问网站的某些页面时候打不开.下面脚本会自动检测Memcached的进程,如果挂掉则自动重启Memcached服务. vim memcached_check.sh #!/bin/sh #check memcached process and restart if down PATH=$PATH:/opt/env/memcache/bin/memcached DATE=`date -d "today" +"%Y-%m-%d-%

php的memcache和memcached扩展区别

老生长谈的问题了.我这里就整理一下. memcache的文档在:http://pecl.php.net/package/memcache memcached的文档在:http://pecl.php.net/package/memcached   首先看下时间,memcache最早是在2004年2月开发的,最后更新是在2013年4月,而memcached最早是在2009年1月开发的,最后更新是在2014年1月更新的.所以memcache的历史比memcached早. 在安装memcache扩展的时