善待Redis里的数据--Unable to validate object

又是一篇关于姿势的文章,为什么是”又”呢?因为上个星期刚写完一篇关于Apache Commons Pool的正确使用姿势的文章,点击此处阅读。

Redis为我们提供便利的同时,我们也要善待里面的数据

Redis是我们数据的保管者,我们可以随时存随时取,大的小的,重要的不重要的,它都毫无怨言的帮我们保存着,甚至有些时候,我们变得很懒,存东西进去的时候顺便还贴张纸:“过了一个星期就帮我扔了吧”,对于这些,Redis也都默默的接受了(谁叫Antirez把redis设计的这么好呢)。

这次要写的就是关于这张留言纸的事。

Redis提供了一套“美好”的过期数据清理机制:

  • 主动过期: Redis对数据是惰性过期,当一个key到了过期时间,Redis也不会马上清理,但如果这个key过期后被再次访问,Redis就会主动将它清理掉
  • 被动过期: 如果过期的Key一直没被访问,Redis也不会一直把它放那不管,它会每秒10次的执行以下的清理工作:
    • 随机从所有带有过期时间的Key里取出20个
    • 如果发现有过期的,就清理
    • 如果这里有25%的Key都是过期的,就继续回到第一步再来一次

这套过期机制设计的很赞,可以这样理解:如果当前有超过1/4的Key过期的话,就不停地清理,直到只剩下1/4不到的Key是要过期的为止,然后就慢慢地随机抽查着清理。

现在我们再说说对待Redis数据的姿势问题。

先看一个现象,某段程序每天24小时的跑着,但时不时地出现拿不到连接的错误:

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at redis.clients.util.Pool.getResource(Pool.java:50)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:86)
               ...
    at sun.reflect.GeneratedMethodAccessor54.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
    at com.dianping.cat.annotation.CatTransaction.doServerAround(CatTransaction.java:70)
    at sun.reflect.GeneratedMethodAccessor52.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
    at com.sun.proxy.$Proxy102.getRecommendedMediaList(Unknown Source)
    at com.restful.MediaController.getMediaList(MediaController.java:139)
    at sun.reflect.GeneratedMethodAccessor53.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.filter.HttpHeaderValidator.doFilter(HttpHeaderValidator.java:198)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.filter.CorrelationFilter.doFilter(CorrelationFilter.java:40)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1756)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1715)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.util.NoSuchElementException: Unable to validate object
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:506)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
    at redis.clients.util.Pool.getResource(Pool.java:48)
    ... 82 more

这个错误是因为JedisPool拿到一个连接后,调用 jedis.ping() 方法没有得到正确的返回”pong”消息,而出现这个问题时,并不是traffic的高峰期,redis的cpu不高,于是程序员很快就怀疑到网络上,难道有”丢包”?

但跑了几天,发现出错的时间固定在某几个时间点(如晚上9点~10点),莫非是扫地阿姨每天定时在机房打扫卫生,碰着网线了?(有时候不得不佩服程序员的想象力)

然而在一个夜黑风高的晚上,程序员的脑子在这个时候一般是最清醒的,这时告警短信又来了,打开redis监控,像在唯品会上逛街一样,对着redis的各项监控数据逛啊逛,突然,看到有几条Expires的数据,当时的表情只能用看到关注了几天的商品上标了大大”0.1折”时的表情来形容。

接下来的事情,想必大家也都猜到个七七八八了,当然是下单抢购支付了,脑子里一系列的运算,掐指一算: “这个时间点正好有一大批key过期”,而且都是大的Set集合,每个都有几十万的数据,再一算: “Redis里的大部分需要过期的key就是这些key”。

于是,答案有了,Redis进行被动过期清理时,发现怎么随机,都有超过1/4的Key都是过期的,就忙着不停的删啊删,再加上又都是大集合的删除,O(N)的操作复杂度,无奈,等redis删完时,客户端那边的命令都等超时了

原因找到了,如何解决?看业务场景吧,对于我们的场景,做法是将过期时间设长一点,然后把这些可以删掉的Key标记一下,丢到一个后台线程那里分页删Set里的数据,这样就算redis再做过期操作,也不会用太多的时间来删除。

最后,稍稍总结下,对于大个的集合对象,放redis的时候需要额外注意,如果想依赖redis的过期,可能会造成过期时短暂的redis block

所以,要善待redis数据,比如不用了就自己清理掉,不要等着redis来帮你。

 

http://neway6655.github.io/redis/2015/12/19/%E5%96%84%E5%BE%85Redis%E9%87%8C%E7%9A%84%E6%95%B0%E6%8D%AE.html

 

时间: 2024-09-18 00:16:03

善待Redis里的数据--Unable to validate object的相关文章

利用redis缓存热门数据,分页的一种思路

普通分页 一般分页做缓存都是直接查找出来,按页放到缓存里,但是这种缓存方式有很多缺点. 如缓存不能及时更新,一旦数据有变化,所有的之前的分页缓存都失效了. 比如像微博这样的场景,微博下面现在有一个顶次数的排序.这个用传统的分页方式很难应对. 一种思路 最近想到了另一种思路. 数据以ID为key缓存到Redis里: 把数据ID和排序打分存到Redis的skip list,即zset里: 当查找数据时,先从Redis里的skip list取出对应的分页数据,得到ID列表. 用multi get从re

添加到redis里的key乱码

问题描述 添加到redis里的key乱码 使用spring-data-redis往redis中添加数据,按照官方文档要求注入redistemplate模板, @Autowired private RedisTemplate<String, String> redisTemplate = null; 此时调用不会有问题, public void put(String key, String hashKey, String value) { redisTemplate.opsForHash().p

asp.net错误解决:Unable to Validate Data in ASP.NET website

原文:http://www.codeproject.com/Articles/43637/Weird-Error-Unable-to-Validate-Data-in-ASP-NET-web Have you ever come across a situation where your website which was working for the last couple of months gives a weird error"Unable to Validate Data"

请问Java Swing编程里怎样将一个Image里的数据复制到另一个Image里?

问题描述 请问Java Swing编程里怎样将一个Image里的数据复制到另一个Image里? 请问如何将一个Image里的所有数据复制到另一个Image(比如Image2)里?让Image里的数据与Image2里的数据相同,对Image的数据进行操作时,对Image2无影响 解决方案 http://www.iteye.com/problems/80309

ibatis 数据库-ibatis如何快速的在千万级以上的数据里检索数据

问题描述 ibatis如何快速的在千万级以上的数据里检索数据 数据库中的数据是千万级以上的,就是一般的操作日志,一共有40种操作类型,其余的都是一些操作时间和操作的描述,用ibatis查询的时候基本不可用,页面检索需要很长很长的时间,请问有什么办法能做到快速的检索吗

asp.net 点击右侧菜单,左侧表格里的数据根据点击的菜单节点显示相应的内容,菜单不需要动态的

问题描述 asp.net 点击右侧菜单,左侧表格里的数据根据点击的菜单节点显示相应的内容,菜单不需要动态的 asp.net 点击右侧菜单,左侧表格里的数据根据点击的菜单节点显示相应的内容,表格里的数据是从数据库里获取的,菜单不需要动态的 解决方案 数据都是从数据库里搜索出来,通过前段的操作,动态生成sql语句,搜索出要的数据 解决方案二: 一个简单的的方式是用iframe的方式 1.显示左侧菜单,参考代码如下: 前台: 要显示的菜单名称 解决方案三: 要显示的菜单名称/asp:LinkButto

事件-DevExpress GridView里添加行数据方法写在grid_RowInserting

问题描述 DevExpress GridView里添加行数据方法写在grid_RowInserting DevExpress GridView里添加行数据方法写在grid_RowInserting事件中,语法格式怎么写! 解决方案 http://down.51cto.com/data/246384

jsp-怎么在action里获取到JSP里的数据

问题描述 怎么在action里获取到JSP里的数据 如题 这些数据包括select radio text的,中间经过struts,求大神指导 解决方案 Struts2 action里获取ip地址 解决方案二: struts1~还是2. 2有OHNL表达式,可直接封装到对象.属性就好.1的话,可以传参,在action类里写private String xx生成get.set方法就可以使用该属性. 解决方案三: 在将表单提交给action之后通过action对应的form(struts1.x)或ac

统计图-怎样把数据库里的数据用图表显示在手机上

问题描述 怎样把数据库里的数据用图表显示在手机上 我们公司有一些业务数据,希望能够通过简单一点的实现方式,对数据进行汇总统计,然后用图表显示出来,最好在手机上也可以查看.想问一下需要做些什么?实现起来难不难? 解决方案 最简单的就是直接看网页,第二种是做一个APP 解决方案二: echarts用的canvas绘图,移动端浏览器主流浏览器都支持canvas 解决方案三: 这种当然是通过web页面了,服务器提供.然后用各种图标控件,比如百度的echarts可以很好的画出各种图形.客户端只要有浏览器就