简介

Redis 从 2.6 版本开始引入使用 Lua 编程语言进行的服务器端脚本编程功能,这个功能可以让用户直接在 Redis 内部执行各种操作,从而达到简化代码并提高性能的作用。 P248

在不编写 C 代码的情况下添加新功能 P248

通过使用 Lua 对 Redis 进行脚本编程,我们可以避免一些减慢开发速度或者导致性能下降对常见陷阱。 P248

将 Lua 脚本载入 Redis P249
  • SCRIPT LOAD 命令可以将脚本载入 Redis ,这个命令接受一个字符串格式的 Lua 脚本为参数,它会把脚本存储起来等待之后使用,然后返回被存储脚本的 SHA1 校验和
  • EVALSHA 命令可以调用之前存储的脚本,这个命令接收脚本的 SHA1 校验和以及脚本所需的全部参数
  • EVAL 命令可以直接执行指定的脚本,这个命令接收脚本字符串以及脚本所需的全部参数。这个命令除了会执行脚本之外,还会将被执行的脚本缓存到 Redis 服务器里面

由于 Lua 的数据传入和传出限制, Lua 与 Redis 需要进行相互转换。因为脚本在返回各种不同类型的数据时可能会产生含糊不清的结果,所以我们应该尽量显式的返回字符串。 P250

我们可以在 官方文档 中找到 Redis 和 Lua 不同类型之间的转换表:

Redis 类型转换至 Lua 类型

Redis Lua
integer reply number
bulk reply string
multi bulk reply table (may have other Redis data types nested)
status reply table with a single ok field containing the status
error reply table with a single err field containing the error
Nil bulk reply false boolean type
Nil multi bulk reply false boolean type

Lua 类型转换至 Redis 类型

Lua Redis
number integer reply (the number is converted into an integer)
string bulk reply
table (array) multi bulk reply (truncated to the first nil inside the Lua array if any)
table with a single ok field status reply
table with a single err field error reply
boolean false Nil bulk reply
boolean true integer reply with value of 1
创建新的状态消息 P251
  • Lua 脚本跟单个 Redis 命令以及 MULTI/EXEC 事务一样,都是原子操作
  • 已经对结构进行了修改的 Lua 脚本将无法被中断
    • 不执行任何写命令对只读脚本:可以在脚本对运行时间超过 lua-time-limit 选项指定的时间之后,执行 SCRIPT KILL 命令杀死正在运行对脚本
    • 有写命令的脚本:杀死脚本将导致 Redis 存储的数据进入一种不一致的状态。在这种情况下

使用 Lua 重写锁和信号量 P254

如果我们事先不知道哪些键会被读取和写入,那么就应该使用 WATCH/MULTI/EXEC 事务或者锁,而不是 Lua 脚本。因此,在脚本里面对未被记录到 KEYS 参数中的键进行读取或者写入,可能会在程序迁移至 Redis 集群的时候出现不兼容或者故障。 P254

获取锁在目前已不需要使用 Lua 脚本实现,可以直接使用 SET ,并用 PXNX 选项即可在键不存在的时候设置带过期时间的值。释放锁时为了保证释放的时自己获取的锁,需要使用 Lua 脚本实现。相关代码已在 实现自动补全、分布式锁和计数信号量 中实现。

移除 WATCH/MULTI/EXEC 事务 P258

一般来说,如果只有少数几个客户端尝试对被 WATCH 命令监视对数据进行修改,那么事务通常可以在不发生明显冲突或重试的情况下完成。但是,如果操作需要进行好几次通信往返,或者操作发生冲突的概率较高,又或者网络延迟较大,那么客户端可能需要重试很多次才能完成操作。 P258

使用 Lua 脚本替代事务不仅可以保证客户端尝试的执行都可以成功,还能降低通信开销,大幅提高 TPS 。同时由于 Lua 脚本没有多次通信往返,所以执行速度也会明显快于细粒度锁的版本。

Lua 脚本可以提供巨大的性能优势,并且能在一些情况下大幅地简化代码,但运行在 Redis 内部但 Lua 脚本只能访问位于 Lua 脚本之内或者 Redis 数据库之内的数据,而锁或者 WATCH/MULTI/EXEC 事务并没有这一限制。 P263

使用 Lua 对列表进行分片 P263

分片列表的构成 P263

为了能够对分片列表的两端执行推入操作和弹出操作,在构建分片列表时除了需要存储组成列表的各个分片之外,还需要记录列表第一个分片的 ID 以及最后一个分片的 ID 。当分片列表为空时,这两个字符串存储的分片 ID 将是相同的。 P263

组成分片列表的每个分片都会被命名为 <listname>:<shardid> ,并按照顺序进行分配。具体来说,如果程序总是从左端弹出元素,并从右端推入元素,那么最后一个分配的索引就会逐渐增大,并且新分片的 ID 也会变得越来越大。如果程序总是从右端弹出元素,并从左端推入元素,那么第一个分片的索引就会逐渐减少,并且新分片的 ID 也会变得越来越小。 P264

当分片列表包含多个列表时,位于分片两端的列表可能是被填满的,但位于两端之间的其他列表总是被填满的。 P264

将元素推入分片列表 P265

Lua 脚本根据命令 LPUSH/RPUSH 找到列表的第一个分片或者最后一个分片,然后将元素推入分片对应的列表中,若分片已达个数上限(可以取配置中的 list-max-ziplist-entries 的值 - 1 作为上限),则会自动产生一个新的分片,继续推入,并更新第一个分片或者最后一个分片的分片 ID 。当推入操作执行完毕后,它会返回被推入元素的数量。 P265

从分片里面弹出元素 P266

Lua 脚本根据命令 LPOP/RPOP 找到列表的第一个分片或者最后一个分片,然后在分片非空的情况下,从分片里面弹出一个元素,如果列表在执行弹出操作之后不再包含任何元素,那么程序就对记录着列表两端分片信息的字符串键进行修改(注意只有列表端分片为空时才修改对应的字符串键,而整个列表为空时,不做调整) P267

对分片列表执行阻塞弹出操作 P267

这一段书上讲得看不懂,也不知道为什么需要书中的花式操作才能完成。

个人觉得分片列表的阻塞弹出其实并不需要列表自身的阻塞弹出,我们可以不断执行上述 Lua 脚本实现的弹出元素的操作,若弹出成功,则直接返回,若弹出失败,则睡 1 ms 后继续执行弹出操作,直至弹出成功或者达到超时时间。这样我们对 Redis 对操作只在 Lua 脚本中,原子性保证了一定会弹出分片列表两端的元素。

本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/redis-in-action

Redis 实战 —— 14. Redis 的 Lua 脚本编程的更多相关文章

  1. Redis源码学习:Lua脚本

    Redis源码学习:Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开 ...

  2. Redis实战之Redis + Jedis

    用Memcached,对于缓存对象大小有要求,单个对象不得大于1MB,且不支持复杂的数据类型,譬如SET 等.基于这些限制,有必要考虑Redis! 相关链接: Redis实战 Redis实战之Redi ...

  3. Redis实战之Redis + Jedis[转]

    http://blog.csdn.net/it_man/article/details/9730605 2013-08-03 11:01 1786人阅读 评论(0) 收藏 举报   目录(?)[-] ...

  4. redis原子性读写操作之LUA脚本和watch机制

    最近在开发电商平台的子系统--储值卡系统,系统核心业务涉及到金额消费以及库存控制,因此为了解决建立在内存上高并发情况下的事务控制,使用了spring封装的RedisTemplate执行lua脚本进行原 ...

  5. Redis篇:事务和lua脚本的使用

    现在多数秒杀,抽奖,抢红包等大并发高流量的功能一般都是基于 redis 实现,然而在选择 redis 的时候,我们也要了解 redis 如何保证服务正确运行的原理 前言 redis 如何实现高性能和高 ...

  6. Redis实战总结-Redis的高可用性

    在之前的博客<Redis实战总结-配置.持久化.复制>给出了一种Redis主从复制机制,简单地实现了Redis高可用.然后,如果Master服务器宕机,会导致整个Redis瘫痪,这种方式的 ...

  7. Redis 实战 —— 05. Redis 其他命令简介

    发布与订阅 P52 Redis 实现了发布与订阅(publish/subscribe)模式,又称 pub/sub 模式(与设计模式中的观察者模式类似).订阅者负责订阅频道,发送者负责向频道发送二进制字 ...

  8. 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  9. 分布式缓存技术redis系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

随机推荐

  1. Redis缓存篇(二)淘汰机制:缓存满了怎么办?

    上一讲提到,缓存的容量总是小于后端数据库的.随着业务系统的使用,缓存数据会撑满内存空间,该怎么处理呢? 本节我们来学习内存淘汰机制.在Redis 4.0之前有6种内存淘汰策略,之后又增加2种,一共8种 ...

  2. [ABP教程]第五章 授权

    原文档 地址: Web Application Development Tutorial - Part 5: Authorization 关于此教程 在这个教程系列中,您将构建一个基于ABP的Web应 ...

  3. 风炫安全WEB安全学习第三十八节课 越权漏洞演示与讲解

    风炫安全WEB安全学习第三十八节课 越权漏洞演示与讲解 越权漏洞 0x01 漏洞介绍 越权漏洞的危害与影响主要是与对应业务的重要性相关,比如说某一页面服务器端响应(不局限于页面返回的信息,有时信息在响 ...

  4. Spring Cloud Gateway 跨域 CORS 配置方式实现

    网上找了一堆文章全是说这样写无效 globalcors: cors-configurations: '[/**]': allowCredentials: true allowedOriginPatte ...

  5. 【Flutter】事件处理与通知之原始指针事件处理

    前言 接口描述 代码示例 总结

  6. zabbix自定义监控nginx

    nginx配置ngx_status 1.编译安装时带上--with-http_stub_status_module参数 2.vi nginx.conf location ~* ^/ngx_status ...

  7. element el-table表格的vue组件二次封装(附表格高度自适应)

    基于vue的el-table表格二次封装组件方法 前言 在公司实习使用vue+element-ui框架进行前端开发,使用表格el-table较为多,有些业务逻辑比较相似,有些地方使用的重复性高,如果多 ...

  8. Jmeter二次开发——自定义函数

    在之前的博文中,Jmeter二次开发--基于Java请求,已介绍了Jmeter二次开发的基础情况,上次分享的是java请求开发,今天来分享下Jmeter中的函数开发.聊到Jmeter的函数,知道Jme ...

  9. ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-国际化,本地化,多语言应用

    开篇 早年写过一篇多语言的应用 :   本地化(多语言)   讲述了如何创建多语言的资源文件,并利用资源文件来获得页面和请求的语言属性 本次补充这篇文章,的原因是在实际项目中,有多种需要多语言的情况 ...

  10. 2020年12月18号--21号 人工智能(深度学习DeepLearning)python、TensorFlow技术实战

    深度学习DeepLearning(Python)实战培训班 时间地点: 2020 年 12 月 18 日-2020 年 12 月 21日 (第一天报到 授课三天:提前环境部署 电脑测试) 一.培训方式 ...