通过lua脚本在redis中处理json数据

在日常开发中,系统都会使用redis作为缓存来加快服务的响应,我们通常会将一个对象数据存储在redis中,对象存储通常有两种方案:一种是存储为hash结构,对象的键是属性名,值为属性值;另一种是序列化为字符串,然后存储成键值对。存储为hash结构在序列化和反序列化的时候稍微麻烦一些,涉及到键值映射关系转换,但是对于字段更新比较方便,通过redis提供的命令就可以完成。而存储为键值对,可以通过相关的类库进行序列化和反序列化操作,有时候就一行代码就能完成,但是这种方式对于更新字段比较麻烦:需要把数据取出来反序列为对象,更新属性值,再将数据反序列为字符串存储回去。整个过程虽然不是很麻烦,但是涉及到数据取存和序列化反序列化过程,在性能上还是有点损耗的,下面介绍使用lua脚本实现数据更新的方案,先看整个数据处理过程,后面在讲解

shell 复制代码
127.0.0.1:6380[1]> set test_json '{"address":"guangdong-shenzhen-nanshan","id":123,"name":"xingo"}'
OK
127.0.0.1:6380[1]> 
127.0.0.1:6380[1]> get test_json
"{\"address\":\"guangdong-shenzhen-nanshan\",\"id\":123,\"name\":\"xingo\"}"
127.0.0.1:6380[1]> 
127.0.0.1:6380[1]> eval "local json_str = redis.call('get', 'test_json'); if json_str then local json_obj = cjson.decode(json_str); json_obj.id=234; redis.call('set', 'test_json', cjson.encode(json_obj)) return 'ok' else return 'fail'; end;" 0
"ok"
127.0.0.1:6380[1]> 
127.0.0.1:6380[1]> get test_json
"{\"id\":234,\"name\":\"xingo\",\"address\":\"guangdong-shenzhen-nanshan\"}"
127.0.0.1:6380[1]> 
127.0.0.1:6380[1]> eval "local json_str = redis.call('get', 'test_json'); if json_str then local json_obj = cjson.decode(json_str); json_obj.id=tonumber(ARGV[1]); redis.call('set', 'test_json', cjson.encode(json_obj)) return 'ok' else return 'fail'; end;" 0 333
"ok"
127.0.0.1:6380[1]> 
127.0.0.1:6380[1]> get test_json
"{\"id\":333,\"name\":\"xingo\",\"address\":\"guangdong-shenzhen-nanshan\"}"
127.0.0.1:6380[1]> 

首先,我们向redis中存入一个键值对数据,值为一个json字符串。

json 复制代码
{
    "address": "guangdong-shenzhen-nanshan",
    "id": 123,
    "name": "xingo"
}

在这个对象中,我们要更新id字段值,将字段值从123修改为234,这里使用了lua脚本:

shell 复制代码
eval "local json_str = redis.call('get', 'test_json'); if json_str then local json_obj = cjson.decode(json_str); json_obj.id=234; redis.call('set', 'test_json', cjson.encode(json_obj)) return 'ok' else return 'fail'; end;" 0

格式化后代码如下:

lua 复制代码
local json_str = redis.call('get', 'test_json'); 
if json_str then 
  local json_obj = cjson.decode(json_str); 
  json_obj.id=234; 
  redis.call('set', 'test_json', cjson.encode(json_obj)); 
  return 'ok';
else 
  return 'fail'; 
end;

处理过程是:先获取到redis中存储的数据,然后使用cjson将字符串序列化为json对象,调整对象的属性值,再将对象序列化为字符串存储回去,整个过程都是在redis服务中完成,没有了redis和应用服务器之间来回的数据传输。

第二种方式是将修改的值通过外部传过去:

shell 复制代码
eval "local json_str = redis.call('get', 'test_json'); if json_str then local json_obj = cjson.decode(json_str); json_obj.id=tonumber(ARGV[1]); redis.call('set', 'test_json', cjson.encode(json_obj)) return 'ok' else return 'fail'; end;" 0 333

运行脚本后面跟了两个参数:第一个参数表示键数量,这里没有键传递过去,所以置数为0,第二个参数是要修改的值,注意在lua中使用了tonumber()函数,它是将参数转换为数值类型,否则更新后的字段类型为字符串。

redis中使用lua脚本非常棒,可以完成一些简单的处理逻辑,不必将数据在redis服务器和应用服务之间来回传递。

相关推荐
A_cot6 小时前
Redis 的三个并发问题及解决方案(面试题)
java·开发语言·数据库·redis·mybatis
芊言芊语8 小时前
分布式缓存服务Redis版解析与配置方式
redis·分布式·缓存
攻城狮的梦9 小时前
redis集群模式连接
数据库·redis·缓存
Amagi.12 小时前
Redis的内存淘汰策略
数据库·redis·mybatis
Snowbowღ13 小时前
OpenAI / GPT-4o:Python 返回结构化 / JSON 输出
python·json·openai·api·gpt-4o·pydantic·结构化输出
无休居士13 小时前
【实践】应用访问Redis突然超时怎么处理?
数据库·redis·缓存
.Net Core 爱好者13 小时前
Redis实践之缓存:设置缓存过期策略
java·redis·缓存·c#·.net
林戈的IT生涯14 小时前
MySQL5.7中增加的JSON特性的处理方法JSON_EXTRACT和JSON_ARRAY_APPEND以及MYSQL中JSON操作的方法大全
json·mysql5.7·json特性·json_extract·mysql中json操作方法
敲代码不忘补水15 小时前
Python 项目实践:简单的计算器
开发语言·python·json·项目实践
码爸16 小时前
flink 批量压缩redis集群 sink
大数据·redis·flink