您现在的位置是:首页 > 文章详情

Redis有的值能存有的值不能存、Jedis不好使了?

日期:2018-11-28点击:842

问题来了


有的值可以存进去

有的值存不进去

是不是redis坏了?是不是Jedis 客户端不好使了。


本地测试一下:


同样的redis.conf 文件(window 和 centos) 

centos 是线上 

windows是本地测试

@Test public void test03(){     Jedis jedis = JedisUtil.getInstance().getJedis();     jedis.hset(Constants.GOODS_KANGO_AMOUNT,"wxg_1803271041153448","1");     jedis.hincrBy(Constants.GOODS_KANGO_AMOUNT,"wxg_1803271041153449",1L);     JedisUtil.returnBrokenResource(jedis); }

结果:redis 看到了这个值。存进去了


为什么 在线上 存不进去呢 ?


解决思路


就是想看下 客户端到底干了什么

./redis-cli #输入你的密码(如果需要) auth **** #开启对客户端的监控  monitor

于是再次测试(真是逻辑。不是上面的testcase) 


images/wKtY64sN5QPKjj2JEnkt5wyDs8Ha8EH2.png


可以看到, 我这里是很明显 执行了 我需要的所有的 命令 

但是不知道 从哪里冒出来了一个 

FLUSHDB 

FLUSHALL 

这个是在select 1 (redis 切换 database 1)的时候 紧跟着调用的 ( redis 是串行的)


那么好的 我为什么要切换database ? 切换成 1 

我想到了我用redis的地方


一些频繁操作,放redis 减少DB压力

一些数据查询,用redis 做mybatis 二级缓存。


就只有这两个地方了。 

查了下mybatis的配置,果真用的是 database 1 


images/tAAkHkzi4ZbkmYjHrFScAdmxXNwEbhci.png


那么好的 我需要看下源码了。为什么会执行 

flushdb 

flushall 

这两个命令呢? 

如果你要自定义你的mybatis二级缓存 

你需要实现这个接口


package org.apache.ibatis.cache; import java.util.concurrent.locks.ReadWriteLock; public interface Cache {   String getId();   int getSize();   void putObject(Object key, Object value);   Object getObject(Object key);   Object removeObject(Object key);   void clear();   ReadWriteLock getReadWriteLock(); }


我的代码是这样实现的(这个也是一个网上找的类) 

大家着重看下 clear() 方法 

这就是造成此次问题的罪魁祸首了


public class RedisCache implements Cache {     private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);     private static JedisConnectionFactory jedisConnectionFactory;     private final String id;     /**      * The {@code ReadWriteLock}.      */     private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();     public RedisCache(final String id) {         if (id == null) {             throw new IllegalArgumentException("Cache instances require an ID");         }         logger.debug("MybatisRedisCache:id=" + id);         this.id = id;     }     @Override     public void clear() {         JedisConnection connection = null;         try {             connection = jedisConnectionFactory.getConnection();             connection.flushDb();             connection.flushAll();         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }     }     @Override     public String getId() {         return this.id;     }     @Override     public Object getObject(Object key) {         Object result = null;         JedisConnection connection = null;         try {             connection = jedisConnectionFactory.getConnection();             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();             result = serializer.deserialize(connection.get(serializer.serialize(key)));         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }         return result;     }     @Override     public ReadWriteLock getReadWriteLock() {         return this.readWriteLock;     }     @Override     public int getSize() {         int result = 0;         JedisConnection connection = null;         try {             connection = jedisConnectionFactory.getConnection();             result = Integer.valueOf(connection.dbSize().toString());         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }         return result;     }     @Override     public void putObject(Object key, Object value) {         JedisConnection connection = null;         try {             connection = jedisConnectionFactory.getConnection();             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();             connection.set(serializer.serialize(key), serializer.serialize(value));         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }     }     @Override     public Object removeObject(Object key) {         JedisConnection connection = null;         Object result = null;         try {             connection = jedisConnectionFactory.getConnection();             RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();             result = connection.expire(serializer.serialize(key), 0);         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }         return result;     }     public static void setJedisConnectionFactory(JedisConnectionFactory jedisConnectionFactory) {         RedisCache.jedisConnectionFactory = jedisConnectionFactory;     } }


那么什么时候clear()会被调用呢 ? 

查看了一下 他的调用(全局搜索的) 


images/yTAbydbdzXwr42bFZmE4M4DjS85bGajj.png


看到这些 好熟悉,这些不就是 mybatis 对应二级缓存的一些处理策略嘛 

fifo 

lru 

… 

也就是说。我们的实现类 cn.bywind46.pc.common.RedisCache 

会最终 被引入到这些 策略 中 然后执行我们的重写方法 

这里面也包括了 clear();


我在一个mapper文件中配置,当前文件所有select都是用缓存 

配置如下 

现在明白了


缓存清理:LRU

失效时间:86400000 (毫秒)

缓存容量:2048

<cache type="cn.bywind46.pc.common.RedisCache"            eviction="LRU"            flushInterval="86400000"            size="2048"            readOnly="false"/>

OK 这很好理解了 

不是时间到了 

就是size到了呗 

但是不对啊。时间是一天已清理。还没有到时间呢 

size 我才存了几个SQL语句,不到2048个对象引用数啊。


那这是为什么呢? 

继续找了一下clear()方法的调用发现了这个类 

他也是调用了clear方法,在什么时候调用的呢?在事务提交的时候。 

这就很好理解了,因为有事务的时候,通常是执行 写操作(insert 、update、delete) 

这些时候回清缓存。了解了。回头看下代码,确实是在存入redis之前 我先做了一次update 和 insert DB 操作。 


images/xyZdzRjCSXQAxxB8A8ZzX2yAwB2N5Anw.png


本地DEBUG看了下调用链路 

(通过这个过程,也知道了,会有那些类参与这个过程(DB写操作,事务提交,缓存清理)) 

这一路的设计如下 


images/d5yD3AmmhSmEk2Kt3e2Tk6EZkyDM8jdE.png


好的到这里总算是找到思路了


怎么做


找到RedisCache这个类,改在下clear()方法 

很简单,直接注释掉 connection.flushAll() 

其他的保持不变 

mybatis的缓存策略还是按照他自己的来 

其他的缓存 我们放到 不同的库,这样大家都不会有干扰了

 @Override     public void clear() {         JedisConnection connection = null;         try {             connection = jedisConnectionFactory.getConnection();             connection.flushDb(); //            connection.flushAll();         } catch (JedisConnectionException e) {             e.printStackTrace();         } finally {             if (connection != null) {                 connection.close();             }         }     }

重新部署下 

看看结果 

images/tF7WRt4DnDxnzDZNkWaKK46ZkKT56GQM.png

存储了下来。问题得以解决了


总结下


第一步查找日志或者监控

源码阅读

测试对比

断点调试,找到调用链路中可能出现问题的地方



相关推荐:https://www.roncoo.com/course/list.html?courseName=redis



原文链接:https://blog.roncoo.com/article/134033
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章