零. 简介
Memcached 是一款开源、 分布式内存对象缓存系统, 用于加快动态网站响应, 降低数据库负载
Memcached 使用简单, 支持多种语言的 API, 解决面对大量数据的缓存问题
一. 设计哲学
(1) 键值对存储
服务器不关心数据是什么样的。 存储单元由 key、 过期时间、 可选的标记和数据组成。 它不懂数据结构, 传输数据前需要序列化。
(2)一半逻辑在客户端, 另一半逻辑在服务端
客户端知道如何选择(哈希)哪个服务端去读/写存储单元, 并且知道如何处理无法连到服务端的情况
服务端知道如何存储和获取数据单元, 并且它知道如何清除或者重用内存。
(3)服务端彼此之间不通信
即服务端之间没有同步、 没有多播、 没有复制。 增加服务器就是增加可用内存。 但是正因为这个特性, Memcached 存在单点故障, 如果需要做高可用, 需要使用 Magent 这样的代理服务软件去实现高可用。
(4)时间复杂度 O(1)
所有的命令都被尽量实现为快速和对锁友好,提供了近乎确定的查询速度。 在慢的机器上查询时间低于 1ms, 在高端服务器上可以达到百万/s 的吞吐量
(5)遗忘是一种特性
Lazy Expiration + LRU :不检测 item 对象是否超时,get 时检查 item 对象是否应该删除;
删除item对象时,不释放内存,作删除标记,指针方式 slot 回收插槽,下次分配的时候直接使用;
(6)缓存无效
客户端通过 hash 直接告诉服务端缓存数据无效, 而不是多播让所有服务器的该 item 缓存无效
二. Memcached 实战
(1) 安装 memcached 很简单, 如果是 CentOS 或者 Red Hat 直接
yum install memcached
如果是 Ubuntu/Debian 则使用 apt-get
(2) 检查 Memcached 是否安装成功:可以查看到帮助信息说明安装成功
memcached -h
(3) 启动 Memcached (假设 138.138.138.11 是你的外网 ip, 在 CentOS7 上设置 11211 默认端口访问不了, 查看也没被占用, 很奇怪, 所以下面监听 11222 端口)
memcached -d -u root -l 138.138.138.11 -p 11222 -vv
-d 以守护程序(daemon)方式运行
-u root 指定用户,如果当前为 root ,需要此参数指定用户
-P /tmp/a.pid 保存PID到指定文件
-m 默认 64,不包含memcached本身占用,单位为 MB
-M 内存不够时禁止 LRU
-n 48 初始 chunk=key+suffix+value+32 结构体,默认 48 字节
-f 1.25 增长因子,默认1.25
-L 启用大内存页,可以降低内存浪费,改进性能
-l 监听的 IP 地址, 服务器有两个网卡一个对内一个对外, 如果设置为 127.0.0.1 则只能本地访问 memcached, 设置外网 ip 则可以接受外来的访问
-p 11211 TCP端口,默认为11211
-U 11211 UDP端口,默认为11211,0为关闭
-c 1024 最大并发连接数,默认1024,最好是200
-t 4 线程数,默认4
-R 20 每个event连接最大并发数,默认20
-C 禁用CAS命令(可以禁止版本计数,减少开销)
-vv 打印交互式详细信息, 可以看到连接进来的人正在做什么操作
(4) 检查 Memcached 是否成功启动:
ps -ef | grep memcached
(5) 远程连接 Memcached: 连接成功显示 Connected to 138.138.138.11. Escape character is ‘^]‘.
telnet 138.138.138.11 11222
(6) 连接成功后输入
stats 可以查看服务整体状态
STAT pid 5013 memcache服务器的进程ID
STAT uptime 3524589 服务器已经运行的秒数
STAT time 1453720323 服务器当前的unix时间戳
STAT version 1.4.17 memcache版本
STAT libevent 2.0.16-stable
STAT pointer_size 64 当前操作系统的指针大小(32位系统一般是32bit)
STAT usage_user 20513.015760 进程的累计用户时间
STAT rusage_system 45473.527888 进程的累计系统时间
STAT cur_connections 102 当前打开着的连接数
STAT total_connections 60437 从服务器启动以后曾经打开过的连接数
STAT connection_structures 243 服务器分配的连接构造数
STAT reserved_fds 20
STAT cmd_get 474507075 get命令(获取)总请求次数
STAT cmd_set 334776915 set命令(保存)总请求次数
STAT cmd_flush 97
STAT cmd_touch 0
STAT get_hits 301883351 总命中次数
STAT get_misses 172623724 总未命中次数
STAT delete_misses 208322
STAT delete_hits 35095518
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 268709016022 总读取字节数(请求字节数)
STAT bytes_written 754717253318 总发送字节数(结果字节数)
STAT limit_maxbytes 536870912 分配给memcache的内存大小(字节)
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4 当前线程数
STAT conn_yields 17561
STAT hash_power_level 18
STAT hash_bytes 2097152
STAT hash_is_expanding 0
STAT malloc_fails 0
STAT bytes 473920740 当前服务器存储items占用的字节数
STAT curr_items 265877 服务器当前存储的items数量
STAT total_items 239409267 从服务器启动以后存储的items总数量
STAT expired_unfetched 49973876
STAT evicted_unfetched 23514857
STAT evictions 27383392 为获取空闲内存而删除的items数(分配给memcache的空间用满后需要删除旧的items来得到空间分配给新的items)
STAT reclaimed 52732799
三. 客户端的选择
客户端有三种选择: spymemcached、 java-memcached、 xmemcached
具体看 https://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html 有性能对比图, 当然因为是 xmemcached 自己的性能测试报告, 不一定和每个人使用的时候场景一样, 具体可以自己测试后再作出选择。
引入 xmemcached-1.3.6.jar 的包
测试代码如下:
package com.wenniuwuren.memcached; import net.rubyeye.xmemcached.MemcachedClient; import net.rubyeye.xmemcached.XMemcachedClientBuilder; import net.rubyeye.xmemcached.utils.AddrUtil; import java.util.HashMap; import java.util.Map; /** * Created by wenniuwuren on 16/3/6. */ public class MemcachedTest { public static void main(String[] args) { try { //New a XMemcachedClient instance XMemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("138.138.138.11:11222")); MemcachedClient client = builder.build(); Map map = new HashMap<>(); map.put("1", "2"); client.set("key", 3000, map); Map cachedMap = (Map)client.get("key"); System.out.println(cachedMap.get("1")); } catch (Exception e) { e.printStackTrace(); } } }
四. Memcached 集群
因为 Memcached 设计哲学的极简, 所以集群不熟非常简单, 服务端新启动一个 Memcached 进程就行。 其他的交给客户端完成。 如果客户端使用普通的 Hash 算法,则新增 Memcached 会重新计算 Hash 值使旧的缓存失效。 如果客户端使用一致性 Hash 算法, 则不会影响旧的缓存数据。
新加个端口为 11333 的 Memcached 进程, 可以看到两个 Memcached 存储内容不同, 证明上述 Memcached 服务间不互相通信。
五. Memcached 局限性
(1)存储格式单一, 就是 <String, Object>, 想使用复杂点的数据结构(list、 set、 hash)一般会选用 Redis
(2)最大只能存储 1M 的 item, 而
(3)最大键长 250 字节
(4)只存在缓存, 数据重启丢失,而 Redis 支持持久化(但是也经常会丢数据), 并在此基础上实现主从同步, 而 Memcached 存在单点故障
(5)数据一致性问题: Memcached 使用 CAS 保证。 而 Redis 使用事务, 保证一系列命令的原子性。