30w+数据使用RedisTemplate pipeline空指针NullPointerException异常分析

 更新时间:2023年08月31日 09:10:57   作者:我不是码农  
这篇文章主要为大家介绍了30w+数据使用RedisTemplate pipeline空指针NullPointerException异常分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

为了实现布控预警的业务需求,在flink流式处理时查询redis进行比对,然后方案是放在redis里的数据格式为Set,key使用的直接是布控对象的值,比如身份证号、车牌号、手机号、imsi码等等,然后value的值为该布控对象所属的任务ID,之所以是这样设计,是因为可能有多个任务都设置了该布控对象,这时候比对到了该布控对象,会根据任务id查询对应的策略进行不同的业务处理。

目前一个任务的布控对象的数据量批量插入20w+的数据到redis中,数据量比较大,所以用到了pipe,在使用过程中发生了错误,发生了NullPointerException异常,进行了排查记录如下

首先debug代码

伪代码就是

redisTemplate.executePipeLined(connection -> {
    objectives.parallelStream().forEach(obj -> {
        connection.setCommands().sAdd(rawKey,rawValue);
    });
})

objectives是所有布控对象,这里我用到了并行流(后面我改成串行就没报错了,原因后面讲),代码是没啥问题的,然后报错地方是执行完上述逻辑后,执行connection.closePipeLine()里的result.getResultHolder()该result是null,发现了异常,为啥list元素里会是空的呢,原来lettuce的pipe是初始化一个arraylist进行存储的,而我外面是一个并行流,相当于是并发调用connection.setCommands().sAdd()->pipe.add(),是这里发生了并发操作list的错误导致的。

我自己写了一个测试用例,并行流调用arraylist.add,然后去遍历里面的元素,也是会报元素为空导致的NullPointerException

没想到啊,虽然lettuce底层是共享一个链接,使用的是netty的异步模型,不过也防止不了开发人员在外面是并发操作这个命令啊,lettuce是线程安全的库,但是这个LettuceConnection是spring这边实现的,结果来这一出...bug蛮多的,之前用spring-data-jpa那块分页也有点问题,模块太多了,依赖多个库之后配置多了可能spring的开发人员都弄不清关系了,给他们提了一个bug

https://github.com/spring-projects/spring-data-redis/issues/2653

调试分析

接上面,我在调试的时候,pipe里有些返回值error是command timed out after 10 seconds,这就是原因,执行redis命令超时了,为啥会超时呢,由于我使用的是Lettuce的redis客户端库(最后发现就是这个库的原因),我开始自己调试,不调试不要紧,一调试震惊了,我在上面的forEach循环里打断点,结果发现执行一个sAdd就会立即把值flush推到redis服务器,马上就生效了。

这说明lettuce的pipe根本就是个摆设,于是进去sAdd()看了一下,在DefaultEndpoint.write()方法里有个autoFlushCommands为true,所以其实每次sAdd()都会直接flush到服务器而不是添加到缓冲区buffer。

这里不得不说一下原因了,lettuce底层使用的是netty,是一个异步非阻塞(通过Future-Listener机制实现异步事件,网络IO使用的是同步非阻塞IO,即NIO)的线程和请求/响应模型(一般情况是不需要禁用自动刷新的,可见官网说明),所以我们才会在上面报command timed out after 10 seconds时没抛出来这个,因为使用的是netty的future每个请求都是异步的。

这个错误是lettuce抛出来的,因为netty的异步模型,所以lettuce会给每个command(Future)设置一个ScheduleFuture定时任务,时间为timeout,里面的逻辑是如果command到时间了还没isDone()就会设置一个超时异常。

后面看了官网文档,其实建议我们用默认的自动flush就行了,我于是做了下对比,先把超时时间改大点,不让抛异常,然后比较性能,并行流因为有bug,所以这里我用反射把pipe的List实现arraylist改成了线程安全的list,并进行测试

我的布控对象测试数据解析后大概key有28w

实现方式耗时线程安全的list
并行流关闭自动flush平均10.08/s
并行流开启自动flush平均9.16/s
串行流关闭自动flush平均15.36/s平均16.20/s
串行流开启自动flush平均6.74/s平均8.06/s

可以看到官网推荐的自动每次执行就flush的性能更高些

lettuce设置timeout超时时间,spring.redis.timeout,我之前设置的是10s,可以自行调整
lettuce设置pipe的禁用自动刷新,这里需要重新构造LettuceConnectionFactory,然后设置PipelingFlushPolicy

我在官网找到了说明https://lettuce.io/core/release/reference/#_pipelining_and_co...里面也说明了pipe默认每个命令在发出后都写入传输的原因,有兴趣的可以看看

以上就是30w+数据使用RedisTemplate pipeline空指针NullPointerException异常分析的详细内容,更多关于RedisTemplate pipeline空指针异常的资料请关注脚本之家其它相关文章!

相关文章

  • 基于Spring实现自定义错误信息返回详解

    基于Spring实现自定义错误信息返回详解

    这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03
  • 带你重新认识Java动态代理

    带你重新认识Java动态代理

    这篇文章主要为大家介绍了Java的动态代理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • 详解Java token主流框架之JWT

    详解Java token主流框架之JWT

    JWT(JSON Web Token)是一种基于JSON格式的轻量级的、用于身份认证的开放标准,它通过在用户和服务器之间传递一个安全的、可靠的、独立的JSON对象来进行身份验证和授权,本文将详细给大家介绍Java token主流框架之JWT,需要的朋友可以参考下
    2023-05-05
  • java枚举类的属性、方法和构造方法应用实战

    java枚举类的属性、方法和构造方法应用实战

    这篇文章主要介绍了java枚举类的属性、方法和构造方法应用,结合实例形式分析了java枚举类的定义、构造及相关应用操作技巧,需要的朋友可以参考下
    2019-08-08
  • java异常:异常处理--try-catch结构详解

    java异常:异常处理--try-catch结构详解

    今天小编就为大家分享一篇关于Java异常处理之try...catch...finally详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2021-09-09
  • Java如何从Redis中批量读取数据

    Java如何从Redis中批量读取数据

    这篇文章主要介绍了Java如何从Redis中批量读取数据的情况,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • SpringBoot integration实现分布式锁的示例详解

    SpringBoot integration实现分布式锁的示例详解

    常规项目都是采用Redission来实现分布式锁,进行分布式系统中资源竞争加锁操作,偶然发现SpringBoot中的integration也实现多种载体的分布式锁控制,下面我们就来看看具体实现方法吧
    2023-12-12
  • Java并发编程之LockSupport类详解

    Java并发编程之LockSupport类详解

    LockSupport是一种线程阻塞工具,它可以在线程内任意位置让线程阻塞.接下来就带着大家详细了解一下LockSupport类,,需要的朋友可以参考下
    2021-05-05
  • 在springboot中使用AOP进行全局日志记录

    在springboot中使用AOP进行全局日志记录

    这篇文章主要介绍就在springboot中使用AOP进行全局日志记录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java数据库连接池的几种配置方法(以MySQL数据库为例)

    Java数据库连接池的几种配置方法(以MySQL数据库为例)

    这篇文章主要介绍了Java数据库连接池的几种配置方法(以MySQL数据库为例) 的相关资料,需要的朋友可以参考下
    2016-07-07

最新评论