zoukankan      html  css  js  c++  java
  • 转:Redis 缓存策略

    转:http://api.crap.cn/index.do#/web/article/detail/web/ARTICLE/7754a002-6400-442d-8dc8-e76e72d948ac

    目前高并发项目会在数据库之上引入数据缓存:

    1. 缓存雪崩,什么是雪崩效应?

            缓存雪崩可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。

           我的理解的雪崩效应是在分层服务调用的系统中,当一个较为基础的服务a因某种原因不可用,导致其调用服务b,c也不可用,而b的不可用又导致其调用服务e,f的不可用,不可用的服务就好像滚雪球一样越滚越大,最后导致系统出现严重故障。我们称之为雪崩效应。如图。

    以此为例,当数据库主备都不可用时,将导致其调用服务的关联失效(红色组件)

    2.基于redis数据过时失效的策略,在缓存过期的短时间内存在数据库短暂压力问题,如下:

    为解决上述问题(主要是后者),可采用一下解决方案如下:

    这里我们以两个键值对的缓存代替之前的一个,将缓存时间(key-time),和缓存数据(key-data)分离,这样

    1.当缓存过期时,第一个线程发现key-time没有,则先更新key-time,

    2.然后去查询数据库(或任何比较耗时的数据查询方式),并更新key-data的值,

    3.当后续线程来获取数据时,虽然第一个还没有从数据库查完并更新缓存,但发现key-time存在,会获取旧的数据。

    虽然按这种方式获取的数据中c类型的数据为旧数据,但可以做到:

    在缓存过期时不至于在短时间内对数据库造成较高压力(解决第2个问题)。

    在数据库不可用,同时缓存过期的时候,其调用服务皆不可用,造成连锁反应,而已该解决方式,则可以返回过期数据,为修复数据库赢得宝贵时间(缓解雪崩效应)

    示例代码:

    Redis 获取数据
    public static String get(String key) {
          Jedis jedis = null;
          int redisIndex = RedisUtils.getRedisIndex(key);
          try {
             jedis = getRedis(redisIndex);
             jedis.select(1);
             // 缓存过期  && 获取锁成功
       // setnx:原子操作,如果不存在则设置值,并返回1。如果缓存存在,则返回0,设置缓存失败
             if(jedis.setnx("lock_"+key, System.currentTimeMillis()+"")==1){
                /**
                 *  将锁的有效时间设为60s,在60s内如果查询数据库成功,则更新该锁的失效时间=缓存时间。
                 *  如果60s内出现异常,则60s后第一个请求又会去访问数据库...
                 *  返回null表示没有查询到数据库,外层代码会通过数据库获取数据
                 */
                jedis.expire("lock_"+key, 60);
                return null;
             }
             // (缓存未过期) || (缓存过期,但是获取锁失败) then 返回旧的数据
             else{
                jedis.select(0);
                return jedis.get(key);
             }
          } catch (JedisException e) {
             if (jedis != null) {
                returnBrokenResource(jedis, redisIndex);
                // 避免在finally中再次关闭reids
                jedis = null;
             }
             throw e;
          } finally {
             if (jedis != null) {
                returnResource(jedis, redisIndex);
             }
          }
       }
     
    Redis 缓存数据
    public static boolean set(String key, String value, int seconds) {
          Jedis jedis = null;
          int redisIndex = RedisUtils.getRedisIndex(key);
          try {
             jedis = getRedis(redisIndex);
             if (seconds > 0){
                // 添加缓存,缓存有效时间=真实时间+1天
                jedis.setex(key, seconds + 60 * 60 *24 , value);
                jedis.select(1);
                // 添加缓存锁,有效时间 =真实时间
                jedis.setex("lock_" + key, seconds, System.currentTimeMillis()+"");
             }else{
                jedis.set(key, value);
                jedis.select(1);
                jedis.set("lock_" + key, System.currentTimeMillis()+"");
             }
             return true;
          } catch (JedisException e) {
             if (jedis != null) {
                returnBrokenResource(jedis, redisIndex);
                // 避免在finally中再次关闭reids
                jedis = null;
             }
             throw e;
          } finally {
             if (jedis != null) {
                returnResource(jedis, redisIndex);
             }
          }
       }
  • 相关阅读:
    正则获取HTML代码中img的src地址
    System.Diagnostics.Process 启动进程资源或调用外部的命令的使用
    按位取反运算符~
    Nhibernate Query By Criteria 条件查询
    Unit Test测试框架中的测试的执行顺序
    Jquery自定义插件之$.extend()、$.fn和$.fn.extend()
    如何采集QQ群中所有成员QQ号码
    Sql server使用Merge关键字做插入或更新操作
    c#类库和可移值类库的区别
    VS代码管理插件AnkhSvn
  • 原文地址:https://www.cnblogs.com/stupidMartian/p/6591099.html
Copyright © 2011-2022 走看看