memcached作为当下比较流行的缓存利器,有很多很多优点,也有一些自身的缺点。
很多时候,需要缓存一个list,可是Memcached颗粒度并不支持修改其中的元素,所以只能采用replace,就是把list整个取出,然后add/remove/modify之类再set回去。
这种解决方案不是很好,比如list增长的很大,又比如list内的对象都比较大。
很多时候可能存在这样的需求,list内的对象都有一定时效,比如30分钟,如果整个list存入memcached显然很难做到这一点,随着set整个List操作,list的时效会恢复成30分钟,虽然可能list内的部分元素你已经不再需要了,可是怎么把它们remove是个问题,这个问题可以在把整个List取出来进行一堆复杂算法之后可以实现,不过这种方法显然很糟糕。
这里提供了一种方法,可以尝试一下。
原本的数据结构式key-value(list);
现修改为 key_index - list[index]。
很显然这样设计的话,需要维护这个index。可能你觉得可以用List下标来实现。可是随着里面元素的增加修改删除或者失效,这样并不起作用,而且运用于分布式系统下这种方法并不明智。
这样的话我们需要一个东西来存储index。 memcached自身提供了一个counter功能。我们可以利用起来。
另外如果批量存进去之后,如何批量取出又是个问题了。还好memcached为我们提供了get(Collection)方法。
application.xml
memcache.properties
附上测试代码
很多时候,需要缓存一个list,可是Memcached颗粒度并不支持修改其中的元素,所以只能采用replace,就是把list整个取出,然后add/remove/modify之类再set回去。
这种解决方案不是很好,比如list增长的很大,又比如list内的对象都比较大。
很多时候可能存在这样的需求,list内的对象都有一定时效,比如30分钟,如果整个list存入memcached显然很难做到这一点,随着set整个List操作,list的时效会恢复成30分钟,虽然可能list内的部分元素你已经不再需要了,可是怎么把它们remove是个问题,这个问题可以在把整个List取出来进行一堆复杂算法之后可以实现,不过这种方法显然很糟糕。
这里提供了一种方法,可以尝试一下。
原本的数据结构式key-value(list);
现修改为 key_index - list[index]。
很显然这样设计的话,需要维护这个index。可能你觉得可以用List下标来实现。可是随着里面元素的增加修改删除或者失效,这样并不起作用,而且运用于分布式系统下这种方法并不明智。
这样的话我们需要一个东西来存储index。 memcached自身提供了一个counter功能。我们可以利用起来。
另外如果批量存进去之后,如何批量取出又是个问题了。还好memcached为我们提供了get(Collection)方法。
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import net.rubyeye.xmemcached.Counter;
import net.rubyeye.xmemcached.GetsResponse;
import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.exception.MemcachedException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MemcachedClientManager{
private static final MemcachedClientManager instance = new MemcachedClientManager();
private static final int storeTime = 5 * 60;
//待更新列表保存时效
private static final String updateObjKey = "UpdateObj_";
private static MemcachedClient memcachedClient;
private static final long counterInitValue = 1;
private static final long maxavilableValue = 10000;
private static final String UpdateObjCounter = "UpdateObjCounter";
public static List<String> UpdateObjList = new ArrayList<String>((int)maxavilableValue);
static{
for( long i = counterInitValue ; i <= maxavilableValue ; i++ ){
UpdateObjList.add(new StringBuffer(updateObjKey).append(i).toString());
}
}
private MemcachedClientManager()
{
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
memcachedClient = (MemcachedClient) app.getBean("memcachedClient");
}
public static MemcachedClientManager getInstance()
{
return instance;
}
/**
* 封装delete方法
*/
public void delete(String arg0) {
try {
memcachedClient.deleteWithNoReply(arg0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 封装gets方法
*/
public <T> T get(String arg0) {
try {
return memcachedClient.get(arg0);
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public void replace(String arg0, int arg1, Object arg2) {
try {
memcachedClient.replaceWithNoReply(arg0, arg1, arg2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void set(String arg0, int arg1, Object arg2) {
try {
memcachedClient.setWithNoReply(arg0, arg1, arg2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public <T> Map<String, GetsResponse<T>> gets(Collection<String> arg0) {
// TODO Auto-generated method stub
try {
return memcachedClient.gets(arg0);
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 计数器取值,支持分布式系统
*/
private long getCount(long min,long max,String key) {
long count = min;
Counter counter = memcachedClient.getCounter(key);
try {
count = counter.incrementAndGet();
if(count > max){
counter.set(min);
count = min;
}
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return count;
}
/**
* 存入list_index-value
*/
public void storeUpdateList(Bean obj)
{
try {
long count = getCount(counterInitValue,maxavilableValue,UpdateObjCounter);
String key = new StringBuffer(updateObjKey).append(count).toString();
memcachedClient.setWithNoReply(key, storeTime, obj);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 根据键集获取值集
*/
@SuppressWarnings("unchecked")
public <T> List<T> getKeys(List<String> keylist){
List<T> rt = new ArrayList<T>();
Map<String, GetsResponse<Object>> map = gets(keylist);
if( null != map && map.size() > 0 )
{
List<GetsResponse<Object>> list = new ArrayList<GetsResponse<Object>>(map.values());
for(GetsResponse<Object> temp : list){
T t = (T) temp.getValue();
rt.add(t);
}
}
return rt;
}
}
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- http://code.google.com/p/xmemcached/wiki/Spring_Integration -->
<context:property-placeholder location="memcache.properties" />
<bean
id="memcachedClientBuilder"
class="net.rubyeye.xmemcached.XMemcachedClientBuilder"
p:connectionPoolSize="${memcached.connectionPoolSize}"
p:failureMode="${memcached.failureMode}"
p:healSessionInterval="${memcached.healSessionInterval}">
<!-- XMemcachedClientBuilder have two arguments.First is server list,and
second is weights array. -->
<constructor-arg>
<list>
<bean class="java.net.InetSocketAddress">
<constructor-arg>
<value>${memcached.server1.host}</value>
</constructor-arg>
<constructor-arg>
<value>${memcached.server1.port}</value>
</constructor-arg>
</bean>
<!-- <bean class="java.net.InetSocketAddress">
<constructor-arg>
<value>${memcached.server2.host}</value>
</constructor-arg>
<constructor-arg>
<value>${memcached.server2.port}</value>
</constructor-arg>
</bean> -->
</list>
</constructor-arg>
<constructor-arg>
<list>
<value>${memcached.server1.weight}</value>
<!-- <value>${memcached.server2.weight}</value> -->
</list>
</constructor-arg>
<property name="commandFactory">
<bean class="net.rubyeye.xmemcached.command.TextCommandFactory" />
</property>
<property name="sessionLocator">
<bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator" />
</property>
<property name="transcoder">
<bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" />
</property>
</bean>
<!-- Use factory bean to build memcached client -->
<bean
id="memcachedClient"
factory-bean="memcachedClientBuilder"
factory-method="build"
destroy-method="shutdown" />
</beans>
memcache.properties
#连接池大小即客户端个数
memcached.connectionPoolSize=50
memcached.failureMode=true
memcached.healSessionInterval=60000
#server1
memcached.server1.host=127.0.0.1
memcached.server1.port=11211
memcached.server1.weight=6
#server2
#memcached.server2.host=127.0.0.1
#memcached.server2.port=11212
#memcached.server2.weight=4
附上测试代码
public static void main(String[] args) {
long beginTime = System.currentTimeMillis();
MemcachedClientManager manager = MemcachedClientManager.getInstance();
for( int i = 0 ; i< 12000 ; i ++ ){
Bean obj = new Bean();
obj.setName(String.valueOf(i));
manager.storeUpdateList(obj);
}
List<Bean> rtList = manager.getKeys(MemcachedClientManager.UpdateObjList);
long endTime = System.currentTimeMillis();
//因为keylist设置了10000个 counter最大值也是10000 所以取出的列表最大为10000个
System.out.println(rtList.size());
System.out.println(endTime - beginTime);
}