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空指针异常的资料请关注脚本之家其它相关文章!

相关文章

  • springboot aop里的@Pointcut()的配置方式

    springboot aop里的@Pointcut()的配置方式

    这篇文章主要介绍了springboot aop里的@Pointcut()的配置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • MybatisPlus逆向工程的项目实践

    MybatisPlus逆向工程的项目实践

    Mybatis-Plus逆向工程,是MP官方提供的一款代码生成器,可以自动生成对应的实体类、Mapper接口和配置文件,,本文主要介绍了MybatisPlus逆向工程的项目实践,感兴趣的可以了解一下
    2024-03-03
  • Spring boot配置多数据源代码实例

    Spring boot配置多数据源代码实例

    这篇文章主要介绍了Spring boot配置多数据源代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • java面试应用上线后Cpu使用率飙升如何排查

    java面试应用上线后Cpu使用率飙升如何排查

    这篇文章主要为大家介绍了java面试中应用上线后Cpu使用率飙升如何排查的方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Spring Cloud详解实现声明式微服务调用OpenFeign方法

    Spring Cloud详解实现声明式微服务调用OpenFeign方法

    这篇文章主要介绍了Spring Cloud实现声明式微服务调用OpenFeign方法,OpenFeign 是 Spring Cloud 家族的一个成员, 它最核心的作用是为 HTTP 形式的 Rest API 提供了非常简洁高效的 RPC 调用方式,希望对大家有所帮助。一起跟随小编过来看看吧
    2022-07-07
  • MybatisPlus自定义Sql实现多表查询的示例

    MybatisPlus自定义Sql实现多表查询的示例

    这篇文章主要介绍了MybatisPlus自定义Sql实现多表查询的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Hibernate用ThreadLocal模式(线程局部变量模式)管理Session

    Hibernate用ThreadLocal模式(线程局部变量模式)管理Session

    今天小编就为大家分享一篇关于Hibernate用ThreadLocal模式(线程局部变量模式)管理Session,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • MyBatis中insert操作返回主键的实现方法

    MyBatis中insert操作返回主键的实现方法

    在使用MyBatis做持久层时,insert语句默认是不返回记录的主键值,而是返回插入的记录条数。这篇文章主要介绍了MyBatis中insert操作返回主键的方法,需要的朋友可以参考下
    2016-09-09
  • idea生成类注释和方法注释的正确方法(推荐)

    idea生成类注释和方法注释的正确方法(推荐)

    这篇文章主要介绍了idea生成类注释和方法注释的正确方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • 详解Huffman编码算法之Java实现

    详解Huffman编码算法之Java实现

    Huffman编码是一种编码方式,常用于无损压缩。本文只介绍用Java语言来实现该编码方式的算法和数据结构。有兴趣的可以了解一下。
    2016-12-12

最新评论