Memcached学习笔记
Memcached学习笔记
一、本地缓存与分布式缓存

本地缓存
如:mybatis,属于数据持久层框架。缓存机制:
- 一级缓存:会话的缓存(SqlSession),其作用范围:局限在一个会话对象内
- 二级缓存:namespace缓存, 以mapper/dao为单位
使用< cache />开启二级缓存,并实现Cache接口
mapper文件中的所有查询操作都会进行结果缓存
分布式缓存:
缓存结构:k(sql语句)+v(执行结果)
缓存使用条件:查询多,增删改少
本地缓存与分布式缓存对比:
- 本地缓存:将缓存结果直接存储到tomcat的 jvm 内存中
- 优点:快,配置简单
- 缺点:
缓存结果的大小受限于tomcat内存空间
缓存结果不可以被共享
- 分布式缓存:将缓存结果保存到第三方缓存服务(memcached\redis)中
< catch type=“第三方缓存服务对于mybatis cache的接口实现类”/>
⼀、概述
Memcached(http://memcached.org/)是⼀款开源免费的,⾼性能的分布式内存对象缓存系统。
Memcached是⼀种基于内存的key-value存储,⽤来存储⼩块的任意数据(字符串、对象)。
Memcached简洁⽽强⼤。它的简洁设计便于快速开发,减轻开发难度,解决了⼤数据量缓存的很多问题。它的API兼容⼤部分流⾏的开发语⾔。
Memcached是基于内存的kv存储的对象缓存系统,没有数据的持久化,所有的数据都基于内存存储,不同于redis(拥有持久化机制:RDB和AOF)。

二、搭建Memcached环境
1. 准备资源
准备安装包 memcached-1.4.31.tar.gz
上传至linux操作系统
下载地址:链接:https://pan.baidu.com/s/13jfQ2VS8_u_YO-YRafBlYQ 提取码:2s6w
2. 安装依赖libevent
[root@memcached ~]# yum install libevent-devel
3. 安装gcc
[root@memcached ~]# yum install gcc-c++ perl-devel pcre-devel openssl-devel zlib-devel wget
4. 解压缩memcached-1.4.31.tar.gz
[root@memcached ~]# tar -zxvf memcached-1.4.31.tar.gz -C /usr
5. 配置memecached的配置
[root@memcached ~]# cd /usr/memcached-1.4.31/
# 指定安装位置
[root@memcached memcached-1.4.31]# ./configure --prefix=/usr/local/memcached
6. 编译并安装
[root@memcached memcached-1.4.31]# make && make install
7. 查看启动命令参数
# 进入安装目录下的bin目录
[root@memcached memcached-1.4.31]# cd /usr/local/memcached/bin/
# 显示所有命令
[root@memcached bin]# ./memcached -h
# ---------------------部分参数说明----------------------
-p <num> TCP端⼝,默认为11211,可以不设置
-l <addr> 监听的 IP 地址,本机可以不设置此参数
-d 以守护程序(daemon)⽅式运⾏
-u 指定⽤户,如果当前为 root ,需要使⽤此参数指定⽤户
-m <num> 最⼤内存使⽤,单位MB。默认64MB
-M 禁⽌LRU策略,内存耗尽时返回错误,⽽不是删除项
-c <num> 最⼤同时连接数,默认是1024
-t <num> 线程数,默认4。由于memcached采⽤NIO,所以更多线程没有太多作⽤
-v ⽇志(错误和警告)
-vv ⽇志(错误、警告、客户端命令和响应)
-vvv ⾮常详细的⽇志
8. 启动memcached服务
[root@memcached bin]# ./memcached -p 11211 -u root -m 128 -vv
9. memcached主要指令
Memcached可以通过 telnet 命令并指定主机ip和端⼝来连接 Memcached 服务
- windows的telnet功能需要自己开启,可以自行百度。
- linux中使用
yum install telnet命令下载安装
1. 连接memcached
[root@memcached ~]# telnet 192.168.114.142 11211
2. 指令操作
i. 存值操作命令
# =========== 存值语法 ============
set key flags exptime bytes
value
key:键值 key-value 结构中的 key,⽤于查找缓存值。
flags:可以包括键值对的整型参数,客户机使⽤它存储关于键值对的额外信息 。
exptime:在缓存中保存键值对的时间⻓度(以秒为单位,0 表示永远)
bytes:在缓存中存储的字节数
get name
VALUE name 0 2
zs
END
ii. 取值操作命令
# =========== 取值语法 ============
get key
# 注意:设置的存储的字节数必须与将要存储的字节数相符合
set name 0 0 2
zs
STORED
iii. 查看状态命令
# =========== stats 命令 ============
# stats 命令⽤于返回统计信息例如 PID(进程号)、版本号、连接数等
stats
stats
STAT pid 5800
STAT uptime 875
STAT time 1547121330
STAT version 1.4.31
STAT libevent 2.0.21-stable
STAT pointer_size 64
STAT rusage_user 0.058724
STAT rusage_system 0.293622
STAT curr_connections 10
STAT total_connections 12
STAT connection_structures 11
STAT reserved_fds 20
STAT cmd_get 1
STAT cmd_set 2
STAT cmd_flush 0
STAT cmd_touch 0
STAT get_hits 1
STAT get_misses 0
STAT get_expired 0
STAT get_flushed 0
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 0
STAT touch_hits 0
STAT touch_misses 0
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 75
STAT bytes_written 76
STAT limit_maxbytes 134217728
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT time_in_listen_disabled_us 0
STAT threads 4
STAT conn_yields 0
STAT hash_power_level 16
STAT hash_bytes 524288
STAT hash_is_expanding 0
STAT malloc_fails 0
STAT log_worker_dropped 0
STAT log_worker_written 0
STAT log_watcher_skipped 0
STAT log_watcher_sent 0
STAT bytes 71
STAT curr_items 1
STAT total_items 1
STAT expired_unfetched 0
STAT evicted_unfetched 0
STAT evictions 0
STAT reclaimed 0
STAT crawler_reclaimed 0
STAT crawler_items_checked 0
STAT lrutail_reflocked 0
END
iv. 退出指令
第一步:ctrl+]
第二步:输入命令 quit
三、Java客户端(XMemcached)
XMemcached是基于Java NIO的Memcached客户端,Java NIO相⽐于传统阻塞io模型来说,有效率⾼(特别在⾼并发下)和资源耗费相对较少的优点。
特性
- ⾼性能
- ⽀持完整的协议
- ⽀持客户端分布
- 动态增删节点
- 允许设置节点权重
参考资料:链接:https://pan.baidu.com/s/1mWnTfdHV5zS2n8-Ujrra-w 提取码:1irj
注:可查看以上资源目录xmemcached-1.4.3-bin-with-dependencies\xmemcached-1.4.3\User_Guide_zh下的index.html
使用
1. 创建maven项目,导入依赖jar
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
2. 编写测试类
# ip 地址改为自己的memcached所在的服务器地址
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.114.142:11211"));
MemcachedClient memcachedClient = builder.build();
try {
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);
} catch (MemcachedException e) {
System.err.println("MemcachedClient operation fail");
e.printStackTrace();
} catch (TimeoutException e) {
System.err.println("MemcachedClient operation timeout");
e.printStackTrace();
} catch (InterruptedException e) {
// ignore
}
try {
//close memcached client
memcachedClient.shutdown();
} catch (IOException e) {
System.err.println("Shutdown MemcachedClient fail");
e.printStackTrace();
}
四、客户端分布
Memcached的分布是通过客户端实现的,客户端根据key的哈希值得到将要存储的memcached节点,并将对应的value存储到相应的节点。
有两种算法确定存储位置:
1. 余数算法(默认)
按照key的哈希值模以连接数(节点数)得到的余数,对应的连接就是将要存储的节点。
key.hashCode() % nodeCount = nodeIndex
缺点:如果服务器数量发⽣变化,所有的服务器的缓存在同⼀时间失效,会导致所有压⼒都在⼀个时间集中到数据库服务器上。

2. 一致性哈希算法
- ⾸先求出memcached服务器(节点)的哈希值,并将其配置到0~2^32的圆(continuum)上;
- 然后采⽤同样的⽅法求出存储数据的键的哈希值,并映射到相同的圆上;
- 再从数据映射到的位置开始顺时针查找,将数据保存到找到的第⼀个服务器上。如果超过2^32仍然找不到服务器,就会保存到第⼀台memcached服务器上。
添加⼀台memcached服务器。余数分布式算法由于保存键的服务器会发⽣巨⼤变化⽽影响缓存的命中率,但Consistent Hashing中,只有在圆(continuum)上增加服务器的地点逆时针⽅向的第⼀台服务器上的键会受到影响,如下图所示:


参考资料:链接:https://pan.baidu.com/s/1mWnTfdHV5zS2n8-Ujrra-w 提取码:1irj
注:可查看以上资源目录xmemcached-1.4.3-bin-with-dependencies\xmemcached-1.4.3\User_Guide_zh下的index.html
五、CAS操作
1. 概念
Memcached是通过CAS协议实现原⼦更新,所谓原⼦更新就是compare and set,原理类似乐观锁,每次请求存储某个数据同时要附带⼀个CAS值,memcached⽐对这个CAS值与当前存储数据的CAS值是否相等,如果相等就让新的数据覆盖⽼的数据,如果不相等就认为更新失败,这在并发环境下特别有⽤。
CAS协议其实是分为两个步骤:获取CAS值和尝试更新,因此一个典型的使用场景如下:
GetsResponse<Integer> result = client.gets("a");
long cas = result.getCas();
//尝试将a的值更新为2
if (!client.cas("a", 0, 2, cas)) {
System.err.println("cas error");
}
2. 乐观锁
乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

参考资料:链接:https://pan.baidu.com/s/1mWnTfdHV5zS2n8-Ujrra-w 提取码:1irj
注:可查看以上资源目录xmemcached-1.4.3-bin-with-dependencies\xmemcached-1.4.3\User_Guide_zh下的index.html
六、命名空间
从1.4.2开始,xmemcached提供了memcached命名空间的封装使⽤,你可以将⼀组缓存项放到同⼀个命名空间下,可以让整个命名空间下所有的缓存项同时失效。
String ns = "namespace" ;
this.memcachedClient.withNamespace(ns, new MemcachedClientCallable<Void>() {
public Void call(MemcachedClient client) throws MemcachedException, InterruptedException,TimeoutException {
//a,b,c都在namespace下
client.set("a",1);
client.set("b",1);
client.set("c",1);
return null;
}
});
//获取命名空间内的a对应的值
Integer aValue = this.memcachedClient.withNamespace(ns, new MemcachedClientCallable<Integer>() {
public Integer call(MemcachedClient client) throws MemcachedException, InterruptedException, TimeoutException {
return client.get("a");
}
});
//使得命名空间失效
this.memcachedClient.invalidateNamespace(ns);
注:更全⾯的例⼦、迭代所有的key、Incr/Decr、查看统计信息、Spring框架集成等可参阅Xmemcached⽤户指南。
参考资料:链接:https://pan.baidu.com/s/1mWnTfdHV5zS2n8-Ujrra-w 提取码:1irj
注:可查看以上资源目录xmemcached-1.4.3-bin-with-dependencies\xmemcached-1.4.3\User_Guide_zh下的index.html
七、应用
1. 分布式集群
Memcached的分布是通过客户端实现的,可查看(四、客户端分布)。
2. 数据库缓存
1. mybatis 本地缓存
- 创建springboot项目,导入mybatis相关jar包
<!-- Mybatis与Springboot集成需要的依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> <!-- 数据源jar --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.19</version> </dependency> <!-- mysql驱动jar --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!-- mybatis的jar --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> - 配置application.properties文件
server.port=8585 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username=root spring.datasource.password=root # 配置dao日志打印级别 logging.level.com.demo.dao=debug mybatis.mapper-locations=classpath:com/demo/mapper/UserDaoMapper.xml mybatis.type-aliases-package=com.demo.entity - 配置入口类
添加@MapperScan(“com.demo.dao”)注解 - 编写实体类,dao,service以及mapper.xml
- 编写测试类测试
- 在mapper.xml文件中添加< cache/>测试二级缓存
2. 分布式缓存
如:使⽤Memcached管理Mybatis⼆级缓存,构建分布式缓存
1. 添加依赖jar
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-memcached</artifactId>
<version>1.0.0</version>
</dependency>
2. 在mapper.xml文件中添加< cache type="…"/>
<mapper namespace="com.demo.dao.UserDa">
<!-- 使用mybatis的二级缓存的第三方实现 -->
<cache type="org.mybatis.caches.memcached.MemcachedCache" />
...
<select id="findAll" resultType="User">
select * from user
</select>
...
</mapper>
注:MemcachedCache类实现了Cache接口
3. 创建memcached.properties文件并配置
准备配置⽂件(memcached.properties)并放置在src/main/resources/下,如果未提供,将使⽤默认配置
| Property | Default | Description |
|---|---|---|
| org.mybatis.caches.memcached.keyprefix | _mybatis_ | any string identifier |
| org.mybatis.caches.memcached.servers | localhost:11211 | space separated list of ${host}:${port} |
| org.mybatis.caches.memcached.connectionfactory | net.spy.memcached.DefaultConnectionFactory | Any class that implements net.spy.memcached.ConnectionFactory |
| org.mybatis.caches.memcached.expiration | the number of seconds in 30 days | the expiration time (in seconds) |
| org.mybatis.caches.memcached.asyncget | false | flag to enable/disable the async get |
| org.mybatis.caches.memcached.timeout | 5 | the timeout when using async get |
| org.mybatis.caches.memcached.timeoutunit | java.util.concurrent.TimeUnit.SECONDS | the timeout unit when using async get |
| org.mybatis.caches.memcached.compression | false | if true, objects will be GZIP compressed before putting them to Memcached |
此处配置如下:
org.mybatis.caches.memcached.servers=192.168.114.142:11211 192.168.114.142:11311
4. 启动两个memcached服务
[root@memcached ~]# cd /usr/local/memcached/bin/
[root@memcached bin]# ./memcached -p 11211 -u root -m 128 -vv
[root@memcached bin]# ./memcached -p 11311 -u root -m 128 -vv
5. 启动测试类测试

2. 服务器之间的数据共享
如:服务器session集中式管理
参考资料:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
分布式会话服务:

1. 准备
准备tomcat安装包,以及各相关jar包
上传至linux操作系统
下载地址:链接:https://pan.baidu.com/s/1Y3et67ZVtCWR5VJyta1yAQ 提取码:y7we
2. 安装jdk并配置java环境
[root@memcached ~]# rpm -ivh jdk-8u181-linux-x64.rpm
[root@memcached ~]# vim /etc/profile
# 在profile末尾添加以下内容
export JAVA_HOME=/usr/java/latest
export CLASSPATH=.
export PATH=$PATH:$JAVA_HOME/bin
# -------------------------------------------
# 保存并退出
esc键 ---> :wq! ---> Enter键
[root@memcached ~]# source /etc/profile
2. 解压缩安装tomcat
[root@memcached ~]# tar -zxvf apache-tomcat-7.0.70.tar.gz -C /usr
3. 将相关jar包放入tomcat的lib文件夹
- memcached-session-manager-${version}.jar
- memcached-session-manager-tc7-1.9.7.jar
- spymemcached-2.11.1.jar
- 选择序列化⽅案所有相关jar包(在这⾥使⽤kryo)
4. 复制tomcat的解压缩目录
[root@memcached ~]# cd /usr/
[root@memcached usr]# cp -R apache-tomcat-7.0.70/ tomcat1
[root@memcached usr]# cp -R apache-tomcat-7.0.70/ tomcat2
5. 修改各个服务器的context.xml配置文件
[root@memcached usr]# vim tomcat1/conf/context.xml
[root@memcached usr]# vim tomcat2/conf/context.xml
# ============添加以下内容===============
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
# IP与端口都是memcached的服务ip与端口,可以继续添加 n3:...
memcachedNodes="n1:192.168.128.137:11211,n2:192.168.128.137:11311"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
5. 修改tomcat2的server.xml配置文件
# =============修改以下内容,与tomcat1区别开==================
...
# 修改端口为8006
22 <Server port="8006" shutdown="SHUTDOWN">
23 <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
...
# 修改端口为8081
71 <Connector port="8081" protocol="HTTP/1.1"
72 connectionTimeout="20000"
73 redirectPort="8443" />
...
# 修改端口为8010
92 <!-- Define an AJP 1.3 Connector on port 8009 -->
93 <Connector port="8010" protocol="AJP/1.3" redirectPort="8443" />
...
6. 分别启动tomcat1和tomcat2测试
使用浏览器访问
按F12使用浏览器的开发者工具查看两个tomcat访问主页的sessinid,可以看的sessionid相同。



浙公网安备 33010602011771号