标题:【Redis14】Redis基础:通用命令(一)

文章目录
    分类:存储运维 标签:Redis

    Redis基础学习:通用命令(一)

    我们已经学过了很多命令,但是有些通用命令是和具体的数据类型或者某些具体的功能无关的。它们主要是为一些通用功能提供的一些命令,就像是我们在业务开发的时候经常会写的那些通用函数一样。这些命令其实都比较简单,但是大部分同学可能还会有很多命令完全没用过。就像我一样,没有系统学习之前,我也真的只知道 GET 和 SET 。

    KEYS

    KEYS 命令应该还是比较入门的而且大家都经常会使用的一个命令。可以说是和 GET/SET 同级别的超常用命令,所以对于这个命令咱们也不过多介绍了。它支持正则匹配来查找 KEY 信息。

    127.0.0.1:6379> keys *
    1) "a"
    2) "data"

    具体支持的正则表达式模式包括:

    • h?llo 匹配 hellohallohxllo

    • h*llo 匹配 hlloheeeello

    • h[ae]llo 匹配 hellohallo, 但是不匹配 hillo

    • h[^e]llo 匹配 hallohbllo, … 但是不匹配 hello

    • h[a-b]llo 匹配 hallohbllo

    KEYS 命令需要注意的一点是,它的查找时间复杂度为 O(N) ,N 为数据库里面 KEY 的数量,在一个有1百万个 KEY 的数据库里面执行一次查询需要的时间是40毫秒 。看起来很快吧?但是在一个大的数据库中使用它仍然可能造成性能问题,别问我怎么知道,哥们试过,太酸爽了。官方的建议是如果你需要从一个数据集中查找特定的 KEYS 可以使用 SCAN 或者 Sets 数据结构。


    至于为什么不能在线上使用 KEYS * ,别忘了,Redis 是单线程的,KEYS * 如果耗时很长会阻塞其它命令。关于单线程的问题我们到进阶系列的时候再说。

    是否存在

    使用 EXISTS 命令可以判断一个 KEY 是否存在,它可以接收多个参数。

    127.0.0.1:6379> EXISTS a1 a2 a3 a4
    (integer) 2
    127.0.0.1:6379> EXISTS a1
    (integer) 1

    复制

    在 Redis6.2 之后,新提供了一个 COPY 命令,可以帮助我们快速方便地复制数据。

    127.0.0.1:6379> COPY a a1
    (integer) 1
    127.0.0.1:6379> get a1
    "111"

    同时,它还提供了参数可以直接将数据复制到其它库中。

    127.0.0.1:6379> COPY a a1 DB 1
    (integer) 1
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> get a1
    "111"

    如果要复制到的 KEY 已经存在,那么 COPY 命令就不会直接覆盖,我们可以使用 REPLACE 参数强制替换。

    127.0.0.1:6379> set b 222
    OK
    127.0.0.1:6379> COPY b a1
    (integer) 0
    127.0.0.1:6379> get a1
    "111"
    127.0.0.1:6379> COPY b a1 REPLACE
    (integer) 1
    127.0.0.1:6379> get a1
    "222"

    移动

    有复制当然也会有移动啦,我们先来看一个跨服务器实例的移动,使用的是 MIGRATE ,这个命令其实更合适的叫法应该是 迁移 。

    // 6379
    127.0.0.1:6379> MIGRATE 127.0.0.1 6380 "" 1 3000 KEYS a
    OK
    
    // 6380
    ➜  ~ redis-cli -p 6380
    127.0.0.1:6380> get a
    (nil)
    127.0.0.1:6380> select 1
    OK
    127.0.0.1:6380[1]> get a
    "111"
    
    // 6379
    127.0.0.1:6379> get a
    (nil)
    127.0.0.1:6379> MIGRATE 127.0.0.1 6380 "" 1 3000 REPLACE KEYS a
    NOKEY

    在上面我们新开启了一个 6380 的服务端实例。然后通过 MIGRATE 命令将 a 这个 KEY 迁移到了 6380 的库中,原先 6379 中已经没有这个 KEY 了。它的参数比较多,大家可以自己去看一下,同样,它也支持 REPLACE 参数。


    这么强大,那么我们可以在同一个实例内移动吗?

    127.0.0.1:6379> MIGRATE 127.0.0.1 6379 "" 1 3000 REPLACE KEYS a
    (error) IOERR error or timeout reading to target instance
    (3.00s)
    127.0.0.1:6379> select 1
    OK
    127.0.0.1:6379[1]> get a
    "111"

    上面的报错是超时 3s 后报的错,但其实数据复制到 1 库了,注意,是复制,默认的 0 库也在,1 库也会出现一条新的。但是这样很明显不是很好,毕竟我们在同一个服务端实例中移动数据完全用不着这么麻烦,只需要使用 MOVE 命令就好啦。

    127.0.0.1:6379> set a 111
    OK
    127.0.0.1:6379> MOVE a 1
    (integer) 1
    127.0.0.1:6379> SELECT 1
    OK
    127.0.0.1:6379[1]> get a
    "111"

    MOVE 命令是没有 REPLACE 的,毕竟它不是复制替换,只是移动,如果目标库有相同的内容了,就无法移动。

    127.0.0.1:6379[1]> SELECT 0
    OK
    127.0.0.1:6379> get a
    (nil)
    127.0.0.1:6379> set a 112
    OK
    127.0.0.1:6379> MOVE a 1
    (integer) 0

    MIGRATE 是一个原子命令,在迁移的时候会同时阻塞两个实例的操作,

    序列化与反序列化

    是的,你没看错,和 PHP 中的 serialize() 以及 unserialize() 一样,Redis 中也有序列化的功能,使用的命令是 DUMP 和 RESTORE 。

    127.0.0.1:6379> DUMP a
    "\x00\xc0o\t\x00\x1f<t\x0e\xeb'\x9cE"
    
    127.0.0.1:6379> RESTORE as1 0 "\x00\xc0o\t\x00\x1f<t\x0e\xeb'\x9cE"
    OK
    
    127.0.0.1:6379> get as1
    "111"

    对于 DUMP 来说,没有别的什么参数,它的唯一作用就是将指定的 KEY 数据转化成序列化的内容。而 RESTORE 则更复杂一些,包含更多的参数。

    RESTORE key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]

    如果在 RESTORE 中指定的 KEY 是已经存在的,那么也需要使用 REPLACE 进行覆盖替换。同时,它对于中文的支持也是没问题的。

    127.0.0.1:6379> set a "ask 123 您"
    OK
    127.0.0.1:6379> DUMP a
    "\x00\x0bask 123 \xe6\x82\xa8\t\x00\xce\xa1\x81D*\xce\xbc<"
    
    127.0.0.1:6379> RESTORE as1 0 "\x00\x06ask\xe4\xb8\xad\t\x00\xbe\xf4I\xb5\x1f\xe0\xbb\xa9"
    (error) BUSYKEY Target key name already exists.
    127.0.0.1:6379>  RESTORE as1 0 "\x00\x0bask 123 \xe6\x82\xa8\t\x00\xce\xa1\x81D*\xce\xbc<" REPLACE
    OK
    
    127.0.0.1:6379> get as1
    "ask 123 \xe6\x82\xa8"

    前面我们学习过的 MIGRATE 在传输过程中,其实就是通过序列化的方式来传输数据的。另外编码的结果还包括这些特点:

    • 它带有 64 位的校验和,用于检测错误,RESTORE 在进行反序列化之前会先检查校验和。

    • 值的编码格式和 RDB 文件保持一致。

    • RDB 版本会被编码在序列化值当中,如果因为 Redis 的版本不同造成 RDB 格式不兼容,那么 Redis 会拒绝对这个值进行反序列化操作。

    时间相关操作

    对于时间的处理,其实在最早的时候学习 String 相关操作时就有说过一点,因为现在 SET 命令已经包含很多选项并且可以直接设置时间了。但是对于其它数据类型,还是需要使用传统的 EXPIRE 相关命令来进行时间设置的。同时,TTL 命令应该也是大家非常熟悉的,这个命令返回还有多久时间会过期。

    127.0.0.1:6379> EXPIRE a 20
    (integer) 1
    
    127.0.0.1:6379> TTL a
    (integer) 15
    127.0.0.1:6379> TTL a
    (integer) 12
    
    // 等20秒后
    127.0.0.1:6379> TTL a
    (integer) -2

    消除过期时间

    对于一个有过期时间的 KEY 来说,如果我们想要消除它的过期时间,可以使用 PERSIST 命令。

    127.0.0.1:6379> SET a 123
    OK
    127.0.0.1:6379> EXPIRE a 20
    (integer) 1
    127.0.0.1:6379> TTL a
    (integer) 18
    127.0.0.1:6379> PERSIST a
    (integer) 1
    127.0.0.1:6379> TTL a
    (integer) -1

    除了这个命令还有什么方式呢?其实我们也可以直接再 SET 一次这个 KEY 。

    127.0.0.1:6379> set a 111
    OK
    127.0.0.1:6379> ttl a
    (integer) -1

    指定时间过期

    通过 EXPIREAT 命令,可以指定一个固定的时间戳,然后 KEY 会在这个时间后过期。

    // 2025-05-30 17:25:01
    127.0.0.1:6379> EXPIREAT a 1748597101
    (integer) 1
    127.0.0.1:6379> TTL a
    (integer) 94694369
    127.0.0.1:6379> TTL a
    (integer) 94694367

    毫秒操作

    上面的几个命令都是秒级的操作,Redis 同时还支持毫秒级的操作,命令都只是在秒级命令前面加了个 P ,比如 PEXPIRE、PTTL、PEXPIREAT 。

    127.0.0.1:6379> PEXPIRE a 500000
    (integer) 1
    127.0.0.1:6379> TTL a
    (integer) 497
    127.0.0.1:6379> PTTL a
    (integer) 492449
    
    127.0.0.1:6379> PEXPIREAT a 1748597101000
    (integer) 1
    127.0.0.1:6379> TTL a
    (integer) 94694135
    127.0.0.1:6379> PTTL a
    (integer) 94694129803

    最新的 Redis7 新增加了 EXPIRETIME 和 PEXPIRETIME 命令,我这里还没有升级,所以就不演示了。它的作用是返回指定的 KEY 具体在哪一个时间点会过期,TTL 返回的是倒数的秒数,而 EXPIRETIME 返回的是到期时间的时间戳。

    随机返回一个 KEY

    从所有的 KEY 中,随机的返回一个 KEY ,这个操作是相当快的,可以达到 O(1) ,使用的是 RANDOMKEY 这个命令。

    127.0.0.1:6379> keys *
    1) "a2"
    2) "d"
    3) "e"
    4) "c"
    5) "b"
    6) "data"
    7) "a"
    8) "a1"
    9) "as1"
    127.0.0.1:6379> RANDOMKEY
    "a2"

    修改 KEY 名称

    想要修改一个 KEY 的名称,可以使用 RENAME 命令,指定原来的 KEY 和想要修改成的名称就可以了。

    127.0.0.1:6379> RENAME a1 a111
    OK
    127.0.0.1:6379> KEYS *
    1) "a2"
    2) "d"
    3) "e"
    4) "c"
    5) "b"
    6) "data"
    7) "a"
    8) "a111"
    9) "as1"

    如果新的名称已经存在了,那么会直接覆盖,如果源名称的 KEY 不存在了,自然也就直接会报错。

    127.0.0.1:6379> set a 555
    OK
    127.0.0.1:6379> RENAME a a111
    OK
    127.0.0.1:6379> get a111
    "555"
    
    127.0.0.1:6379> RENAME a2 a222
    OK
    127.0.0.1:6379> KEYS *
    1) "d"
    2) "e"
    3) "a222"
    4) "c"
    5) "b"
    6) "data"
    7) "a"
    8) "a111"
    9) "as1"
    127.0.0.1:6379> RENAMENX a2 a222
    (error) ERR no such key

    SCAN

    最后就是上面说过的一个问题,KEYS * 的执行速度慢,在单线程程序中会影响到后面命令的执行。其中官方第一个推荐的就是使用 SCAN 。


    关于 SCAN 的内容之前在学习 Hash、Set、Sorted Set 时就已经学习过了,HSCAN、SSCAN 以及 ZSCAN 分别就是对应上述三种数据类型的迭代命令。而不带任何前缀的 SCAN 命令就是用来迭代所有的 KEY 信息的。

    127.0.0.1:6379> scan 0
    1) "0"
    2) 1) "d"
       2) "e"
       3) "a"
       4) "c"
       5) "data"
       6) "a222"
       7) "b"
       8) "a111"
       9) "as1"

    详细的测试就不演示了,大家可以使用外部程序多创建一些 KEY 就可以体现出 SCAN 的强大了。其它的用法以及参数作用和我们之前学习过的那些 HSCAN 之类的都一样。

    总结

    通用命令的第一部分就是这些,命令都比较简单,但说实话可能很多同学包括我,除了 EXPIRE 和 KEYS 就没用过别的了。没关系,起码现在咱们都知道了 COPY、MOVE 命令,也知道了 Redis 中也有序列化操作了吧。下一篇还是基础通用命令的部分,大家加油,整个基础部分的学习就快要结束了哦!

    视频链接

    微信文章地址:https://mp.weixin.qq.com/s/Xg_bmexfJKTG65UYmMWcnA

    B站视频地址:https://www.bilibili.com/video/BV1Nz4y1e7uv

    微信视频地址:https://mp.weixin.qq.com/s/v_D9gdOMEvZFZUP55Co5-g

    搜索
    关注