Redis 实战 —— 04. Redis 数据结构常用命令简介

字符串 P39

Redis 的字符串是一个有字节组成的序列,可以存储以下 3 种类型的值:字节串(byte string)、整数、浮点数。

在需要的时候, Redis 会将整数转换成浮点数。整数的取值范围和系统的长整型(long)的相同,浮点数取值范围和精度与 IEEE 754 标准下的双精度浮点数(double)的相同。

Redis 中的自增命令和自减命令 P39
命令 格式 描述
INCR INCR key 将键存储的数字值加上 1
DECR DECR key 将键存储的数字值减去 1
INCRBY INCRBY key increment 将键存储的数字值加上整数增量 increment
DECRBY DECRBY key decrement 将键存储的数字值减去整数减量 decrement
INCRBYFLOAT INCRBYFLOAT key increment 将键存储的数字值加上浮点数增量 increment

相关演示代码如下(mainhandleResult 定义见:01. Redis 数据结构简介.md):

// 执行字符串类型数字相关操作
func executeNumberOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "number")))
	// 获取值,输出 -> ERROR:  redigo: nil returned
	handleResult(redis.Int(conn.Do("GET", "number")))
	// 自增 1,返回自增后的值 -> 1
	handleResult(redis.Int(conn.Do("INCR", "number")))
	// 自增 2,返回自增后的值 -> 3
	handleResult(redis.Int(conn.Do("INCRBY", "number", "2")))
	// 自减 1,返回自减后的值 -> 2
	handleResult(redis.Int(conn.Do("DECR", "number")))
	// 自减 2,返回自减后的值 -> 0
	handleResult(redis.Int(conn.Do("DECRBY", "number", "2")))
	// 自增 1.5,返回自增后的值 -> 1.5
	handleResult(redis.Float64(conn.Do("INCRBYFLOAT", "number", "1.5")))
	// 自增 -1.3,返回自增后的值 -> 0.2
	handleResult(redis.Float64(conn.Do("INCRBYFLOAT", "number", "-1.3")))
}
供 Redis 处理子串和二进制位的命令 P40
命令 格式 描述
APPEND APPEND key value 将 value 追加到 key 当前值的末尾
GETRANGE GETRANGE key start end 返回 [start, end] 范围内子串
SETRANGE SETRANGE key offset value 将子串 [offset, offset + len(value)) 设置为 value
GETBIT GETBIT key offset 将字符串看作是二进制位串,获取 offset 上的位
SETBIT SETBIT key offset value 将字符串看作是二进制位串,设置 offset 上的位为 value
BITCOUNT BITCOUNT key [start end] 统计 [start, end] 范围内子串在二进制下有多少个 1
BITOP BITOP operation destkey key [key …] operation 可选位运算 AND , OR , XOR , NOT ,将一个或多个二进制位串执行的操作结果存到 destkey 中

相关演示代码如下:

// 执行字符串类型字符串相关操作
func executeStringOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "string")))
	// 追加串,返回当前字符串长度 -> 6,值变为 -> append
	handleResult(redis.Int(conn.Do("APPEND", "string", "append")))
	// 获取子串,返回 -> en
	handleResult(redis.String(conn.Do("GETRANGE", "string", 3, 4)))
	// 设置子串,返回当前字符串长度 -> 6,值变为 -> appled
	handleResult(redis.Int(conn.Do("SETRANGE", "string", 3, "le")))
	// 设置子串,返回当前字符串长度 -> 11,值变为 -> application
	handleResult(redis.Int(conn.Do("SETRANGE", "string", 3, "lication")))
	// 获取二进制位,返回 -> 1
	// (获取第 7/8 个字符 a 在二进制下第 7%8 位上的二进制位,即 0110 0001 的第 7 位 1)
	handleResult(redis.Int(conn.Do("GETBIT", "string", 7)))
	// 设置二进制位,返回原来的二进制位 -> 0,值变为 -> cpplication
	// (设置第 6/8 个字符 a 在二进制下第 6%8 位上的二进制位为1,即 0110 0001 变为 0110 0011)
	handleResult(redis.Int(conn.Do("SETBIT", "string", 6, 1)))
	// 统计二进制位,返回 -> 7
	// (统计 [0, 1] 范围内子串 cp 在二进制下 0110 0011 0111 0000 二进制位为 1 的数量)
	handleResult(redis.Int(conn.Do("BITCOUNT", "string", 0, 1)))

	handleResult(redis.String(conn.Do("SET", "aKey", "aa")))
	handleResult(redis.String(conn.Do("SET", "bKey", "b")))
	// 对 aa(0110 0001 0110 0001) 和 b(0110 0010 0000 0000) 进行 按位或,结果存储到 cKey 中
	// 返回字符串长度 -> 2,值为 ca(0110 0011 0110 0001),
	handleResult(redis.Int(conn.Do("BITOP", "OR", "cKey", "aKey", "bKey")))
}

Redis 可以通过使用子串操作和二进制位操作,配合 WATCHMULTIEXEC 命令(后面会初步介绍,以后将深入讲解),构建任何想要的数据结构。

列表 P42

一些常用的列表命令 P42
命令 格式 描述
RPUSH RPUSH key value [value …] 依次将一个或多个 value 从列表右端插入
LPUSH LPUSH key value [value …] 依次将一个或多个 value 从列表左端插入
RPOP RPOP key 移除并返回列表最右端的元素
LPOP LPOP key 移除并返回列表最左端的元素
LINDEX LINDEX key offset 返回列表左端开始偏移量为 offset 的元素
LRANGE LRANGE key start end 返回列表左端开始 [start, end] 范围内的所有元素
LTRIM LTRIM key start end 移除列表左端开始 [start, end] 范围外的所有元素

相关演示代码如下:

// 执行列表类型相关操作
func executeListOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "list")))
	// 右端插入一次插入 a, b, c,返回当前列表长度 -> 3,列表变为 -> a b c
	handleResult(redis.Int(conn.Do("RPUSH", "list", "a", "b", "c")))
	// 左端插入一次插入 d, e, f,返回当前列表长度 -> 6,列表变为 -> f e d a b c
	handleResult(redis.Int(conn.Do("LPUSH", "list", "d", "e", "f")))
	// 弹出并返回列表最右端的值,返回 -> c,列表变为 -> f e d a b
	handleResult(redis.String(conn.Do("RPOP", "list")))
	// 弹出并返回列表最左端的值,返回 -> f,列表变为 -> e d a b
	handleResult(redis.String(conn.Do("LPOP", "list")))
	// 返回左端开始下标偏移量为 offset 的值,返回 -> d
	handleResult(redis.String(conn.Do("LINDEX", "list", 1)))
	// 移除列表左端开始 [1, 2] 范围外的所有元素,列表变为 -> d a
	handleResult(redis.String(conn.Do("LTRIM", "list", 1, 2)))
}

利用 LTRIM 命令可以原子地弹出多个元素。 P43

阻塞式的列表弹出命令以及在列表之间移动元素的命令 P43
命令 格式 描述
BLPOP BLPOP key [key …] timeout 从第一个非空列表中弹出最左端的元素,或者在 timeout 秒内阻塞并等待可弹出的元素出现,返回被弹出的列表名及元素, timeout 为 0 表示无限等待
BRPOP BRPOP key [key …] timeout 从第一个非空列表中弹出最右端的元素,或者在 timeout 秒内阻塞并等待可弹出的元素出现,返回被弹出的列表名及元素, timeout 为 0 表示无限等待
RPOPLPUSH RPOPLPUSH source destination 从 source 列表中弹出最右端的元素,然后将这个元素退出 destination 列表的最左端,并返回这个元素
BRPOPLPUSH BRPOPLPUSH source destination timeout 从 source 列表中弹出最右端的元素,然后将这个元素退出 destination 列表的最左端,并返回这个元素;如果 source 列表为空,则在 timeout 秒内阻塞并等待可弹出元素出现

相关演示代码如下:

// 执行列表类型阻塞相关操作
func executeListBlockOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "source", "destination")))
	// 从第一个非空列表中弹出并返回列表最左端的值,最多等待 1秒,输出 -> ERROR:  redigo: nil returned
	handleResult(redis.Strings(conn.Do("BLPOP", "source", "destination", 1)))

	// 初始化
	handleResult(redis.Int(conn.Do("RPUSH", "source", "a", "b", "c")))
	handleResult(redis.Int(conn.Do("RPUSH", "destination", "d", "e", "f")))
	// 从第一个非空列表中弹出并返回列表最左端的值,无限等待,返回 -> a,source 变为 -> b c,destination 变为 -> d e f
	handleResult(redis.Strings(conn.Do("BLPOP", "source", "destination", 0)))
	// 从第一个非空列表中弹出并返回列表最右端的值,无限等待,返回 -> f,source 变为 -> b c,destination 变为 -> d e
	handleResult(redis.Strings(conn.Do("BRPOP", "destination", "source", 0)))

	// 从 source 弹出最右端元素,然后推入到 destination 最左端,并返回这个元素
	// 返回 -> c,source 变为 -> b,destination 变为 -> c d e
	handleResult(redis.String(conn.Do("RPOPLPUSH", "source", "destination")))
	// 从 source 弹出最右端元素,然后推入到 destination 最左端,并返回这个元素,无限等待
	// 返回 -> b,source 变为 -> <nil>,destination 变为 -> b c d e
	handleResult(redis.String(conn.Do("BRPOPLPUSH", "source", "destination", 0)))
	// 从 source 弹出最右端元素,然后推入到 destination 最左端,并返回这个元素,最多等待 1秒
	// 输出 -> ERROR:  redigo: nil returned,source 变为 -> <nil>,destination 变为 -> b c d e
	handleResult(redis.String(conn.Do("BRPOPLPUSH", "source", "destination", 1)))
}

对于阻塞弹出命令和弹出并推入命令,最常见的用例就是消息传递(messaging)和任务队列(task queue),将在以后对这两个主题进行介绍。 P44

练习题:通过列表来降低内存占用 P44

上篇文章中,我们使用了有序集合来记录用户最近浏览过的商品,并把用户浏览这些商品时的时间戳设置为分值,从而使得程序可以在清理旧会话的过程中或是在执行完购买操作后,进行相应的数据分析。但由于保存时间戳需要占用相应的空间,所以如果分析操作并不需要用到时间戳的话,那么就没有必要使用有序集合来保存用户最近浏览过的商品了。为此,请在保证语义不变的情况下,将 UpdateToken 函数里面是用的有序集合替换成列表。

提示:如果在解答这个问题时遇上困难的话,可以到 6.1.1 节中找找灵感。

  • 由于列表是有序的,所有最新访问的一定在列表的左端,所以每次操作时先删除列表中这个访问记录,再推入列表左端,最后修剪列表为长度为 25 即可。由于每次需要遍历整个列表,所以时间复杂度较高,但是列表长度总共只有 25 ,时间上相差不大,但是空间可以节省很多。

    // 更新最近商品访问列表
    func UpdateLatestViewedItem(conn redis.Conn, itemId int) {
    	// 移除列表中所有值为 itemId 的元素
    	_ = conn.Send("LREM", "latestViewedItem", 0, itemId)
    	// 将最近访问的商品推入列表最左端
    	_ = conn.Send("LPUSH", "latestViewedItem", itemId)
    	// 修剪列表,保留最近访问的 25 个
    	_ = conn.Send("LTRIM", "latestViewedItem", 0, 24)
    	// 执行上述命令
    	_ = conn.Flush()
    }
    

集合 P44

一些常用的集合命令 P45
命令 格式 描述
SADD SADD key member [member …] 将一个或多个元素添加到集合中,返回添加到集合中的新元素的数量(不包括已存在的元素)
SREM SREM keymember [member …] 将一个或多个元素从集合中删除,返回成功从集合中删除的元素(不包括不存在的元素)
SISMEMBER SISMEMBER key member 判断元素 member 是否在集合 key 中
SCARD SCARD key 返回结合中元素的数量
SMEMBERS SMEMBERS key 返回集合的所有元素
SRANDMEMBER SRANDMEMBER key [count] 随机返回集合中一个或多个元素。count 为正数时,返回 count 个各不相同的元素(最多返回整个集合);count 为负数时,返回 |count| 个可能会重复的元素,无最长限制。
SPOP SPOP key 随机移除并返回集合中的一个元素
SMOVE SMOVE source destination member 将元素 member 从集合 source 移动到集合 destination 中

相关演示代码如下:

// 执行集合类型相关操作
func executeSetOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "source", "destination")))
	// 集合中添加三个元素,输出 -> 6,source 变为 -> 1 2 3 4 5 6 7
	handleResult(redis.Int(conn.Do("SADD", "source", 1, 2, 3, 4, 5, 6, 7, 1)))
	// 从集合中删除两个元素: 1 2,输出 -> 2,source 变为 -> 3 4 5 6 7
	handleResult(redis.Int(conn.Do("SREM", "source", 1, 2)))
	// 判断集合是否含有元素 3,输出 -> 1
	handleResult(redis.Int(conn.Do("SISMEMBER", "source", 3)))
	// 返回集合的元素个数,输出 -> 5
	handleResult(redis.Int(conn.Do("SCARD", "source")))
	// 返回集合的所有元素,输出 -> [3 4 5 6 7]
	handleResult(redis.Ints(conn.Do("SMEMBERS", "source")))
	// 随机返回集合中不同的 3 个元素,输出 -> [6 5 3] (随机结果可能存在不同,以实际为准)
	handleResult(redis.Ints(conn.Do("SRANDMEMBER", "source", 3)))
	// 随机返回集合中可重复的 6 个元素,输出 -> [7 5 6 3 7 6] (随机结果可能存在不同,以实际为准)
	handleResult(redis.Ints(conn.Do("SRANDMEMBER", "source", -6)))
	// 随机删除集合中的 1 个元素,输出 -> 3 ,source 变为 -> 4 5 6 7(随机结果可能存在不同,以实际为准)
	handleResult(redis.Int(conn.Do("SPOP", "source")))
	// 移动 source 集合中的元素 7 到 destination 集合中(由于前面存在随机,结果可能存在不同,以实际为准)
	// 输出 -> 1 ,source 变为 -> 4 5 6 ,destination 变为 -> 7
	handleResult(redis.Int(conn.Do("SMOVE", "source", "destination", 7)))
}
用于组合和处理多个集合的命令 P45
命令 格式 描述
SDIFF SDIFF key [key …] 返回存在于第一个集合,而不存在于其他集合的元素(差集)
SDIFFSTORE SDIFFSTORE destination key [key …] 将存在于第一个集合,而不存在于其他集合的元素(差集)存储到 destination 中,返回差集大小
SINTER SINTER key [key …] 返回同时存在于所有集合中的元素(交集)
SINTERSTORE SINTERSTORE destination key [key …] 将同时存在于所有集合中的元素(交集)存储到 destination 中,返回交集大小
SUNION SUNIONkey [key …] 返回至少存在于一个集合中的元素(并集)
SUNIONSTORE SUNIONSTORE destination key [key …] 将至少存在于一个集合中的元素(并集)存储到 destination 中,返回并集大小

相关演示代码如下:

// 执行集合类型多个集合相关操作
func executeSetMutiOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "source_1", "source_2", "source_3", "destination")))
	// 初始化
	handleResult(redis.Int(conn.Do("SADD", "source_1", 1, 2, 4, 8)))
	handleResult(redis.Int(conn.Do("SADD", "source_2", 2, 3, 4, 5)))
	handleResult(redis.Int(conn.Do("SADD", "source_3", 5, 6, 7, 8)))
	// 返回三个集合的差集,输出 -> [1]
	handleResult(redis.Ints(conn.Do("SDIFF", "source_1", "source_2", "source_3")))
	// 将三个集合的差集存储到 destination 中,输出 -> 1,destination 变为 -> 1
	handleResult(redis.Int(conn.Do("SDIFFSTORE", "destination", "source_1", "source_2", "source_3")))
	// 返回两个集合的交集,输出 -> [2 4]
	handleResult(redis.Ints(conn.Do("SINTER", "source_1", "source_2")))
	// 将两个集合的交集存储到 destination 中,输出 -> 2,destination 变为 -> 2 4
	handleResult(redis.Int(conn.Do("SINTERSTORE", "destination", "source_1", "source_2")))
	// 返回三个集合的并集,输出 -> [1 2 3 4 5 6 7 8]
	handleResult(redis.Ints(conn.Do("SUNION", "source_1", "source_2", "source_3")))
	// 将三个集合的并集存储到 destination 中,输出 -> 8,destination 变为 -> 1 2 3 4 5 6 7 8
	handleResult(redis.Int(conn.Do("SUNIONSTORE", "destination", "source_1", "source_2", "source_3")))
}

哈希表 P46

用于添加和删除键值对的散列操作 P47
命令 格式 描述
HMGET HMGET key field [field …] 从哈希表中获取一个或多个 field 的值
HMSET HMSET key field value [field value …] 向哈希表中设置一个或多个 field 的值
HDEL HDEL key field [field …] 从哈希表中删除一个或多个 field 的值
HLEN HLEN key 返回哈希表中包含的 field 的数量

相关演示代码如下:

// 执行哈希表类型相关操作
func executeHashOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "hash")))
	// 向哈希表中设置一个或多个 field 的值,输出 -> OK,hash 变为 -> {field_1: value_1, field_2: value_2, field_3: value_3}
	handleResult(redis.String(conn.Do("HMSET", "hash", "field_1", "value_1", "field_2", "value_2", "field_3", "value_3")))
	// 从哈希表中获取一个或多个 field 的值,输出 -> [value_1 value_3 value_2]
	handleResult(redis.Strings(conn.Do("HMGET", "hash", "field_1", "field_3", "field_2")))
	// 从哈希表中删除一个或多个 field 的值,输出 -> 2,hash 变为 -> field_2: value_2}
	handleResult(redis.Int(conn.Do("HDEL", "hash", "field_1", "field_3")))
	// 返回哈希表中包含的 field 的数量,输出 -> 1
	handleResult(redis.Int(conn.Do("HLEN", "hash")))
}
哈希表的更高级特性 P47
命令 格式 描述
HEXISTS HEXISTS key field 判断 field 是否存在于哈希表中
HKEYS HKEYS key 返回哈希表中所有的 field
HVALS HVALS key 返回哈希表中所有 field 的值
HGETALL HGETALL key 返回哈希表中所有的 field 及其值
HINCRBY HINCRBY key field increment 将哈希表中 field 的值增加整数 increment
HINCRBYFLOAT HINCRBYFLOAT key field increment 将哈希表中 field 的值增加浮点数 increment

相关演示代码如下:

// 执行哈希表类型高级特性相关操作
func executeHashFeatureOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "hash")))
	// 初始化
	handleResult(redis.String(conn.Do("HMSET", "hash", "field_1", "value_1", "field_2", "value_2", "field_3", "3")))
	// 判断 field 是否存在于哈希表中,输出 -> 1
	handleResult(redis.Int(conn.Do("HEXISTS", "hash", "field_1")))
	// 返回哈希表中所有的 field,输出 -> [field_1 field_2 3]
	handleResult(redis.Strings(conn.Do("HKEYS", "hash")))
	// 返回哈希表中所有 field 的值,输出 -> [value_1 value_2 value_3]
	handleResult(redis.Strings(conn.Do("HVALS", "hash")))
	// 返回哈希表中所有的 field 及其值,输出 -> map[field_1:value_1 field_2:value_2 field_3:3]
	handleResult(redis.StringMap(conn.Do("HGETALL", "hash")))
	// 将哈希表中 field 的值增加 1,输出 -> 4,field_3 的值变为 -> 4
	handleResult(redis.Int(conn.Do("HINCRBY", "hash", "field_3", 1)))
	// 将哈希表中 field 的值增加 -1.5,输出 -> 2.5,field_3 的值变为 -> 2.5
	handleResult(redis.Float64(conn.Do("HINCRBYFLOAT", "hash", "field_3", -1.5)))
}

如果哈希表包含的值非常大,可以先使用 HKEYS 取出所有的 field,然后再使用 HGET 取出值,防止一次取出多个大体积的值而导致服务器阻塞。 P48

有序集合 P48

一些常用的有序集合命令 P49
命令 格式 描述
ZADD ZADD key socre member [score member …] 向有序集合中添加一个或多个元素及其分值
ZREM ZREM key member [member …] 从有序集合中删除一个或多个元素及其分值
ZCARD ZCARD key 返回有序集合中元素的个数
ZINCRBY ZINCRBY key increment member 给有序集合中的元素的分值增加 increment
ZCOUNT ZCOUNT key min max 返回分值在 [min, max] 范围内的元素的数量
ZRANK ZRANK key member 返回元素的升序排名(升序,从 0 开始)
ZREVRANK ZREVRANK key member 返回元素的降序排名(降序,从 0 开始)
ZSCORE ZSCORE key member 返回元素的排名的分值
ZRANGE ZRANGE key start stop [WITHSCORES] 返回升序排名在 [start, stop] 范围内的元素,WITHSCORES 选项会同时在相应的元素后返回分值
ZRANRANGE ZRANGE key start stop [WITHSCORES] 返回降序排名在 [start, stop] 范围内的元素,WITHSCORES 选项会同时在相应的元素后返回分值

相关演示代码如下:

// 执行有序集合相关操作
func executeZsetOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "zset")))
	// 有序集合中添加 5 个元素及其分值,输出 -> 5,zset 变为 -> ["a":1, "b":2, "c":3, "d":4, "e":5]
	handleResult(redis.Int(conn.Do("ZADD", "zset", 1, "a", 2, "b", 3, "c", 4, "d", 5, "e")))
	// 有序集合中删除 3 个元素及其分值,输出 -> 2,zset 变为 -> ["a":1, "b":2, "c":3]
	handleResult(redis.Int(conn.Do("ZREM", "zset", "d", "e", "f")))
	// 返回有序集合的元素个数,输出 -> 3
	handleResult(redis.Int(conn.Do("ZCARD", "zset")))
	// 给有序集合中的元素的分值增加 0.5,输出 -> 1.5,a 的值变为 -> 1.5
	handleResult(redis.Int(conn.Do("ZINCRBY", "zset", 1, "a")))
	// 给有序集合中的元素的分值增加 -1.5,输出 -> 0.5,a 的值变为 -> 0.5
	handleResult(redis.Float64(conn.Do("ZINCRBY", "zset", -1.5, "a")))
	// 返回分值在 [1, 3] 范围内的元素的数量,输出 -> 2
	handleResult(redis.Int(conn.Do("ZCOUNT", "zset", 1, 3)))
	// 返回元素的升序排名(升序,从 0 开始),输出 -> 0
	handleResult(redis.Int(conn.Do("ZRANK", "zset", "a")))
	// 返回元素的降序排名(降序,从 0 开始),输出 -> 2
	handleResult(redis.Int(conn.Do("ZREVRANK", "zset", "a")))
	// 返回元素的排名的分值,输出 -> 0.5
	handleResult(redis.Float64(conn.Do("ZSCORE", "zset", "a")))
	// 返回升序排名在 [1, 2] 范围内的元素,并且返回分值,输出 -> map[b:2 c:3]
	handleResult(redis.StringMap(conn.Do("ZRANGE", "zset", "1", "2", "WITHSCORES")))
	// 返回降序排名在 [1, 2] 范围内的元素,并且返回分值,输出 -> map[a:0.5 b:2]
	handleResult(redis.StringMap(conn.Do("ZREVRANGE", "zset", "1", "2", "WITHSCORES")))
}
有序集合的范围性命令及并交集命令 P50
命令 格式 描述
ZRANGEBYSCORE ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] 返回升序分值在 [min, max] 范围内的元素,WITHSCORES 选项会同时在相应的元素后返回分值
ZREVRANGEBYSCORE ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count] 返回降序分值在 [max, min] 范围内的元素,WITHSCORES 选项会同时在相应的元素后返回分值
ZREMRANGEBYRANK ZREMRANGEBYRANK key start stop 移除升序排名在 [start, stop] 范围内的元素
ZREMRANGEBYSCORE ZREMRANGEBYSCORE key min max 移除升序分值在 [min, max] 范围内的元素
ZINTERSTORE ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]]  [AGGREGATE SUM|MIN|MAX] 求一个或多个(有序)集合的交集,并存储到 destination 中,WEIGHTS 权重存在时,weight 数量必须等于 numkeys(集合默认分值为 1)
ZUNIONSTORE ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]]  [AGGREGATE SUM|MIN|MAX] 求一个或多个(有序)集合的并集,并存储到 destination 中,WEIGHTS 权重存在时,weight 数量必须等于 numkeys(集合默认分值为 1)

相关演示代码如下:

// 执行有序集合范围及交并集相关操作
func executeZsetMutiOperation(conn redis.Conn) {
	// 删除原有值
	handleResult(redis.Int(conn.Do("DEL", "zset_1", "zset_2", "destination")))
	// 初始化
	handleResult(redis.Int(conn.Do("ZADD", "zset_1", 1, "a", 2, "b", 3, "c")))
	handleResult(redis.Int(conn.Do("ZADD", "zset_2", 2, "b", 3, "c", 4, "d")))
	// 返回升序分值在 [1, 2] 范围内的元素,并且返回分值,输出 -> map[a:1 b:2]
	handleResult(redis.StringMap(conn.Do("ZRANGEBYSCORE", "zset_1", "1", "2", "WITHSCORES")))
	// 返回降序分值在 [4, 3] 范围内的元素,并且返回分值,输出 -> map[c:3 d:4]
	handleResult(redis.StringMap(conn.Do("ZREVRANGEBYSCORE", "zset_2", "4", "3", "WITHSCORES")))
	// 移除升序排名在 [1, 1] 范围内的元素,输出 -> 1,zset_1 变为 -> ["b":2, "c":3]
	handleResult(redis.Int(conn.Do("ZREMRANGEBYRANK", "zset_1", "1", "1")))
	// 移除降序排名在 [2, 2] 范围内的元素,输出 -> 1,zset_2 变为 -> ["c":3, "d":4]
	handleResult(redis.Int(conn.Do("ZREMRANGEBYSCORE", "zset_2", "2", "2")))
	// 求 2 个有序集合的交集,权重分别为 2, 3,分值默认采用加法
	// 并存储到 destination 中,输出 -> 1,destination 变为 ->
	handleResult(redis.Int(conn.Do("ZINTERSTORE", "destination", 2, "zset_1", "zset_2", "WEIGHTS", 2, 3)))
	// 求 2 个有序集合的并集,权重分别为 2, 3,分值指定采用最大值
	// 并存储到 destination 中,输出 -> 3,destination 变为 -> ["a":2, "c":9, "d":12]
	handleResult(redis.Int(conn.Do("ZUNIONSTORE", "destination", 2, "zset_1", "zset_2", "WEIGHTS", 2, 3, "AGGREGATE", "MAX")))
}

所思所想

  • 这一章又是比较枯燥的命令介绍,不过还是坚持看下来了,发现还是挺有用的,有很多平常没接触的命令,也没想到 Redis 竟然这么强大。

  • 即使时比较精细地阅读,也不需要全部阅读,可以快速浏览已经知道的基础,重点还是要放在不知道的地方,带着思考去阅读,先想然后用实践验证。

本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/redis-in-action