一、 memcached 介绍
1.1 简介
Memcached 是一个高性能的分布式内存对象缓存系统 ,用于动态 Web 应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。
Memcached 基于一个存储键/值对的 HashMap,在内存中对任意的数据(比如字符串、对象等)使用 key-value 存储,数据可以来自数据库调用、API 调用,或者页面渲染的结果。其守护进程(daemon )是用 C 写的,但是客户端可以用任何语言来编写,并通过 Memcached 协议与守护进程通信。
1.2 特性
1、本质上就是一个内存 key-value 缓存
2、协议简单,使用的是基于文本行的协议
3、不支持数据的持久化,服务器关闭之后数据全部丢失
4、Memcached 简洁而强大,便于快速开发,上手较为容易
5、互不通信的 Memcached 之间具有分布特征 ,没有安全机制
1.3 优点
Memcached 可以利用多核优势,单实例吞吐量极高,可以达到几十万 QPS(取决于 key/value 的字节大小以及服务器硬件性能,日常环境中 QPS 高峰大约在 4-6w 左右)。适用于最大程度扛量。
支持直接配置为 session handle
1.4 缺点
1、只支持简单的 key-value 数据结构,不像 Redis 可以支持丰富的数据类型
2、无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失
3、无法进行数据同步,不能将 Memcached 中的数据迁移到其他 Memcached 实例中
4、Memcached 内存分配采用 Slab Allocation 机制管理内存,value 大小分布差异较大时会造成内存利用率降低,并引发低利用率时依然出现踢出等问题。需要用户注重 value 设计。
1.5 实现原理
Memcached 处理的原理是每一个 key/value,key 会通过一个 hash 表转换成 hash 的 key,便于查找对比以及尽可能的做到散列。同时 Memcached 用的是一个二级散列,通过一个 hash 表来维护。
Memcached 有两个核心组件:服务端(server)和客户端(client)。
在一个 Memcached 组件查询中,client 先通过 key 的 hash 值来确定 key 在 server 端的位置,当 server 端确定后,客户端就会发送一个查询请求给对应的 server 端。让它来查找出确切数据,因为这之间没有交互以及多播协议,因此 Memcached 带给网络的影响最小。
1.6 适用场景
1、利用 Memcached 可以缓存 session 数据 、临时数据以减少对他们的数据库写操作
2、缓存一些很小但是被频繁访问的文件
3、经常被读取并且实时性要求不强可以等到自动过期的数据。
4、变化频繁,查询频繁,但是不需要入库的场景读多写少的场景中用于页面缓存
1.7 不适用场景
1、缓存的数据需要持久化
2、key 的长度大于 250 字符
3、变化频繁且需要入库
4、过大的数据不适宜放在 Memcached 中
1.8 与 Redis 比较相同点
1、都是基于内存的数据库系统,最大存储量是根据机器内存大小而定
2、都有不同的过期策略,分布式数据的备份可以设置一主多从,也可以一主一从(Master-Slave)
3、都支持 key-value 数据缓存
1.9 与 Redis 比较不同点
1、数据持久化支持:Redis 虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB 快照和 AOF 日志。而 Memcached 是不支持数据持久化操作的。
2、灾难恢复:Memcached 挂掉后,数据不可恢复。 Redis 数据丢失后可以通过 AOF 恢复
3、IO 方面:Redis 使用的单线程 IO 复用网络模型, 而 Memcached 多线程非阻塞 IO 复用模型
4、数据支持类型:Redis 支持 key-value 数据类型,还有 list、set、zset、hash 等数据结构,而 Memcached 只支持 key-value 数据
5、Value 值大小不同:Redis 最大可以达到 512mb;Memcached 只有 1mb。
6、数据一致性:Memcached 提供了 CAS 命令,可以保证多个并发访问操作同一份数据的一致性问题。Redis 没有提供 CAS 命令,并不能保证这点,不过 Redis 提供了事务的功能,可以保证一串命令的原子性,中间不会被任何操作打断。
二、对 java 的支持
可以使用三种不同的 Java 客户端来支持 Memcached, 分别为 Memcached-java-client、Spymemcached 和 XMemcached。我们分别介绍下这三种客户端。
2.1 Memcached-java-client 客户端
Memcached-java-client 客户端推出较早,应用最广泛,阻塞式 IO,稳定性高,高并发下性能低。较早推出的 Memcached JAVA 客户端 API,应用广泛,运行比较稳定,使用阻塞 IO,不支持 CAS 操作,已停止更新。
2.2 Spymemcached 客户端
Spymemcached 客户端时基于 JDK 的 Concurrent 和 NIO,存取速度高,并发性能高,支持 CAS 操作,已停止更新。
2.3 XMemcached 客户端
XMemcached 客户端同样基于 NIO 实现,减少了线程创建和切换的开销,这一点在高并发下特别明显,一直更新。推荐使用。
三、下载安装
在菜鸟教程里面下载相应的版本,我安装的是 1.4.4 版本,具体如何安装以及修改参数都在上面的网页教程里面有相应的记录。
这里我需要说明的是:当执行
c:\memcached\memcached.exe -d start
之后,这个 memcached 进程就会启动,关机重启电脑也会自动启动这个进程。不用再刻意的去配置开机启动。
四、使用 Memcached-java-client
4.1 添加 maven 依赖
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
</dependency>
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>
4.2 修改 application.properties 配置信息
# memcached config
memcache.servers=127.0.0.1:11211
memcache.failover=true
memcache.initConn=100
memcache.minConn=20
memcache.maxConn=1000
memcache.maintSleep=50
memcache.nagel=false
memcache.socketTO=3000
memcache.aliveCheck=true
4.3 编写 memcache 配置类
@Configuration
public class MemcacheConfig {
@Value("${memcache.servers}")
private String[] servers;
@Value("${memcache.failover}")
private boolean failover;
@Value("${memcache.initConn}")
private int initConn;
@Value("${memcache.minConn}")
private int minConn;
@Value("${memcache.maxConn}")
private int maxConn;
@Value("${memcache.maintSleep}")
private int maintSleep;
@Value("${memcache.nagel}")
private boolean nagel;
@Value("${memcache.socketTO}")
private int socketTO;
@Value("${memcache.aliveCheck}")
private boolean aliveCheck;
@Bean
public SockIOPool sockIOPool () {
SockIOPool pool = SockIOPool.getInstance();
pool.setServers(servers);
pool.setFailover(failover);
pool.setInitConn(initConn);
pool.setMinConn(minConn);
pool.setMaxConn(maxConn);
pool.setMaintSleep(maintSleep);
pool.setNagle(nagel);
pool.setSocketTO(socketTO);
pool.setAliveCheck(aliveCheck);
pool.initialize();
return pool;
}
@Bean
@ConditionalOnBean(SockIOPool.class)
public MemCachedClient memCachedClient(){
return new MemCachedClient();
}
}
4.4 编写测试类
@RestController
public class MemcacheController {
@Autowired
private MemCachedClient memCachedClient;
@RequestMapping("/memcacheIndex")
public String memcacheIndex() throws InterruptedException {
// 放入缓存
boolean flag = memCachedClient.set("a", 1);
System.out.println(flag);
// 取出缓存
Object a = memCachedClient.get("a");
System.out.println(a);
// 3s后过期
memCachedClient.set("b", "2", new Date(3000));
Object b = memCachedClient.get("b");
System.out.println(b);
Thread.sleep(3000);
b = memCachedClient.get("b");
System.out.println(a);
System.out.println(b);
return "aaaa";
}
}
4.5 测试
启动程序,输入 http://localhost:8080/memcacheIndex,输出结果如图所示:
五、使用 SpyMemcached 方式
5.1 添加 maven 依赖
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.2</version>
</dependency>
5.2 修改 application.properties 配置信息
memcache.ip=127.0.0.1
memcache.port=11211
5.3 编写 SpyMemcacheConfig 配置类
@Configuration
public class SpyMemcacheConfig implements CommandLineRunner{
@Value("${memcache.ip}")
private String ip;
@Value("${memcache.port}")
private int port;
private MemcachedClient client = null;
@Override
public void run(String... args) throws Exception {
try {
client = new MemcachedClient(new InetSocketAddress(ip,port));
} catch (IOException e) {
}
}
public MemcachedClient getClient() {
return client;
}
}
5.4 编写测试类
@RestController
public class SpyMemcacheController {
@Autowired
private SpyMemcacheConfig spyMemcacheConfig;
@RequestMapping("/spyMemcacheIndex")
public String spyMemcacheIndex() throws InterruptedException {
/*这个过期时间单位是秒,最大值是60*60*24*30*/
spyMemcacheConfig.getClient().set("spyMemcachedKey",1,"张三");
System.out.println("基于spyMemcached实现,现在的值为 "+spyMemcacheConfig.getClient().get("spyMemcachedKey"));
Thread.sleep(2000);
System.out.println("1秒后缓存内容清除,现在的值为: "+spyMemcacheConfig.getClient().get("spyMemcachedKey"));
return "aaaaa";
}
}
5.5 测试
启动程序,输入 http://localhost:8080/spyMemcacheIndex,输出结果如图所示:
六、使用 XMemcached 方式
6.1 添加 maven 依赖
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.5</version>
</dependency>
6.2 修改 application.properties 配置信息
# memcached服务器集群(格式为host:port,多个服务器之间用空格隔开)
memcached.server=127.0.0.1:11211 10.86.54122:11211
# 接口操作的默认超时时间,可以被接口覆盖
memcached.opTimeout=3000
# 池子大小
memcached.poolSize=10
# 是否开启失败模式,默认为false
memcached.failureMode=false
# 是否使用memcached缓存
memcached.enabled=true
6.3 编写 XMemcacheConfig 配置类
@Configuration
public class XMemcacheConfig {
@Value("${memcached.server}")
private String server;
@Value("${memcached.opTimeout}")
private Integer opTimeout;
@Value("${memcached.poolSize}")
private Integer poolSize;
@Value("${memcached.failureMode}")
private boolean failureMode;
@Value("${memcached.enabled}")
private boolean enabled;
@Bean(name = "memcachedClientBuilder")
public MemcachedClientBuilder getBuilder() {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(server);
// 内部采用一致性哈希算法
memcachedClientBuilder.setSessionLocator(new KetamaMemcachedSessionLocator());
// 操作的超时时间
memcachedClientBuilder.setOpTimeout(opTimeout);
// 采用二进制传输协议(默认为文本协议)
memcachedClientBuilder.setCommandFactory(new BinaryCommandFactory());
// 设置连接池的大小
memcachedClientBuilder.setConnectionPoolSize(poolSize);
// 是否开起失败模式
memcachedClientBuilder.setFailureMode(failureMode);
return memcachedClientBuilder;
}
/**
* 由Builder创建memcachedClient对象,并注入spring容器中
* @param memcachedClientBuilder
* @return
*/
@Bean(name = "memcachedClient")
public MemcachedClient getClient(@Qualifier("memcachedClientBuilder") MemcachedClientBuilder memcachedClientBuilder) {
MemcachedClient client = null;
try {
client = memcachedClientBuilder.build();
} catch(Exception e) {
e.printStackTrace();
}
return client;
}
}
6.4 编写测试类
@RestController
public class XMemcacheController {
@Autowired
private MemcachedClient memcachedClient;
@RequestMapping("/XMemcacheIndex")
public String XMemcacheIndex() throws InterruptedException {
try {
//新增操作
memcachedClient.set("XMemcacheKeyOne",0,"张三");
System.out.println((String)memcachedClient.get("XMemcacheKeyOne"));
//删除操作
memcachedClient.delete("XMemcacheKeyOne");
System.out.println((String)memcachedClient.get("XMemcacheKeyOne"));
//设置存活时间
memcachedClient.set("XMemcacheKeyTwo",1,"李四");
Thread.sleep(2000);
System.out.println((String)memcachedClient.get("XMemcacheKeyTwo"));
//更新操作
memcachedClient.set("XMemcacheKeyThree",0,"王五");
System.out.println((String)memcachedClient.get("XMemcacheKeyThree"));
memcachedClient.set("XMemcacheKeyThree",0,"王五他儿子");
System.out.println((String)memcachedClient.get("XMemcacheKeyThree"));
} catch (Exception e) {
e.printStackTrace();
}
return "xMemcacheIndex";
}
}
6.5 测试
启动程序,输入 http://localhost:8080/XMemcacheIndex,输出结果如下所示: