「Redis」Lua脚本

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: RedisLua脚本介绍

Lua语言简介


Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。


语言特性


Lua语言拥有以下特性:


  • 轻量级 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展 Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
    语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。


应用场景


Lua的应用场景有:


  • 游戏开发
  • 独立应用脚本
  • Web 应用脚本
  • 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
  • 安全系统,如入侵检测系统
  • Redis


数据结构


struct redisServer {

// 忽略其他配置信息

 

   // Lua 环境

   lua_State *lua; /* The Lua interpreter. We use just one for all clients */

   

   // 复制执行 Lua 脚本中的 Redis 命令的伪客户端

   redisClient *lua_client;   /* The "fake client" to query Redis from Lua */


   // 当前正在执行 EVAL 命令的客户端,如果没有就是 NULL

   redisClient *lua_caller;   /* The client running EVAL right now, or NULL */


   // 一个字典,值为 Lua 脚本,键为脚本的 SHA1 校验和

   dict *lua_scripts;         /* A dictionary of SHA1 -> Lua scripts */

   // Lua 脚本的执行时限

   mstime_t lua_time_limit;  /* Script timeout in milliseconds */

   // 脚本开始执行的时间

   mstime_t lua_time_start;  /* Start time of script, milliseconds time */


   // 脚本是否执行过写命令

   int lua_write_dirty;  /* True if a write command was called during the

                            execution of the current script. */


   // 脚本是否执行过带有随机性质的命令

   int lua_random_dirty; /* True if a random command was called during the

                            execution of the current script. */


   // 脚本是否超时

   int lua_timedout;     /* True if we reached the time limit for script

                            execution. */


   // 是否要杀死脚本

   int lua_kill;         /* Kill the script if true. */

};


如上,关于Lua在Redis中的数据表示部分均存储在redisServer的Redis服务端中


  • lua 表示Lua的执行环境,所有的客户端共享一个Lua环境
  • lua_client 复制执行Lua脚本中的 Redis 命令的伪客户端
  • lua_caller 当前正在执行EVAL命令的客户端,如果没有就是NULL
  • lua_scripts 这里使用字典存储,键为脚本的SHA1校验和,值为 Lua 脚本,即<SHA1,SCRIPT>


协作组件


伪客户端


网络异常,图片无法展示
|

在Redis中,所有的客户端的Lua执行环境只有一个,这个Lua执行环境是专门处理Lua语言脚本相关功能的,体现了执行环境较好的 隔离性 独立性 ,而它又是作为Redis的一种寄存语言存在的,Redis作为Lua的宿主存在,因此在利用Lua的各种语言特性和优势的同时,也要兼容宿主Redis语言的命令支持,为了避免耦合,Redis在这里采用了一种 伪客户端 的形式来联通Redis与Lua,使得Lua脚本可以轻松调用Redis提供的命令支持。
网络异常,图片无法展示
|

Lua脚本中可以通过 redis.call redis.pcall 函数进行Redis命令的调用,执行步骤如下:


  • Lua环境redis.callredis.pcall函数要执行的命令传递给伪客户端
  • 伪客户端将执行命令传递给Redis的命令执行器
  • 命令执行器执行命令,并将执行结果返回给伪客户端
  • 伪客户端接收返回结果,将其返回给Lua环境
  • 返回结果会从调用发起的redis.callredis.pcall函数中返回给调用者


脚本缓存


网络异常,图片无法展示
|

Lua脚本缓存会以 字典 形式进行存储,键Key为脚本文件的 SHA1校验和 ,值Value为 脚本文件 ,即脚本内容。


由于Lua脚本对于Redis命令来说可能是巨大且批量的命令,为了减少脚本执行耗费在网络传输上的性能损耗,这里会进行缓存,通过文件校验和来匹配已缓存脚本数据提高性能


命令实现


eval


网络异常,图片无法展示
|

如上,Lua脚本执行过程大体分为三个主要步骤


  • (1)定义脚本函数
  • ① 计算脚本SHA1文件校验和
  • ② 以f_SHA1格式构造脚本函数,函数体内容是脚本本身,如


f_1as8dsdsjz8sww9(){

return 1 + 2;

}


使用函数体来保存脚本的优点是:


  • 调用简单,通过函数SHA1便可定位到
  • 可以利用函数的局部性特点让Lua环境保持清洁,减少了垃圾回收工作,也避免了全局变量
  • 可以提高脚本利用率,如果已经进行了脚本函数定义,那么之后的函数执行只需要进行SHA1调用即可,免去了重复性处理


  • (2)缓存脚本函数
  • ③ 在lua_scripts字典中进行<SHA1,SCRIPT>映射关系存储
  • (3)执行脚本函数
  • ④ 在执行脚本函数之前会将脚本参数进行加载
  • ⑤ 设置脚本执行超时的钩子,可以在执行script skillshutdown终止脚本执行
  • ⑥ 执行脚本
  • ⑦ 移除脚本执行超时的钩子
  • ⑧ 保存脚本执行结果到客户端缓冲区
  • ⑨ Lua环境垃圾回收操作


evalsha


网络异常,图片无法展示
|


  • 计算脚本SHA1校验和
  • 拼接SHA1方法函数
  • 校验是否存在函数


script flush


清空lua_scripts字典中缓存的脚本,并且重置Lua环境


script load


加载脚本到lua_scripts字典中


script exists


校验脚本是否存在于lua_scripts字典中


script kill


通过脚本钩子停止当前执行的脚本


Lua与Redis命令区别


网络异常,图片无法展示
|

Lua相当于是一系列Redis命令的集合,由于运行在独立的Lua环境中,因此Lua脚本内容执行的命令是原子执行的,因此运行在单线程环境下,这与Redis原生命令相比客户端执行一系列命令时不会被其他客户端命令 插队


Lua脚本优点


  • Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
  • Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这 些命令常驻在Redis内存中,实现复用的效果。
  • Lua脚本可以将多条命令一次性打包,有效地减少网络开销


总结


  • Lua脚本由于执行环境的天然隔离性,在执行过程中能对批量命令和复杂逻辑进行封装而拥有较好的原子性,这是对Redis事务机制的极大补充,为批执行的原子性操作提供了使用方式
  • 由于Lua与Redis通信是采用了伪客户端传递形式,理论上执行性能要比Redis原生执行命令差,需要在实际使用中根据实际情况来进行取舍


参考

《Redis设计与实现》

https://www.runoob.com/lua/lua-tutorial.html

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
16天前
|
缓存 NoSQL Redis
Redis 脚本
10月更文挑战第18天
29 3
|
3月前
|
NoSQL Redis
Redis 执行 Lua保证原子性原理
Redis 执行 Lua 保证原子性原理
276 1
|
30天前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
53 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
30天前
|
缓存 分布式计算 NoSQL
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
大数据-43 Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
25 2
|
1月前
|
NoSQL Java 关系型数据库
阿里 P7二面:Redis 执行 Lua,到底能不能保证原子性?
Redis 和 Lua,两个看似风流马不相及的技术点,为何能产生“爱”的火花,成为工作开发中的黄金搭档?技术面试中更是高频出现,Redis 执行 Lua 到底能不能保证原子性?今天就来聊一聊。 
77 1
|
2月前
|
存储 JSON Ubuntu
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
如何使用 Lua 脚本进行更复杂的网络请求,比如 POST 请求?
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
|
1月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
|
30天前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
67 6