“Redis:常用命令”的版本间差异
第1,456行: | 第1,456行: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | === 详述:“CONFIG XXX” === | ||
'''CONFIG GET <parameter>''':取得运行中的 Redis 服务器的配置参数。 | |||
* 在 Redis 2.4 版本中,有部分参数没有办法用 CONFIG GET 访问,但在 Redis 2.6 版本后,所有配置参数都已经可以用 CONFIG GET 访问; | |||
* 接受单个参数 parameter 作为搜索关键字,查找所有匹配的配置参数; | |||
: <syntaxhighlight lang="bash" highlight=""> | |||
# 获取特定的参数 | |||
redis> CONFIG GET slowlog-max-len | |||
1) "slowlog-max-len" | |||
2) "1000" | |||
# 获取以 s 开头的配置参数及参数的值 | |||
redis> CONFIG GET s* | |||
1) "save" # 参数名:save | |||
2) "900 1 300 10 60 10000" # save 参数的值 | |||
3) "slave-serve-stale-data" # 参数名: slave-serve-stale-data | |||
4) "yes" # slave-serve-stale-data 参数的值 | |||
5) "set-max-intset-entries" # ... | |||
6) "512" | |||
7) "slowlog-log-slower-than" | |||
8) "1000" | |||
9) "slowlog-max-len" | |||
10) "1000" | |||
# 列出 CONFIG GET 命令支持的所有参数 | |||
redis> CONFIG GET * | |||
1) "dir" | |||
2) "/var/lib/redis" | |||
3) "dbfilename" | |||
4) "dump.rdb" | |||
5) "requirepass" | |||
6) (nil) | |||
7) "masterauth" | |||
8) (nil) | |||
9) "maxmemory" | |||
10) "0" | |||
11) "maxmemory-policy" | |||
12) "volatile-lru" | |||
13) "maxmemory-samples" | |||
14) "3" | |||
15) "timeout" | |||
16) "0" | |||
17) "appendonly" | |||
18) "no" | |||
... | |||
</syntaxhighlight> | |||
'''CONFIG SET <parameter> <value>''':动态地调整 Redis 服务器的配置,'''立即生效而无须重启'''。 | |||
: <syntaxhighlight lang="bash" highlight=""> | |||
redis> CONFIG GET slowlog-max-len | |||
1) "slowlog-max-len" | |||
2) "1024" | |||
redis> CONFIG SET slowlog-max-len 10086 | |||
OK | |||
redis> CONFIG GET slowlog-max-len | |||
1) "slowlog-max-len" | |||
2) "10086" | |||
</syntaxhighlight> | |||
'''CONFIG RESETSTAT''':重置 INFO 命令中的某些统计数据。 | |||
* 这些数据包括: | |||
** Keyspace hits(键空间命中次数) | |||
** Keyspace misses(键空间不命中次数) | |||
** Number of commands processed(执行命令的次数) | |||
** Number of connections received(连接服务器的次数) | |||
** Number of expired keys(过期key的数量) | |||
** Number of rejected connections(被拒绝的连接数量) | |||
** Latest fork(2) time(最后执行 fork(2) 的时间) | |||
** The aof_delayed_fsync counter(aof_delayed_fsync 计数器的值) | |||
'''CONFIG REWRITE''':对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写。【将服务器当前所使用的配置(可能由“CONFIG SET”修改过)记录到 redis.conf 文件中】 | |||
* 重写会以非常保守的方式进行: | |||
*# 原有 redis.conf 文件的整体结构和注释会被尽可能地保留。 | |||
*# 如果一个选项已经存在于原有 redis.conf 文件中 ,那么对该选项的重写会在选项原本所在的位置(行号)上进行。 | |||
*# 如果一个选项不存在于原有 redis.conf 文件中,并且该选项被设置为默认值,那么重写程序不会将这个选项添加到重写后的 redis.conf 文件中。 | |||
*# 如果一个选项不存在于原有 redis.conf 文件中,并且该选项被设置为非默认值,那么这个选项将被添加到重写后的 redis.conf 文件的末尾。 | |||
*# 未使用的行会被留白。 | |||
*#: 比如说, 如果你在原有 redis.conf 文件上设置了数个关于 save 选项的参数,但现在你将这些 save 参数的一个或全部都关闭了,那么这些不再使用的参数原本所在的行就会变成空白的。 | |||
* 即使启动服务器时所指定的 redis.conf 文件已经不再存在,该命令也可以重新构建并生成出一个新的 redis.conf 文件。 | |||
* 如果启动服务器时没有载入 redis.conf 文件, 那么该命令将引发一个错误。 | |||
* '''对 redis.conf 文件的重写是原子性的''',并且是一致的: 如果重写出错或重写期间服务器崩溃,那么重写失败,原有 redis.conf 文件不会被修改;如果重写成功,那么 redis.conf 文件为重写后的新文件。 | |||
==== “CONFIG命令”和“redis.conf文件”所使用的格式的不同 ==== | |||
所有被 CONFIG SET 所支持的配置参数都可以在配置文件 redis.conf 中找到,不过 CONFIG GET 和 CONFIG SET 使用的格式和 redis.conf 文件所使用的格式有以下两点不同: | |||
# 10kb 、 2gb 这些在配置文件中所使用的储存单位缩写,不可以用在 CONFIG 命令中, CONFIG SET 的值只能通过数字值显式地设定。 | |||
#: 像“CONFIG SET xxx 1k”这样的命令是错误的,正确的格式是“CONFIG SET xxx 1000”。 | |||
# “save”选项在 redis.conf 中是用多行文字储存的,但在 CONFIG GET 命令中,它只打印一行文字。 | |||
#: <syntaxhighlight lang="bash" highlight=""> | |||
# 以下是 save 选项在 redis.conf 文件中的表示: | |||
save 900 1 | |||
save 300 10 | |||
save 60 10000 | |||
# 但是 CONFIG GET 命令的输出只有一行: | |||
redis> CONFIG GET save | |||
1) "save" | |||
2) "900 1 300 10 60 10000" | |||
</syntaxhighlight> | |||
=== 详述:“xxx”、“xxx”、“xxx”、“xxx” === | === 详述:“xxx”、“xxx”、“xxx”、“xxx” === |
2021年10月8日 (五) 21:51的版本
关于
Redis 使用过程中可能用到的命令。
参考:
redis英文版命令大全:“https://redis.io/commands”redis中文版命令大全:“http://redisdoc.com/”- Redis 命令参考:“http://doc.redisfans.com/”
Key(键)
redis 以 key-value 存储数据,所有的操作均为对 key 的操作。
命令 | 说明 |
---|---|
keys <pattern> | 查找所有符合给定模式( pattern)的 key 。如“keys *”:列出所有的key; |
type <key> | 查看 key 所储存的值的类型 |
exists <key> | 检查某个key是否存在 |
del <key> | 删除 key |
move <key> db | 将当前库的 key 移动到给定的库db中,比如:“move k1 2” |
randomkey <key> | 从当前数据库中随机返回一个 key 。 |
rename <key> <newkey> | 修改 key 的名称为 newkey |
renamenx <key> <newkey> | 修改 key 的名称为 newkey (仅当 newkey 不存在时)。 |
expire <key> seconds | 设置 key 的值的过期时间,以秒计。 |
expireat <key> timestamp | 设置 key 的值的过期时间,时间参数是 UNIX 时间戳(unix timestamp)。 |
pexpire <key> milliseconds | 设置 key 的过期时间,以毫秒计。 |
pexpireat <key> milliseconds-timestamp | 设置 key 过期时间的时间戳(unix timestamp) ,以毫秒计。 |
ttl <key> | 以秒为单位,返回给定 key 的剩余生存时间:-1 永不过期,-2 已过期或 key 不存在。ttl(time to live) |
pttl <key> | 以毫秒为单位,返回 key 的剩余的过期时间。 |
presist <key> | 移除 key 的过期时间,key 将持久保持。 |
dump <key> | 序列化给定 key ,并返回被序列化的值。
|
restore <key> <ttl> <serialized-value> | 反序列化给定的序列化值,并将它和给定的 key 关联。
|
object <subcommand> [arguments [arguments]] | OBJECT 命令允许从内部察看给定 key 的 Redis 对象。
|
migrate <host> <port> <key> <destination-db>
<timeout> [COPY] [REPLACE] |
将 key 原子性地从当前实例传送到目标实例的指定数据库上。(原实例会被删除)
|
sort <key> [BY pattern] [LIMIT offset count]
[GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination] |
返回或保存给定列表、集合、有序集合 key 中经过排序的元素。
|
scan <cursor> [MATCH pattern] [COUNT count] | 迭代数据库中的数据库键。
|
详述:“dump”与“restore”
dump:序列化给定 key ,并返回被序列化的值:
dump <key>
- 使用 “RESTORE” 命令可以将这个值反序列化为 Redis 键。
- 序列化生成的值有以下几个特点:
- 它带有 64 位的校验和,用于检测错误, RESTORE 在进行反序列化之前会先检查校验和。
- 值的编码格式和 RDB 文件保持一致。
- RDB 版本会被编码在序列化值当中,如果因为 Redis 的版本不同造成 RDB 格式不兼容,那么 Redis 会拒绝对这个值进行反序列化操作。
- 序列化的值不包括任何生存时间信息。
restore:反序列化给定的序列化值,并将它和给定的 key 关联:
restore <key> <ttl> <serialized-value>
- 参数 ttl 以毫秒为单位为 key 设置生存时间;如果 ttl 为 0 ,那么不设置生存时间。
- RESTORE 在执行反序列化之前会先对序列化值的 RDB 版本和数据校验和进行检查,如果 RDB 版本不相同或者数据不完整的话,那么 RESTORE 会拒绝进行反序列化,并返回一个错误。
示例:
redis> SET greeting "hello, dumping world!"
OK
redis> DUMP greeting
"\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde"
redis> RESTORE greeting-again 0 "\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde"
OK
redis> GET greeting-again
"hello, dumping world!"
redis> RESTORE fake-message 0 "hello moto moto blah blah" ; 使用错误的值进行反序列化
(error) ERR DUMP payload version or checksum are wrong
详述:“object”
object:OBJECT 命令允许从内部察看给定 key 的 Redis 对象。
object subcommand [arguments [arguments]]
- 它通常用在除错(debugging)或者了解为了节省空间而对 key 使用特殊编码的情况。
- 当将 Redis 用作缓存程序时,你也可以通过 OBJECT 命令中的信息,决定 key 的驱逐策略(eviction policies)。
- OBJECT 命令有多个子命令:
- “OBJECT REFCOUNT <key>”:返回给定 key 引用所储存的值的次数。此命令主要用于除错。
- “OBJECT ENCODING <key>”:返回给定 key 锁储存的值所使用的内部表示(representation)。
- “OBJECT IDLETIME <key>”:返回给定 key 自储存以来的空转时间(idle, 没有被读取也没有被写入),以秒为单位。
- 对象可以以多种方式编码:
- 字符串可以被编码为 raw(一般字符串)或 int(用字符串表示64位数字是为了节约空间)。
- 列表可以被编码为 ziplist 或 linkedlist 。 ziplist 是为节约大小较小的列表空间而作的特殊表示。
- 集合可以被编码为 intset 或者 hashtable 。 intset 是只储存数字的小集合的特殊表示。
- 哈希表可以编码为 zipmap 或者 hashtable 。 zipmap 是小哈希表的特殊表示。
- 有序集合可以被编码为 ziplist 或者 skiplist 格式。 ziplist 用于表示小的有序集合,而 skiplist 则用于表示任何大小的有序集合。
- 假如你做了什么让 Redis 没办法再使用节省空间的编码时(比如将一个只有 1 个元素的集合扩展为一个有 100 万个元素的集合),特殊编码类型(specially encoded types)会自动转换成通用类型(general type)。
示例:
redis> SET game "COD" # 设置一个字符串
OK
redis> OBJECT REFCOUNT game # 只有一个引用
(integer) 1
redis> OBJECT IDLETIME game # 等待一阵。。。然后查看空转时间
(integer) 90
redis> GET game # 提取game, 让它处于活跃(active)状态
"COD"
redis> OBJECT IDLETIME game # 不再处于空转
(integer) 0
redis> OBJECT ENCODING game # 字符串的编码方式
"raw"
redis> SET phone 15820123123 # 大的数字也被编码为字符串
OK
redis> OBJECT ENCODING phone
"raw"
redis> SET age 20 # 短数字被编码为 int
OK
redis> OBJECT ENCODING age
"int"
详述:“migrate”
migrate:将 key 原子性地从当前实例传送到目标实例的指定数据库上。
migrate <host> <port> <key> <destination-db> <timeout> [COPY] [REPLACE]
- 一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。
- 这个命令是一个原子操作,它在执行的时候会阻塞进行迁移的两个实例,直到以下任意结果发生:迁移成功,迁移失败,等到超时。
内部实现:
migrate 在当前实例对给定 key 执行【DUMP】 命令 ,将它序列化,然后传送到目标实例,目标实例再使用 【RESTORE】 对数据进行反序列化,并将反序列化所得的数据添加到数据库中;当前实例就像目标实例的客户端那样,只要看到 RESTORE 命令返回 OK ,它就会调用 【DEL】 删除自己数据库上的 key 。
- “timeout”参数以毫秒为格式,指定当前实例和目标实例“进行沟通的最大间隔时间”。(即,数据传送的时间不能超过这个 timeout 数,但操作并不一定要在 timeout 毫秒内完成)
- 如果在传送数据时发生 IO 错误,或者达到了超时时间,那么命令会停止执行,并返回一个特殊的错误: IOERR。
- 当 IOERR 出现时,有以下两种可能:
- key 可能存在于两个实例;
- key 可能只存在于当前实例;
- 唯一不可能发生的情况就是丢失 key。因此,如果一个客户端执行 MIGRATE 命令遇上 IOERR 错误,那么这个客户端唯一要做的就是检查自己数据库上的 key 是否已经被正确地删除。【!!!】
- 如果在传送数据时发生 IO 错误,或者达到了超时时间,那么命令会停止执行,并返回一个特殊的错误: IOERR。
- 可选项:
- COPY :不移除源实例上的 key;
- REPLACE :替换目标实例上已存在的 key;
示例:
- 先启动两个 Redis 服务实例,一个使用默认的 6379 端口,一个使用 7777 端口。
$ ./redis-server & [1] 3557 ... $ ./redis-server --port 7777 & [2] 3560 ...
- 然后用客户端连上 6379 端口的实例,设置一个键,然后将它迁移到 7777 端口的实例上:
$ ./redis-cli redis 127.0.0.1:6379> flushdb OK redis 127.0.0.1:6379> SET greeting "Hello from 6379 instance" OK redis 127.0.0.1:6379> MIGRATE 127.0.0.1 7777 greeting 0 1000 OK redis 127.0.0.1:6379> EXISTS greeting # 迁移成功后 key 被删除 (integer) 0
- 使用另一个客户端,查看 7777 端口上的实例:
$ ./redis-cli -p 7777 redis 127.0.0.1:7777> GET greeting "Hello from 6379 instance"
详述:“sort”
sort:返回或保存给定列表、集合、有序集合 key 中经过排序的元素。
sort <key> [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination]
- 排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。
用法如下:
一般排序
一般用法:“SORT key”和“SORT key DESC”分别返回键值“从小到大”和“从大到小”排序的结果;
redis> LPUSH today_cost 30 1.5 10 8 (integer) 4 redis> SORT today_cost 1) "1.5" 2) "8" 3) "10" 4) "30" redis 127.0.0.1:6379> SORT today_cost DESC 1) "30" 2) "10" 3) "8" 4) "1.5"
对字符串排序
需要对字符串进行排序时, 需要显式地在 SORT 命令之后添加 ALPHA 修饰符;
redis> LPUSH website "www.reddit.com" (integer) 1 redis> LPUSH website "www.slashdot.com" (integer) 2 redis> LPUSH website "www.infoq.com" (integer) 3 redis> SORT website 1) "www.infoq.com" 2) "www.slashdot.com" 3) "www.reddit.com" redis> SORT website ALPHA 1) "www.infoq.com" 2) "www.reddit.com" 3) "www.slashdot.com"
- 如果系统正确地设置了 LC_COLLATE 环境变量的话,Redis能识别 UTF-8 编码。【!】
限制排序结果
排序之后返回元素的数量可以通过 LIMIT 修饰符进行限制, 修饰符可接受两个参数:
- offset:指定要跳过的元素数量;
- count:指定跳过 offset 个指定的元素之后,要返回多少个对象;
redis 127.0.0.1:6379> RPUSH rank 1 3 5 7 9 (integer) 5 redis 127.0.0.1:6379> RPUSH rank 2 4 6 8 10 (integer) 10 redis 127.0.0.1:6379> SORT rank LIMIT 0 5 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" redis 127.0.0.1:6379> SORT rank LIMIT 0 5 DESC 1) "10" 2) "9" 3) "8" 4) "7" 5) "6"
使用外部 key 进行排序
可以使用外部 key 的数据作为权重,代替默认的直接对比键值的方式来进行排序;【!!!】
- 假设现在有用户数据如下:
uid user_name_{uid} user_level_{uid} 1 admin 9999 2 jack 10 3 peter 25 4 mary 70
- 输入到 Redis 中:【如下,保存信息的不同类型 key 之间是有关系的:“uid”的值,可以替换“user_name_{uid}”、“user_level_{uid}”的占位符】
# admin redis 127.0.0.1:6379> LPUSH uid 1 (integer) 1 redis 127.0.0.1:6379> SET user_name_1 admin OK redis 127.0.0.1:6379> SET user_level_1 9999 OK # jack redis 127.0.0.1:6379> LPUSH uid 2 (integer) 2 redis 127.0.0.1:6379> SET user_name_2 jack OK redis 127.0.0.1:6379> SET user_level_2 10 OK # peter redis 127.0.0.1:6379> LPUSH uid 3 (integer) 3 redis 127.0.0.1:6379> SET user_name_3 peter OK redis 127.0.0.1:6379> SET user_level_3 25 OK # mary redis 127.0.0.1:6379> LPUSH uid 4 (integer) 4 redis 127.0.0.1:6379> SET user_name_4 mary OK redis 127.0.0.1:6379> SET user_level_4 70 OK
- 默认情况下, SORT uid 直接按 uid 中的值排序:
redis 127.0.0.1:6379> SORT uid 1) "1" # admin 2) "2" # jack 3) "3" # peter 4) "4" # mary
- 使用 BY 选项,可以让 uid 按其他键的元素来排序:
# 让 uid 键按照 user_level_{uid} 的大小来排序 redis 127.0.0.1:6379> SORT uid BY user_level_* 1) "2" # jack , level = 10 2) "3" # peter, level = 25 3) "4" # mary, level = 70 4) "1" # admin, level = 9999
- 【上例中“user_level_*”是一个占位符, 它先取出 uid 中的值, 然后再用这个值来查找相应的键】
- 【如:先取出 uid 的值 1、2、3、4 ,然后使用 user_level_1、user_level_2、user_level_3、user_level_4 的值作为排序 uid 的权重】
- 使用 GET 选项,可以根据排序的结果来取出相应的键值:
# 先排序 uid , 再取出键 user_name_{uid} 的值 redis 127.0.0.1:6379> SORT uid GET user_name_* 1) "admin" 2) "jack" 3) "peter" 4) "mary"
- 组合使用 BY 和 GET,可以让排序结果以更直观的方式显示出来:
# 先按 user_level_{uid} 来排序 uid 列表, 再取出相应的 user_name_{uid} 的值 redis 127.0.0.1:6379> SORT uid BY user_level_* GET user_name_* 1) "jack" # level = 10 2) "peter" # level = 25 3) "mary" # level = 70 4) "admin" # level = 9999
- 同时使用多个 GET 选项, 获取多个外部键的值:
# 按 uid 分别获取 user_level_{uid} 和 user_name_{uid} redis 127.0.0.1:6379> SORT uid GET user_level_* GET user_name_* 1) "9999" # level 2) "admin" # name 3) "10" 4) "jack" 5) "25" 6) "peter" 7) "70" 8) "mary"
- GET 选项可以用 “#” 获取被排序键的值:
redis 127.0.0.1:6379> SORT uid GET # GET user_level_* GET user_name_* 1) "1" # uid 2) "9999" # level 3) "admin" # name 4) "2" 5) "10" 6) "jack" 7) "3" 8) "25" 9) "peter" 10) "4" 11) "70" 12) "mary"
- 通过将一个不存在的键作为参数传给 BY 选项, 可以让 SORT 跳过排序操作, 直接返回结果:【获取外部键,但不进行排序】
redis 127.0.0.1:6379> SORT uid BY not-exists-key 1) "4" 2) "3" 3) "2" 4) "1"
- 【这种用法在单独使用时,没什么实际用处。——不过,将其与 GET 选项配合,就可以在不排序的情况下,获取多个外部键,相当于执行一个整合的获取操作(类似于 SQL 数据库的 “join” 关键字)】
redis 127.0.0.1:6379> SORT uid BY not-exists-key GET # GET user_level_* GET user_name_* 1) "4" # id 2) "70" # level 3) "mary" # name 4) "3" 5) "25" 6) "peter" 7) "2" 8) "10" 9) "jack" 10) "1" 11) "9999" 12) "admin"
- 除了可以将字符串键之外, 哈希表也可以作为 GET 或 BY 选项的参数来使用:【将哈希表作为 GET 或 BY 的参数】
- 对于上述数据,用一个带有 “name” 域和 “level” 域的哈希表 “user_info_{uid}” 来保存用户的名字和级别信息:
redis 127.0.0.1:6379> LPUSH uid 1 (integer) 1 redis 127.0.0.1:6379> LPUSH uid 2 (integer) 1 redis 127.0.0.1:6379> LPUSH uid 3 (integer) 1 redis 127.0.0.1:6379> LPUSH uid 4 (integer) 1 redis 127.0.0.1:6379> HMSET user_info_1 name admin level 9999 OK redis 127.0.0.1:6379> HMSET user_info_2 name jack level 10 OK redis 127.0.0.1:6379> HMSET user_info_3 name peter level 25 OK redis 127.0.0.1:6379> HMSET user_info_4 name mary level 70 OK
- 则,BY 和 GET 选项都可以用 key->field 的格式来获取哈希表中的域的值, 其中 key 表示哈希表键, 而 field 则表示哈希表的域:
redis 127.0.0.1:6379> SORT uid BY user_info_*->level 1) "2" 2) "3" 3) "4" 4) "1" redis 127.0.0.1:6379> SORT uid BY user_info_*->level GET user_info_*->name 1) "jack" 2) "peter" 3) "mary" 4) "admin"
保存排序结果
通过给 STORE 选项指定一个 key 参数,可以将排序结果保存到给定的键上:
- 默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作;
- 如果被指定的 key 已存在,那么原有的值将被排序结果覆盖;
redis 127.0.0.1:6379> RPUSH numbers 1 3 5 7 9 (integer) 5 redis 127.0.0.1:6379> RPUSH numbers 2 4 6 8 10 (integer) 10 redis 127.0.0.1:6379> LRANGE numbers 0 -1 1) "1" 2) "3" 3) "5" 4) "7" 5) "9" 6) "2" 7) "4" 8) "6" 9) "8" 10) "10" redis 127.0.0.1:6379> SORT numbers STORE sorted-numbers (integer) 10 redis 127.0.0.1:6379> LRANGE sorted-numbers 0 -1 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6" 7) "7" 8) "8" 9) "9" 10) "10"
- 【可以通过将 SORT 命令的执行结果保存,并用 EXPIRE 为结果设置生存时间,以此来产生一个 SORT 操作的结果缓存。】???
- (这样就可以避免对 SORT 操作的频繁调用:只有当结果集过期时,才需要再调用一次 SORT 操作)
- 【为了正确实现这一用法,可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行 SORT 操作,并保存为结果集),具体参见 SETNX 命令】???
详述:“scan”
scan:SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements):
- “SCAN”:用于迭代当前数据库中的数据库键;
- “HSCAN”:用于迭代哈希(“hash”)键中的键值对;
- “SSCAN”:用于迭代集合(“set”)键中的元素;
- “ZSCAN”:用于迭代有序集合(“zset”)中的元素(包括元素成员和元素分值)。
四者的区别:
- 参数:
- HSCAN、SSCAN、ZSCAN 命令的第一个参数总是一个数据库键;
- 而 SCAN 命令则不需要在第一个参数提供任何数据库键,因为它迭代的是当前数据库中的所有键。
- 返回值:
- SCAN 返回的每个元素都是一个数据库键。
- HSCAN 返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
- SSCAN 返回的每个元素都是一个集合成员。
- ZSCAN 返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。
以上列出的四个命令都支持增量式迭代, 它们每次执行都只会返回少量元素, 所以这些命令可以用于生产环境, 而不会出现像 “KEYS” 命令、 “SMEMBERS” 命令带来的问题 —— 当 KEYS 命令被用于处理一个大的数据库时, 又或者 SMEMBERS 命令被用于处理一个大的集合键时, 它们可能会阻塞服务器达数秒之久。 不过, 增量式迭代命令也不是没有缺点的:举个例子,使用 SMEMBERS 命令可以返回集合键当前包含的所有元素,但是对于 SCAN 这类增量式迭代命令来说,因为在对键进行增量式迭代的过程中,键可能会被修改,所以增量式迭代命令【只能对被返回的元素提供有限的保证(offer limited guarantees about the returned elements)】。
基本用法
SCAN 命令是一个【基于游标的迭代器(cursor based iterator)】: SCAN 命令每次被调用之后,都会向用户返回一个新的【游标】,用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数,以此来延续之前的迭代过程。
用法:
scan <cursor> [MATCH <pattern>] [COUNT <count>]
- 当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束;
- 以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历(full iteration)。
- SCAN 命令的回复是一个包含两个元素的数组:
- 第一个数组元素:用于进行下一次迭代的新游标;
- 第二个数组元素:数组,包含了所有被迭代的元素。
示例:
redis 127.0.0.1:6379> scan 0 1) "17" 2) 1) "key:12" 2) "key:8" 3) "key:4" 4) "key:14" 5) "key:16" 6) "key:17" 7) "key:15" 8) "key:10" 9) "key:3" 10) "key:7" 11) "key:1" redis 127.0.0.1:6379> scan 17 1) "0" 2) 1) "key:5" 2) "key:18" 3) "key:0" 4) "key:2" 5) "key:19" 6) "key:13" 7) "key:6" 8) "key:9" 9) "key:11"
- 第一次迭代使用 0 作为游标, 表示开始一次新的迭代;
- 第二次迭代使用的是第一次迭代时返回的游标, 也即是命令回复第一个元素的值 —— 17。
迭代的保证
迭代命令的保证:
SCAN 命令,以及其他增量式迭代命令,在进行完整遍历的情况下可以为用户带来以下保证: 从完整遍历开始直到完整遍历结束期间,一直存在于数据集内的所有元素都会被完整遍历返回; 【这意味着:如果有一个元素,它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中,那么 SCAN 命令总会在某次迭代中将这个元素返回给用户。】 然而因为增量式命令仅仅使用游标来记录迭代状态, 所以这些命令带有以下缺点: 1、【同一个元素可能会被返回多次】。——处理重复元素的工作交由应用程序负责,比如说,可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。 2、【如果一个元素是在迭代过程中被添加到数据集的,又或者是在迭代过程中从数据集中被删除的,那么这个元素可能会被返回,也可能不会,这是未定义的(undefined)】。
迭代终结的保证:
增量式迭代命令所使用的算法只保证【在数据集的大小有界(bounded)的情况下,迭代才会停止】,换句话说,如果被迭代数据集的大小不断地增长的话,增量式迭代命令可能永远也无法完成一次完整迭代。 从直觉上可以看出, 当一个数据集不断地变大时, 想要访问这个数据集中的所有元素就需要做越来越多的工作, 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。
每次执行返回的元素数量
1、增量式迭代命令【并不保证每次执行都返回某个给定数量的元素】。 2、增量式命令甚至可能会返回零个元素,但【只要命令返回的游标不是 0,应用程序就不应该将迭代视作结束】。 不过命令返回的元素数量总是符合一定规则的, 在实际中: 1、对于一个大数据集来说, 增量式迭代命令每次最多可能会返回数十个元素; 2、对于一个足够小的数据集来说,如果这个数据集的底层表示为编码数据结构(encoded data structure,适用于是小集合键、小哈希键和小有序集合键),那么增量迭代命令将在一次调用中返回数据集中的所有元素。 最后, 用户可以通过增量式迭代命令提供的 【“COUNT” 选项,来指定每次迭代返回元素的最大值】。
COUNT 选项
COUNT选项:用于告知命令在每次迭代中应该从数据集里返回多少元素。
- COUNT 参数的默认值为 10;
- COUNT 选项只是对增量式迭代命令的一种提示(hint),但是在大多数情况下,这种提示都是有效的:
- 在迭代一个足够大的、由哈希表实现的数据库、集合键、哈希键或者有序集合键时,如果用户没有使用MATCH选项,那么命令返回的元素数量通常和 COUNT 选项指定的一样,或者比 COUNT 选项指定的数量稍多一些。
- 在迭代一个编码为整数集合(intset,一个只由整数值构成的小集合)、 或者编码为压缩列表(ziplist,由不同值构成的一个小哈希或者一个小有序集合)时, 增量式迭代命令通常会无视 COUNT 选项指定的值, 在第一次迭代就将数据集包含的所有元素都返回给用户。
- 并非每次迭代都要使用相同的 COUNT 值:用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。
MATCH 选项
MATCH选项:用于让命令只返回和给定模式相匹配的元素。
示例:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood (integer) 6 redis 127.0.0.1:6379> sscan myset 0 match f* 1) "0" 2) 1) "foo" 2) "feelsgood" 3) "foobar"
- 对元素的模式匹配工作是在命令从数据集中取出元素之后,向客户端返回元素之前的这段时间内进行的,所以如果被迭代的数据集中只有少量元素和模式相匹配,那么迭代命令或许会在多次执行中都不返回任何元素。
redis 127.0.0.1:6379> scan 0 MATCH *11* 1) "288" 2) 1) "key:911" redis 127.0.0.1:6379> scan 288 MATCH *11* 1) "224" 2) (empty list or set) redis 127.0.0.1:6379> scan 224 MATCH *11* 1) "80" 2) (empty list or set) redis 127.0.0.1:6379> scan 80 MATCH *11* 1) "176" 2) (empty list or set) redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000 1) "0" 2) 1) "key:611" 2) "key:711" 3) "key:118" 4) "key:117" 5) "key:311" 6) "key:112" 7) "key:111" 8) "key:110" 9) "key:113" 10) "key:211" 11) "key:411" 12) "key:115" 13) "key:116" 14) "key:114" 15) "key:119" 16) "key:811" 17) "key:511" 18) "key:11"
并发执行、终止迭代
客户端每次执行迭代都需要传入一个游标,并在迭代执行之后获得一个新的游标,而这个游标就包含了迭代的所有状态,所以,【服务器无须为迭代记录任何状态】。 正因如此: 1、【在同一时间,可以有任意多个客户端对同一数据集进行迭代】。 2、【客户端可以在中途停止一个迭代,而无须对服务器进行任何通知】,即使有任意数量的迭代在中途停止, 也不会产生任何问题。
使用错误的游标进行增量式迭代
使用间断的(broken)、负数、超出范围或者其他“非正常的游标”来执行增量式迭代并【不会造成服务器崩溃,但可能会让命令产生“未定义的行为”(增量式命令对返回值所做的保证可能会不再为真)】。 只有两种游标是合法的: 1、在开始一个新的迭代时,游标必须为 0。 2、增量式迭代命令在执行之后返回的,用于延续(continue)迭代过程的游标。
数据类型
- 见:
Pub/Sub(发布/订阅)
- 见:“Redis:发布/订阅”
Transaction(事务)
- 见:“Redis:事务”
Script(脚本)
Redis 脚本使用 Lua 解释器来执行脚本。
- Redis 2.6 版本通过内嵌支持 Lua 环境。
- 执行脚本的常用命令为 EVAL。
命令 | 描述 |
---|---|
EVAL <script> <numkeys> <key> [<key>] <arg> [<arg>] | 对 Lua 脚本进行求值。(执行 Lua 脚本)
|
EVALSHA <sha1> <numkeys> <key> [<key>] <arg> [<arg>] | 根据给定的 sha1 校验码,对缓存在服务器中的脚本进行求值。(根据 sha1 执行缓存的 Lua 脚本)
示例:
|
SCRIPT LOAD <script> | 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。
|
SCRIPT EXISTS <script> [<script>] | 给定一个或多个脚本的 SHA1 校验和,返回一个包含 0 和 1 的列表,表示校验和所指定的脚本是否已经被保存在缓存当中。
示例:
|
SCRIPT FLUSH | 清除所有 Lua 脚本缓存。 |
SCRIPT KILL | 杀死当前正在运行的 Lua 脚本。
|
详述:“EVAL”
EVAL:从 Redis 2.6.0 版本开始,通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行求值。
EVAL <script> <numkeys> <key> [<key> ...] <arg> [<arg> ...]
- “script”:参数是一段 Lua 5.1 脚本程序,它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。
- “numkeys”:参数用于指定键名参数的个数。
- “key [key ...]”:键名参数,从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问(KEYS[1],KEYS[2],以此类推)。
- “arg [arg ...]”:附加参数,在命令的最后,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似(ARGV[1]、ARGV[2],诸如此类)。
示例:
> 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.pcall()
- 这两个函数的唯一区别在于它们使用不同的方式处理执行命令所产生的错误;
- redis.call() 和 redis.pcall() 两个函数的参数可以是任何格式良好的 Redis 命令;
EVAL 命令中,脚本里使用的所有键都应该由 KEYS 数组来传递:
- 示例:
# 违反 EVAL 命令语义 > eval "return redis.call('set','foo','bar')" 0 OK # 符合 EVAL 命令语义 > eval "return redis.call('set',KEYS[1],'bar')" 1 foo OK
要求使用正确的形式来传递键(key)是有原因的,因为不仅仅是 EVAL 这个命令,所有的 Redis 命令,在执行之前都会被分析,籍此来确定命令会对哪些键进行操作。 因此,对于 EVAL 命令来说,必须使用正确的形式来传递键,才能确保分析工作正确地执行。除此之外,使用正确的形式来传递键还有很多其他好处,它的一个特别重要的用途就是确保 Redis 集群可以将你的请求发送到正确的集群节点。(对 Redis 集群的工作还在进行当中,但是脚本功能被设计成可以与集群功能保持兼容。)不过,这条规矩并不是强制性的,从而使得用户有机会滥用(abuse) Redis 单实例配置(single instance configuration),代价是这样写出的脚本不能被 Redis 集群所兼容。
在 Lua 数据类型和 Redis 数据类型之间转换
1、【当 Lua 通过 call() 或 pcall() 函数执行 Redis 命令的时候,命令的返回值会被转换成 Lua 数据结构】。 2、【当 Lua 脚本在 Redis 内置的解释器里运行时,Lua 脚本的返回值也会被转换成 Redis 协议(protocol),然后由 EVAL 将值返回给客户端】。 数据类型之间的转换遵循这样一个设计原则:如果将一个 Redis 值转换成 Lua 值,之后再将转换所得的 Lua 值转换回 Redis 值,那么这个转换所得的 Redis 值应该和最初时的 Redis 值一样。 换句话说, Lua 类型和 Redis 类型之间存在着一一对应的转换关系。
从 Redis 转换到 Lua :
- Redis integer reply -> Lua number :Redis 整数转换成 Lua 数字
- Redis bulk reply -> Lua string :Redis bulk 回复转换成 Lua 字符串
- Redis multi bulk reply -> Lua table (may have other Redis data types nested) :Redis 多条 bulk 回复转换成 Lua 表,表内可能有其他别的 Redis 数据类型
- Redis status reply -> Lua table with a single ok field containing the status :Redis 状态回复转换成 Lua 表,表内的 ok 域包含了状态信息
- Redis error reply -> Lua table with a single err field containing the error :Redis 错误回复转换成 Lua 表,表内的 err 域包含了错误信息
- Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type :Redis 的 Nil 回复和 Nil 多条回复转换成 Lua 的布尔值 false
从 Lua 转换到 Redis:
- Lua number -> Redis integer reply :Lua 数字转换成 Redis 整数
- Lua string -> Redis bulk reply :Lua 字符串转换成 Redis bulk 回复
- Lua table (array) -> Redis multi bulk reply :Lua 表(数组)转换成 Redis 多条 bulk 回复
- Lua table with a single ok field -> Redis status reply :一个带单个 ok 域的 Lua 表,转换成 Redis 状态回复
- Lua table with a single err field -> Redis error reply :一个带单个 err 域的 Lua 表,转换成 Redis 错误回复
- Lua boolean false -> Redis Nil bulk reply :Lua 的布尔值 false 转换成 Redis 的 Nil bulk 回复
- 从 Lua 转换到 Redis 有一条额外的规则,这条规则没有和它对应的从 Redis 转换到 Lua 的规则:
- Lua boolean true -> Redis integer reply with value of 1 :Lua 布尔值 true 转换成 Redis 整数回复中的 1
示例:
# 将 Lua 值转换成 Redis 值
> eval "return 10" 0
(integer) 10
# 将 Lua 值转换成 Redis 值
> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
2) "Hello World!"
# 将 Redis 值转换成 Lua 值,然后再将 Lua 值转换成 Redis 值的类型转过程
> eval "return redis.call('get','foo')" 0
"bar"
脚本的原子性
【Redis 使用单个 Lua 解释器去运行所有脚本】,并且, Redis 也保证脚本会以【原子性(atomic)】的方式执行: 当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。 这和使用 MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effect)要么是【不可见的(not visible)】,要么就是【已完成的(already completed)】。 另一方面,这也意味着,执行一个运行缓慢的脚本并不是一个好主意。写一个跑得很快很顺溜的脚本并不难,因为脚本的运行开销(overhead)非常少,但是当你不得不使用一些跑得比较慢的脚本时,请小心,因为当这些蜗牛脚本在慢吞吞地运行的时候,其他客户端会因为服务器正忙而无法执行命令。
错误处理
redis.call() 和 redis.pcall() 的唯一区别在于它们对错误处理的不同:
- redis.call():在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因:
redis> lpush foo a (integer) 1 redis> eval "return redis.call('get', 'foo')" 0 (error) ERR Error running script (call to f_282297a0228f48cd3fc6a55de6316f31422f5d17): ERR Operation against a key holding the wrong kind of value
- redis.pcall():出错时并不引发(raise)错误,而是返回一个带 err 域的 Lua 表(table),用于表示错误:
redis> lpush foo a (integer) 1 redis 127.0.0.1:6379> EVAL "return redis.pcall('get', 'foo')" 0 (error) ERR Operation against a key holding the wrong kind of value
带宽和 EVALSHA
EVAL 命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)。Redis 有一个内部的【缓存机制】,因此它不会每次都重新编译脚本,不过在很多场合,付出无谓的带宽来传送脚本主体并不是最佳选择。 为了减少带宽的消耗, Redis 实现了 EVALSHA 命令,它的作用和 EVAL 一样,都用于对脚本求值,但它接受的第一个参数不是脚本,而是【脚本的 SHA1 校验和(sum)】。
EVALSHA 命令的表现如下:
- 如果服务器还记得给定的 SHA1 校验和所指定的脚本,那么执行这个脚本;
- 如果服务器不记得给定的 SHA1 校验和所指定的脚本,那么它返回一个特殊的错误,提醒用户使用 EVAL 代替 EVALSHA;
- 客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ,并期望着要使用的脚本已经保存在服务器上了,只有当 NOSCRIPT 错误发生时,才使用 EVAL 命令重新发送脚本,这样就可以最大限度地节省带宽。
- 这也说明了执行 EVAL 命令时,“使用正确的格式来传递键名参数和附加参数”的重要性:
- 因为如果将参数硬写在脚本中,那么每次当参数改变的时候,都要重新发送脚本,即使脚本的主体并没有改变,相反,通过使用正确的格式来传递键名参数和附加参数,就可以在脚本主体不变的情况下,直接使用 EVALSHA 命令对脚本进行复用,免去了无谓的带宽消耗。
示例:
> set foo bar
OK
> eval "return redis.call('get','foo')" 0
"bar"
> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"
> evalsha ffffffffffffffffffffffffffffffffffffffff 0
(error) `NOSCRIPT` No matching script. Please use [EVAL](/commands/eval).
脚本缓存
Redis 保证【所有被运行过的脚本都会被永久保存在脚本缓存当中】,这意味着,当 EVAL 命令在一个 Redis 实例上成功执行某个脚本之后,随后针对这个脚本的所有 EVALSHA 命令都会成功执行。 刷新脚本缓存的唯一办法是显式地调用 【SCRIPT FLUSH】 命令,这个命令会清空运行过的所有脚本的缓存。 (通常只有在云计算环境中,Redis 实例被改作其他客户或者别的应用程序的实例时,才会执行这个命令。)
缓存可以长时间储存而不产生内存问题的原因是,它们的体积非常小,而且数量也非常少,即使脚本在概念上类似于实现一个新命令,即使在一个大规模的程序里有成百上千的脚本,即使这些脚本会经常修改,即便如此,储存这些脚本的内存仍然是微不足道的。 事实上,用户会发现 Redis 不移除缓存中的脚本实际上是一个好主意。比如说,对于一个和 Redis 保持持久化链接(persistent connection)的程序来说,它可以确信,执行过一次的脚本会一直保留在内存当中,因此它可以在流水线中使用 EVALSHA 命令而不必担心因为找不到所需的脚本而产生错误(稍候我们会看到在流水线中执行脚本的相关问题)。
SCRIPT 命令
Redis 提供了以下几个 SCRIPT 命令,用于对脚本子系统(scripting subsystem)进行控制:
- SCRIPT FLUSH :清除所有脚本缓存
- SCRIPT EXISTS :根据给定的脚本校验和,检查指定的脚本是否存在于脚本缓存
- SCRIPT LOAD :将一个脚本装入脚本缓存,但并不立即运行它
- SCRIPT KILL :杀死当前正在运行的脚本
纯函数脚本
在编写脚本方面,一个重要的要求就是,脚本应该被写成【纯函数(pure function)】。 也就是说,脚本应该具有以下属性: 对于同样的数据集输入,给定相同的参数,脚本执行的 Redis 写命令总是相同的。 脚本执行的操作不能依赖于任何隐藏(非显式)数据,不能依赖于脚本在执行过程中、或脚本在不同执行时期之间可能变更的状态,并且它也不能依赖于任何来自 I/O 设备的外部输入。 使用系统时间(system time),调用像 RANDOMKEY 那样的随机命令,或者使用 Lua 的随机数生成器,类似以上的这些操作,都会造成脚本的求值无法每次都得出同样的结果。
为了确保脚本符合上面所说的属性, Redis 做了以下工作:
- Lua 没有访问系统时间或者其他内部状态的命令
- Redis 会返回一个错误,阻止这样的脚本运行:这些脚本在执行随机命令之后(比如 RANDOMKEY 、 SRANDMEMBER 或 TIME 等),还会执行可以修改数据集的 Redis 命令。
- 如果脚本只是执行只读操作,那么就没有这一限制。
- 注意,随机命令并不一定就指那些带 RAND 字眼的命令,任何带有非确定性的命令都会被认为是随机命令,比如 TIME 命令就是这方面的一个很好的例子。
- 每当从 Lua 脚本中调用那些返回无序元素的命令时,执行命令所得的数据在返回给 Lua 之前会先执行一个静默(slient)的字典序排序(lexicographical sorting)。
- 举个例子,因为 Redis 的 Set 保存的是无序的元素,所以在 Redis 命令行客户端中直接执行 SMEMBERS,返回的元素是无序的,但是,假如在脚本中执行 redis.call("smembers", KEYS[1]) ,那么返回的总是排过序的元素。
- 对 Lua 的伪随机数生成函数 “math.random” 和 “math.randomseed” 进行修改,使得每次在运行新脚本的时候,总是拥有同样的 seed 值。
- 这意味着,每次运行脚本时,只要不使用 math.randomseed ,那么 math.random 产生的随机数序列总是相同的。
示例:
- 编写一个 Redis 脚本,这个脚本从列表中弹出 N 个随机数。一个 Ruby 写的例子如下:
require 'rubygems' require 'redis' r = Redis.new RandomPushScript = <<EOF local i = tonumber(ARGV[1]) local res while (i > 0) do res = redis.call('lpush',KEYS[1],math.random()) i = i-1 end return res EOF r.del(:mylist) puts r.eval(RandomPushScript,[:mylist],[10,rand(2**32)])
- 这个程序每次运行都会生成带有以下元素的列表:
> lrange mylist 0 -1 1) "0.74509509873814" 2) "0.87390407681181" 3) "0.36876626981831" 4) "0.6921941534114" 5) "0.7857992587545" 6) "0.57730350670279" 7) "0.87046522734243" 8) "0.09637165539729" 9) "0.74990198051087" 10) "0.17082803611217"
上面的 Ruby 程序每次都只生成同样的列表,用途并不是太大。那么,该怎样修改这个脚本,使得它仍然是一个纯函数(符合 Redis 的要求),但是每次调用都可以产生不同的随机元素呢?
- 一个简单的办法是,为脚本添加一个额外的参数,让这个参数作为 Lua 的随机数生成器的 seed 值,这样的话,只要给脚本传入不同的 seed ,脚本就会生成不同的列表元素:
RandomPushScript = <<EOF local i = tonumber(ARGV[1]) local res math.randomseed(tonumber(ARGV[2])) while (i > 0) do res = redis.call('lpush',KEYS[1],math.random()) i = i-1 end return res EOF r.del(:mylist) puts r.eval(RandomPushScript,1,:mylist,10,rand(2**32))
- 尽管对于同样的 seed ,上面的脚本产生的列表元素是一样的(因为它是一个纯函数),但是只要每次在执行脚本的时候传入不同的 seed ,我们就可以得到带有不同随机元素的列表。
- Seed 会在复制(replication link)和写 AOF 文件时作为一个参数来传播,保证在载入 AOF 文件或附属节点(slave)处理脚本时, seed 仍然可以及时得到更新。
注意,Redis 实现保证 math.random 和 math.randomseed 的输出和运行 Redis 的系统架构无关,无论是 32 位还是 64 位系统,无论是小端(little endian)还是大端(big endian)系统,这两个函数的输出总是相同的。
全局变量保护
实现全局变量保护并不难,不过有时候还是会不小心而为之。一旦用户在脚本中混入了 Lua 全局状态,那么【 AOF 持久化和复制(replication)都会无法保证】,所以,请不要使用全局变量。 所以: 为了防止不必要的数据泄漏进 Lua 环境,【Redis 脚本不允许创建全局变量】。 如果一个脚本需要在多次执行之间维持某种状态,它应该【使用 Redis key 来进行状态保存】。
企图在脚本中访问一个全局变量(不论这个变量是否存在)将引起脚本停止, EVAL 命令会返回一个错误:
redis 127.0.0.1:6379> eval 'a=10' 0 (error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a'
- Lua 的 debug 工具,或者其他设施,比如打印(alter)用于实现全局保护的 meta table ,都可以用于实现全局变量保护。
- 避免引入全局变量的一个诀窍是:将脚本中用到的所有变量都使用 local 关键字定义为局部变量。
库
Redis 内置的 Lua 解释器加载了以下 Lua 库:
- base
- table
- string
- math
- debug
- cjson
- cmsgpack
- 其中 cjson 库可以让 Lua 以非常快的速度处理 JSON 数据,除此之外,其他别的都是 Lua 的标准库。
- 每个 Redis 实例都保证会加载上面列举的库,从而确保每个 Redis 脚本的运行环境都是相同的。
使用脚本散发 Redis 日志
在 Lua 脚本中,可以通过调用 redis.log 函数来写 Redis 日志(log):
redis.log(loglevel, message)
其中, message 参数是一个字符串,而 loglevel 参数可以是以下任意一个值:
- redis.LOG_DEBUG
- redis.LOG_VERBOSE
- redis.LOG_NOTICE
- redis.LOG_WARNING
- 上面的这些等级(level)和标准 Redis 日志的等级相对应。
- 对于脚本散发(emit)的日志,只有那些和当前 Redis 实例所设置的日志等级相同或更高级的日志才会被散发。【???】
示例:
redis.log(redis.LOG_WARNING, "Something is wrong with this script.")
- 执行上面的函数会产生这样的信息:
[32343] 22 Mar 15:21:39 # Something is wrong with this script.
沙箱(sandbox)和最大执行时间
脚本应该仅仅用于传递参数和对 Redis 数据进行处理,它不应该尝试去访问外部系统(比如文件系统),或者执行任何系统调用。 除此之外,脚本还有一个最大执行时间限制,它的默认值是 5 秒钟,一般正常运作的脚本通常可以在几分之几毫秒之内完成,花不了那么多时间,这个限制主要是为了防止因编程错误而造成的无限循环而设置的。
- 最大执行时间的长短由 lua-time-limit 选项来控制(以毫秒为单位),可以通过编辑 redis.conf 文件或者使用 CONFIG GET 和 CONFIG SET 命令来修改它。
当一个脚本达到最大执行时间的时候,它并不会自动被 Redis 结束,因为 Redis 必须保证脚本执行的原子性,而中途停止脚本的运行意味着可能会留下未处理完的数据在数据集(data set)里面。
因此,当脚本运行的时间超过最大执行时间后,以下动作会被执行:
- Redis 记录一个脚本正在超时运行;
- Redis 开始重新接受其他客户端的命令请求,但是只有 SCRIPT KILL 和 SHUTDOWN NOSAVE 两个命令会被处理,对于其他命令请求, Redis 服务器只是简单地返回 BUSY 错误;
- 可以使用 SCRIPT KILL 命令将一个仅执行只读命令的脚本杀死,因为只读命令并不修改数据,因此杀死这个脚本并不破坏数据的完整性。
- 如果脚本已经执行过写命令,那么唯一允许执行的操作就是 SHUTDOWN NOSAVE ,它通过停止服务器来阻止当前数据集写入磁盘。
流水线(pipeline)上下文(context)中的 EVALSHA
在流水线请求的上下文中使用 EVALSHA 命令时,要特别小心,因为在流水线中,必须保证命令的执行顺序。 【一旦在流水线中因为 EVALSHA 命令而发生 NOSCRIPT 错误,那么这个流水线就再也没有办法重新执行了,否则的话,命令的执行顺序就会被打乱。】
为了防止出现以上所说的问题,客户端库实现应该实施以下的其中一项措施:
- 总是在流水线中使用 EVAL 命令。
- 检查流水线中要用到的所有命令,找到其中的 EVAL 命令,并使用 SCRIPT EXISTS 命令检查要用到的脚本是不是全都已经保存在缓存里面了:
- 如果所需的全部脚本都可以在缓存里找到,那么就可以放心地将所有 EVAL 命令改成 EVALSHA 命令,否则的话,就要在流水线的顶端(top)将缺少的脚本用 SCRIPT LOAD 命令加上去。
详述:“SCRIPT KILL”
SCRIPT KILL:杀死当前正在运行的 Lua 脚本。
- 这个命令主要用于终止运行时间过长的脚本,比如一个因为 BUG 而发生无限 loop 的脚本,诸如此类。
- SCRIPT KILL 执行之后,当前正在运行的脚本会被杀死,执行这个脚本的客户端会从 EVAL 命令的阻塞当中退出,并收到一个错误作为返回值。
- 当且仅当这个脚本没有执行过任何写操作时,这个命令才生效。
假如当前正在运行的脚本已经执行过写操作,那么即使执行 SCRIPT KILL ,也无法将它杀死,因为这是违反 Lua 脚本的原子性执行原则的。 在这种情况下,唯一可行的办法是使用 【“SHUTDOWN NOSAVE”】 命令,通过停止整个 Redis 进程来停止脚本的运行,并防止不完整(half-written)的信息被写入数据库中。
示例:
# 没有脚本在执行时 redis> SCRIPT KILL (error) ERR No scripts in execution right now. # 成功杀死脚本时 redis> SCRIPT KILL OK (1.30s) # 尝试杀死一个已经执行过写操作的脚本,失败 redis> SCRIPT KILL (error) ERR Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command. (1.69s)
- 脚本被杀死之后,返回给执行脚本的客户端的错误
redis> EVAL "while true do end" 0 (error) ERR Error running script (call to f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): Script killed by user with SCRIPT KILL... (5.00s)
Connection(连接)
Redis 连接命令主要是用于连接 redis 服务:
命令 | 描述 |
---|---|
AUTH <password> | 连接 Redis 服务器后使用该命令解锁验证密码,之后才能使用其他 Redis 命令
|
ECHO <message> | 打印一个特定的信息 message
|
PING | 使用客户端向 Redis 服务器发送 PING,服务器运作正常则会返回 PONG
|
QUIT | 请求服务器关闭与当前客户端的连接。
|
SELECT <index> | 切换到指定的数据库
|
示例:
- “AUTH”:
# 设置密码为 secret_password redis> CONFIG SET requirepass secret_password OK # 退出再连接,让新密码对客户端生效 redis> QUIT [huangz@mypad]$ redis # 未验证密码,操作被拒绝 redis> PING (error) ERR operation not permitted # 尝试输入错误的密码 redis> AUTH wrong_password_testing (error) ERR invalid password # 输入正确的密码 redis> AUTH secret_password OK # 密码验证成功,可以正常操作命令了 redis> PING PONG # 清空密码:将密码设为空字符 redis> CONFIG SET requirepass "" OK # 重新进入客户端 redis> QUIT $ redis # 执行命令不再需要密码,清空密码操作成功 redis> PING PONG
- “PING”:
redis 127.0.0.1:6379> PING PONG
- “SELECT”:
redis> SET db_number 0 # 默认使用 0 号数据库 OK redis> SELECT 1 # 使用 1 号数据库 OK redis[1]> GET db_number # 已经切换到 1 号数据库,注意 Redis 现在的命令提示符多了个 [1] (nil) redis[1]> SET db_number 1 OK redis[1]> GET db_number "1" redis[1]> SELECT 3 # 再切换到 3 号数据库 OK redis[3]> # 提示符从 [1] 改变成了 [3]
Server(服务器)
Redis 服务器命令主要是用于管理 redis 服务:
命令 | 描述 | |
---|---|---|
持久化 | BGREWRITEAOF | 异步执行一个 AOF(AppendOnly File) 文件重写操作 |
BGSAVE | 在后台异步保存当前数据库的数据到磁盘 | |
SAVE | 同步保存数据到硬盘 | |
LASTSAVE | 返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 | |
客户端 | CLIENT GETNAME | 获取连接的名称 |
CLIENT SETNAME <connection-name> | 设置当前连接的名称 | |
CLIENT LIST | 获取连接到服务器的客户端连接列表 | |
CLIENT KILL [ip:port] [ID client-id] | 关闭客户端连接 | |
配置 | CONFIG GET <parameter> | 获取指定配置参数的值 |
CONFIG SET <parameter> <value> | 修改 redis 配置参数,无需重启 | |
CONFIG RESETSTAT | 重置 INFO 命令中的某些统计数据 | |
CONFIG REWRITE | 对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写 | |
Debug | DEBUG OBJECT <key> | 获取 key 的调试信息 |
DEBUG SEGFAULT | 让 Redis 服务崩溃【???】 | |
Flush | FLUSHALL | 删除所有数据库的所有key |
FLUSHDB | 删除当前数据库的所有key | |
复制 | SYNC | 用于复制功能(replication)的内部命令 |
PSYNC <MASTER_RUN_ID> <OFFSET> | 用于复制功能(replication)的内部命令 | |
其他 | DBSIZE | 返回当前数据库的 key 的数量 |
INFO [section] | 获取 Redis 服务器的各种信息和统计数值 | |
MONITOR | 实时打印出 Redis 服务器接收到的命令,调试用 | |
SHUTDOWN [NOSAVE] [SAVE] | 异步保存数据到硬盘,并关闭服务器 | |
SLAVEOF <host> <port> | 将当前服务器转变为指定服务器的从属服务器(slave server) | |
SLOWLOG <subcommand> [argument] | 管理 redis 的慢日志 | |
TIME | 返回当前服务器时间 | |
菜鸟看到的,不晓得有不有这些命令 | ||
CLIENT PAUSE <timeout> | 在指定时间内终止运行来自客户端的命令 | |
COMMAND | 获取 Redis 命令详情数组 | |
COMMAND COUNT | 获取 Redis 命令总数 | |
COMMAND GETKEYS | 获取给定命令的所有键 | |
COMMAND INFO <command-name> [<command-name>] | 获取指定 Redis 命令描述的数组 | |
CLUSTER SLOTS | 获取集群节点的映射数组 | |
ROLE | 返回主从实例所属的角色 |
详述:“BGREWRITEAOF”、“BGSAVE”、“SAVE”、“LASTSAVE”
BGREWRITEAOF:执行一个 AOF 文件 重写操作。
- 重写会创建一个当前 AOF 文件的体积优化版本。
- 即使 BGREWRITEAOF 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 BGREWRITEAOF 成功之前不会被修改。
- 重写操作只会在没有其他持久化工作在后台执行时被触发,也就是说:
- 如果 Redis 的子进程正在执行快照的保存工作,那么 AOF 重写的操作会被预定(scheduled),BGREWRITEAOF 的返回值仍然是 OK ,但还会加上一条额外的信息,说明 BGREWRITEAOF 要等到保存操作完成之后才能执行。
- 如果已经有别的 AOF 文件重写在执行,那么 BGREWRITEAOF 返回一个错误,并且这个新的 BGREWRITEAOF 请求也不会被预定到下次执行。
- 从 Redis 2.4 开始, AOF 重写由 Redis 自行触发, BGREWRITEAOF 仅仅用于手动触发重写操作。
- 在 Redis 2.6 或以上的版本,可以使用 INFO 命令查看 BGREWRITEAOF 是否被预定。
- 示例:
redis> BGREWRITEAOF Background append only file rewriting started
BGSAVE:在后台异步(Asynchronously)保存当前数据库的数据到磁盘。
- BGSAVE 命令执行之后立即返回,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。
- 客户端可以通过 LASTSAVE 命令查看相关信息,判断 BGSAVE 命令是否执行成功。
- 示例:
redis> BGSAVE Background saving started
SAVE:执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。
- 一般来说,在生产环境很少执行 SAVE 操作,因为它会阻塞所有客户端,保存数据库的任务通常由 BGSAVE 命令异步地执行。
- 然而,如果负责保存数据的后台子进程不幸出现问题时, SAVE 可以作为保存数据的最后手段来使用。
- 示例:
redis> SAVE OK
LASTSAVE:返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示。
- 示例:
redis> LASTSAVE (integer) 1324043588
详述:“CLIENT XXX”
CLIENT GETNAME:返回 CLIENT SETNAME 命令为连接设置的名字。
- 新创建的连接默认是没有名字的,CLIENT GETNAME 将返回空白回复。
CLIENT SETNAME <connection-name>:为当前连接分配一个名字。
- 这个名字会显示在 CLIENT LIST 命令的结果中, 用于识别当前正在与服务器进行连接的客户端。
- <connection-name> 使用 Redis 的字符串类型来保存,最大可以占用 512 MB,名字里不允许使用空格。
- 要移除一个连接的名字,可以将连接的名字设为空字符串 "" 。
- 在 Redis 应用程序发生连接泄漏时,为连接设置名字是一种很好的 debug 手段。
# 新连接默认没有名字 redis 127.0.0.1:6379> CLIENT GETNAME (nil) # 设置名字 redis 127.0.0.1:6379> CLIENT SETNAME hello-world-connection OK # 返回名字 redis 127.0.0.1:6379> CLIENT GETNAME "hello-world-connection" # 在客户端列表中查看 redis 127.0.0.1:6379> CLIENT LIST addr=127.0.0.1:36851 fd=5 name=hello-world-connection age=51 ... ... # 清除名字 redis 127.0.0.1:6379> CLIENT SETNAME # 只用空格是不行的! (error) ERR Syntax error, try CLIENT (LIST | KILL ip:port) redis 127.0.0.1:6379> CLIENT SETNAME "" # 必须双引号显示包围 OK redis 127.0.0.1:6379> CLIENT GETNAME # 清除完毕 (nil)
CLIENT LIST:返回所有连接到服务器的客户端信息和统计数据。
- 命令返回多行字符串,每个已连接客户端对应一行(以 LF 分割),每行字符串由一系列 “属性=值” 形式的域组成,每个域之间以空格分开;
- 各域的含义:
- addr : 客户端的地址和端口
- fd : 套接字所使用的文件描述符
- age : 以秒计算的已连接时长
- idle : 以秒计算的空闲时长
- flags : 客户端 flag :
- “O”:客户端是 MONITOR 模式下的附属节点(slave)
- “S”:客户端是一般模式下(normal)的附属节点
- “M”:客户端是主节点(master)
- “x”:客户端正在执行事务
- “b”:客户端正在等待阻塞事件
- “i”:客户端正在等待 VM I/O 操作(已废弃)
- “d”:一个受监视(watched)的键已被修改, EXEC 命令将失败
- “c”:在将回复完整地写出之后,关闭链接
- “u”:客户端未被阻塞(unblocked)
- “A”:尽可能快地关闭连接
- “N”:未设置任何 flag
- db : 该客户端正在使用的数据库 ID
- sub : 已订阅频道的数量
- psub : 已订阅模式的数量
- multi : 在事务中被执行的命令数量
- qbuf : 查询缓存的长度( 0 表示没有查询在等待)
- qbuf-free : 查询缓存的剩余空间( 0 表示没有剩余空间)
- obl : 输出缓存的长度
- oll : 输出列表的长度(当输出缓存没有剩余空间时,回复被入队到这个队列里)
- omem : 输出缓存的内存占用量
- events : 文件描述符事件:
- “r”:客户端套接字(在事件 loop 中)是可读的(readable)
- “w”:客户端套接字(在事件 loop 中)是可写的(writeable)
- cmd : 最近一次执行的命令
redis> CLIENT LIST addr=127.0.0.1:43143 fd=6 age=183 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client addr=127.0.0.1:43163 fd=5 age=35 idle=15 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping addr=127.0.0.1:43167 fd=7 age=24 idle=6 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get
CLIENT KILL <ip>:<port>:关闭地址为 ip:port 的客户端。
- “ip:port” 应该和 CLIENT LIST 命令输出的其中一行匹配。
- 因为 Redis 使用单线程设计,所以当 Redis 正在执行命令的时候,不会有客户端被断开连接。
- 如果要被断开连接的客户端正在执行命令,那么当这个命令执行之后,在发送下一个命令的时候,它就会收到一个网络错误,告知它自身的连接已被关闭。
# 列出所有已连接客户端 redis 127.0.0.1:6379> CLIENT LIST addr=127.0.0.1:43501 fd=5 age=10 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client # 杀死当前客户端的连接 redis 127.0.0.1:6379> CLIENT KILL 127.0.0.1:43501 OK # 之前的连接已经被关闭,CLI 客户端又重新建立了连接 # 之前的端口是 43501 ,现在是 43504 redis 127.0.0.1:6379> CLIENT LIST addr=127.0.0.1:43504 fd=5 age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
详述:“CONFIG XXX”
CONFIG GET <parameter>:取得运行中的 Redis 服务器的配置参数。
- 在 Redis 2.4 版本中,有部分参数没有办法用 CONFIG GET 访问,但在 Redis 2.6 版本后,所有配置参数都已经可以用 CONFIG GET 访问;
- 接受单个参数 parameter 作为搜索关键字,查找所有匹配的配置参数;
# 获取特定的参数 redis> CONFIG GET slowlog-max-len 1) "slowlog-max-len" 2) "1000" # 获取以 s 开头的配置参数及参数的值 redis> CONFIG GET s* 1) "save" # 参数名:save 2) "900 1 300 10 60 10000" # save 参数的值 3) "slave-serve-stale-data" # 参数名: slave-serve-stale-data 4) "yes" # slave-serve-stale-data 参数的值 5) "set-max-intset-entries" # ... 6) "512" 7) "slowlog-log-slower-than" 8) "1000" 9) "slowlog-max-len" 10) "1000" # 列出 CONFIG GET 命令支持的所有参数 redis> CONFIG GET * 1) "dir" 2) "/var/lib/redis" 3) "dbfilename" 4) "dump.rdb" 5) "requirepass" 6) (nil) 7) "masterauth" 8) (nil) 9) "maxmemory" 10) "0" 11) "maxmemory-policy" 12) "volatile-lru" 13) "maxmemory-samples" 14) "3" 15) "timeout" 16) "0" 17) "appendonly" 18) "no" ...
CONFIG SET <parameter> <value>:动态地调整 Redis 服务器的配置,立即生效而无须重启。
redis> CONFIG GET slowlog-max-len 1) "slowlog-max-len" 2) "1024" redis> CONFIG SET slowlog-max-len 10086 OK redis> CONFIG GET slowlog-max-len 1) "slowlog-max-len" 2) "10086"
CONFIG RESETSTAT:重置 INFO 命令中的某些统计数据。
- 这些数据包括:
- Keyspace hits(键空间命中次数)
- Keyspace misses(键空间不命中次数)
- Number of commands processed(执行命令的次数)
- Number of connections received(连接服务器的次数)
- Number of expired keys(过期key的数量)
- Number of rejected connections(被拒绝的连接数量)
- Latest fork(2) time(最后执行 fork(2) 的时间)
- The aof_delayed_fsync counter(aof_delayed_fsync 计数器的值)
CONFIG REWRITE:对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写。【将服务器当前所使用的配置(可能由“CONFIG SET”修改过)记录到 redis.conf 文件中】
- 重写会以非常保守的方式进行:
- 原有 redis.conf 文件的整体结构和注释会被尽可能地保留。
- 如果一个选项已经存在于原有 redis.conf 文件中 ,那么对该选项的重写会在选项原本所在的位置(行号)上进行。
- 如果一个选项不存在于原有 redis.conf 文件中,并且该选项被设置为默认值,那么重写程序不会将这个选项添加到重写后的 redis.conf 文件中。
- 如果一个选项不存在于原有 redis.conf 文件中,并且该选项被设置为非默认值,那么这个选项将被添加到重写后的 redis.conf 文件的末尾。
- 未使用的行会被留白。
- 比如说, 如果你在原有 redis.conf 文件上设置了数个关于 save 选项的参数,但现在你将这些 save 参数的一个或全部都关闭了,那么这些不再使用的参数原本所在的行就会变成空白的。
- 即使启动服务器时所指定的 redis.conf 文件已经不再存在,该命令也可以重新构建并生成出一个新的 redis.conf 文件。
- 如果启动服务器时没有载入 redis.conf 文件, 那么该命令将引发一个错误。
- 对 redis.conf 文件的重写是原子性的,并且是一致的: 如果重写出错或重写期间服务器崩溃,那么重写失败,原有 redis.conf 文件不会被修改;如果重写成功,那么 redis.conf 文件为重写后的新文件。
“CONFIG命令”和“redis.conf文件”所使用的格式的不同
所有被 CONFIG SET 所支持的配置参数都可以在配置文件 redis.conf 中找到,不过 CONFIG GET 和 CONFIG SET 使用的格式和 redis.conf 文件所使用的格式有以下两点不同:
- 10kb 、 2gb 这些在配置文件中所使用的储存单位缩写,不可以用在 CONFIG 命令中, CONFIG SET 的值只能通过数字值显式地设定。
- 像“CONFIG SET xxx 1k”这样的命令是错误的,正确的格式是“CONFIG SET xxx 1000”。
- “save”选项在 redis.conf 中是用多行文字储存的,但在 CONFIG GET 命令中,它只打印一行文字。
# 以下是 save 选项在 redis.conf 文件中的表示: save 900 1 save 300 10 save 60 10000 # 但是 CONFIG GET 命令的输出只有一行: redis> CONFIG GET save 1) "save" 2) "900 1 300 10 60 10000"
详述:“xxx”、“xxx”、“xxx”、“xxx”
详述:“xxx”、“xxx”、“xxx”、“xxx”
详述:“INFO”
以下实例演示了如何获取 redis 服务器的统计信息:
redis 127.0.0.1:6379> INFO
# Server
redis_version:2.8.13
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:c2238b38b1edb0e2
redis_mode:standalone
os:Linux 3.5.0-48-generic x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.7.2
process_id:3856
run_id:0e61abd297771de3fe812a3c21027732ac9f41fe
tcp_port:6379
uptime_in_seconds:11554
uptime_in_days:0
hz:10
lru_clock:16651447
config_file:
# Clients
connected_clients:1
client-longest_output_list:0
client-biggest_input_buf:0
blocked_clients:0
# Memory
used_memory:589016
used_memory_human:575.21K
used_memory_rss:2461696
used_memory_peak:667312
used_memory_peak_human:651.67K
used_memory_lua:33792
mem_fragmentation_ratio:4.18
mem_allocator:jemalloc-3.6.0
# Persistence
loading:0
rdb_changes_since_last_save:3
rdb_bgsave_in_progress:0
rdb_last_save_time:1409158561
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
# Stats
total_connections_received:24
total_commands_processed:294
instantaneous_ops_per_sec:0
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:41
keyspace_misses:82
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:264
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# CPU
used_cpu_sys:10.49
used_cpu_user:4.96
used_cpu_sys_children:0.00
used_cpu_user_children:0.01
# Keyspace
db0:keys=94,expires=1,avg_ttl=41638810
db1:keys=1,expires=0,avg_ttl=0
db3:keys=1,expires=0,avg_ttl=0