企业项目开发--分布式缓存memcached
本地缓存的问题
- 本地缓存速度一开始高于分布式缓存,但是随着其缓存数量的增加,所占内存越来越大,系统运行内存越来越小,最后系统会被拖慢(这一点与第二点联系起来)
- 本地缓存存于本机,其缓存数量与大小受本机内存大小限制
- 本地缓存存于本机,其他机器的访问不到这样的缓存
解决方案:分布式缓存
- Jboss cache:缓存还存于本机,但是会同步更新到其他机器(解决了第三个问题,解决不了第一和第二个问题),如果缓存机器数量很多,同步更新很耗时
- memcached:缓存存于其他机器,理论上缓存数量与大小无限(因为集群可伸缩),且不需要同步,所以即使缓存机器数量很多,也无所谓,但是这样就会造成单点故障问题,最简单易行的解决方案是缓存备份,即缓存至少存两份。
demo
一个web平台
假如我们有一个web项目,通过url请求缓存的数据,如果存在,直接拿,如果没有,查sql,写入缓存中
localhost:8080/project/admin/findAdminById?id=1转到controller
@ResponseBody
@RequestMapping("/findAdminById")
public Admin findAdminById(@RequestParam(value="id") int id){
return adminService.findAdminById(id);
}
//adminservice的实现
//通过id在缓存中获取数据,有直接返回,没有,查找数据库,并写入缓存,返回数据
public Admin findAdminById(int id) {
Admin admin = (Admin)MemcachedUtil.getCache(CachePrefix.USER_MANAGEMENT, String.valueOf(id));//获取序列化的值,转对象
.......
MemcachedUtil.setCacheWithNoReply(CachePrefix.USER_MANAGEMENT, String.valueOf(id), admin);//对象序列化后更新缓存
}
项目中引入我们实现的分布式缓存工程
//分布式缓存的实现和配置
MemcachedUtil的设计
读取配置文件,设置缓存的参数,并发数,过期时间等等
存数据
取数据
MemecathcedClientBuilder创建客户端,10客户端几乎可以同时处理100万请求。
注意:
- 我们提供了三个MemcachedClient,那是不是说明同时只能处理三个并发请求呢?不是,Xmemcached基于Java NIO,每一个MemcachedClient都会启动一个reactor线程和一些工作线程,基于IO多路复用技术,一个reactor线程理论上可以接受极高的并发量,甚至可以说成,该reactor线程可以接过所有到达memcached的请求,然后通过事件机制(类似于观察者模式)将这些请求派发给工作线程,进行相应的操作。关于Java NIO、reactor线程模型、事件机制等可以参看《netty权威指南(第2版)》。
- 正如上边所说,每启动一个MemcachedClient,就必须启动一个reactor线程和一些工作线程,这其实是一个昂贵的操作,所以构建多个客户端是比较昂贵的,基于此,XMemcached提供了池化操作,即一个配置参数(setConnectionPoolSize),但是在实际使用中发现,当配置该参数>1的情况下会发生线程死锁现象,所以还是采用多客户端的方式吧!在我们的实际使用中,10个客户端接收百万级请求绝对是轻轻松松的!
Redis:新一代的分布式缓存,有替代memcached的趋势
Redis
- 可以存储复杂的数据结构(5种)
- strings-->即简单的key-value,就是memcached可以存储的唯一的一种形式,接下来的四种是memcached不能直接存储的四种格式(当然理论上可以先将下面的一些数据结构中的东西封装成对象,然后存入memcached,但是不推荐将大对象存入memcached,因为memcached的单一value的最大存储为1M,可能即使采用了压缩算法也不够,即使够,可能存取的效率也不高,而redis的value最大为1G)
- 有两种方式可以对缓存数据进行持久化
- RDB
- AOF
页面缓存
在使用jsp的时候,我们会将一些复杂的页面使用Oscache进行页面缓存,使用非常简单,就是几个标签的事儿;但是,现在一般的企业,前台都会使用velocity、freemaker这两种模板引擎,本身速度就已经很快了,页面缓存使用的也就很少了。
总结:
- 在实际生产中,我们通常会使用guava cache做本地缓存+redis做分布式缓存+spring cache就集成缓存(底层使用redis来实现)的形式
- guava cache使用在更快的获取缓存数据,同时缓存的数据量并不大的情况
- spring cache集成缓存是为了简单便捷的去使用缓存(以注解的方式即可),使用redis做其实现类是为了可以存更多的数据在机器上
- redis缓存单独使用是为了弥补spring cache集成缓存的不灵活
- 就我个人而言,如果需要使用分布式缓存,那么首先redis是必选的,因为在实际开发中,我们会缓存各种各样的数据类型,在使用了redis的同时,memcached就完全可以舍弃了,但是现在还有很多公司在同时使用memcached和redis两种缓存。