redis取值错乱问题

152 阅读3分钟

事件过程

  1. 2023-04-28 01:30 被报警电话吵醒,看连连卫士报错为NumberFormatException,以为是用户乱传值导致的就没管
  2. 差不多隔十分钟会有一次电话报警,感觉不像用户导致的,于是02:00起床开电脑
  3. 看代码为从redis取值转int报错了,如图 image.png
  4. 是不是往redis设值的时候错了,看了下设值的代码的确是个数字 image.png
  5. 是不是key有什么特殊符号导致取到了别的key的值,查看日志由于手机号脱敏了所以也看不到准确的key值,打开注册页面发送验证码看滑块是否正常,试了几次都正常
  6. 静思了会只能往redis值错乱去排查了,于是百度搜索,搜到了类似的问题 image.png
  7. 02:50开始重启机器
  8. 重启后问题不再出现,由于是凌晨并且问题也解决了就没在大群里问是不是对redis做了什么变更,打算上班了再问
  9. 9:20在群里问是不是凌晨对redis做了变更,技术中心反馈做了热迁移,将凌晨的问题告知了技术中心,负责redis的同事凌晨在做redis热迁移得下午才上班
  10. 操作redis用的是spring的redisTemplate,然后看了下redisTemplate的初始化,怀疑是受事务属性的影响,于是百度搜了下发现redisTemplate的事务属性的确存在问题,于是告知了技术中心 www.dandelioncloud.cn/article/det…
  11. 14:30跟技术中心一起看redisTemplate源码,发现获取连接的时候如果设置了事务属性会将连接跟线程绑定,即这个线程始终用这个连接,释放连接的时候设置了事务属性不会将连接放回池里 image.png image.png 1.**猜测:**线程1使用连接1操作redis,redis热迁移的时候出现过慢响应,客户端读超时没将响应读取也没将这个异常连接关闭,线程1第二次使用连接1操作redis,读取了第一次的响应,导致值错乱 **验证:**设置事务属性同个线程请求redis十次,看是否为同个连接;不设置事务属性同个线程请求redis十次,看是否为同个连接 image.png

问题原因

设置了事务属性,操作完redis不会将连接放回连接池里,当连接出现读超时不会将连接关闭,第二次读取会读取到第一次响应

举一反三

  1. dubbo也是长连接,业务层基本不做请求和响应是否一致的校验,会不会出现请求和响应对不上的问题?

    答案是不会,dubbo每次发送报文前会创建个DefaultFuture,DefaultFuture会有个唯一标识,这个唯一标识会放到报文头里然后发送给服务端,收到响应报文会取报文头里的唯一标识找到DefaultFuture,将响应报文设置给DefaultFuture并将线程唤醒,所以请求和响应一定是对应的

  2. 平时关注的比较多的是redis服务端的线程模型,客户端基本不关注,服务端虽然是NIO方式启动的,但是客户端建立连接的时候用的是BIO方式,所以连接池是BIO长连接池。

    客户端线程执行步骤:

    1. 从连接池里获取连接

    2. 发送指令

    3. 读取响应(阻塞)

    4. 响应回来进行读取执行业务代码

      当读取响应(阻塞)超时又没将连接关闭,再次去该连接读取就会读取到上次的响应,故BIO长连接出现异常一定要将连接关闭,NIO长连接由于报文头里带了唯一标识没有关系