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

相关文章

  • JAVA提高第七篇 类加载器解析

    JAVA提高第七篇 类加载器解析

    这篇文章主要为大家详细介绍了JAVA提高第七篇类加载器的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Java利用poi读取Excel详解实现

    Java利用poi读取Excel详解实现

    Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为简洁版的模糊实现
    2022-07-07
  • Java中Arrays.sort()方法的比较器详解

    Java中Arrays.sort()方法的比较器详解

    这篇文章主要介绍了Java中Arrays.sort()方法的比较器详解,Arrays.sort(Object[] a)此方法看似没有要求我们实现比较器,对于基本数据类型,String类型确实如此,因为这些类型中已经自带了compareTo()方法,需要的朋友可以参考下
    2023-12-12
  • 解析spring事务管理@Transactional为什么要添加rollbackFor=Exception.class

    解析spring事务管理@Transactional为什么要添加rollbackFor=Exception.class

    这篇文章主要介绍了spring事务管理@Transactional为什么要添加rollbackFor=Exception.class,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Spring Security Oauth2.0 实现短信验证码登录示例

    Spring Security Oauth2.0 实现短信验证码登录示例

    本篇文章主要介绍了Spring Security Oauth2.0 实现短信验证码登录示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Spring容器注册组件实现过程解析

    Spring容器注册组件实现过程解析

    这篇文章主要介绍了Spring容器注册组件实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Java基础之方法重写详解

    Java基础之方法重写详解

    这篇文章主要介绍了Java基础之方法重写详解,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-05-05
  • 关于Java单个TCP(Socket)连接发送多个文件的问题

    关于Java单个TCP(Socket)连接发送多个文件的问题

    这篇文章主要介绍了关于Java单个TCP(Socket)连接发送多个文件的问题,每次我只能使用一个 Socket 发送一个文件,没有办法做到连续发送文件,本文来解决这个问题,需要的朋友可以参考下
    2023-04-04
  • java中实体类转Json的2种方法

    java中实体类转Json的2种方法

    本篇文章中主要介绍了java中实体类转Json的2种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧。
    2017-01-01
  • 你应该知道的这些Mybatis-Plus使用技巧(小结)

    你应该知道的这些Mybatis-Plus使用技巧(小结)

    这篇文章主要介绍了你应该知道的这些Mybatis-Plus使用技巧(小结),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08

最新评论