Memcache 介绍
Memcache 是一个自由和开放源代码、高性能、分配的内存对象缓存系统。简单来说,Memcache 是一个高性能的分布式内存对象的 key-value 缓存系统,用于加速动态 Web 应用程序,减轻数据库负载,现在也有很多人将它作为内存式数据库在使用。
它可以应对任意多个连接,使用非阻塞的网络 IO,由于它的工作机制是在内存中开辟一块空间,然后建立一个 Hash 表,Memcached 自动管理这些 Hash 表。
Memcache 由国外社区网站 LiveJournal 开发团队开发,设计理念就是小而强大,它简单的设计促进了快速部署、易于开发并解决面对大规模的数据缓存的许多难题,而所开放的 API 使得 Memcache 能用于 Java、C/C++/C#、Perl、Python、PHP、Ruby 等大部分流行的程序语言。
Memcache 和 Memcached 的区别:Memcache 是这个项目的名称,而 Memcached 是服务器端的主程序名称。
Memcache 特点
协议简单
Memcache 的服务端客户端通信使用简单的文本协议,通过 Telnet 即可在 Memcached 上存取数据。
基于 Libevent 的事件处理
Libevent 是一套跨平台的事件处理接口的封装,能够兼容包括这些操作系统:Windows/Linux/BSD/Solaris 等操作系统的的事件处理,包装的接口包括:poll、select(Windows)、epoll(Linux)、kqueue(BSD)/dev/pool(Solaris)。
Memcache 使用 Libevent 来进行网络并发连接的处理,能够保持在很大并发情况下,仍旧能够保持快速的响应能力。
内置内存存储方式
Memcache 中保存的数据都存储在 Memcache 内置的内存存储空间中。由于数据仅存在于内存中,因此重启 Memcache、重启操作系统会导致数据全部丢失。Memcache LRU(Least Recently Used)算法自动删除不使用的缓存,不过这个功能是可以配置的,Memcache 启动时通过“-M”参数可以禁止 LRU。不过,Memcache 本身是为缓存而设计的,建议开启 LRU。
不适应场景
缓存对象不能大于 1 MB
key 的长度大于 250 字符
Memcache 未提供任何安全策略
不支持持久化
linux下安装Memcached
-
环境
由于memcached和libevent是由C编写的,所以首先确保你的系统安装了gcc,如果
没有安装,使用yum安装即可
命令:
yum -y install gcc
yum -y install gcc-c++ -
手动安装(1)
然后下载 libevent 然后上传centos,进行给予权限,然后解压 # tar xzvf libevent-2.0.21-stable.tar.gz ##解压 # cd libevent-2.0.21-stable # ./configure --prefix=/usr # make # make install 安装完后可以查看下/usr/lib是否有libevent等文件(ls -al /usr/lib | grep libevent) 然后下载 memcached # tar xzvf memcached-1.4.15.tar.gz # cd memcached-1.4.15 # ./configure --with-libevent=/usr # make # make install
-
yum安装方式(2)
1.查找memcached
yum search memcached
2.安装memcached
yum -y install memcached
3.验证安装memcached
memcached -h
4.修改memcached配置文件
(1) 加入服务
chkconfig --level 2345 memcached on
热后可以使用 service memcached restart/start/stop
(2)可修改参数
vi /etc/sysconfig/memcached
memcached命令参数解释:
-p 监听的端口
-l <ip_addr> 连接的IP地址, 默认是本机
-d start 启动memcached 服务
-d restart 重起memcached 服务
-d stop|shutdown 关闭正在运行的memcached 服务
-d install 安装memcached 服务
-d uninstall 卸载memcached 服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助
(3) 查看服务
ps -ef | grep memcached
(4) 或者命令启动
/usr/bin/memcached -b -p 11211 -m 150 -u root >> /tmp/memcached.log &
Idea中boot集成使用memcached
-
映入memcachedx jar包
<dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>2.4.5</version> </dependency>
2.添加配置文件:
# 单个 Memcached 配置,用集群时可以拼接多个地址:"host1:port1 host2:port2 …"。
memcached.servers=192.168.25.130:11211
# 连接池
memcached.poolSize=10
#操作超时时间
memcached.opTimeout=6000
3.创建 XMemcachedProperties 类,读配置信息
@Component
@ConfigurationProperties(prefix = "memcached")
public class XMemcachedProperties {
private String servers;
private int poolSize;
private long opTimeout;
xxxx
xxxx
....
}
4.创建memcached操作客户端类
@Configuration
public class MemcachedBuilder {
protected static Logger logger = LoggerFactory.getLogger(MemcachedBuilder.class);
@Resource
private XMemcachedProperties xMemcachedProperties;
@Bean
public MemcachedClient getMemcachedClient() {
MemcachedClient memcachedClient = null;
try {
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(xMemcachedProperties.getServers()));
// 设置集群权重
// MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(memcacheConfig.getServers()),new int[]{3,2,1});
// 开启/关闭failure模式
builder.setFailureMode(false);
// 多 Memcached 时启用 一致性哈希 算法
builder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 多 Memcached 时启用 选举散列 算法
// builder.setSessionLocator(new ElectionMemcachedSessionLocator());
builder.setConnectionPoolSize(xMemcachedProperties.getPoolSize());
//操作超时时间
builder.setOpTimeout(xMemcachedProperties.getOpTimeout());
// 进行数据压缩,大于1KB时进行压缩
builder.getTranscoder().setCompressionThreshold(1024);
// 使用序列化传输编码
builder.setTranscoder(new SerializingTranscoder());
// use binary protocol
builder.setCommandFactory(new BinaryCommandFactory());
memcachedClient = builder.build();
} catch (IOException e) {
logger.error("inint MemcachedClient failed ",e);
}
return memcachedClient;
}
}
5.测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class MemcachedTests {
@Autowired
private MemcachedClient memcachedClient;
/**
* set 方法,它有三个参数,
* 第一个是存储的 key 名称,
* 第二个是 expire 时间(单位秒),超过这个时间,memcached 将这个数据替换出去,0 表示永久存储(默认是一个月),
* 第三个参数就是实际存储的数据,可以是任意的 Java 可序列化类型。
* @throws Exception
*/
@Test
public void testGetSet() throws Exception {
memcachedClient.set("hello", 0, "Hello,xmemcached");
String value = memcachedClient.get("hello");
System.out.println("hello=" + value);
memcachedClient.delete("hello");
value = memcachedClient.get("hello");
System.out.println("hello=" + value);
}
/**
* 如果 add 的 key 已经存在,则不会更新数据(过期的 key 会更新),之前的值将仍然保持相同
* replace 命令,用于替换已存在的 key(键)的 value(数据值)。如果 key 不存在,则替换失败
* append 命令,用于向已存在 key(键)的 value(数据值)后面追加数据。
* prepend 命令,用于向已存在 key(键)的 value(数据值)前面追加数据。
* deleteWithNoReply 方法,这个方法删除数据并且告诉 Memcached,
* 不用返回应答,因此这个方法不会等待应答直接返回,比较适合于批量处理。
* @throws Exception
*/
@Test
public void testMore() throws Exception {
if (!memcachedClient.set("hello", 0, "world")) {
System.err.println("set error");
}
if (!memcachedClient.add("hello", 0, "dennis")) {
System.err.println("Add error,key is existed");
}
if (!memcachedClient.replace("hello", 0, "dennis")) {
System.err.println("replace error");
}
memcachedClient.append("hello", " good");
memcachedClient.prepend("hello", "hello ");
String name = memcachedClient.get("hello", new StringTranscoder());
System.out.println(name);
memcachedClient.deleteWithNoReply("hello");
}
/**
* Incr 和 Decr 类似数据的增和减,两个操作类似 Java 中的原子类如 AtomicIntger
* @throws Exception
*/
@Test
public void testIncrDecr() throws Exception {
memcachedClient.delete("Incr");
memcachedClient.delete("Decr");
System.out.println(memcachedClient.incr("Incr", 6, 12));
System.out.println(memcachedClient.incr("Incr", 3));
System.out.println(memcachedClient.incr("Incr", 2));
System.out.println(memcachedClient.decr("Decr", 1, 6));
System.out.println(memcachedClient.decr("Decr", 2));
}
/**
* memcachedClient.getCounter("counter",10),第一个参数为计数器的 key,第二参数当 key 不存在时的默认值;
* counter.incrementAndGet(),执行一次给计数器加 1;
* counter.decrementAndGet(),执行一次给计数器减 1。
* @throws Exception
*/
@Test
public void testCounter() throws Exception {
Counter counter=memcachedClient.getCounter("counter1",10);
System.out.println("counter="+counter.get());
long c1 =counter.incrementAndGet();
System.out.println("counter="+c1);
long c2 =counter.decrementAndGet();
System.out.println("counter="+c2);
long c3 =counter.addAndGet(-6);
System.out.println("counter="+c3);
}
@Test
public void testTouch() throws Exception {
memcachedClient.set("Touch", 2, "Touch Value");
Thread.sleep(1000);
//memcached 不支持
// memcachedClient.touch("Touch",6);
Thread.sleep(2000);
String value =memcachedClient.get("Touch",3000);
System.out.println("Touch=" + value);
}
/**
* Memcached 是通过 CAS 协议实现原子更新,所谓原子更新就是 Compare and Set
* @throws Exception
*/
@Test
public void testCas() throws Exception {
memcachedClient.set("cas", 0, 100);
GetsResponse<Integer> result = memcachedClient.gets("cas");
System.out.println("result value "+result.getValue());
long cas = result.getCas();
//尝试将a的值更新为2
if (!memcachedClient.cas("cas", 0, 200, cas)) {
System.err.println("cas error");
}
System.out.println("cas value "+memcachedClient.get("cas"));
memcachedClient.cas("cas", 0, new CASOperation<Integer>() {
public int getMaxTries() {
return 1;
}
public Integer getNewValue(long currentCAS, Integer currentValue) {
return 300;
}
});
System.out.println("cas value "+memcachedClient.get("cas"));
}
@Test
public void testStat() throws Exception {
Map<InetSocketAddress,Map<String,String>> result=memcachedClient.getStats();
System.out.println("Stats=" + result.toString());
Map<InetSocketAddress,Map<String,String>> items=memcachedClient.getStatsByItem("items");
System.out.println("items=" + items.toString());
}
}