一次Redis生产事故,公司损失百万

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 一、前因公司有个核心项目redis的客户端一直是使用的jedis,后面技术负责人要求把jedis客户端替换成效能更高的lettuce客户端,同时使用spring框架自带的RedisTemplate类来操作redis。然而世事难料,就是这么一个简单的需求却让老师傅翻了船。。。

一、前因

公司有个核心项目redis的客户端一直是使用的jedis,后面技术负责人要求把jedis客户端替换成效能更高的lettuce客户端,同时使用spring框架自带的RedisTemplate类来操作redis。

然而世事难料,就是这么一个简单的需求却让老师傅翻了船。。。

二、事故预演

按照预设的结果,本次开发任务应该是非常轻松的:

  1. 将配置文件中jedis连接池的配置项平移替换成lettuce的;
  2. 把项目中jedis配置相关的代码删掉;
  3. 把使用到jedis的地方替换成redisTemplate。

伪代码

其他配置项不一一展示

spring.redis.jedis.pool.max-idle = 200
spring.redis.jedis.pool.min-idle = 10
spring.redis.jedis.pool.max-active = 200
spring.redis.jedis.pool.max-wait = 2000
复制代码

替换成

spring.redis.lettuce.pool.max-idle = 200
spring.redis.lettuce.pool.min-idle = 10
spring.redis.lettuce.pool.max-wait = 2000
spring.redis.lettuce.pool.max-active = 200
复制代码

业务代码也从jedis换成redisTemplate

jedis的伪代码:

/**
 * 设置商品库存到redis - jedis
 * @param goodId 商品id
 * @param count 库存量
 * @return
 */    
@PatchMapping("/storage/jedis")
public String setStorageByJedis(
    @RequestParam("goodId") String goodId,
    @RequestParam("count") String count) {
    Jedis jedis = getJedis();
    jedis.set("good:" + goodId, count);
    jedis.close();
    return "success";
}
复制代码

redisTemplate的伪代码:

/**
 * 设置商品库存到redis - redisTemplate
 * @param goodId 商品id
 * @param count 库存量
 * @return
 */
@PatchMapping("/storage")
public String setStorage(
    @RequestParam("goodId") String goodId,
    @RequestParam("count") String count) {
    redisTemplate.opsForValue().set("good:" + goodId, count);
    return "success";
}
复制代码

然而一切工作做完,信心满满的上线发布之后,却大面积的爆发了线上bug。属于严重的生产事故。

网络异常,图片无法展示
|

从错误日志中我们可以清晰的看到是因为String类型的数据无法转换成int类型,我心中出现了一个大大的问号:明明我存到redis的是可以转成数字类型的字符串呀?

原因分析

通过Redis-Desktop-Manager可视化工具查看数据

网络异常,图片无法展示
|

发现string类型的键值对value值多了一对双引号

纳尼!怎么用jedis的时候就没有,换成redisTemplate就有了?

经过一番代码检查,发现使用redisTemplate的过程中好像少了一个步骤:配置序列化 一般如果没有特殊配置或者要使用redis连接池,就只用在配置中心或者配置文件中加入

spring.redis.host = 172.0.0.1
spring.redis.port = 6379
spring.redis.password = 123456Copy to clipboardErrorCopied
复制代码

然后注入redisTemplate就可以使用了,非常简单。

然而RedisTemplate使用的默认序列化器是JDK自带的序列化器,看源码:

网络异常,图片无法展示
|

看RedisTemplate的类图

网络异常,图片无法展示
|

由于RedisTemplate继承了RedisAccessor,RedisAccessor实现了InitializingBean,所以在RedisTemplate类初始化完成后,可以重写afterPropertiesSet()方法,设置序列化器。

解决方案

写一个redis的配置类,重新设置序列化器。

@Configuration
@ConditionalOnClass(RedisOperations.class)
public class RedisTemplateAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(name="redisTemplate")
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate template=new RedisTemplate();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
复制代码

这里只针对redis的string类型配置StringRedisSerializer序列化器,大家可以根据项目实际需求增加Hash对象类型的配置。

Spring自带提供了多种序列化器,如下

网络异常,图片无法展示
|

也可以自定义序列化器,需要实现RedisSerializer接口,并重写serialize()和deserialize()方法。

为了方便演示,没有写全局的redis配置类,直接在接口中重置序列化器,伪代码如下:

@PatchMapping("/storage")
public String setStorage(
    @RequestParam("goodId") String goodId,
    @RequestParam("count") String count) {
    redisTemplate.setKeySerializer(new StringRedisSerializer()); // 重置redis string类型key的序列化器
    redisTemplate.setValueSerializer(new StringRedisSerializer()); // 重置redis string类型value的序列化器
    redisTemplate.opsForValue().set("good:" + goodId, count);
    return "success";
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3月前
|
存储 NoSQL 关系型数据库
redis.conf 7.0 配置和原理全解,生产王者必备
redis.conf 7.0 配置和原理全解,生产王者必备
126 0
|
5月前
|
存储 NoSQL 中间件
GitHub数据库榜单第一:Redis核心原理实践PDF,点赞已过百万+
Redis是互联网技术领域使用最为广泛的存储中间件,它是「Remote DictionaryService」的首字母缩写,也就是「远程字典服务」。Redis 以其超高的性能、完美的文档、简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。国内外很多大型互联网公司都在使用Redis, 比如Twitter、YouPom、暴雪娱乐、Github、StackOverflow、 腾讯、阿里、京东、华为、新浪微博等等,很多中小型公司也都有应用。也可以说,对Redis的了解和应用实践已成为当下中高级后端开发者绕不开的必备技能。
|
11月前
|
NoSQL Redis
redis生产排查和数据恢复
redis生产排查和数据恢复
82 0
|
缓存 运维 NoSQL
老大让我复盘上次Redis缓存雪崩事故
老大让我复盘上次Redis缓存雪崩事故
老大让我复盘上次Redis缓存雪崩事故
|
缓存 NoSQL 网络协议
阿粉巧用 Redis pipeline 命令,解决真实的生产问题
最近阿粉接到了一个业务需求,需要开发一个业务接口,批量删除 Redis 中数据。 这个功能点其实很简单,只要让外部传入需要删除键信息,然后在接口内部遍历调用删除命令即可。 按照这个思路,功能很快就开发完成,然后顺利的上线。 上线之后,运行一段时间,调用业务方反馈,当要删除的数据很多的时候,这个接口响应时间就比较长,然后希望我们这边优化一下,降低响应时间。 那优化办法其实有很多,比如使用多线程删除等,不过这一次并没有采用这个,最终使用了 Redis pipeline(管道)命令进行了优化。 所以今天这篇文章就给大家介绍一下 Redis pipeline 命令,以及
阿粉巧用 Redis pipeline 命令,解决真实的生产问题
|
存储 机器学习/深度学习 NoSQL
想在生产搞事情?那试试这些 Redis 命令
事情是这样的,前一段时间阿粉公司生产交易偶发报错,一番排查下来最终原因是因为 Redis 命令执行超时。 可是令人不解的是,生产交易仅仅使用 Redis set 这个简单命令,这个命令讲道理是不可能会执行这么慢。 那到底是什么导致这个问题那?
想在生产搞事情?那试试这些 Redis 命令
|
NoSQL Java Redis
Redis Cluster 宕机引发的事故(下)
Redis Cluster 宕机引发的事故(下)
225 0
Redis Cluster 宕机引发的事故(下)
|
NoSQL 算法 Java
Redis Cluster 宕机引发的事故(上)
Redis Cluster 宕机引发的事故(上)导读: Redis官方号称支持并发11万读操作,并发8万写操作。由于优异的性能和方便的操作,相信很多人都在项目中都使用了Redis,为了不让应用过分的依赖 Redis服务,Redis的作用只作为提升应用并发和降低应用响应时间存在,即使Redis出现异常,应用程序也不应该出现提供服务失败问题,对此拍拍信最近安排了一次全环境的Redis Cluster 宕机演练。 本文作者系拍拍信架构负责人朱荣松和拍拍信架构开发工程师许彬,授权“技术锁话”进行发布。
309 0
Redis Cluster 宕机引发的事故(上)
|
存储 NoSQL 固态存储
为什么 Redis 单线程能达到百万+QPS?
性能测试报告 查看了下阿里 Redis 的性能测试报告如下,能够达到数十万、百万级别的 QPS(暂时忽略阿里对 Redis 所做的优化),我们从 Redis 的设计和实现来分析一下 Redis 是怎么做的。
为什么 Redis 单线程能达到百万+QPS?
|
运维 NoSQL 安全
一个致命的 Redis 命令,导致公司损失 400 万!!
最近安全事故濒发啊,前几天发生了《顺丰高级运维工程师的删库事件》,今天又看到了 PHP 工程师在线执行了 Redis 危险命令导致某公司损失 400 万。。

热门文章

最新文章