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

相关推荐
Json_181790144802 小时前
An In-depth Look into the 1688 Product Details Data API Interface
大数据·json
哟哟耶耶3 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
水月梦镜花4 小时前
redis:list列表命令和内部编码
数据库·redis·list
掘金-我是哪吒5 小时前
微服务mysql,redis,elasticsearch, kibana,cassandra,mongodb, kafka
redis·mysql·mongodb·elasticsearch·微服务
努力的家伙是不讨厌的7 小时前
解析json导出csv或者直接入库
开发语言·python·json
ketil277 小时前
Ubuntu 安装 redis
redis
王佑辉8 小时前
【redis】redis缓存和数据库保证一致性的方案
redis·面试
Karoku0669 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
gorgor在码农9 小时前
Redis 热key总结
java·redis·热key
想进大厂的小王9 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构