Memcached

一、概述

传统关系型数据:Oracle 、 Mysql 指标

  1. 容量 2000W
  2. tps 1500个
  3. 响应时间:10MS

缓存类型:

1.本地缓存

2.客户端缓存

3.分布式缓存

缓存的设计指标:

  1. 容量 key-value
  2. tps 12W
  3. 成本 (64G 128G )
  4. 响应时间

常用缓存技术:

ehcache oscache memcached redis tair

二、Memcached 基本介绍

Memcached 是一款开源、免费、高性能的分布式内存对象缓存系统

Memcached是一种基于内存的key-value存储,用于存储小块的任意数据(字符串、对象)

Memcached简洁且强大。它的简洁设计便于快捷开发、减轻开发难度,解决了大数据缓存的很多问题。它的api兼容大部分流行的开发语言。

MemcachedMemcached

  • 感性认知

老牌的、轻量级、高性能、key-value类型的分布式缓存系统,主要用于分布式Web应用中去减轻数据库的负载(压力),提高了访问速度。

  • Memcached
    • key-value
    • 不支持持久化
    • 不支持服务端集群
    • 255字符 1mB
    • 最大存储时长 30day

三、使用场景

1.分布式缓存
将缓存的功能剥离出去,存在于其他的服务器中称之为分布式缓存。

本地缓存,缓存本身存在于本地的机器中,类似mybatis的缓存
  • 优点
    • 减少缓存给应用服务器带来压力
    • 本地缓存变为分布式缓存,缓存命中率高、节省内存
  • 缺点
    • 服务集中化(单点故障)-成本的提高
    • 带来额外的网络io开销-缓存服务器在同一局域网(同一机房/机架/城市/地区)
2.会话Session管理

tomcat集群对session的管理方式

  • tomcat之间进行session的复制
  • 通过nginx锁定用户,用户所有的访问都在一台机器当中

上述两种方式都有缺陷:(1)内存告急(2)单点故障

从宏观上来说应用服务器进行了无状态化的处理。

思考:为什么需要进行无状态化处理?

四、环境搭建

安装
  • 本地上传安装
http://memcached.org/downloads
在此下载页面中下载tar包解压即可

安装
yum install libevent-devel
yum install gcc


解压
tar -zxvf memcached-1.4.31.tar.gz

cd memcached-1.4.31
# 指定memcached安装⽬目录
./configure --prefix=/usr/local/memcached
 make && make install
  • 云端下载安装
// 注意 以下  latest  memcached-1.x.x.tar.gz  都是模糊链接(想要下载需要输入正确版本号)

wget http://memcached.org/latest
tar -zxvf memcached-1.x.x.tar.gz
cd memcached-1.x.x
./configure && make && make test && sudo make install
参数说明
cd /usr/local/memcached/bin  (这里指的是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 ⾮非常详细的⽇日志
启动
cd /usr/local/memcached/bin

./memcached -m 128mb -vv -u root
命令
Memcached可以通过 telnet 命令并指定主机ip和端⼝口来连接 Memcached 服务

例如:telnet HOST PORT

telnet localhodt 11211
# 存值语法
set key flags exptime bytes
value
key:键值 key-value 结构中的 key,⽤用于查找缓存值。
flags:可以包括键值对的整型参数,客户机使⽤用它存储关于键值对的额外信息 。
exptime:在缓存中保存键值对的时间⻓长度(以秒为单位,0 表示永远)
bytes:在缓存中存储的字节数
# 取值语法
get key
# stats 命令⽤用于返回统计信息例例如 PID(进程号)、版本号、连接数等
stats

五、Java客户端

XMemcached是基于Java NIO的Memcached客户端,Java NIO相⽐比于传统阻塞io模型来说,有效率 ⾼高(特别在
高并发下)和资源耗费相对较少的优点。

特性

高性能
支持完整协议
支持客户端分布
动态增删节点
允许设置节点权重

Maven坐标
<!--
https://mvnrepository.com/artifact/com.googlecode.xmemcached/xmemcached -->
<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>
简单示例
package com.baizhi;

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.exception.MemcachedException;
import net.rubyeye.xmemcached.utils.AddrUtil;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * Unit test for simple App.
 */
public class AppTest {
    public static void main(String[] args) throws IOException {
        MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("192.168.21.143:11211"));
        MemcachedClient memcachedClient = builder.build();
        try {
            // 存储数据 参数⼀一:key名 参数⼆二:expire时间(单位秒)表示永久存储(默认是⼀一个⽉月)
            // 参数三:value值
            memcachedClient.set("hello",0,"hello baby");
            // 获取数据
            String value = memcachedClient.get("hello");
            System.out.println("hello=" + value);


            // 删除数据
            memcachedClient.delete("hello");
            // 获取数据
            String value2 = memcachedClient.get("hello");
            System.out.println("hello=" + value2);
        } catch (TimeoutException e) {
            System.err.println("******MemcachedClient operation timeout");
            e.printStackTrace();
        } catch (InterruptedException e) {
            System.err.println("******MemcachedClient operation ignore");
            e.printStackTrace();
        } catch (MemcachedException e) {
            System.err.println("******MemcachedClient operation fail");
            e.printStackTrace();
        } finally {
            memcachedClient.shutdown();
        }
    }


}
客户端分布

Memcached的分布是通过客户端实现的,客户端根据key的哈希值得到将要存储的memcached节点,并将对应的value存储到相应的节点。

MemcachedMemcached

1.余数算法(默认)
  • 按照key的哈希值模以连接数得到的余数,对应的连接就是将要存储的节点
    key.hashCode() % nodeCount = nodeIndex
  • 缺点:如果服务器数量发生变化,所有的服务器的缓存在同一时间失效,会导致所有压力都在一个时间集中到数据库服务器上
2.一致性哈希

原理可以参考:https://www.cnblogs.com/lpfuture/p/5796398.html

  • 首先求出memcached服务器(节点)的哈希值,并将其配置到0-2的32次方的圆(continuum)上。

  • 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
    然后从数据映射到位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器码,就会保存到第一台memcached服务器上

  • 添加一台memcached服务器。余数分布式算法由于保存键的服务器会发生巨大变化而影响缓存的命中率,但
    Consistent Hashing 中,只有在圆(continuum)上增加服务器的地点逆时针方向的第一台服务器上键会受到
    影响,如下图所示:

    MemcachedMemcached

Memcached
Memcached

CAS操作

参考资料:https://blog.csdn.net/qq_34337272/article/details/81072874
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");
}
命名空间

从1.4.2开始xmemcached提供了memcached命名空间的封装使用,你可以将一组缓存项放到同一个命名空间下,
可以让整个命名空间所有的缓存项同时失效

String ns = "namespace" ;
this.memcachedClient.withNamespace(ns,new MemcachedClientCallable<Void>() {
  public Void call(MemcachedClientclient)throwsMemcachedException,InterruptedException,
																	TimeoutException {
        //a,b,c都在namespace下
        client.set("a",0,1);
        client.set("b",0,1);
        client.set("c",0,1);
        return null;
        }
	});
//获取命名空间内的a的对应值
Integer aValue = this.memcachedClient.withNamespace(ns,
new MemcachedClientCallable<Integer>() {
    public Integer call(MemcachedClientclient)throwsMemcachedException, 													InterruptedException,TimeoutException {
		return client.get("a");
		}
	});
//使得命名空间失效
this.memcachedClient.invalidateNamespace(ns);

注意:更全面的例子、迭代所有的key、Incr/Decr、查看统计信息、Spring框架继承等可参阅Xmemcached用户指南。

六、应用

分布式集群

Memcached的分布是通过客户端实现的(具体可参阅:章节三值客户端分布)

数据库缓存

如:使用Memcached管理MyBatis二级缓存,构建分布式缓存

参考资料:
  1. Maven坐标
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-memcached</artifactId>
    <version>1.0.0</version>
</dependency>
  1. 配置Mapper文件

    <mapper namespace="org.acme.FooMapper">
    <cache type="org.mybatis.caches.memcached.MemcachedCache" />
    ...
    </mapper>
    

4.需要日志缓存操作

<mapper namespace="org.acme.FooMapper">
<cache type="org.mybatis.caches.memcached.LoggingMemcachedCache" />
...
</mapper>

5.测试缓存

服务器间的数据共享

如:服务器session集中式管理
参考资料:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration

  1. 导入jar包到服务器tomcat\lib目录
    memcached-session-manager-${version}.jar
    memcached-session-manager-tc7-1.9.7.jar
    spymemcached-2.11.1.jar
    选择序列化方案所有相关的jar包(这里使用kyro)

  2. 修改配置文件

       vi ~/tomcat1/conf/context.xml
       <Manager
       className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
       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"/>
      
           
      
     vi ~/tomcat2/conf/context.xml
       <Manager
       className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
       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"/>
    

3.分别启动tomcat1和tomcat2测试