myBatis使用redis做二级缓存时需要注意的几个地方
最新在做的一个项目,由于读业务多,写业务不频繁,所以采用redis做了个二级缓存来提高读的效率,但是在整个过程中,遇到了一些比较容易犯,而且也很严重的错误。
环境:spring boot
redis
redis缓存突然全部丢失
由于原来redis缓存只应用在myBatis的缓存上,所以并没有即时发现这个问题,后来由于业务需要,额外在redis中主动缓存了一些需要处理一段时间才能计算出来的数据,才发现这个redis缓存在某些条件触发下会全部清空的问题。
具体导致这个问题的是由于实现org.apache.ibatis.cache.Cache
的配置类中的clear
方法不合适导致的。
在网上查到的很多Spring Boot + Mybatis + Redis做二级缓存的资料都是这样配置的:
上面这种写法,直接调用connection.flushDb();
会删除当前选定数据库的所有键,这也就导致很多不该被删除的缓存数据,都被删除了。很多像我一样的小白并没有深究这种写法,照抄下来,结果就会很悲剧了,而且发生之后还一脸懵逼,不知道哪儿出了问题。
那应该如何解决这个问题呢?我目前采用的是以下方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); String id = this.id;
redisTemplate.execute((RedisCallback) connection -> { String patten = "*:" + id + "*"; Set<byte[]> keys = connection.keys(patten.getBytes()); for (byte[] data : keys) { connection.del(data); } connection.close(); return null; }); logger.warn("Clear cached query result from redis"); }
|
这种方式就只会去删除与缓存实例ID相匹配的键的缓存数据。完整的配置类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public class MyBatisRedisCache implements Cache { private static Logger logger = LoggerFactory.getLogger(MyBatisRedisCache.class);
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final String id; private RedisTemplate redisTemplate; private static final long EXPIRE_TIME_IN_MINUTES = 72 * 60 * 60;
public MyBatisRedisCache(String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; }
@Override public String getId() { return id; }
@Override @SuppressWarnings("unchecked") public void putObject(Object key, Object value) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); opsForValue.set(key.toString(), value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); logger.debug("Put query result to redis"); }
@Override public Object getObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); ValueOperations opsForValue = redisTemplate.opsForValue(); logger.debug("Get cached query result from redis"); return opsForValue.get(key.toString()); }
@Override @SuppressWarnings("unchecked") public Object removeObject(Object key) { RedisTemplate redisTemplate = getRedisTemplate(); redisTemplate.delete(key.toString()); logger.debug("Remove cached query result from redis"); return null; }
@Override public void clear() { RedisTemplate redisTemplate = getRedisTemplate(); String id = this.id;
redisTemplate.execute((RedisCallback) connection -> { String patten = "*:" + id + "*"; Set<byte[]> keys = connection.keys(patten.getBytes()); for (byte[] data : keys) { connection.del(data); } connection.close(); return null; }); logger.warn("Clear cached query result from redis"); }
@Override public int getSize() { Long size = (Long) redisTemplate.execute((RedisCallback<Long>) connection -> connection.dbSize()); logger.info("cached count:" + size.intValue()); return size.intValue(); }
@Override public ReadWriteLock getReadWriteLock() { return readWriteLock; }
private RedisTemplate getRedisTemplate() { if (redisTemplate == null) { redisTemplate = (RedisTemplate) SpringContextUtil.getApplicationContext().getBean("redisTemplate"); } return redisTemplate; } }
|