`

redis被动缓存

 
阅读更多
package com.pingan.haofang.agent.saas.util.cache.redis;

import com.pingan.haofang.agent.saas.common.utils.Log;
import com.pingan.haofang.agent.saas.common.utils.RedisUtils;
import org.apache.log4j.Logger;
import org.springframework.data.redis.core.RedisTemplate;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

/**
 * redis缓存
 *
 */
public class RedisCache<K, V> {
    private static final Logger log = Logger.getLogger(RedisCache.class);

    private static final ExecutorService executor = Executors.newCachedThreadPool();

    /**
     * 对Null类型的值,用户希望设定的过期时间 ,时间单位为秒
     */
    private long nullValueExpiredTime = 10;

    /** 对于正常的缓存值,用户希望设定的过期时间,时间单位为秒 */
    private long valueExpiredTime = 24 * 60 * 60;

    /**
     * 对于通过getDataByKey接口获取数据是报错的情况,用户希望Null在本地缓存的过期时间,时间单位为秒
     */
    private long errorValueExpiredTime = 10;


    /**
     * RedisCacheDataCallBack的实现,在创建一个DaemonCache,强制要求用户实现
     */
    private RedisCacheDataCallBack<K, V> callback;

    private RedisTemplate<String, V> redisTemplate;

    private RedisUtils redisUtils;

    //private RedisUtils<K,V> redisUtils;

    /**
     * 需要传入RedisCacheDataCallBack构造
     */
    public RedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack, RedisTemplate<String, V> redisTemplate) {
        if (redisCacheDataCallBack == null) {
            throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
        }
        this.callback = redisCacheDataCallBack;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 需要传入RedisCacheDataCallBack构造
     */
    public RedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack, RedisUtils redisUtils) {
        if (redisCacheDataCallBack == null) {
            throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
        }
        this.callback = redisCacheDataCallBack;
        this.redisUtils = redisUtils;
    }

    /**
     * 创建RedisCache实例 ,默认本地缓存大小为10万
     *
     * @param nullValueExpiredTime  空数据过期时间
     * @param valueExpiredTime      正常时间期望过期时间
     * @param errorValueExpiredTime 数据接口查询错误过期时间
     */
    public RedisCache(long valueExpiredTime, long nullValueExpiredTime, long errorValueExpiredTime) {
        this.valueExpiredTime = valueExpiredTime;
        this.nullValueExpiredTime = nullValueExpiredTime;
        this.errorValueExpiredTime = errorValueExpiredTime;
    }

    /**
     * 创建RedisCache实例 ,带有数据层逻辑回调实现,默认本地缓存大小为10万
     *
     * @param valueExpiredTime      空数据过期时间 ,时间单位为"秒"
     * @param nullValueExpiredTime  正常时间期望过期时间,时间单位为"秒"
     * @param errorValueExpiredTime 数据接口查询错误过期时间,时间单位为"秒"
     * @param callback              数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
     *                              例子:
     *                              RedisCache localCache = new RedisCache<Integer, List<Item>>(
     *                              60,//nullValueExpiredTime 空数据过期时间
     *                              60 * 60,//valueExpiredTime 正常时间期望过期时间
     *                              60,//errorValueExpiredTime 数据接口查询错误过期时间
     *                              new RedisCacheDataCallBack<Integer, List<Item>>() {
     * @Override public List<Item> getDataByKey(Integer key) {
     * return getTopItemsImpl(key); //这里为业务逻辑实现
     * }
     * }
     * );
     */
    public RedisCache(long valueExpiredTime, long nullValueExpiredTime,
                      long errorValueExpiredTime, RedisCacheDataCallBack<K, V> callback) {
        if (callback == null) {
            throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
        }
        this.callback = callback;
        this.nullValueExpiredTime = nullValueExpiredTime;
        this.valueExpiredTime = valueExpiredTime;
        this.errorValueExpiredTime = errorValueExpiredTime;
    }


    /**
     * 获取默认的RedisCache,过期时间默认为都为10s , 默认本地缓存大小为10万
     *
     * @param redisCacheDataCallBack 数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
     * @return RedisCache 默认的实例
     */
    public RedisCache<K, V> getDefaultRedisCache(RedisCacheDataCallBack<K, V> redisCacheDataCallBack) {
        if (redisCacheDataCallBack == null) {
            throw new IllegalArgumentException("when create a new RedisCache, please implmement the RedisCacheDataCallBack first!");
        }
        this.callback = redisCacheDataCallBack;
        return new RedisCache<K, V>(nullValueExpiredTime, valueExpiredTime, errorValueExpiredTime, redisCacheDataCallBack);
    }

    /**
     * 通过key从redis中获取对应的Value l
     *
     * @param key <K>
     * @return V value
     */
    public V get(String key, K k) {
       return get(key,k,valueExpiredTime);
    }

    public V get(String key, K k, long expireTime) {
        V result = (V) redisUtils.get(key);
        if (result != null) {
            return result;
        }

        // -> 没有数据,或者为NULL(NULL数据已经过期)
        log.debug("没有数据,或者为NULL(NULL数据已经过期)");
        try {
            // 先调用数据查询接口(下层Memcache和DB的查询逻辑)
            V callBackResult = callback.getRedisDataByKey(k);
            if (callBackResult == null) {
                // 没有数据
                log.debug("5-无数据");
                put(key, callBackResult, this.nullValueExpiredTime);
                return null;
            } else {
                // 有数据,填充数据并设定时间(业务数据过期时间)
                log.debug("6-有数据,填充数据并设定时间(业务数据过期时间)");
                put(key, callBackResult, expireTime);
                return callBackResult;
            }
        } catch (RedisCacheDataCallBackException e) {
            e.printStackTrace();
            // 数据查询报错,向Cache中回写Null,并设定过期时间(这里的时间可能偏短)
            log.debug("7-数据查询报错");
            putNULL(key, this.nullValueExpiredTime);
            return null;
        }
    }

    /**
     * 向localcache中放入Null值
     *
     * @param key         cache的Key
     * @param expiredTime Null 值的过期时间
     */
    private void putNULL(String key, long expiredTime) {
        redisTemplate.opsForValue().set(key, null, expiredTime);
    }

    /**
     * 向Cache中放入key和Value,需要指定过期时间
     *
     * @param key
     * @param value
     * @param expiredTime
     */
    private void put(String key, V value, long expiredTime) {

        redisUtils.set(key, value, expiredTime);

    }

    /**
     * 注入对应的业务回调实例
     *
     * @param callback 数据层逻辑回调实现,实现自RedisCacheDataCallBack<K, V>接口
     */
    public void setCallback(RedisCacheDataCallBack<K, V> callback) {
        if (callback == null) {
            throw new IllegalArgumentException(
                    "the callback is null here , please check !");
        }
        this.callback = callback;
    }

    /**
     *
     * @param key
     * @param value
     * @param expiredTime
     */
    public int updateDbAndRedis(String key, V value, long expiredTime) {

        //先存db,再删缓存
        int result = callback.updateDbFromRedis(value);
        redisUtils.delete(key);

        //思考:为什么后通知,先更新DB,后更新缓存
//1.先更新DB,后删缓存:
//正常思考:并发查询时,查出的是redis的旧值,产生了1次脏读,但减少对DB的冲击。
//深度思考:此脏读是一时的旧数据,因随后会删redis,代价小,1次脏读。
//2.先删缓存,后更新DB:
//正常思考:并发查询时,因没有缓存,去数据库查询,对其冲击大。好处是可以查到最新的数据,但!
//深度思考:但查到实际不是最新的数据,因为还没有更新DB,会把不是最新的数据会放到redis里,导致长时间脏读,概率大,代价大。


//        FutureTask future = new FutureTask(() -> {
//
//            //推缓存
//            Log.info("redis异步更新数据库", "key:{} data:{}", key, value);
//            if (!redisUtils.hasKey(key)) {
//                try {
//
//                    Field idField;
//                    PropertyDescriptor pd;
//
//                    if (value.getClass().getName().endsWith("WithBLOBs")) {
//                        idField = value.getClass().getSuperclass().getDeclaredField("id");
//                        pd = new PropertyDescriptor(idField.getName(), value.getClass().getSuperclass());
//                    } else {
//                        idField = value.getClass().getDeclaredField("id");
//                        pd = new PropertyDescriptor(idField.getName(), value.getClass());
//                    }
//
//                    Method getMethod = pd.getReadMethod();
//                    Long id = (Long) getMethod.invoke(value);
//
//                    if (id == null) {
//                        return null;
//                    }
//                    V v = callback.getById(id);
//                    put(key, v, expiredTime);
//                } catch (NoSuchFieldException e) {
//                    log.error("id 不存在! 或者 调用 getById 失败", e);
//                } catch (IllegalAccessException e) {
//                    log.error("redis异步更新数据库", e);
//                } catch (Exception e) {
//                    log.error("redis异步更新数据库", e);
//                }
//            }
//            return null;
//        });
//        executor.execute(future);
        return result;
    }

    /**
     *
     * @param key
     * @param value
     */
    public int updateDbAndRedis(String key, V value) {
        return this.updateDbAndRedis(key,value,valueExpiredTime);
    }

    public int insertDbAndRedis(String cacheConsants,V value, long expiredTime){
        int primaryKey = callback.insertDbFromRedis(value);

//        FutureTask<Void> future = new FutureTask(new Callable<Void>() {
//            public Void call() {
//                //推缓存
//                Log.info("redis异步更新数据库","{key}:{data}",cacheConsants + primaryKey, value);
//                if (!redisUtils.hasKey(cacheConsants + primaryKey)) {
//                    put(cacheConsants + primaryKey, value, expiredTime);
//                }
//                return null;
//            }
//        });
//        executor.execute(future);
        return primaryKey;

    }

    public int insertDbAndRedis(String key,V value){
        return this.insertDbAndRedis(key,value,valueExpiredTime);
    }

    public static void main(String[] args) {
        System.out.println("1");
        FutureTask<Void> future = new FutureTask(new Callable<Void>() {
            public Void call() {
                //Thread.sleep(5);
                System.out.println("线程执行");
                return null;
            }
        });
        executor.execute(future);
        System.out.println("2");
    }
}





分享到:
评论

相关推荐

    缓存使用场景,介绍redis/内存等缓存的使用场景,更新策略等

    被动刷新是比较常用的刷新方式,存入缓存的数据会被设置一个过期时间。查询请求进来时,当缓存中有数据时则从缓存取,缓存中没有数据时,则从数据库查询数据,并更新至缓存。但是,需要注意从数据库查询的逻辑需要...

    Redis使用-缓存穿透,雪崩,击穿以及解决方案分析.docx

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。...

    window手动操作清理redis缓存的技巧总结

    缓存穿透是指查询一个缓存和数据库中都没有的数据,由于大部分缓存策略是被动加载的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的...

    Redis硬核性能优化

    Redis作为一款高性能的键值存储数据库,广泛应用于缓存和数据存储场景,但其性能受到多种因素影响。 一、Redis变慢的原因: 1. 复杂度过高的命令:执行时间较长的操作会导致阻塞其他客户端的请求。 2. 操作bigkey:...

    redis面试题及答案

    Redis,全称Remote Dictionary Server,是一款高性能的键值存储系统,常被用于缓存、消息队列等场景。在Java开发中,Redis是常见的数据存储和高速访问解决方案。以下是一些关于Redis的面试题及其答案,帮助你深入...

    如何设计缓存系统:缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。...

    常见的缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx

    缓存穿透是指查询一个确定不存在的数据,由于缓存是不命中时被动写入的,并且为了容错,如果从存储层查不到数据,则不会写入缓存。这样一来,该不存在的数据每次请求都会到达存储层,导致失去缓存的意义。特别是在高...

    基于云计算的分布式缓存.pptx

    - 被动失效策略则是在缓存空间不足时采用LRU算法来淘汰数据。 - 基于时间戳的失效策略则是为每个缓存条目设置一个有效期,一旦过期则自动删除。 - **缓存命中率优化**: - 缓存命中率是指缓存中数据被有效利用的...

    缓存穿透,缓存击穿,缓存雪崩解决方案分析.docx

    缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。...

    Redis过期-淘汰机制的解析和内存占用过高的解决方案.docx

    被动淘汰是指Redis 数据占满了内存,这个时候就会将过期的数据或者很久没有访问的数据删除掉。 定时过期是指每个设置过期时间的 key 都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据...

    大促多级缓存方案及库存并发操作方案.docx

    这一方案主要结合了本地缓存和分布式缓存,如Redis,以提高系统的响应速度和处理能力。在这种场景下,读操作远超写操作,尤其是对于热卖商品,本地缓存能有效缓解分布式缓存的压力,防止因热点数据导致的单点瓶颈。 ...

    java面试 分布式 集群 mongodb redis

    - **心跳机制**:客户端定期向服务端发送心跳包,由服务端被动接收并响应;减轻服务端负担,提高灵活性。 #### Java模板引擎对比:Velocity与FreeMarker 在Java开发中,模板引擎是非常重要的工具之一,主要用于将...

    X系统高可用&高并发解决方案.pptx

    在有限的物理资源下,如果实现服务的高可用性与稳定性。主要解决如下一些问题: ...解决数据库主从延迟,数据库与redis缓存,跨系统&跨数据库,本地缓存与redis缓存数据,同库多表冗余数据等问题;实现数据最终一致性。

    redis数据库查找key在内存中的位置的方法

    Redis 是一个广泛使用的开源键值存储系统,常用于缓存和数据持久化。要查找 Redis 数据库中的某个 key 在内存中的位置,首先需要理解 Redis 的内部数据结构和工作原理。 1. **Redis 数据库结构**: Redis 中的每个...

    一种新的内存对象缓存系统预取策略

    CBase策略被实现在了Redis系统中,Redis是一个开源的内存数据结构存储系统,用作数据库、缓存和消息代理。实验结果显示,CBase能够有效提升内存对象缓存系统的命中率,同时整体上增加系统吞吐量,并且没有明显的系统...

    interprocess-push-stream:通过Redis在进程之间使用的流式推送接口

    Redis是一个开源的、基于键值存储的数据结构服务器,常被用作数据库、缓存和消息代理。在"interprocess-push-stream"中,Redis被用来作为进程间流事件的通道,允许一个进程发送数据流,而其他进程可以订阅并接收这些...

    Redis中主键失效的原理及实现机制剖析

    总的来说,Redis 的主键失效机制结合了主动和被动的删除策略,既保证了缓存的时效性,又尽可能减少了对系统资源的影响。开发者可以根据实际需求调整过期策略,如调整活跃删除的频率或调整过期策略的算法,以优化缓存...

    基于node的微博的设计与实现.doc

    koa2 框架、mysql 数据库、session 会话机制、EJS 后端模板引擎、CSS 样式表、Redis 缓存数据库等。 Node.js 微博系统的特点 基于 Node.js 的微博系统具有轻量型且易部署的特点,该系统适合于想要推出自己的微博...

    高并发服务器框架设计方案.docx

    缓存管理算法包括FIFO(先进先出)、LRU(最近最少使用)和LFU(最不经常使用)等,开源的NoSQL数据库如Redis和Memcached提供了成熟的缓存解决方案。 缓存服务器部署是另一个要考虑的因素。推荐将缓存服务器独立...

Global site tag (gtag.js) - Google Analytics