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);
             }
          }
       }
  • 相关阅读:
    ABAP 程序中的类 沧海
    ABAP类的方法(转载) 沧海
    More than 100 ABAP Interview Faq's(2) 沧海
    SAP and ABAP Memory总结 沧海
    ABAP Frequently Asked Question 沧海
    ABAP System Reports(Additional functions) 沧海
    ABAP Questions Commonly Asked 1 沧海
    ABAP Tips and Tricks 沧海
    ABAP System Fields 沧海
    ABAP 面试问题及答案(一):数据库更新及更改 SAP Standard (转) 沧海
  • 原文地址:https://www.cnblogs.com/stupidMartian/p/6591099.html
Copyright © 2011-2022 走看看