在Redis 数据库中,我们除了经常操作数据类型外,键Key操作也是Redis 中非常重要和常用的的操作。Redis 提供很多键管理相关命令,如:可以通过KEYS命令查找键、EXPIREAT可以设置键的过期时间、RENAME命令可以对键进行重命名。
1. 查找、删除
1.1 KEYS - 查找键
KEYS pattern
按指定的匹配模式pattern查找key。如:
KEYS *匹配数据库中所有的keyKEYS h?llo匹配 hello、hallo、hxllo 等KEYS h*llo匹配 hllo、heeeeello等KEYS h[ae]llo匹配 hello、hallo,但不匹配 hillo
复杂度、返回值:
- 时间复杂度:
O(N),N 为数据库中key的数量 - 返回值:匹配结果列表
使用示例
# 4 个测试数据 redis> MSET one 1 two 2 three 3 four 4 OK redis> KEYS *o* 1) "four" 2) "two" 3) "one" redis> KEYS t?? 1) "two" redis> KEYS t[w]* 1) "two" # 匹配数据库内所有 key redis> KEYS * 1) "four" 2) "three" 3) "two" 4) "one"
1.2 EXISTS - 判断key是否存在
EXISTS key
检查指定的key是否存在。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:如果
key存在返回1,否则返回0
使用示例
redis> SET db "redis" OK redis> EXISTS db (integer) 1 redis> DEL db (integer) 1 redis> EXISTS db (integer) 0
1.3 RANDOMKEY - 随机返回一个key
RANDOMKEY
从当前数据库中随机返回一个key,但不删除。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:数据库非空时,返回一个
key;为空时,返回nil
使用示例
# 设置多个 key redis> MSET fruit "apple" drink "beer" food "cookies" OK redis> RANDOMKEY "fruit" redis> RANDOMKEY "food" # 返回 key 但不删除 redis> KEYS * 1) "food" 2) "drink" 3) "fruit" # 删除当前数据库所有 key,数据库为空 redis> FLUSHDB OK redis> RANDOMKEY (nil)
1.4 TYPE - 返回值类型
TYPE key
返回指定key所存储的值的类型
复杂度、返回值:
- 时间复杂度:
O(1)。 - 返回值:
none,key 不存在;string,字符串;list,列表;set,集合;zset,有序集合;hash,哈希表。
使用示例
# 字符串 redis> SET weather "sunny" OK redis> TYPE weather string # 列表 redis> LPUSH book_list "programming in scala" (integer) 1 redis> TYPE book_list list # 集合 redis> SADD pat "dog" (integer) 1 redis> TYPE pat set
1.5 SORT - 排序
SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC | DESC] [ALPHA] [STORE destination]
返回指定key中经过排序的元素,key可能是列表、集合、有序集合。排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。
SORT的一般用法
以下是SORT的两种最简单的用法:
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> SORT today_cost DESC 1) "30" 2) "10" 3) "8" 4) "1.5"
使用ALPHA排序字符串
SORT默认的排序对象为数字,如果需要对字符串进行排序,就要增加ALPHA参数:
# 一些网址 redis> LPUSH website "www.reddit.com" (integer) 1 redis> LPUSH website "www.slashdot.com" (integer) 2 redis> LPUSH website "www.niefengjun.cn" (integer) 3 # 默认(按数字)排序 redis> SORT website 1) "www.niefengjun.cn" 2) "www.slashdot.com" 3) "www.reddit.com" # 按字符排序 redis> SORT website ALPHA 1) "www.niefengjun.cn" 2) "www.reddit.com" 3) "www.slashdot.com"
使用LIMIT限制返回结果
与SQL查询类似,Redis 同样可以使用LIMIT限制返回结果数量,该修饰符接收offset和count两个参数:
offset,指定偏移量count,指定返回数量
如,返回前5个元素:
# 添加测试数据,列表值为 1 指 10 redis> RPUSH rank 1 3 5 7 9 (integer) 5 redis> RPUSH rank 2 4 6 8 10 (integer) 10 # 返回列表中最小的 5 个值 redis> SORT rank LIMIT 0 5 1) "1" 2) "2" 3) "3" 4) "4" 5) "5"
也可以对返回结果进行排序:
redis> SORT rank LIMIT 0 5 DESC 1) "10" 2) "9" 3) "8" 4) "7" 5) "6"
排序可以使用外部key的数据权重做为排序依据。
现在以下数据表:
| uid | user_name_{uid} | user_level_{uid} |
|---|---|---|
| 1 | admin | 9999 |
| 2 | jack | 10 |
| 3 | peter | 25 |
| 4 | mary | 70 |
将数据存入 Redis 中:
# admin redis> LPUSH uid 1 (integer) 1 redis> SET user_name_1 admin OK redis> SET user_level_1 9999 OK # jack redis> LPUSH uid 2 (integer) 2 redis> SET user_name_2 jack OK redis> SET user_level_2 10 OK # peter redis> LPUSH uid 3 (integer) 3 redis> SET user_name_3 peter OK redis> SET user_level_3 25 OK # mary redis> LPUSH uid 4 (integer) 4 redis> SET user_name_4 mary OK redis> SET user_level_4 70 OK
BY 选项
默认情况下,SORT uid会按uid的值排序:
redis> SORT uid 1) "1" # admin 2) "2" # jack 3) "3" # peter 4) "4" # mary
可以通过BY选项为其指定其它元素来排序:
redis> 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
GET 选项
GET 选项可以根据排序结果取出对应的键值:
redis> SORT uid GET user_name_* 1) "admin" 2) "jack" 3) "peter" 4) "mary"
BY和GET结合使用
如,先按user_level_{uid}来排序uid列表,再取出相应的user_name_{uid}的值:
value="redis> SORT uid BY user_level_* GET user_name_* 1) "jack" # level = 10 2) "peter" # level = 25 3) "mary" # level = 70 4) "admin" # level = 9999" max=""
获取多个外部键
使用多个GET,可以获取多个外部键的值:
redis> 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> 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"
保存排序结果
默认情况下,SORT命令只是返回排序结果,而不做其它处理。我们可以为其指定一个key参数,将结果保存在指定键上。如果指定key已存在,会发生覆盖:
redis> RPUSH numbers 1 3 5 7 9 (integer) 5 redis> RPUSH numbers 2 4 6 8 10 (integer) 10 redis> 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> SORT numbers STORE sorted-numbers (integer) 10 # 排序后的结果 redis> 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"
- 时间复杂度:
O(N+M*log(M)),N为要排序的列表或集合内的元素数量,M为要返回的元素数量。 如果只是使用SORT命令的GET选项获取数据而没有进行排序,时间复杂度O(N)。 - 返回值:没有使用
STORE,返回列表形式的排序结果;使用STORE参数,返回排序结果的元素数量
1.6 DEL - 删除key
DEL key [key ...]
删除指定的一个或多个key,不存在的key将被忽略。
复杂度、返回值:
- 时间复杂度:
O(N),N为要删除的key的数量 - 返回值:被删除
key的数量
使用示例
# 删除单个 key redis> SET itbilu niefengjun.cn OK redis> DEL itbilu (integer) 1 # 删除一个不存在的 key redis> EXISTS phone (integer) 0 redis> DEL phone # 失败,没有 key 被删除 (integer) 0 # 同时删除多个 key redis> SET name "redis" OK redis> SET type "key-value store" OK redis> SET website "niefengjun.cn" OK redis> DEL name type website (integer) 3
2. 重命名
2.1 RENAME - 重命令
RENAME key newkey
将key重命名为newkey。
当key和newkey相同,或key不存在返回一个错误。当newkey已存在,将会被覆盖。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:操作成功
1;否则返回一个错误
使用示例
# key 存在且 newkey 不存在 redis> SET message "hello world" OK redis> RENAME message greeting OK # message 不复存在 redis> EXISTS message (integer) 0 # 已被重命名为 greeting redis> EXISTS greeting (integer) 1 # 当 key 不存在时,返回错误 redis> RENAME fake_key never_exists (error) ERR no such key # newkey 已存在时, RENAME 会覆盖旧 newkey redis> SET pc "lenovo" OK redis> SET personal_computer "dell" OK redis> RENAME pc personal_computer OK redis> GET pc (nil) # 原来的值 dell 被覆盖了 redis:1> GET personal_computer "lenovo"
2.2 RENAMENX - 仅当不存在时重命名
RENAMENX key newkey
命令与RENAME类似,但仅当新名称newkey有数据库中不存在时,才会对key进行重命名。
当key不存在会返回一个错误。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:操作成功
1;newkey已存在返回0
使用示例
# newkey 不存在时,重命名成功 redis> SET player "MPlyaer" OK redis> EXISTS best_player (integer) 0 redis> RENAMENX player best_player (integer) 1 # newkey存在时,失败 redis> SET animal "bear" OK redis> SET favorite_animal "butterfly" OK redis> RENAMENX animal favorite_animal (integer) 0 redis> get animal "bear" redis> get favorite_animal "butterfly"
3. 序列化、反序列化
3.1 DUMP - 序列化key
DUMP key
序列化指定的key,并返回被序列化的值。
序列化后,可以使用RESTORE命令将序列化值反序列化为Redis 键。
序列化生成的值有以下特征:
- 带有 64 位的校验和,用于检测错误,
RESTORE在进行反序列化之前会先检查该校验和 - 值的编码格式和 RDB 文件保持一致
- RDB 版本会被编码在序列化值当中,如果因为 Redis 的版本不同造成 RDB 格式不兼容,那么 Redis 会拒绝对这个值的反序列化操作
复杂度、返回值:
- 查找指定键的复杂度为
O(1);对键进行序列化的复杂度为O(N*M),其中N是构成key的 Redis 对象的数量,而M为这些对象的平均大小。 如果序列化的对象是比较小的字符串,那么复杂度为O(1)。 - 返回值:
key不存在时返回时返回nil,否则返回序列化之后的值
使用示例
redis> SET greeting "hello, dumping world!" OK redis> DUMP greeting "\x00\x15hello, dumping world!\x06\x00E\xa0Z\x82\xd8r\xc1\xde" redis> DUMP not-exists-key (nil)
3.2 RESTORE - 反序列化
RESTORE key ttl serialized-value
将序列化值进行反序列化,并将结果与指定的key进行关联。
ttl表示以毫秒为单位设置key的生存时间;如果ttl值为 0 ,表示不设置生存时间。
Redis 在进行反序化前,首先会对序列化值进行RDB较验,如果版本不符或数据不完整,会拒绝反序列化并返回一个错误
复杂度、返回值:
- 时间复杂度:查找键复杂度为
O(1);反序列化为O(N*M),N为构成key的 Redis 对象数,M为这些对象的平均大小。 - 返回值:操作成功,返回
OK;否则返回一个错误
使用示例
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
4. 生存时间
4.1 EXPIRE - 设置key的生存时间
EXPIRE key seconds
设置指定的key的生存时间,当key过期(生存时间为0)时,会被自动删除。
对key设置生存时间后,可以通过再次执行EXPIRE命令更新生存时间。
带有生存时间的key可以通过DEL命令删除,也可通过SET或GETSET命令。在修改一个带生存时间的key时,只是修改key的值而不是用一个新键来替换它。所以,使用INCR命令对一个字符串类型的key值进行自增、对一个列表进行LPUSH、对一个哈希进行HSET等操作时,甚至使用RENAME对key进行重命名时,都不会修改key的生存时间。使用PERSIST命令,可以不删除的key的情况下修改key的生存时间,让key重新成为一个持久key。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:设置成功时返回
1。key不存在或者不能设置生存时间时,返回0。
使用示例
redis> SET cache_page "www.google.com" OK # 设置过期时间为 30 秒 redis> EXPIRE cache_page 30 (integer) 1 # 查看剩余生存时间 redis> TTL cache_page (integer) 23 # 更新过期时间 redis> EXPIRE cache_page 30000 (integer) 1 redis> TTL cache_page (integer) 29996
4.2 EXPIREAT - 以时间戳格式设置生存时间
EXPIREAT key timestamp
EXPIREAT与EXPIRE命令一样,同样用于设置key的生存时间,但命令接受的时间参数是 UNIX 时间戳。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:如果
key存在返回1,否则返回0
使用示例
redis> SET cache www.google.com OK # 将 key 设置为 2016.09.28过期 redis> EXPIREAT cache 1475032000 1355292000 (integer) 1 redis> TTL cache (integer) 45081860
4.3 PERSISTAT - 设置过期时间
PEXPIREAT key milliseconds-timestamp
类似于EXPIREAT命令,同样用于设置key的过期时间,但它以毫秒为单位的 UNIX 时间戳表示。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:执行返回
1,否则返回0
使用示例
# 设置一个 key redis> SET mykey "Hello" OK # 设置过期时间 redis> PEXPIREAT mykey 1555555555005 (integer) 1 # TTL 返回秒 redis> TTL mykey (integer) 223157079 # PTTL 返回毫秒 redis> PTTL mykey (integer) 223157079318
4.4 PERSIST - 移除生存时间
PERSIST key
移除指定key的生存时间,使其从易失键转换同为持久键。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:执行返回
1,否则返回0
使用示例
# 设置一个 key redis> SET mykey "Hello" OK # 为 key 设置生存时间 redis> EXPIRE mykey 10 (integer) 1 redis> TTL mykey (integer) 10 # 移除 key 的生存时间 redis> PERSIST mykey (integer) 1 redis> TTL mykey (integer) -1
4.5 TTL - 返回剩余生存时间(秒)
TTL key
以秒为单位,返回指定key的剩余生存时间(TTL, time to live)
复杂度、返回值:
- 时间复杂度:
O(1)。 - 返回值:
key不存在时返回-2;key但没有生存时间时,返回-1;否则返回以毫秒为单位的剩余生存时间
使用示例
# 不存在的 key redis> FLUSHDB OK redis> TTL key (integer) -2 # key 存在,但没有设置剩余生存时间 redis> SET key value OK redis> TTL key (integer) -1 # 有剩余生存时间的 key redis> EXPIRE key 10086 (integer) 1 redis> TTL key (integer) 10084
4.6 PTTL - 返回剩余生存时间(毫秒)
PTTL key
该命令类似于TTL命令,同样用于返回key的剩余自下而上时间,但它以毫秒为单位返回。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:
key不存在时返回-2;key但没有生存时间时,返回-1;否则返回以毫秒为单位的剩余生存时间
使用示例
# 不存在的 key redis> FLUSHDB OK redis> PTTL key (integer) -2 # key 存在,但没有设置剩余生存时间 redis> SET key value OK redis> PTTL key (integer) -1 # 有剩余生存时间的 key redis> PEXPIRE key 10086 (integer) 1 redis> PTTL key (integer) 6179
5. 键迁移
5.1 MIGRATE - 实例间键迁移
MIGRATE host port key destination-db timeout [COPY] [REPLACE]
将key 原子性地从当前实例迁移到到目标实例,迁移成功后源实例上的key会被删除。
timeout是以毫秒表示的超时时间。Redis 会在指定时间内完成IO操作,如果传送时间内发送IO错误或达到了超时时间,命令就会停止,并返回一个IOERR错误。
该命令是一个原子操作,命令执行进会发生阻塞,直到迁移成功、迁移失败或等待超时。
key迁移流程如下:
- 源实例执行DUMP命令进行序列化,并将序列化数据传送到目标实例
- 目标实例使用RESTORE命令进行反序列化,并将数据添加到数据库
- 当前实例和目标实例一样,只要看到RESTORE命令返回
ok,就执行DEL命令删除自已数据库中的key
复杂度、返回值:
- 时间复杂度:
key在两个实例之间传递的复杂度为O(N) - 返回值:迁移成功返回
ok,否则返回相应错误
使用示例
启动两个 Redis 实例,一个使用默认的 6379 端口,一个使用 7777 端口:
$ ./redis-server & [1] 3557 ... $ ./redis-server --port 7777 & [2] 3560 ...
客户端连接 6379 端口的实例。添加一个键,并将其迁移到 7777 端口的实例上:
$ ./redis-cli redis> flushdb OK redis> SET greeting "Hello from 6379 instance" OK redis> MIGRATE 127.0.0.1 7777 greeting 0 1000 OK # 迁移成功后 key 会被删除 redis> EXISTS greeting (integer) 0
查看 7777 端口的实例上的数据:
$ ./redis-cli -p 7777 redis 127.0.0.1:7777> GET greeting "Hello from 6379 instance"
5.2 MOVE - 同实例不同库间的键移动
MOVE key db
移动当前数据库中指定的key到指定数据库db中。
如果key在源数据库中不存在,或目标数据库中存在相同的key,则MOVE命令无效。
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:执行返回
1,否则返回0
使用示例
# key 存在于当前数据库 redis> SELECT 0 # redis默认使用数据库 0,为了清晰起见,这里再显式指定一次。 OK redis> SET song "secret base - Zone" OK redis> MOVE song 1 # 将 song 移动到数据库 1 (integer) 1 redis> EXISTS song # song 已经被移走 (integer) 0 redis> SELECT 1 # 使用数据库 1 OK redis:1> EXISTS song # 证实 song 被移到了数据库 1 (注意命令提示符变成了"redis:1",表明正在使用数据库 1) (integer) 1 # 当 key 不存在的时候 redis:1> EXISTS fake_key (integer) 0 redis:1> MOVE fake_key 0 # 试图从数据库 1 移动一个不存在的 key 到数据库 0,失败 (integer) 0 redis:1> select 0 # 使用数据库0 OK redis> EXISTS fake_key # 证实 fake_key 不存在 (integer) 0 # 当源数据库和目标数据库有相同的 key 时 redis> SELECT 0 # 使用数据库0 OK redis> SET favorite_fruit "banana" OK redis> SELECT 1 # 使用数据库1 OK redis:1> SET favorite_fruit "apple" OK redis:1> SELECT 0 # 使用数据库0,并试图将 favorite_fruit 移动到数据库 1 OK redis> MOVE favorite_fruit 1 # 因为两个数据库有相同的 key,MOVE 失败 (integer) 0 redis> GET favorite_fruit # 数据库 0 的 favorite_fruit 没变 "banana" redis> SELECT 1 OK redis:1> GET favorite_fruit # 数据库 1 的 favorite_fruit 也是 "apple"
6. 其它
6.1 OBJECT - 内部调试
OBJECT subcommand [arguments [arguments]]
OBJECT一般用于调试、除错等情况,或为节省空间而对key使用特殊的编码,它可以查看key对象在Redis内部的执行情况。
它有多个子命令:
OBJECT REFCOUNT >key<-返回 key 存储值的引用次数,此命令用于除错OBJECT ENCODING >key<-返回 key 锁储存的值所使用的内部表示OBJECT IDLETIME >key<-返回指定 key 自储存以来的空转时间,以秒为单位
对象可以使用以下编码方式:
- 字符串类型可以被编码为
raw(表示普通字符串)或int(表示64位数字) - 列表类型可以被编码为
ziplist或linkedlist,ziplist是为节约列表大小而使用的一种特殊表示方式 - 哈希类型可以被编码为
zipmap或hashtable,zipmap是小哈希表的特殊表示 - 集合类型可以被编码为
intset或hashtable,intset是只储存数字的小集合的特殊表示 - 有序集合类型可以被编码为
ziplist或skiplist,ziplist用于表示小的有序集合,而skiplist用于表示任意大小的有序集合
复杂度、返回值:
- 时间复杂度:
O(1) - 返回值:
REFCOUNT和IDLETIME返回数字。ENCODING返回相应的编码类型。
使用示例
# 设置一个字符串 redis> SET game "COD" OK # 只有一个引用 redis> OBJECT REFCOUNT game (integer) 1 # 等待一段时间,然后查看空转时间 redis> OBJECT IDLETIME game (integer) 90 # 提取game, 让它处于活跃(active)状态 redis> GET game "COD" # 不再处于空转 redis> OBJECT IDLETIME game (integer) 0 # 字符串的编码方式 redis> OBJECT ENCODING game "raw" # 大的数字也被编码为字符串 redis> SET phone 15820123123 OK redis> OBJECT ENCODING phone "raw" # 短数字被编码为 int redis> SET age 20 OK redis> OBJECT ENCODING age "int"
6.2 SCAN - 增量迭代
SCAN cursor [MATCH pattern] [COUNT count]
该命令与SSCAN与HSCAN、ZSCAN等命令都用于增量迭代集合元素。其中,SCAN用于迭代当前数据库中的数据库键;HSCAN用于迭代哈希类型中的键;SSCAN用于迭代集合类型中的键;ZSCAN用于迭代有序集合类型中的键。
SCAN的基本用法
SCAN是一个基于游标的迭代器,它每次被调用后都会向用户返回一个新的游标, 用户在下次迭代时要使用这个新游标,作为SCAN命令的游标参数,来继续之前的迭代过程。
当SCAN命令的cursor参数为0时,表示服务开始一次新的迭代。而当服务器返回<0时,表示迭代已经结束。
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"
复杂度、返回值:
- 时间复杂度:每执行一次迭代复杂度为
O(1);对数据集执行一次完整 - 返回值:
SCAN、SSCAN、HSCAN、ZSCAN命令都返回一个包含两个元素的multi-bulk,其中:第一个元素是字符串表示的无符号 64 位整数游标, 第二个元素是另一个multi-bulk回复,其中包含了本次被迭代的元素。SCAN返回的每个元素都是一个数据库键。SSCAN返回的每个元素都是一个集合成员HSCAN命令返回的每个元素都是一个键值对ZSCAN返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成
