常用缓存框架及redis应用

常用缓存框架及redis应用

 

一.缓存简介

互联网高速发展的今天,缓存技术已经成为大型网站的关键技术之一,其设计的好坏直接关系到用户体验,在考虑对服务性能表现进行提升时,缓存机制也往往是解决问题的重要起点--缓存技术被认为是减轻服务器负载、降低网络拥塞,减少访问延迟的有效途径之一.

多级缓存:缓存按不同位置和使用场景分类如下图
010445_JgUx_2407751.png

浏览器缓存:是最靠近用户的缓存,用户在访问同一个页面时,将不再从服务器下载页面,而是从本机的缓存目录中读取页面

网关或代理服务器缓存:是将网页缓存在网关服务器上,多用户访问同一个页面时,将直接从网关服务器把页面传送给用户,eg.Nginx.

页面缓存:是将动态页面直接生成静态的页面放在服务器端,用户调取相同页面时,直接下载到客户端,除了整个页面的缓存,还有一种技术叫做”网页片段缓存技术”,将页面的部分而不是全部进行缓存,代表作有ESIcache.

数据库缓存:数据库的缓存一般由数据库或提供,可以对表建立高速缓存,提高对经常访问的数据的访问速度

ORM框架:Hibernate的一级缓存(session级别)、二级缓存(sessionFactory级别)以及查询缓存.

数据缓存:详见2

二.三种缓存框架

常用数据缓存框架&技术有EhcacheMemcached,Redis

2.1 Ecache

号称使用最广泛的轻量级缓存框架,基于java开发,用于大型分布式应用的各个节点中.

特点:高性能,简单可靠,易扩展

  •   广泛的运用于其他的开源项目比如:hibernate
  •   发布版本最大不会超过2M,一般只有几百K
  •   核心程序仅仅依赖slf4j这一个包,没有之一!
  •   提供LRU、LFU、FIFO等淘汰算法,支持热配置、支持插件

010520_NqAL_2407751.png

2.2 Memcached

Memcached最初是由BradFitzpatrick于2003年开发而成,并在逐步发展为现代Web应用程序的构建基石.Memcache致力于一种高性能、分布式对象缓存系统,可以把它想象成一个大的内存HashTable,最初设计用于缓解动态网站数据库加载数据的延迟.

  •  C语言编写,高性能(最初实现方式为Perl语言),理论上性能的瓶颈落在网卡上
  •   分布式
  •   多线程机制
  •  当前开发工作主要关注运行稳定性及效果优化,不再积极为打造更多功能

010540_kdBJ_2407751.png

2.3 Redis

Redis则由SalvatoreSanfilippo于2009年创建,时至今日Sanfilippo仍然担任着该项目的首席开发者以及惟一维护者的角色.redis是一款先进的Key/Value存储系统,具有丰富的数据类型,它与Memcached类似,本质是缓存技术和数据库技术的一种融合产物.Redis有时候会被人们称为”强化版的Memcached”.

  •   丰富的数据类型和功能
  •   简单事务支持
  •   持久化
  •   具备数据库特征
  •   提供主从复制功能

2.4三种缓存框架比较

Ecache

Ecache主要用于进程内缓存管理,虽然通过RMI,JGroups,JMS三种方式提供了分布式缓存同步机制,但比较低级,不适合分布式环境下的应用.

Memcached的优点

  • 1.单实例更快:使用预分配内存池的方式,可以省去申请/释放内存的开销,并且能减小内存碎片产生.多线程实现可以更充分利用cpu.
  • 2.第三方支持更加成熟稳定:Memcached历史更加悠久,redis协议本身比Memcached复杂

Redis的优点

Redis在很多方面具备数据库的特征,或者说就是一个数据库系统,而Memcached只是简单的K/V缓存.Redis被大部分技术人员视为首选目标.

  • 1.支持持久化:redis的本地持久化支持两种方式:RDB和AOF.RDB配置持久化触发器,AOF指的是redis每增加一条记录都会保存到持久化文件中.在灾难恢复和数据备份上有天然的优势
  • 2.丰富的数据类型:redis支持String、Lists、sets、sortedsets、hashes多种数据类型
  • 3.功能丰富,更新快
  • 4.高性能

在别人发了一个memcache性能比redis好很多的博客后,redis作者发表了一篇博文,主要是说到如何给redis和memcache做压力测试,作者在相同的运行环境做了三次测试的取值,得到的结果如下图:

010557_d1Bx_2407751.png

需要指出的是此次测试是单核处理过程,memcache是支持多核多线程操作的(默认没开),其实没有比较的意义.作者从2.2版本后专注rediscluster的方向开发,通过纵向扩展的方式来缓解其性能上的弊端.

何时使用Memcached

有两类特殊场景仍然是Memcached的天下.首先就是对小型静态数据进行缓存处理,最具代表性的例子就是HTML代码片段.Memcached在处理元数据时所消耗的内存资源相对更少.除此之外,Memcached在横向扩展方面也比Redis更具优势.由于其在设计上的思路倾向以及相对更为简单的功能设置,Memcached在实现扩展时的难度比Redis低得多.

何时使用Redis

  除非大家需要考虑某种限定性条件(例如处理传统应用程序)对于Memcached的特殊依赖性,或者实际用例属于前面提到的两类场景中的一种,请直接选择Redis.     

可能的结论

 Redis与Memcached作为两款广受赞誉而且久经考验的解决方案,成为完成缓存机制的两大首选技术成果.Redis更多场景是作为Memcached的替代者来使用,从功能多样性以及设计先进性的角度出发,Redis几乎在缓存管理工作中的每一个侧面都表现出显而易见的优越性,显然更适合作为通用首选方案.

 

三.redis的事务

redis支持简单的事务,简单的意思指的是它不支持’事务’或者ACID原则.

Redis事务就是把几个命令合并,一次性执行全部命令——不支持回滚,只保证命令依次被执行,即使出错也会继续往下执行,本质上是客户端可以一次性执行多条命令.

3.1 multi / exec

执行过程:执行multi命令开始事务--输入需要执行的命令--输入exec执行事务.

过程分析:redis服务器收到multi命令后,会将对应的client的状态设置为REDIS_MULTI,表示client处于事务阶段,并在client的multiState结构体里面保持事务的命令具体信息(当然首先也会检查命令是否能否识别,错误的命令不会保存),即命令的个数和具体的各个命令,当收到exec命令后,redis会顺序执行multiState里面保存的命令,然后保存每个命令的返回值,当有命令发生错误是否继续执行需要区分版本和场景—— 语法错误Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行.运行错误表示命令在执行过程中出现错误,这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果事务里有一条命令执行错误,其他命令依旧会执行

3.2 watch / unwatch

执行过程:redis收到exec命令后,事务的执行过程是不会被打断的,但是在用户逐个输入事务的命令的时候,期间可能已经有别的客户修改了事务里面用到的数据,所以redis还提供了watch命令,用户可以在输入multi之前,执行watch命令,指定需要观察的数据,这样如果在exec之前,有其他的客户端修改了这些被watch的数据,则exec执行失败,提示数据已经dirty.

如何实现:每一个redisdb中还有一个dict watched_keys,dictentry的key是被watch的key,而value则是一个list,里面存储的是watch它的client.每个client也有一个watched_keys,里面保存的是这个client当前watch的key.在执行watch的时候,redis在对应的数据库的watched_keys中找到这个key(如果没有,则新建一个dictentry),然后在它的客户列表中加入这个client,同时,往这个client的watched_keys中加入这个key.当有客户执行一个命令修改数据的时候,redis首先在watched_keys中找这个key,遍历所有watch它的client,将这些client设置为REDIS_DIRTY_CAS.当client执行的事务的时候,首先会检查是否被设置了REDIS_DIRTY_CAS,如果是,则表明数据dirty了,事务无法执行,会立即返回错误. 执行exec后,该client的所有watch的key都会被清除,同时db中该key的client列表也会清除该client,即使exec没有执行成功也是一样).

如果不想执行事务中的命令,也可以使用UNWATCH命令来取消监控

 

四.reids应用及线上事故分析

项目中应用了缓存框架:Spring+Ecache+redis, 其中Ecache作单机缓存,redis作集中缓存处理,使用了Spring框架的 Spring-Cache和Spring-Data-Redis模块(Spring-Data-Redis核心是对jedis进一步的封装.)

4.1 redis配置

在Spring项目中配置reids应用相对简单,贴图如下


Spring-data-redis和jedis依赖

010614_ODyd_2407751.png
注入相关Bean并配置初始化参数

010641_W3ub_2407751.png

010734_X9iG_2407751.png

注:上图省去了redis连接参数的配置

RedisTemplate事务应用.待补充

1.       启用redisTemalate事务支持

配置文件中配置enableTransactionSupport=true.或者template.setEnableTransactionSupport(true);

2.       注意!!!:启用Spring事务注解@Transactional,否则会导致redis连接不释放。详见5参考资料

3.       实现方式,

(1)直接使用redis命令.没有绑定同一个连接,可能有坑

  •  redisTemplate.watch(key)
  •   redisTemplate.multi ()
  •   redisTemplate.exec ()
  •   redisTemplate.unwatch()

(2)回调方式.保持同一连接,推荐!!!

redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) {
operations.watch(key);
String value = operations.opsForValue().get(key);
operations.multi();
operations.opsForValue().set(key,value );
Object rs = operations.exec();
return rs;
}});

4.2 线上事故分析.待补充

事故表现:

  • 日志显示大量空指针异常.
  • 上线前QA多次回归功能,未发现问题.
  • 线下调试发现该问题概率性出现 

010810_SHoO_2407751.png
初步定位:

l  通过堆栈信息定位到抛空指针的代码,redisTemplate.size返回NULL,确认是redis使用问题

l  上线功能涉及redis的改动:redis事务支持,spring-data-session-redis.连接池配置

进一步定位:

l  Spring-data-session-redis属于框架使用,不涉及业务,出问题概率较低.

l  由于使用场景增加,上线前出现无法获取连接的问题,更改redis连接池配置后上线,属于重点怀疑对象.初期反复调试更改最大最小连接数,超时时间等后,失败概率有所降低,但无法完全根治.问题可能和连接池配置有一定关系.待详解

源码定位<待补充>:
下载Spring-data-redis源码

010824_UfX6_2407751.png

1.       定位size操作—DefaultSetOperations.

010843_s1zz_2407751.png

可以看出size函数最终使用的是redisConnectin的sCard命令,接着跟踪

2.     定位sCard操作---JedisConnection

010854_4er5_2407751.png

jedisConnection.sCard,spring-data-redis的底层还是使用的jedis,在执行scard命令前,分别经过两个步骤判断isPipelined和isQueueing,inspect发现isQueueing=true!!!

3.      isInMulti !!!

010905_mfqT_2407751.png

加上transaction这个诡异的名字,大概可以窥出端倪

4.      BinaryClient中唯一处将client置为isinmulti=true的操作只有一个命令multi,大坑!!

010916_Tp7N_2407751.png

问题已经比较明显,multi命令将client isInMulti标志位置为true,导致请求被放到队列内而本次调用返回null.

由于代码中使用到multi的地方只有一处,很容易就定位到了问题代码,随后下线redis事务得到解决,略坑! 

QuestionredisTempate执行事务期间,仍能从连接池中获取到同一个client!?

Why?
定位源码.redisTemplate.excute – redisConnectionUtils从连接池中获取connection

 

逻辑稍复杂,有时间再详细分析,先给出结论如下:

4.3 结论

1.Redis很好很强大.

2.RedisTempalte各种坑, 慎用

 

五.参考资料

http://tech.it168.com/a2014/1016/1674/000001674122.shtml

http://www.cnblogs.com/xiaoluo501395377/p/3377604.html

https://msdn.microsoft.com/zh-cn/library/ff648482.aspx

https://segmentfault.com/a/1190000004393573

http://www.blogways.net/blog/2013/06/02/jedis-demo.html

转载于:https://my.oschina.net/Bai1991/blog/1503160

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值