Memcached
官方定义
Memcached 是免费和开源、高性能、分布式内存对象缓存系统,本质上是通用的,但用于通过减轻数据库负载来加速动态web应用程序。
Memcached是内存中的一个键值存储,用于存储来自数据库调用、API调用或页面呈现结果的任意数据块(字符串、对象)。
Memcached简单而强大。其简单的设计促进了快速部署,易于开发,并解决了大型数据缓存所面临的许多问题。它的API适用于大多数流行的语言。
Docker
拉取
$ docker pull memcached
运行
$ docker run --name memcached -d --rm -p 11211:11211 memcached
- 参数说明
-d 后台运行
--name memcached 指定名称
--rm 容器终止运行后,自动删除容器文件
-p 11211:11211 指定端口号
查看状态
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a73c5ba9a78b memcached "docker-entrypoint.s…" 4 seconds ago Up 3 seconds 0.0.0.0:11211->11211/tcp memcached
Memcached 测试
telnet
另开一个命令行
$ telnet 127.0.0.1 11211
日志
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
设置
$ set foo 0 0 3
$ bar
日志
STORED
获取
$ get foo
日志
VALUE foo 0 3
bar
END
退出
$ quit
日志
Connection closed by foreign host.
java 代码访问
跑去 github 看了下,还是使用 java-memcached-client 比较靠谱。
引入
本地 jdk1.8 版本。
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.3</version>
</dependency>
实战代码
链接测试
@Test
public void connectTest() throws IOException {
MemcachedClient mcc = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
System.out.println("Connection to server sucessful.");
// 关闭连接
mcc.shutdown();
}
日志:
Connection to server sucessful.
为了后续测试方便,将此方法连接放在每个测试方法之前执行,关闭连接放在每个方法之后执行。
public class MemcachedTest {
private MemcachedClient mcc;
@Before
public void initConnection() throws IOException {
// 本地连接 Memcached 服务
mcc = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
System.out.println("Connection to server sucessful.");
}
@After
public void shutdownConnection() {
// 关闭连接
mcc.shutdown();
}
}
set
- 方法说明
Memcached set 命令用于将 value(数据值) 存储在指定的 key(键) 中。
如果set的key已经存在,该命令可以更新该key所对应的原来的数据,也就是实现更新的作用。
- 实例
@Test
public void setTest() throws ExecutionException, InterruptedException {
// 存储数据
Future fo = mcc.set("runoob", 900, "Free Education");
// 查看存储状态
System.out.println("set status:" + fo.get());
// 输出值
System.out.println("runoob value in cache - " + mcc.get("runoob"));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
add
- 说明
Memcached add 命令用于将 value(数据值) 存储在指定的 key(键) 中。
如果 add 的 key 已经存在,则不会更新数据(过期的 key 会更新),之前的值将仍然保持相同,并且您将获得响应 NOT_STORED。
- 实例
@Test
public void addTest() throws ExecutionException, InterruptedException {
// 添加数据
Future fo = mcc.set("runoob", 900, "Free Education");
// 打印状态
System.out.println("set status:" + fo.get());
// 输出
System.out.println("runoob value in cache - " + mcc.get("runoob"));
// 添加
fo = mcc.add("runoob", 900, "memcached");
// 打印状态
System.out.println("add status:" + fo.get());
// 添加新key
fo = mcc.add("codingground", 900, "All Free Compilers");
// 打印状态
System.out.println("add status:" + fo.get());
// 输出
System.out.println("codingground value in cache - " + mcc.get("codingground"));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
add status:false
add status:false
codingground value in cache - All Free Compilers
replace
- 说明
Memcached replace 命令用于替换已存在的 key(键) 的 value(数据值)。
如果 key 不存在,则替换失败,并且您将获得响应 NOT_STORED。
- 实例
@Test
public void replaceTest() throws ExecutionException, InterruptedException {
// 添加第一个 key=》value 对
Future fo = mcc.set("runoob", 900, "Free Education");
// 输出执行 add 方法后的状态
System.out.println("add status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get("runoob"));
// 添加新的 key
fo = mcc.replace("runoob", 900, "Largest Tutorials' Library");
// 输出执行 set 方法后的状态
System.out.println("replace status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get("runoob"));
}
日志
Connection to server sucessful.
add status:true
runoob value in cache - Free Education
replace status:true
runoob value in cache - Largest Tutorials' Library
append
- 说明
Memcached append 命令用于向已存在 key(键) 的 value(数据值) 后面追加数据 。
- 实例
@Test
public void appendTest() throws ExecutionException, InterruptedException {
final String key = "runoob";
// 添加数据
Future fo = mcc.set(key, 900, "Free Education");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
// 对存在的key进行数据添加操作
fo = mcc.append("runoob", " for All");
// 输出执行 set 方法后的状态
System.out.println("append status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
append status:true
runoob value in cache - Free Education for All
prepend
- 说明
Memcached prepend 命令用于向已存在 key(键) 的 value(数据值) 前面追加数据 。
- 实例
@Test
public void prependTest() throws ExecutionException, InterruptedException {
final String key = "runoob";
// 添加数据
Future fo = mcc.set(key, 900, "Free Education");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
// 对存在的key进行数据添加操作
fo = mcc.prepend("runoob", "For All ");
// 输出执行 set 方法后的状态
System.out.println("append status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
append status:true
runoob value in cache - For All Free Education
CAS
- 说明
Memcached CAS(Check-And-Set 或 Compare-And-Swap) 命令用于执行一个”检查并设置”的操作
它仅在当前客户端最后一次取值后,该 key 对应的值没有被其他客户端修改的情况下,才能够将值写入。
检查是通过 cas_token 参数进行的,这个参数是Memcach指定给已经存在的元素的一个唯一的64位值。
- 实例
@Test
public void casTest() throws ExecutionException, InterruptedException {
// 添加数据
Future fo = mcc.set("runoob", 900, "Free Education");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 使用 get 方法获取数据
System.out.println("runoob value in cache - " + mcc.get("runoob"));
// 通过 gets 方法获取 CAS token(令牌)
CASValue casValue = mcc.gets("runoob");
// 输出 CAS token(令牌) 值
System.out.println("CAS token - " + casValue);
// 尝试使用cas方法来更新数据
CASResponse casresp = mcc.cas("runoob", casValue.getCas(), 900, "Largest Tutorials-Library");
// 输出 CAS 响应信息
System.out.println("CAS Response - " + casresp);
// 输出值
System.out.println("runoob value in cache - " + mcc.get("runoob"));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
CAS token - {CasValue 18/Free Education}
CAS Response - OK
runoob value in cache - Largest Tutorials-Library
查询相关
get
- 说明
Memcached get 命令获取存储在 key(键) 中的 value(数据值) ,如果 key 不存在,则返回空。
- 实例
@Test
public void getTest() throws ExecutionException, InterruptedException {
// 添加数据
Future fo = mcc.set("runoob", 900, "Free Education");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 使用 get 方法获取数据
System.out.println("runoob value in cache - " + mcc.get("runoob"));
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
gets
- 说明
Memcached gets 命令获取带有 CAS 令牌存 的 value(数据值) ,如果 key 不存在,则返回空。
- 实例
@Test
public void getsTest() throws ExecutionException, InterruptedException {
// 添加数据
Future fo = mcc.set("runoob", 900, "Free Education");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 从缓存中获取键为 runoob 的值
System.out.println("runoob value in cache - " + mcc.get("runoob"));
// 通过 gets 方法获取 CAS token(令牌)
CASValue casValue = mcc.gets("runoob");
// 输出 CAS token(令牌) 值
System.out.println("CAS value in cache - " + casValue);
}
日志
Connection to server sucessful.
set status:true
runoob value in cache - Free Education
CAS value in cache - {CasValue 21/Free Education}
delete
- 说明
Memcached delete 命令用于删除已存在的 key(键)。
返回值:
DELETED:删除成功。
ERROR:语法错误或删除失败。
NOT_FOUND:key 不存在。
- 实例
@Test
public void deleteTest() throws ExecutionException, InterruptedException {
final String key = "runoob";
// 添加数据
Future fo = mcc.set(key, 900, "World's largest online tutorials library");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
// 对存在的key进行数据添加操作
fo = mcc.delete(key);
// 输出执行 delete 方法后的状态
System.out.println("delete status:" + fo.get());
// 获取键对应的值
System.out.println("runoob value in cache - " + mcc.get(key));
}
日志信息
Connection to server sucessful.
set status:true
runoob value in cache - World's largest online tutorials library
delete status:true
runoob value in cache - null
inc/decr
- 说明
Memcached incr 与 decr 命令用于对已存在的 key(键) 的数字值进行自增或自减操作。
incr 与 decr 命令操作的数据必须是十进制的32位无符号整数。
如果 key 不存在返回 NOT_FOUND
,如果键的值不为数字,则返回 CLIENT_ERROR
,其他错误返回 ERROR
。
- 实例
@Test
public void decrIncrTest() throws ExecutionException, InterruptedException {
final String key = "number";
// 添加数字值
Future fo = mcc.set(key, 900, "1000");
// 输出执行 set 方法后的状态
System.out.println("set status:" + fo.get());
// 获取键对应的值
System.out.println("value in cache - " + mcc.get(key));
// 自增并输出
System.out.println("value in cache after increment - " + mcc.incr(key, 111));
// 自减并输出
System.out.println("value in cache after decrement - " + mcc.decr(key, 112));
}
日志信息
Connection to server sucessful.
set status:true
value in cache - 1000
value in cache after increment - 1111
value in cache after decrement - 999
状态相关
stats
Memcached stats
命令用于返回统计信息例如 PID(进程号)、版本号、连接数等。
stats items
Memcached stats items
命令用于显示各个 slab 中 item 的数目和存储时长(最后一次访问距离现在的秒数)。
stats slabs
Memcached stats slabs
命令用于显示各个slab的信息,包括chunk的大小、数目、使用情况等。
stats sizes
Memcached stats sizes
命令用于显示所有item的大小和个数。
该信息返回两列,第一列是 item 的大小,第二列是 item 的个数。
flush_all
Memcached flush_all
命令用于清理缓存中的所有 key=>value(键=>值) 对。
该命令提供了一个可选参数 time,用于在制定的时间后执行清理缓存操作。
与 Redis 对比
数据结构
- Memcached
只支持最简单的 key-value
- Redis
支持较为复杂的数据结构。
Memcached单个key-value大小有限,一个value最大只支持1MB,而Redis最大支持512MB
多线程 vs 多线程
没有必要过多的关心性能,因为二者的性能都已经足够高了。
由于Redis只使用单核,而Memcached可以使用多核,所以在比较上,平均每一个核上Redis在存储小数据时比Memcached性能更高。
而在100k以上的数据中,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。说了这么多,结论是,无论你使用哪一个,每秒处理请求的次数都不会成为瓶颈。(比如瓶颈可能会在网卡)
持久化
Memcached 不支持持久化,Redis 支持持久化。
不要把 redis 当数据库
千万不要把redis当作数据库用:
(1)redis的定期快照不能保证数据不丢失
(2)redis的AOF会降低效率,并且不能支持太大的数据量
不要期望redis做固化存储会比mysql做得好,不同的工具做各自擅长的事情,把redis当作数据库用,这样的设计八成是错误的。
持久化的利弊
- 优点
redis 挂了再重启,内存里能够快速恢复热数据,不会瞬时将压力压到数据库上,没有一个 cache 预热的过程。
- 缺点
在redis挂了的过程中,如果数据库中有数据的修改,可能导致redis重启后,数据库与redis的数据不一致。
高可用
redis 有比较成熟的高可用集成框架。
memcached 应该也是天然支持高可用的。官方定义就是这个噱头。(和 58 不同,58 认为还需要二次开发)
技术人,清楚用什么,清楚怎么用还不够,更重要的是明白为什么。
参考资料
- Memcached
https://memcached.org/
https://wiki.linuxchina.net/index.php?title=Docker_%E5%AE%89%E8%A3%85_Memcached
http://www.runoob.com/memcached/memcached-tutorial.html
- docker
https://hub.docker.com/r/_/memcached/
- vs redis