[原创]【牛🐂🍺】使用Redis作为Mybatis的二级缓存MybatisCacheRedis

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: [原创]【牛🐂🍺】使用Redis作为Mybatis的二级缓存MybatisCacheRedis

看过市面上其他人写的相关的文章,基本上都是没有深入思考与实际使用的。

大都问题是clear()的时候将整个redisDB全部删除了,而没有做到只将单前某个mapper对应的缓存删除。

package com.west.lake.blog.foundation;
import com.west.lake.blog.model.RedisKeySet;
import com.west.lake.blog.model.SystemConfig;
import com.west.lake.blog.tools.SpringTools;
import org.apache.ibatis.cache.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * 使用redis作为mybatis的二级缓存
 * <p>
 * 规则:
 * 以mapper为单位进行存储,前缀为mybatis.${MapperDao}.****。
 * 所以删除的时候也以mapper为单位进行删除。
 * 对于删除:
 * 1.思路:先根据前缀通过keys操作获取keyList。==>因为redis是单线程的,所以不允许使用keys操作,会阻塞其他的线程获取redis操作。(可以考虑使用scan操作)
 * 2.思路:先将所有的mybatis二级缓存的key根据mapper进行分组存入redis的每个list中,删除的时候先从redis的list中获取对应mapper的所有的key
 *
 * @author futao
 * Created on 2019-03-06.
 */
@SuppressWarnings("unchecked")
public class MybatisCacheRedis implements Cache {
    private static final Logger logger = LoggerFactory.getLogger(MybatisCacheRedis.class);
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    /**
     * cache instance id
     */
    private final String id;
    private RedisTemplate redisTemplate;
    /**
     * redis过期时间
     */
    private static final long EXPIRE_TIME_IN_MINUTES = SystemConfig.MYBATIS_CACHE_REDIS_SECOND;
    public MybatisCacheRedis(String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }
    @Override
    public String getId() {
        return id;
    }
    /**
     * Put query result to redis
     *
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        if (key != null && value != null) {
            RedisTemplate redisTemplate = getRedisTemplate();
            String redisKey = RedisKeySet.Mybatis.mybatisSecondCacheMapperKey(id + ":" + key);
            //不要设置过期时间
            redisTemplate.opsForList().rightPush(RedisKeySet.Mybatis.mybatisSecondCacheListKey(id), redisKey);
            redisTemplate.opsForValue().set(redisKey, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.SECONDS);
            logger.info("\n<<< 结果插入redis缓存\n【key】{}\n【值】:{}", redisKey, value);
        }
    }
    /**
     * Get cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        RedisTemplate redisTemplate = getRedisTemplate();
        ValueOperations opsForValue = redisTemplate.opsForValue();
        String redisKey = RedisKeySet.Mybatis.mybatisSecondCacheMapperKey(id + ":" + key);
        Object result = opsForValue.get(redisKey);
        logger.info("\n<<< 从redis中查询缓存\n【key】{}\n【结果】:{}", redisKey, result);
        return result;
    }
    /**
     * Remove cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        RedisTemplate redisTemplate = getRedisTemplate();
        String redisKey = RedisKeySet.Mybatis.mybatisSecondCacheMapperKey(id + ":" + key);
        logger.info("\n<<< 从redis中移除缓存\n【key】{}", redisKey);
        //TODO(...)
        return redisTemplate.delete(redisKey);
    }
    /**
     * delete
     * Clears this cache instance
     */
    @Override
    public void clear() {
        RedisTemplate redisTemplate = getRedisTemplate();
//        redisTemplate.execute((RedisCallback) connection -> {
////            connection.flushDb();
////            return null;
////        });
        String key = RedisKeySet.Mybatis.mybatisSecondCacheListKey(id);
        //从mapper list中获取mapper keys
        List range = redisTemplate.opsForList().range(key, 0, -1);
        //删除mapper list
        range.add(key);
        //删除mapper kv
        Long delete = redisTemplate.delete(range);
        logger.info("\n<<< 删除redis中【{}】的缓存\n删除条数【{}】", key, delete);
    }
    @Override
    public int getSize() {
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
    private RedisTemplate getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = SpringTools.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
1天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
19天前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
77 10
|
21天前
|
缓存 NoSQL Serverless
函数计算产品使用问题之如何使用Redis作为缓存插件
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
5天前
|
存储 缓存 NoSQL
基于SpringBoot+Redis解决缓存与数据库一致性、缓存穿透、缓存雪崩、缓存击穿问题
这篇文章讨论了在使用SpringBoot和Redis时如何解决缓存与数据库一致性问题、缓存穿透、缓存雪崩和缓存击穿问题,并提供了相应的解决策略和示例代码。
18 0
|
5天前
|
缓存 NoSQL Ubuntu
如何在 Ubuntu 14.04 上配置 Redis 缓存以加速 WordPress
如何在 Ubuntu 14.04 上配置 Redis 缓存以加速 WordPress
12 0
|
23天前
|
缓存 NoSQL Java
Redis 缓存与数据库数据不一致问题
Redis 缓存与数据库数据不一致问题
55 3
|
1月前
|
canal 缓存 NoSQL
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;先删除缓存还是先修改数据库,双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
Redis常见面试题(一):Redis使用场景,缓存、分布式锁;缓存穿透、缓存击穿、缓存雪崩;双写一致,Canal,Redis持久化,数据过期策略,数据淘汰策略
|
23天前
|
存储 缓存 NoSQL
Redis 缓存常见问题
Redis 缓存常见问题
32 3