简介

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. ORB-SLAM3 细读单目初始化过程(上)

    作者:乔不思 来源:微信公众号|3D视觉工坊(系投稿) 3D视觉精品文章汇总:https://github.com/qxiaofan/awesome-3D-Vision-Papers/ 点击上方&qu ...

  2. spring cache 学习 —— @Cacheable 使用详解

    1. 功能说明 @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的.也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方 ...

  3. git的基础知识

    git 分布式版本控制工具 具备的功能 协同开发 多人并行不悖修改服务器端的同一个文件 数据备份 不仅保持目录和文件当前状态,还能保存每一个提交的历史版本 版本管理 保存每一个版本的文件信息的时候做到 ...

  4. SpringBoot同时接收单个对象和List<object>参数

    最近做项目的有个需求,是把多个文件移动到另一个文件夹下,这需要把 新的文件夹id -- Long类型 多个文件的信息 -- List< Object > 类型 这两个参数传给后台,我的后台 ...

  5. 【Oracle】row_number() over(partition by )函数用法

    row_number() OVER (PARTITION BY COL1 ORDER BY COL2) 表示根据COL1分组,在分组内部根据 COL2排序,而此函数计算的值就表示每组内部排序后的顺序编 ...

  6. P1220 关路灯(区间规划)

    题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯. 为了给村 ...

  7. 以我的亲身经历,聊聊学python的流程,同时推荐学python的书

    因为干活要用到,所以我大概于19年5月开始学python,大概学了1个月后,我就能干公司的活了,而且这python项目还包含了机器学习等要素,大概3个月后,我还承担了项目里开发机器学习数据分析的任务. ...

  8. ASP.NET MVC--sqlserver数据库脚本的导入导出

    1.右键选择数据库---任务----生成脚本 2.弹出如下框 导出整个表,默认下一步,否则选择特定数据库对象表单选框 3.修改文件名路径,可以保存脚本到制定路径,否则为默认,点击高级进入 要编写脚本的 ...

  9. 入门OJ:简单的网络游戏

    题目描述 在某款极具技术含量的网络游戏中,佳佳靠着他的聪明智慧垄断了游戏中的油田系统.油田里有许多油井,这些油井排成一个M*N的矩形.每个油井都有一个固定的采油量.每两个相邻的油井之间有一条公路,这些 ...

  10. Bagging和Boosting的介绍及对比

    "团结就是力量"这句老话很好地表达了机器学习领域中强大「集成方法」的基本思想.总的来说,许多机器学习竞赛(包括 Kaggle)中最优秀的解决方案所采用的集成方法都建立在一个这样的假 ...