SpringBoot整合Redis调用lua脚本出现空指针异常(序列化器问题)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 一、问题描述业务中出现需要保证原子性的一系列缓存操作,所以决定使用lua脚本来保证原子性。但是调用过程中lua脚本抛出了异常:attempt to perform arithmetic on local ‘xxx’ (a nil value)发生异常的lua脚本代码(部分)

一、问题描述

业务中出现需要保证原子性的一系列缓存操作,所以决定使用lua脚本来保证原子性。

但是调用过程中lua脚本抛出了异常:attempt to perform arithmetic on local ‘xxx’ (a nil value)

发生异常的lua脚本代码(部分)

--- 省略...
local chosenCreditKey = "student:credits:chosen:" .. studentId
local chosenCredit = tonumber(redis.call("get", chosenCreditKey))
local maxCreditKey = "student:credits:max:" .. studentId
local maxCredit = tonumber(redis.call("get", maxCreditKey))
if chosenCredit + credits > maxCredit then --- 报错,提示chosenCredit为nil
    return 1
end
--- 省略...

二、问题分析

根据出现的异常描述,说的是尝试使用一个空值进行数学运算。

首先我尝试直接在代码中进行调试,尝试获取redis.call中get的值是什么,结果发现print语句输出的结果并不会出现在控制台

于是我直接在redis中运行脚本

ECHO "return redis.call("get", "xxx")" 0

发现取出来的值并非是空值而是"{\"1\"}",随后再运行

ECHO "return tonumber(redis.call("get", "xxx"))" 0

很显然输出的值是nil

那么到这里就很清楚了,因为redis中存储的值并不能直接转换为数字,所以出现了空指针异常


三、问题解决

仔细观察第一次运行输出的值,我们不难发现这其实是带双引号的1而且双引号被转移导致了无法调用tonumber

也就是说只要我们存储的时候没有这个双引号就可以解决这个问题了

而之所以存储的时候会带上双引号是因为我使用的是自定义的redisTemplate进行插入,key和value的泛型都设置为了string类型,并且在定义该类时选用了Jackson2JsonRedisSerializer<String>作为值的序列化器。

因为默认使用的JdkSerializationRedisSerializer序列器,需要被序列化Class实现Serializable接口,而且Redis数据库中数据很不直观

而这个序列化器在处理字符串时会自动为字符串带上双引号进行存储


那么自然也就有对应的两种解决方法:


1.既然是因为这个序列化器导致的存入数据带上了双引号,那么我们更换序列化器就可以了,比如StringRedisSerializer,就可以存入字符串时不带双引号因为是存储字符串时才带上双引号,那么我们也


2.可以选择将redisTemplate的值的泛型更改为object,然后插入数据时直接插入数字,那么自然也不会带上双引号

相关文章
|
2月前
|
NoSQL Java 网络安全
SpringBoot启动时连接Redis报错:ERR This instance has cluster support disabled - 如何解决?
通过以上步骤一般可以解决由于配置不匹配造成的连接错误。在调试问题时,一定要确保服务端和客户端的Redis配置保持同步一致。这能够确保SpringBoot应用顺利连接到正确配置的Redis服务,无论是单机模式还是集群模式。
236 5
|
3月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
226 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
6月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
1649 7
|
6月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
443 0
|
10月前
|
缓存 NoSQL 搜索推荐
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
本文介绍了如何通过Lua脚本在Redis中实现分布式锁的原子性操作,避免并发问题。首先讲解了Lua脚本的基本概念及其在Redis中的使用方法,包括通过`eval`指令执行Lua脚本和通过`script load`指令缓存脚本。接着详细展示了如何用Lua脚本实现加锁、解锁及可重入锁的功能,确保同一线程可以多次获取锁而不发生死锁。最后,通过代码示例演示了如何在实际业务中调用这些Lua脚本,确保锁操作的原子性和安全性。
528 6
【📕分布式锁通关指南 03】通过Lua脚本保证redis操作的原子性
|
10月前
|
NoSQL Redis 数据库
Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
通过本文的介绍,我们详细讲解了 Lua 脚本在 Redis 中的作用、`eval` 命令的使用方法以及 `redis.call` 和 `redis.pcall` 的区别和用法。通过合理使用 Lua 脚本,可以实现复杂的业务逻辑,确保操作的原子性,并减少网络开销,从而提高系统的性能和可靠性。
548 13
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
1143 13
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
499 4
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
178 0
|
编译器 C语言
【C语言初阶】指针篇—下
【C语言初阶】指针篇—下

热门文章

最新文章