Memcached简介

一、关于Memcached

1.1 Memcached的介绍

  Memcached是一个自由开源的,高性能,分布式内存对象缓存系统。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。

1.2 Memcached的工作流程

  MemCache的工作流程如下:先检查客户端的请求数据是否在memcached中,如有,直接把请求数据返回,不再对数据库进行任何操作;如果请求的数据不在memcached中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcached中;每次更新数据库的同时更新memcached中的数据,保证一致性;当分配给memcached内存空间用完之后,会使用LRU(Least Recently Used,最近最少使用)策略加上到期失效策略,失效数据首先被替换,然后再替换掉最近未使用的数据。

1.3 Memcached的安装

1.下载地址:
  1. 32位系统 1.4.5版本:http://static.runoob.com/download/memcached-1.4.5-x86.zip
  2. 64位系统 1.4.5版本:http://static.runoob.com/download/memcached-1.4.5-amd64.zip
2.安装步骤
  1、解压下载的安装包到指定目录。

  2、在 memcached1.4.5 版本之后,memcached 不能作为服务来运行,需要使用任务计划中来开启一个普通的进程,在 window 启动时设置 memcached自动执行。

  我们使用管理员身份执行以下命令将 memcached 添加来任务计划表中:
  bash schtasks /create /sc onstart /tn memcached /tr "'c:\memcached\memcached.exe' -m 512"
  然后再解压的目录中打开cmd,并输入:bash memcached -m 128 -p 11211 -vvv 运行Memcached。

  
  我们通过telnet客户端就可以连接Memcached,在命令串口输入:cmd telnet 127.0.0.1 11211

二、Memcached相关命令

2.1 存储命令

  1.Memcached set 命令
    作用:Memcached set 命令用于将 value(数据值) 存储在指定的 key(键) 中。如果set的key已经存在,该命令可以更新该key所对应的原来的数据,也就是实现更新的作用。
    语法:

set key flags exptime bytes [noreply] 
value 
**参数说明如下:**

1.key:键值 key-value 结构中的 key,用于查找缓存值。
2.flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
3.exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
4.bytes:在缓存中存储的字节数
5.noreply(可选): 该参数告知服务器不需要返回数据
6.value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)

  2.Memcached add 命令
    作用:Memcached add 命令用于将 value(数据值) 存储在指定的 key(键) 中。如果 add 的 key 已经存在,则不会更新数据(过期的 key 会更新),之前的值将仍然保持相同,并且您将获得响应 NOT_STORED。
    语法:

add key flags exptime bytes [noreply]
value

  3.Memcached replace 命令
    作用:Memcached replace 命令用于替换已存在的 key(键) 的 value(数据值)。如果 key 不存在,则替换失败,并且您将获得响应 NOT_STORED。
    语法:

replace key flags exptime bytes [noreply]
value

  4.Memcached append 命令
    作用:Memcached append 命令用于向已存在 key(键) 的 value(数据值) 后面追加数据 。
    语法:

append key flags exptime bytes [noreply]
value

  5.Memcached prepend 命令
    作用:Memcached prepend 命令用于向已存在 key(键) 的 value(数据值) 前面追加数据 。
    语法:

prepend key flags exptime bytes [noreply]
value

2.2 查找命令

  1.Memcached get 命令
    作用:Memcached get 命令获取存储在 key(键) 中的 value(数据值) ,如果 key 不存在,则返回空。
    语法:

get key [,key1,....keyn]

  2.Memcached delete命令
    作用:Memcached delete 命令用于删除已存在的 key(键)。
    语法:

delete key

  2.Memcached 自增/自减命令
    作用:Memcached incr 与 decr 命令用于对已存在的 key(键) 的数字值进行自增或自减操作。incr 与 decr 命令操作的数据必须是十进制的32位无符号整数。如果 key 不存在返回 NOT_FOUND,如果键的值不为数字,则返回 CLIENT_ERROR,其他错误返回 ERROR。
    语法:

incr key increment_value // 让key增加 increment_value 
decr key decrement_value // 让key减少 decrement_value 

Memcached, Redis三者的区别

Memcached

  是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。
Memcached的优点:

1.Memcached可以利用多核优势,单实例吞吐量极高,可以达到几十万QPS(取决于key、value的字节大小以及服务器硬件性能,日常环境中QPS高峰大约在4-6w左右)。适用于最大程度扛量。
2.支持直接配置为session handle。

Memcached的局限性:

1.只支持简单的key/value数据结构,不像Redis可以支持丰富的数据类型。
2.无法进行持久化,数据不能备份,只能用于缓存使用,且重启后数据全部丢失。
3.无法进行数据同步,不能将MC中的数据迁移到其他MC实例中。

redis

  是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
redis 的优点:

1.支持多种数据结构,如 string(字符串)、 list(双向链表)、dict(hash表)、set(集合)、zset(排序set)、hyperloglog(基数估算)。
2.支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
3.单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。
4.支持pub/sub消息订阅机制,可以用来进行消息订阅与通知。

redis 的缺点:

1.Redis在string类型上会消耗较多内存,可以使用dict(hash表)压缩存储以降低内存耗用。
2.Mc和Redis都是Key-Value类型,不适合在不同数据集之间建立关系,也不适合进行查询搜索。比如redis的keys pattern这种匹配操作,对redis的性能是灾难。

实际案例

  假设我们有一个web应用,里面有商品信息,文章信息,评论信息,其他信息,我们希望对其做缓存,那么我们在ServiceImpl层就不在调用DAOmpl层,而是调用CacheImpl层,在CacheImpl层中判断要取出的商品信息是否已经在缓存中,如果在了,那么直接从缓存中去,如果没有这个时候还是从数据库中取,同时将它放到缓存中,以便下次使用。
第一步、新建一个常量类,用于上面的四种信息的在数组中的索引。

public class MemcachedConstant {
	public static final int MEMCACHED_GOODSDETAIL = 0;
	public static final int MEMCACHED_ARTICLEDETAIL = 1;
	public static final int MEMCACHED_COMMENTDETAIL = 2;
	public static final int MEMCACHED_OTHERDETAIL = 3;
}

第二步、由于有大量的商品信息,我们在放入缓存时必须给定一个key,那么我们最好规范的命名不同类别的key,如商品的key就是商品的前缀加上商品的编号。

public class MemcachedKeyUtil {
	private static final String GOODS_KEY_PREFIX = "goods_";
	
	public static String getGoodsKey(long goodsId) {
		return GOODS_KEY_PREFIX + goodsId;
	}
}

第三步、使用工具类来操作memcached

public class MemcachedUtil {
	private int MEMCACHED_SERVER_NUM = 4;
	private SockIOPool[] pools = new SockIOPool[MEMCACHED_SERVER_NUM];
	private MemCachedClient[] mcs = new MemCachedClient[MEMCACHED_SERVER_NUM];
	private final String[] poolNames = new String[] { "GOODSDETAIL_POOL", "", "", "" };
	private static MemcachedUtil instance;
	private MemcachedUtil() {
		this.init();
	}
	// 单例
	public static MemcachedUtil getInstance() {
		if (MemcachedUtil.instance == null) {
			synchronized (MemcachedUtil.class) {
				if (MemcachedUtil.instance == null) {
					MemcachedUtil.instance = new MemcachedUtil();
				}
			}
		}
		return MemcachedUtil.instance;
	}
	
	public Object get(int index, String key) {
		return this.mcs[index].get(key);
	}
	
	public boolean set(int index, String key, Object value) {
		return this.mcs[index].set(key, value);
	}
	
	public boolean delete(String key) {
		return this.mcs[index].delete(key);
	}
	public MemCachedClient getMemCachedClient(int index) {
		return this.mcs[index];
	}
	
	public void init() {
		for (int i = 0; i < MEMCACHED_SERVER_NUM; ++i) {
			this.pools[i] = SockIOPool.getInstance(poolNames[i]);
			this.pools[i].setServers(servers);
			this.pools[i].setWeights(weights);
			this.pools[i].setInitConn(initConn);
			this.pools[i].setMinConn(minConn);
			this.pools[i].setMaxConn(maxConn);
			this.pools[i].setMaxIdle(maxIdle);
			this.pools[i].setMaxBusyTime(maxBusyTime);
			this.pools[i].setMaintSleep(maintSleep);
			this.pools[i].setNagle(ifNagle);
			this.pools[i].setSocketTO(socketTO);
			this.pools[i].setSocketConnectTO(socketConnectTO);
			this.pools[i].setFailover(ifFailOver);
			this.pools[i].setFailback(ifFailback);
			this.pools[i].setAliveCheck(ifAliveCheck);
			this.pools[i].initialize();
			this.mcs[i] = new MemCachedClient(poolNames[i]);
		}
	}
}

第四步、新建一个基类以供所用继承它的CacheImpl直接调用MemcachedUtil里的方法,如果不写该类那么在CacheImpl中会有很多重复的操作MemcachedUtil的代码。


public class MemcachedSupport {
	public boolean setDetailData(String key, Object value) {
		return MemcachedUtil.getInstance().set(MemcachedConstant.MEMCACHED_DETAIL, key, value);
	}
	
	public Object getDetailData(String key) {
		return MemcachedUtil.getInstance().get(MemcachedConstant.MEMCACHED_DETAIL, key);
	}
	
	public boolean deleteDetailData(String key) {
		return MemcachedUtil.getInstance().delete(MemcachedConstant.MEMCACHED_DETAIL);
	}
}

第五步、新建一个GoodsCacheImpl,该类的作用就是一开始所说的,娶不到缓存,就调用DAO查询并放入缓存,如果缓存中有就直接从缓存中拿。


public class GoodsCacheImpl extends MemcachedSupport{
	@Resource(name = "goodsDaoImpl")
	private GoodsDao goodsDao;
	
	public Goods selectGoodsById(long goodsId) {
		Goods goods = null;
		String goodsKey = MemcachedKeyUtil.getGoodsKey(goodsId);
		goods = (Goods) getDetailData(goodsKey);
		if (goods == null) {
			goods = goodsDao.selectGoodsById(goodsId, false);
			if (goods != null) {
				setDetailData(goodsKey, goods);
			}
		}
		return goods;
	}
}
posted @ 2020-12-13 11:58  cyoking  阅读(770)  评论(2编辑  收藏  举报