Redis基础


1. 概述

Redis: REmote DIctionary Server。是一个Key - Value 型的缓存产品。

可以用来干什么?

  • 内存存储和持久化: redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务。
  • 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面。
  • 模拟类似于HttpSession这种需要设定过期时间的功能。
  • 发布、订阅消息系统
  • 定时器、计数器

Redis6之前是单线程执行的,6之后支持IO的多线程。默认有16个数据库,类似数组下标从零开始,初始默认使用0号库。 统一密码管理,16个库都是同样密码。要么都OK,要么一个也连不上。

2. Redis的持久化

Redis 是一个内存数据库,为了保证数据的持久性,它提供了三种持久化方案:

  • RDB 方式(默认)
  • AOF 方式
  • 混合持久化模式(4.0增加,5.0默认开启-当开启AOF时候,它也开启了)它是AOF的一个补充

1. RDB

RDB 是 Redis 默认采用的持久化方式。 RDB 方式是通过快照( snapshotting )完成的,当符合一定条件时 Redis 会自动将内存中的数据进行 快照并持久化到硬盘。

触发RDB快照的时机

  1. 符合指定配置的快照规则
  2. 执行save或bgsave命令 save主线程去快照 bgsave 调用异步线程去快照 主线程是单线程 4.0 I/O操作 已经有多线程概念
  3. 执行flushall 或flushdb
  4. 执行主从复制操作

a) 设置快照规则

  save 多少秒内 数据变了多少

  save "" : 不使用RDB存储

  save 900 1 : 表示15分钟(900秒钟)内至少1个键被更改则进行快照。

  save 300 10 : 表示5分钟(300秒)内至少10个键被更改则进行快照。

  save 60 10000 :表示1分钟内至少10000个键被更改则进行快照。

b) RDB快照的实现原理

快照过程

  1. Redis 调用系统中的 fork 函数复制一份当前进程的副本(子进程)

  2. 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件。

  3. 当子进程写入完所有数据后会用该临时文件替换旧的 RDB 文件,至此,一次快照操作完成。

fork 是操作系统的函数 linux/ uinx *nux

fork-调用一个子进程,制作快照,生成RDB文件, 替换掉原来的RDB文件

注意事项:

1. Redis 在进行快照的过程中不会修改 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就 是说任何时候 RDB 文件都是完整的。

2. 这就使得我们可以通过定时备份 RDB 文件来实现 Redis 数据库的备份, RDB 文件是 经过压缩的二进制文件 ,占用的空间会小于内存中的数据,更加利于传输。

RDB优缺点

  • 缺点:使用 RDB 方式实现持久化,一旦 Redis 异常退出,就会丢失最后一次快照以后更改的所有数据。这个时候我们就需要根据具体的应用场景,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受范围。如果数据相对来说比较重要,希望将损失降到最小,则可以使用 AOF 方式进行持久化
  • 优点: RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无需执行任何磁盘 I/O 操作。同时这个也是一个缺点,如果数据集比较大的时候, fork 可以能比较耗时,造成服务器在一段时间内停止处理客户端的请求;

 

2. AOF( append only file )

默认情况下 Redis 没有开启 AOF方式的持久化。 开启AOF持久化后,每执行一条会更改 Redis 中的数据的命令, Redis 就会将该命令写入硬盘中的 AOF 文件,这一过程显然会降低 Redis 的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高 AOF 的性能。

redis.conf 配置

# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir ./
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof

同步磁盘数据

Redis 每次更改数据的时候, aof 机制都会将命令记录到 aof 文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存。再通过硬盘缓存机制去刷新到保存到文件。

redis.conf 配置

# 每次执行写入都会进行同步, 这个是最安全但是是效率比较低的方式
appendfsync always
# 每一秒执行(默认)
appendfsync everysec
# 不主动进行同步操作,由操作系统去执行,这个是最快但是最不安全的方式
appendfsync no

最开始的数据它是通过读取内存中的数据,转换成命令。后边的就是每次的更新指令,进行存储。

AOF rewrite 重写原理(优化AOF文件)

Redis 在 AOF 文件大小触碰到临界时,rewrite会被运行,自动地在后台对 AOF 进行重写。重写后的新 AOF 文 件包含了恢复当前数据集所需的最小命令集合。

AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议(RESP)的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析( parse )也很轻松。

优化前
set name zhangsan
set name lisi
set name wangwu

优化后
set name wangwu

优化前
lpush list1 1 2 3
lpush list1 4 5 6

优化后
lpush list1 1 2 3 4 5 6

AOF进行优化时候,并不是读取原来的AOF文件 ,而是读取redis db进行转化。

auto-aof-rewrite-min-size 64mb //当AOF文件超过了最小size后才会自动rewrite,该值不要配的太小,否则重写就是浪费。 
auto-aof-rewrite-percentage 100 // 当AOF文件超过上次重写时的大小的百分比就会重写。

AOF rewrite 过程

rewrite会像replication一样,fork出一个子进程,创建一个临时文件,遍历数据库,将每个key、value对输出到临时文件。输出格式就是Redis的命令,但是为了减小文件大小,会将多个key、value对集合起来用一条命令表达。在rewrite期间的写操作会保存在内存的rewrite buffer中,rewrite成功后这些操作也会复制到临时文件中,在最后临时文件会代替AOF文件。

Redis AOF流程
  1. Redis Server启动,如果AOF机制打开那么初始化AOF状态,并且如果存在AOF文件,读取AOF文件。
  2. 随着Redis不断接受命令,每个写命令都被添加到AOF文件,AOF文件膨胀到需要rewrite时又或者接收到客户端的bgrewriteaof命令。
  3. fork出一个子进程进行rewrite,而父进程继续接受命令,现在的写操作命令都会被额外添加到一个aof_rewrite_buf_blocks缓冲中。
  4. 当子进程rewrite结束后,父进程收到子进程退出信号,把aof_rewrite_buf_blocks的缓冲添加到rewrite后的文件中,然后切换AOF的文件fd。rewrite任务完成,继续第二个步骤。
关键点
  • 由于写操作通常是有缓冲的,所以有可能AOF操作并没有写到硬盘中,一般可以通过fsync()来强制输出到硬盘中。而fsync()的频率可以通过配置文件中的flush策略来指定,可以选择每次事件循环写操作都强制fsync或者每秒fsync至少运行一次。
  • 当rewrite子进程开始后,父进程接受到的命令会添加到aof_rewrite_buf_blocks中,使得rewrite成功后,将这些命令添加到新文件中。在rewrite过程中,原来的AOF也可以选择是不是继续添加,由于存在性能上的问题,在rewrite过程中,如果fsync()继续执行,会导致IO性能受损影响Redis性能。所以一般情况下rewrite期间禁止fsync()到旧AOF文件。这策略可以在配置文件中修改。
  • 在rewrite结束后,在将新rewrite文件重命名为配置中指定的文件时,如果旧AOF存在,那么会unlink掉旧文件。这是就存在一个问题,处理rewrite文件迁移的是主线程,rename(oldpath, newpath)过程会覆盖旧文件,这是rename会unlink(oldfd),而unlink操作会导致block主线程。这时,我们就需要类似libeio(http://software.schmorp.de/pkg/libeio.html)这样的库去进行异步的底层IO。作者在bio.c有一个类似的机制,通过创建新线程来进行异步操作。

如何选择RDB和AOF

  • 内存数据库 : rdb(redis database)+aof 数据不能丢 , 恢复时: 先aof再rdb
  • 缓存服务器 : rdb 
  • 不建议 只使用 aof (性能差)

3. 混合持久化方式

Redis 4.0 之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保 证 Redis 重启时的速度,又能减低数据丢失的风险。

RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时拥有 RDB 和 AOF 的优点,Redis 4.0 之后新增了混合持久化的方式,因 此我们在必须要进行持久化操作时,应该选择混合持久化的方式。

查询是否开启混合持久化可以使用 config get aof-use-rdb-preamble 命令,执行结果

127.0.0.1:6379> config get aof-use-rdb-preamble
1) "aof-use-rdb-preamble"
2) "yes"

其中 yes 表示已经开启混合持久化,no 表示关闭,Redis 5.0 默认值为 yes。如果是其他版本的 Redis 首先需要检查一下,是否已经开启了混合持久化,如果关闭的情况下,

可以通过以下两种方式开启:

  • 通过命令行开启
  • 通过修改 Redis 配置文件开启。

通过命令行开启

使用命令 config set aof-use-rdb-preamble yes 命令行设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。

通过修改 Redis 配置文件开启

redis.conf 文件,把配置文件中的 aof-use-rdb-preamble no 改为 aof-userdb-preamble yes 配置完成之后,需要重启 Redis 服务器,配置才能生效,但修改配置文件的方式,在每次重启 Redis 服 务之后,配置信息不会丢失。

需要注意的是,在非必须进行持久化的业务中,可以关闭持久化,这样可以有效的提升 Redis 的运行速 度,不会出现间歇性卡顿的困扰。 混合持久化 是RDB+指令。如果AOF文件过大时候,会重写:把当前数据以RDB格式保存,后续指令用aof。

3. Redis内存过期淘汰机制

1. 设置键值的过期时间

我们应该根据实际的业务情况,对键值设置合理的过期时间,这样 Redis 会帮你自动清除过期的键值对,以节约对内存的占用,以避免键值过多的堆积,频繁的触发内存淘汰策略。

Redis 有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除) :

  • EXPlRE 命令用于将键key 的生存时间设置为ttl 秒。
  • PEXPIRE 命令用于将键key 的生存时间设置为ttl 毫秒。
  • EXPIREAT < timestamp> 命令用于将键key 的过期时间设置为timestamp所指定的秒数时间戳。
  • PEXPIREAT < timestamp > 命令用于将键key 的过期时间设置为timestamp所指定的毫秒数时间戳。

另外通过set 命令也可以设置生存时间: set key value EX seconds | PX milliseconds

2. 使用 lazy free 特性

lazy free 特性是 Redis 4.0 新增的一个非常使用的功能,它可以理解为惰性删除或延迟删除。意思是在 删除的时候提供异步延时释放键值的功能,把键值释放操作放在 BIO(Background I/O) 单独的子线程处 理中,以减少删除删除对 Redis 主线程的阻塞,可以有效地避免删除 big key 时带来的性能和可用性问题。

lazy free 对应了 4 种场景,默认都是关闭的:

lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
slave-lazy-flush no

它们代表的含义如下:

lazyfree-lazy-eviction:表示当 Redis 运行内存超过最大内存时,是否开启 lazy free 机制删除;

lazyfree-lazy-expire:表示设置了过期时间的键值,当过期之后是否开启 lazy free 机制删除;

lazyfree-lazy-server-del:有些指令在处理已存在的键时,会带有一个隐式的 del 键的操作,比如 rename 命令,当目标键已存在,Redis 会先删除目标键,如果这些目标键是一个 big key,就会造 成阻塞删除的问题,此配置表示在这种场景中是否开启 lazy free 机制删除;

slave-lazy-flush:针对 slave(从节点) 进行全量数据同步,slave 在加载 master 的 RDB 文件前, 会运行 flushall 来清理自己的数据,它表示此时是否开启 lazy free 机制删除。

建议开启其中的 lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-server-del 等配置,这样 就可以有效的提高主线程的执行效率。

 

3.  保存过期时间的内存结构

数据库结构redisDb中的expires字典中保存了数据库中所有键的过期时间,我们称expire这个字典为过期字典。
(1)过期字典是一个指针,指向键空间的某个键对象。
(2)过期字典的值是一个longlong类型的整数,这个整数保存了键所指向的数据库键的过期时间 – 一个毫秒级的 UNIX 时间戳。

过期字典是存储在redisDb这个结构里的:

typedef struct redisDb {
  dict *dict; /* The keyspace for this DB */
  dict *expires; /* Timeout of keys with a timeout set */
  dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
  dict *ready_keys; /* Blocked keys that received a PUSH */
  dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
  int id; /* Database ID */
  long long avg_ttl; /* Average TTL, just for stats */
  list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

4. 过期键的删除策略

如果一个键是过期的,那它到了过期时间之后是不是马上就从内存中被被删除呢?如果不是,那过期后到底什么时候被删除呢??

其实有三种不同的删除策略:
(1):立即删除。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。
(2):惰性删除。键过期了就过期了,不管。每次从dict字典中按key取值时,先检查此key是否已经过期,如果过期了就删除它,并返回nil,如果没过期,就返回键值。
(3):定时删除。每隔一段时间,对expires字典进行检查,删除里面的过期键。


可以看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。下面对这三种删除策略进行具体分析。

a). 立即删除

立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对cpu是最不友好的。因为删除操作会占用cpu的时间。

目前redis事件处理器对时间事件的处理方式--无序链表,查找一个key的时间复杂度为O(n),所以并不适合用来处理大量的时间事件.

b) 惰性删除

惰性删除是指,某个键值过期后,此键值不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点很明显:浪费内存。dict字典和expires字典都要保存这个键值的信息。这对于性能非常依赖于内存大小的redis来说,是比较致命的。

c) 定时删除

定期删除是立即删除和惰性删除的折中.定期删除策略每隔一段时间执行一次删除过期键的操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响. 周期性轮询redis中的数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度.定期删除策略的难点是确定删除操作执行的时长和频率.这种方式也会过期的key堆积在内存中,造成内存资源浪费. redis默认1秒执行10次, 即每100ms检查一次(随机抽取进行检查),是否有过期的key,有过期key则删除

redis.conf中的配置:

hz 10  // 1秒10次。尽量不要把这个值改到超过100.

redis使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。

5. 限制 Redis 内存大小,设置内存淘汰策略

a) 最大缓存设置:

maxmemory 100MB

maxmemory 1GB

maxmemory 100M

maxmemory 1G

没有指定最大缓存,如果有新的数据添加,超过最大内存,则32位会使redis崩溃,所以一定要设置。最佳设置是物理内存的75% ,写操作比较多 60% 。

Redis缓存淘汰策略

redis中当内存超过限制时,按照配置的策略,淘汰掉相应的key-value,使得内存可以继续留有足够的空间保存新的数据。。

  1. noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;

  2. allkeys-lru:淘汰整个键值中最久未使用的键值;

  3. allkeys-random:随机淘汰任意键值;

  4. volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;

  5. volatile-random:随机淘汰设置了过期时间的任意键值;

  6. volatile-ttl:优先淘汰更早过期的键值。

在 Redis 4.0 版本中又新增了 2 种淘汰策略:

  1. volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;

  2. allkeys-lfu:淘汰整个键值中最少使用的键值。 其中 allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期键的键值中淘汰数 据。

我们可以根据实际的业务情况进行设置,默认的淘汰策略不淘汰任何数据,在新增时会报错。

redis.conf配置

# The default is:
#
# maxmemory-policy noeviction

LRU原理

LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:

1. 新数据插入到链表头部;
2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃。

LFU原理

 LFU,全称是:Least Frequently Used,最不经常使用策略,在一段时间内,数据被使用频次最少的,优先 被淘汰。*最少使用*(*LFU*)是一种用于管理计算机内存的缓存算法。主要是记录和追踪内存块的使 用次数,当缓存已满并且需要更多空间时,系统将以最低内存块使用频率清除内存.采用LFU算法的最简单 方法是为每个加载到缓存的块分配一个计数器。每次引用该块时,计数器将增加一。当缓存达到容量并 有一个新的内存块等待插入时,系统将搜索计数器最低的块并将其从缓存中删除(本段摘自维基百科)。

LRU和LFU侧重点不同,LRU主要体现在对元素的使用时间上,而LFU主要体现在对元素的使用频次上。

LFU的缺陷是:在短期的时间内,对某些缓存的访问频次很高,这些缓存会立刻晋升为热点数据,而保 证不会淘汰,这样会驻留在系统内存里面。而实际上,这部分数据只是短暂的高频率访问,之后将会长 期不访问,瞬时的高频访问将会造成这部分数据的引用频率加快,而一些新加入的缓存很容易被快速删 除,因为它们的引用频率很低。

4. Redis事务

redis事务只是个批处理,有隔离性但是没有原子性。

MULTI 、 EXEC 、 DISCARD 和 WATCH 命令是 Redis 事务的基础。

用法

MULTI 命令用于开启一个事务,它总是返回 OK 。
MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC 命令被调用时, 所有队列中的命令才会被执行。
另一方面, 通过调用 DISCARD , 客户端可以清空事务队列, 并放弃执行事务

EXEC 命令的回复是一个数组, 数组中的每个元素都是执行事务中的命令所产生的回复。 其中, 回复元素的先后顺序和命令发送的先后顺序一致。
当客户端处于事务状态时, 所有传入的命令都会返回一个内容为 QUEUED 的状态回复(status reply), 这些被入队的命令将在 EXEC 命令被调用时执行。

WATCH 使得 EXEC 命令需要有条件地执行: 事务只能在所有被监视键都没有被修改的前提下执行, 如果这个前提不能满足的话,事务就不会被执行
WATCH 命令可以被调用多次。 对键的监视从 WATCH 执行之后开始生效, 直到调用 EXEC 为止。

用户还可以在单个 WATCH 命令中监视任意多个键, 就像这样:
redis> WATCH key1 key2 key3

EXEC 被调用时, 不管事务是否成功执行, 对所有键的监视都会被取消。另外, 当客户端断开连接时, 该客户端对键的监视也会被取消。使用无参数的 UNWATCH 命令可以手动取消对所有键的监视

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 111
QUEUED
127.0.0.1:6379> hset set1 name zhangsan
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s2 222
QUEUED
127.0.0.1:6379> hset set2 age 20
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> watch s1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 555
QUEUED
127.0.0.1:6379> exec # 此时在没有exec之前,通过另一个命令窗口对监控的s1字段进行修改
(nil)
127.0.0.1:6379> get s1
111

事务失败处理

1. Redis 语法错误
   整个事务的命令在队列里全部清除,全部不执行。

2. Redis 运行错误

在队列里正确的命令可以执行 (弱事务性)
弱事务性 :
1、在队列里正确的命令可以执行 (非原子操作)
2、不支持回滚

3. Redis 不支持事务回滚

1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
2、Redis 为了性能方面就忽略了事务回滚。 (回滚记录历史版本)

5. Redis和Lua整合

Redis整合lua是对Redis事务的补充。

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

Redis中使用lua的好处

  1. 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行

  2. 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚本的过程中无需担心会出现竞态条件。 隔离性

  3. 复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑。

Redis整合lua脚本

从Redis2.6.0版本开始,通过内置的lua编译/解释器,可以使用EVAL命令对lua脚本进行求值。

EVAL命令

  • 在redis客户端中,执行以下命令:
EVAL script numkeys key [key 1 ...] arg [arg ...]

命令说明:

  • script参数:是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。
  • numkeys参数:用于指定键名参数的个数。
  • key [key ...]参数: 从EVAL的第三个参数开始算起,使用了numkeys个键(key),表示在脚本中所用到的那些Redis键(key),这些键名参数可以在Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • arg [arg ...]参数:可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似(ARGV[1] 、 ARGV[2] ,诸如此类)。
./redis-cli
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

lua 脚本调用Redis 命令

redis.call();
返回值就是redis命令执行的返回值
如果出错,返回错误信息,不继续执行

redis.pcall();
返回值就是redis命令执行的返回值
如果出错了记录错误信息,继续执行

在脚本中,使用return语句将返回值返回给客户端,如果没有return,则返回nil。

127.0.0.1:6379> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK

redis-cli --eval

可以使用redis-cli --eval命令指定一个lua脚本文件去执行。

脚本文件(redis.lua),内容如下

local num = redis.call('GET', KEYS[1]);
if not num then
    return 0;
else
    local res = num * ARGV[1];
    redis.call('SET',KEYS[1], res);
    return res;
end

在redis客户机,执行脚本命令:

[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 0
[root@localhost bin]# ./redis-cli incr lua:incrbyml
(integer) 1
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 8
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 8
(integer) 64
[root@localhost bin]# ./redis-cli --eval redis.lua lua:incrbyml , 2
(integer) 128

命令格式说明:

--eval:告诉redis客户端去执行后面的lua脚本
redis.lua:具体的lua脚本文件名称
lua:incrbymul : lua脚本中需要的key
8:lua脚本中需要的value

上面命令中keys和values中间需要使用逗号隔开,并且逗号两边都要有空格。