惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
Intezer
宝玉的分享
宝玉的分享
V
Visual Studio Blog
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
Stack Overflow Blog
Stack Overflow Blog
Vercel News
Vercel News
P
Proofpoint News Feed
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
博客园 - Franky
J
Java Code Geeks
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
D
Docker
IT之家
IT之家
小众软件
小众软件
M
MIT News - Artificial intelligence
Spread Privacy
Spread Privacy
雷峰网
雷峰网
C
CERT Recently Published Vulnerability Notes
N
News | PayPal Newsroom
量子位
The Last Watchdog
The Last Watchdog
The Register - Security
The Register - Security
PCI Perspectives
PCI Perspectives
罗磊的独立博客
S
Secure Thoughts
WordPress大学
WordPress大学
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog
T
Threatpost
The GitHub Blog
The GitHub Blog
博客园 - 叶小钗
U
Unit 42
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Help Net Security
Cloudbric
Cloudbric
G
Google Developers Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
人人都是产品经理
人人都是产品经理
H
Heimdal Security Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Last Week in AI
Last Week in AI
Jina AI
Jina AI
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
美团技术团队

博客园 - 青石路

记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来 Claude Code安装,接入阿里云百炼模型,蹭蹭免费额度 异源数据同步 → 记一次 DataX 已同步数据量优化 明明连接的是Redis的DB0,为什么能查到DB3的数据? TINYINT(1) 类型的字段,明明数据存的是 2,为什么查出来是 true 用了MySQL的INSERT ON DUPLICATE KEY UPDATE,怎么还报唯一索引冲突错误 那些年不该放到事务中的操作,你实现过哪些 关于布尔类型的变量不要加 is 前缀,被网友们吐槽了,特来完善下 都说了布尔类型的变量不要加 is 前缀,非要加,这不是坑我了嘛 安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题排查与处理 记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑 经由同个文件多次压缩的文件MD5都不一样问题排查,感慨AI的强大! 记一次cannot access its superinterface问题的的排查 → 强如Spring也一样写Bug SpringBoot支持Kafka多源配置的同时还要支持启停配置化,是真的会玩 如果XXL-JOB执行器在执行某任务中被重启了,重启后该任务能够被自动弥补调度吗 Spring Boot读取外部配置文件失败,原因绝对出乎你意料 不依赖 Spring,你会如何自实现 RabbitMQ 消息的消费(一) 异源数据同步 → DataX 同步启动后如何手动终止? 异源数据同步 → 如何获取 DataX 已同步数据量? 记一次 RabbitMQ 消费者莫名消失问题的排查 不升级 POI 版本,如何生成符合新版标准的Excel 2007文件 以MySQL为例,来看看maven-shade-plugin如何解决多版本驱动共存的问题? maven 插件之 maven-shade-plugin,解决同包同名 class 共存问题的神器
如果写Redis序列化与读Redis序列化不一致,你觉得会发生什么
青石路 · 2026-07-24 · via 博客园 - 青石路

开心一刻

昨天在我大哥家玩,嫂子在给小侄女泡奶粉

小侄女走到大嫂面前,大声的说了一句:妈妈,我爱你

大哥很期待的看着小侄女,然而小侄女只是一味的跟大嫂互动

大哥从沙发上起身,不开心的走进了卧室

大嫂看出大哥吃醋了,跟小侄女说到:你去把刚才给妈妈说的那句话也给爸爸说一遍

小侄女小跑进了卧室,趴着大哥的腿上说到:爸爸,我爱妈妈

杀人还要诛心

Redis 写读

Spring Boot 接入 Redis 非常简单,2 步搞定

  1. POM 添加依赖

    常用的方式有两种

    • 添加 spring-boot-starter-data-redis 依赖
    • 添加 redisson-spring-boot-starter 依赖

    如果用到分布式锁的话,推荐用 redisson-spring-boot-starter

    Redisson 分布式锁实现之源码篇 → 为什么推荐用 Redisson 客户端

  2. 配置连接

    在配置文件(一般是 application.yml 或 application.properties)中配置 Redis 的连接信息

    spring:
      redis:
        database: 0
        host: 192.168.1.225
        password: redis@qsl
        timeout: 1000
    

接入就算完成了;我们写读 Redis 试试

假设我们有 class

@Data
public class CallbackStatus implements Serializable {

    private static final long serialVersionUID = -1L;

    private String taskId;
    private String bizLogId;
    private String status;

    public CallbackStatus() {
    }

    public CallbackStatus(String taskId, String bizLogId, String status) {
        this.taskId = taskId;
        this.bizLogId = bizLogId;
        this.status = status;
    }
    
    @Override
    public String toString() {
        return "CallbackStatus{taskId='" + taskId + "', bizLogId='" + bizLogId + "', status='" + status + "'}";
    }
}

将该类对象写入 Redis

@Test
public void testSet() {
    CallbackStatus callbackStatus = new CallbackStatus(
        "1",
        "10000",
        "SUCCESS"
    );
    redisTemplate.opsForValue().set("qsl:callback-demo", callbackStatus);
}

写入成功后,Redis 中数据如下

写入Redis_demo

怎么是一串乱码?这是因为我们未配置数据序列化,RedisTemplate 默认用的 JdkSerializationRedisSerializer,所以可读性差

然后我们从 Redis 中读取该对象

@Test
public void testGet() {
    CallbackStatus callbackStatus = (CallbackStatus) redisTemplate.opsForValue().get("qsl:callback-demo");
    System.out.println(callbackStatus);
}

输出结果:CallbackStatus{taskId='1', bizLogId='10000', status='SUCCESS'}

是不是没问题?

写读序列化不一致

如果通过 Redis 实现服务间通信,上游服务将数据写入 Redis 时用的时默认序列化 JdkSerializationRedisSerializer

@Bean("jdkObjRedisTemplate")
public RedisTemplate<String, Object> jdkObjRedisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new JdkSerializationRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(new JdkSerializationRedisSerializer());
    return template;
}

而下游服务读取的时候用的序列化是 FastJsonRedisSerializer

@Bean("fastJsonRedisTemplate")
public RedisTemplate<String, Object> fastJsonRedisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(factory);

    // 添加连接验证
    template.setEnableTransactionSupport(false);

    StringRedisSerializer stringRedisSerializer = new
        StringRedisSerializer();

    // 配置 FastJson 序列化器
    FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);

    // 设置 FastJson 的配置(如果有需要)
    ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

    // key采用String的序列化方式
    template.setKeySerializer(stringRedisSerializer);
    // hash的key也采用String的序列化方式
    template.setHashKeySerializer(stringRedisSerializer);
    // value序列化方式采用jackson
    template.setValueSerializer(fastJsonRedisSerializer);
    // hash的value序列化方式采用jackson
    template.setHashValueSerializer(fastJsonRedisSerializer);
    template.afterPropertiesSet();
    return template;
}

毋庸置疑,通信会失败,但具体失败形式是什么?读取失败并抛出异常,还是其他形式?

我们试下就知道了,直接用 fastJsonRedisTemplate 去读 qsl:callback-demo

@Test
public void testFastGet() {
    CallbackStatus callbackStatus = (CallbackStatus) fastJsonRedisTemplate.opsForValue().get("qsl:callback-demo");
    System.out.println(callbackStatus);
}

执行正常,并未抛出异常,只是读取的结果是 null

jdk写fast读

问题不就来了?

明明 Redis 中 qsl:callback-demo 有值,为什么读取的是 null

我再往真实业务上靠一靠,用 Redis 实现消息队列,简化版是不是直接用 Redis 的 List 来实现?

上游服务从 List 右边写入数据

/**
 * 从 list 右边写入数据
 */
@Test
public void testWriteList() {
    CallbackStatus data = new CallbackStatus(
            "2",
            "20000",
            "failed"
    );
    jdkObjRedisTemplate.opsForList().rightPush("qsl:status:callback", data);
}

数据写入成功后,Redis 中数据如下

写入Redis_List数据

下游服务从 List 左边读取数据

/**
 * 从 list 的左边读取数据
 */
@Test
public void testReadList() {
    Object o = fastJsonRedisTemplate.opsForList().leftPop("qsl:status:callback");
    CallbackStatus callbackStatus = convertToCallbackStatus(o);
    
    // 实际项目代码中没有 System 这一行
    System.out.println(callbackStatus);
    
    if (callbackStatus != null) {
        // TODO 业务处理,并打印相关日志
    }
}

private CallbackStatus convertToCallbackStatus(Object obj) {
    if (obj == null) {
        return null;
    }

    try {
        if (obj instanceof CallbackStatus) {
            return (CallbackStatus) obj;
        }

        if (obj instanceof JSONObject) {
            return ((JSONObject) obj).toJavaObject(CallbackStatus.class);
        }

        if (obj instanceof Map) {
            String jsonStr = JSON.toJSONString(obj);
            return JSON.parseObject(jsonStr, CallbackStatus.class);
        }

        String jsonStr = JSON.toJSONString(obj);
        return JSON.parseObject(jsonStr, CallbackStatus.class);

    } catch (Exception e) {
        LOGGER.error("【工作流任务回调】===》对象转换失败: class={}", obj.getClass().getName(), e);
        return null;
    }
}

读取并未异常,但结果却是 null

读取Redis_List数据

同时,Redis 中 qsl:status:callback 已经没了

Redi中List数据已经没了

然后上游开发与下游开发就开启了 battle

下游开发:你没往 Redis 中写入状态数据吗

上游开发:你把你的服务停下,我跑下试试,看 Redis 中有没有状态数据

几分钟后,上游开发给下游开发截了个 Redis 数据图,并气冲冲的跑到他面前,说到:Redis 里面不是有数据吗

下游开发启动服务,并看了看代码,说到:该打的日志都打了,现在一行日志都没有输出,就是从 Redis 没读到数据嘛

上游开发:那就不知道了,反正我是往 Redis 中成功写入数据了

然后下游开发陷入了无尽的猜想中

有别的节点消费了?开发环境的节点我已经停了,应该只有我本地节点了

Redis List 中数据已经没了,说明已经被消费了,本地没有业务日志,也没有异常日志,到底谁消费了?

......

摸不着头脑

从源码中找根因

从 Redis List leftPop 数据,到底是没查到数据,还是查到了反序列化成 null 了,跟下源码就知道了

先执行 testWriteList() 往 List 中插入一条数据

然后打好断点

打断点

以 debug 方式启动 testReadList

具体细节就不给你们展示了,直接给你们看答案

源码寻找答案

堆栈信息从下往上看,从 Redis leftPop 到的数据是 byte[131],说明什么?说明查到数据了!!!

只是 FastJsonRedisSerializer 将其反序列化异常了

反序列化异常

可恶的是

fastjson 把异常吃了,没把异常往外抛,直接 return null

你怎么敢的呀

根因是不是就找到了?

通过 JdkSerializationRedisSerializer 把数据写入 Redis,读取的时候,FastJsonRedisSerializer 反序列化异常,但 fastjson 捕获了异常后直接返回 null

这个 null 并非指从 Redis 未查询到数据,而是读取到数据后反序列化异常,fastjson 返回的 null

不同的 Serializer 反序列化异常时所做的处理肯定是不一样的,FastJsonRedisSerializer 是捕获异常后什么也不做,只是返回 null,那 JdkSerializationRedisSerializer、Jackson2JsonRedisSerializer 等 Serializer 反序列化异常时,是不是也如 FastJsonRedisSerializer 一样呢,你们可以去试试

总结

  1. Redis 的写读序列化要保持一致,推荐用 json 序列化,可读性高

    json序列化包括 FastJsonRedisSerializer、Jackson2JsonRedisSerializer 等

  2. 捋清楚数据从 Redis 到程序,经历了哪些环节,逐个环节分析,而不是武断的省略中间环节

    Redis -> Redis 驱动 -> 反序列化器 -> 程序

  3. 从源码中找原因,有时候不仅治本,还很高效,不要畏惧它,结合 Debug 方式看源码,没那么难