通过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服务器和应用服务之间来回传递。

相关推荐
二宝哥1 天前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
ShiXZ2131 天前
Redis docker-compose 部署指南
redis·docker·eureka
CedarQR1 天前
万字长文:从零在 RK3588 上部署 PaddleSpeech 中文 TTS 全流程(FastSpeech2 + HiFiGAN)
开发语言·c++·嵌入式硬件·ubuntu·json
青山木1 天前
Redis 缓存一致性全解:从 Cache Aside 到延迟双删、Canal+MQ 的进阶之路
java·数据库·redis·后端·缓存
YDS8291 天前
大营销平台 —— 活动SKU库存扣减业务及其一致性处理
java·spring boot·redis·rabbitmq·ddd
爬也要爬着前进2 天前
redis主从搭建
数据库·redis·缓存
用户095367515832 天前
在系统的学习 redis 前的疑惑
redis·go
Geoking.2 天前
JSON vs JSONL:从数据格式到 AI Agent 的工程实践
人工智能·深度学习·json
吴声子夜歌2 天前
Redis 3.x——集群故障转移
java·数据库·redis·集群
明豆2 天前
Redis 分布式缓存生产实战
redis·分布式·缓存