Redis系列第二章
发表于:2023-11-12 | 分类: 中间件
字数统计: 15.9k | 阅读时长: 58分钟 | 阅读量:

1. 基本命令

  • 心跳命令 ping

输入 ping 会看到 PONG 响应,说明客户端与 Redis 的连接是正常的

  • 读写键值命令

set key value 会将指定 key-value 写入到 DB。get key 则会读取指定 key 的 value 值

  • DB 切换命令

在概述中有介绍 Redis 数据库情况。我们可以通过 select db索引来切换DB

  • 查看 key 数量

dbsize 命令可以查看当前数据库中 key 的数量。

  • 删除库中数据

flushdb 命令仅仅删除的是当前数据库中的数据,不影响其它库。

flushall 命令可以删除所有库中的所有数据。

  • 退出客户端

使用 exit 或 quit 命令均可退出 Redis 命令行客户端。

2. key 操作命令

Redis 中存储的数据整体是一个 Map,其 key 为 String 类型,而 value 则可以是 String、Hash 表、List、Set 等类型。

  • keys

格式:KEYS pattern

查找所有符合给定模式 pattern 的 key,pattern 为正则表达式。

KEYS 的速度非常快,但在一个大的数据库中使用它可能会阻塞当前服务器的服务。所以生产环境中一般不使用该命令,而使用 scan 命令代替。

  • exists

格式:EXISTS key ;检查给定 key 是否存在,若存在返回 1 ,否则返回 0

  • del

格式:DEL key [key …]

删除给定的一个或多个 key ,不存在的 key 会被忽略。会返回被删除 key 的数量。

  • rename

格式:RENAME key newkey;将 key 改名为 newkey。

说明:当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。当 newkey 已经存在时, RENAME 命令将覆盖旧值。改名成功时提示 OK ,失败时候返回一个错误。

5b8b0c9210a022ab0362b65e7abb728d.png

  • move

格式:MOVE key db

将当前数据库的 key 移动到给定的数据库 db 当中。

如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key , 或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。移动成功返回 1 ,失败 则返回 0 。

  • type

格式:TYPE key

返回 key 所储存的值的类型:none (key 不存在)、string (字符串)、list (列表)、set (集合)、zset (有序集)、hash (哈希表)

  • expire 与 pexpire

格式:EXPIRE key seconds

为给定 key 设置生存时间。当 key 过期时(生存时间为 0),它会被自动删除。 expire 的时间单位为秒,pexpire 的时间单位为毫秒。在 Redis 中,带有生存时间的 key 被称为“易失的”(volatile)。

生存时间设置成功返回 1;若 key 不存在时返回 0 ;rename 操作不会改变 key 的生存时间。

  • ttl 与 pttl

格式:TTL key

TTL, time to live,返回给定 key 的剩余生存时间。其返回值存在三种可能:

  • 当 key 不存在时,返回 -2 。

  • 当 key 存在但没有设置剩余生存时间时,返回 -1 。

  • 返回 key 的剩余生存时间。ttl 命令返回的时间单位为秒,而 pttl 命令返回的时间单位为毫秒。

  • persist

格式:PERSIST key

去除给定 key 的生存时间,将这个 key 从“易失的”转换成“持久的”。

当生存时间移除成功时,返回 1;若 key 不存在或 key 没有设置生存时间,则返回 0。

  • randomkey

格式:RANDOMKEY

从当前数据库中随机返回(不删除)一个 key。

当数据库不为空时,返回一个 key;当数据库为空时,返回 nil。常用来判断数据库是否为空

  • scan

格式:

1
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
  • cursor:本次迭代开始的游标。
  • pattern :本次迭代要匹配的 key 的模式。
  • count :本次迭代要从数据集里返回多少元素,默认值为 10 。
  • type:本次迭代要返回的 value 的类型,默认为所有类型。

SCAN 命令是一个基于游标 cursor 的迭代器:SCAN 命令每次被调用之后,都会向用户返回返回一个包含两个元素的数组, 第一个元素是用于进行下一次迭代的新游标, 而第二个元素则是一个数组, 这个数组中包含了所有被迭代的元素。用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数,以此来延续之前的迭代过程。当 SCAN 命令的游标参数被设置为 0 时,服务器将开始一次新的迭代。如果新游标返回 0 表示迭代已结束。

说明:使用间断的、负数、超出范围或者其他非正常的游标来执行增量式迭代不会造成服务器崩溃。

当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。由于 scan 命令每次执行都只会返回少量元素,所以该命令可以用于生产环境, 而不会出现像 KEYS 命令带来的服务器阻塞问题。

scan可能返回少于count个元素(只剩下少于count个数的元素了),可能返回等于count个元素,也可能返回比count多一点。官方文档解释count参数just a hint,只是一个提示作用的参数。

增量式迭代命令所使用的算法只保证在数据集的大小有界的情况下迭代才会停止,换句话说,如果被迭代数据集的大小不断地增长的话,增量式迭代命令可能永远也无法完成一次完整迭代。

示例:

9ed7aa7152e4c5de79408a0e76242676.png

相关命令:

  • hscan:Hash 型 Value 操作命令集合,用于遍历当前 db 中指定 Hash 表的所有 field-value 对。
  • sscan:Set 型 Value 操作命令集合,用于遍历当前 db 中指定 set 集合的所有元素
  • zscan:ZSet 型 Value 操作命令集合,用于遍历当前 db 中指定有序集合的所有元素(数值与元素值)

3. String 型 Value 操作命令

String 类型的 Value 中可以存放任意数据,包括数值型,甚至是二进制的图片、音频、视频、序列化对象等。一个 String 类型的 Value 最大是 512M 大小。

  • set
1
格式:SET key value [EX seconds | PX milliseconds] [NX|XX]
  1. EX seconds:为当前 key 设置过期时间,单位秒。等价于 SETEX 命令。
  2. PX milliseconds:为当前 key 设置过期时间,单位毫秒。等价于 PSETEX 命令。
  3. NX:指定的 key 不存在才会设置成功,用于添加指定的 key。等价于 SETNX 命令。
  4. XX:指定的 key 必须存在才会设置成功,用于更新指定 key 的 value。

如果 value 字符串中带有空格,则该字符串需要使用双引号或单引号引起来,否则会认为 set 命令的参数数量不正确,报错。

  • setex 与 psetex
1
SETEX/PSETEX key seconds value

不仅为 key 指定了 value,还为其设置了生存时间。setex 的单位为秒,psetex 的单位为毫秒。

如果 key 已经存在, 则覆写旧值。SETEX 是一个原子性操作,关联值和设置生存时间两个动作会在同一时间内完成。

  • setnx

格式:SETNX key value

功能:SET if Not eXists,当且仅当 key 不存在时将 key 的值设为 value 。若给定的 key 已经存在,则 SETNX 不做任何动作。成功返回 1,否则返回 0。

  • getset

格式:GETSET key value

功能:将给定 key 的值设为 value ,并返回 key 的旧值。

当 key 存在但不是字符串类型时,返回一个错误;当 key 不存在时,返回 nil 。

  • mset 与 msetnx
1
MSET/MSETNX key value [key value ...]

同时设置一个或多个 key-value 对。

MSET 会用新值覆盖原来的旧值;MSETNX 命令只会在所有给定 key 都不存在的情况下进行设置操作。MSET/MSETNX 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况不可能发生。

  • mget

格式:MGET key [key …]

返回所有(一个或多个)给定 key 的值。若其中某个 key 不存在,那么这个 key 返回特殊值 nil

  • append

格式:APPEND key value

如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。

追加 value 之后,会返回 key 中字符串的长度。

  • incr 与 decr

格式:INCR key 或 DECR key

功能:increment,自动递增。将 key 中存储的数字值增一。decrement,自动递减。将 key 中存储的数字值减一。

如果 key 不存在,那么 key 的值会先被初始化为 0,然后再执行增一/减一操作。如果值不能表示为数字,那么返回一个错误提示。如果执行正确,则返回增一/减一后的值。

可以输入负数,所以记住 incr 即可

  • incrby 与 decrby

格式:INCRBY key increment 或 DECRBY key decrement

将 key 中存储的数字值增加/减少指定的数值,这个数值只能是整数,可以是负 数,但不能是小数。

  • incrbyfloat

格式:INCRBYFLOAT key increment

功能:为 key 中所储存的值加上浮点数增量 increment 。

  • strlen

格式:STRLEN key

功能:返回 key 所储存的字符串值的长度。

当 key 储存的不是字符串值时,返回一个错误;当 key 不存在时,返回 0 。

  • getrange

格式:GETRANGE key start end

功能:返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定,包括 start 和 end 在内。

end 必须要比 start 大。支持负数偏移量,表示从字符串最后开始计数,-1 表示 最后一个字符,-2 表示倒数第二个,以此类推。

  • setrange

格式:SETRANGE key offset value

功能:用 value 参数替换给定 key 所储存的字符串值 str,从偏移量 offset 开始。

说明:当 offset 值大于 str 长度时,中间使用零字节\x00 填充,即 0000 0000 字节填充;对于不存在的 key 当作空串处理。

  • 位操作命令

名称中包含 BIT 的命令,都是对二进制位的操作命令,例如,setbit、getbit、bitcount、 bittop、bitfield,这些命令不常用。

典型应用场景

  1. 数据缓存

Redis 作为数据缓存层,MySQL 作为数据存储层。应用服务器首先从 Redis 中获取数据,如果缓存层中没有,则从 MySQL 中获取后先存入缓存层再返回给应用服务器。

  1. 计数器

在 Redis 中写入一个 value 为数值型的 key 作为平台计数器、视频播放计数器等。每个有效客户端访问一次,或视频每播放一次,都是直接修改 Redis 中的计数器,然后再以异步 方式持久化到其它数据源中

  1. 共享 Session

对于一个分布式应用系统,如果将类似用户登录信息这样的 Session 数据保存在提供登录服务的服务器中,那么如果用户再次提交像收藏、支付等请求时可能会出现问题:在提供收藏、支付等服务的服务器中并没有该用户的 Session 数据,从而导致该用户需要重新登录。 对于用户来说,这是不能接受的。

此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从 Redis 中查找相应的 Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。这样就不会引发“重新登录”问题。

  1. 限速器

现在很多平台为了防止 DoS(Denial of Service,拒绝服务)攻击,一般都会限制一个 IP 不能在一秒内访问超过 n 次。而 Redis 可以结合 key 的过期时间与 incr 命令来完成限速功能,充当限速器。

无法防止 DDoS(Distributed Denial of Service,分布式拒绝服务)攻击。

4. Hash 型 Value 操作命令

Redis 存储数据的 Value 可以是一个 Hash 类型。Hash 类型也称为 Hash 表、字典等。

Hash 表就是一个映射表 Map,也是由键-值对构成,为了与整体的 key 进行区分,这里的键称为 field,值称为 value。注意,Redis 的 Hash 表中的 field-value 对均为 String 类型。

  • hset

格式:HSET key field value

功能:将哈希表 key 中的域 field 的值设为 value 。

如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。如果域 field 已经存在于哈希表中,旧值将被覆盖。如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。

  • hget

格式:HGET key field

功能:返回哈希表 key 中给定域 field 的值。

当给定域不存在或是给定 key 不存在时,返回 nil 。

  • hmset

格式:HMSET key field value [field value …]

功能:同时将多个 field-value (域-值)对设置到哈希表 key 中。

此命令会覆盖哈希表中已存在的域。如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。如果命令执行成功,返回 OK 。当 key 不是哈希表(hash)类型时,返回一个错误。

  • hmget

格式:HMGET key field [field …]

功能:按照给出顺序返回哈希表 key 中一个或多个域的值。

如果给定的域不存在于哈希表,那么返回一个 nil 值。因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。

  • hgetall

格式:HGETALL key

功能:返回哈希表 key 中所有的域和值。

  • hsetnx

格式:HSETNX key field value

功能:将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。

若域 field 已经存在,该操作无效。如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令。

  • hdel

格式:HDEL key field [field …]

功能:删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。

返回被成功移除的域的数量,不包括被忽略的域。

  • hexits

格式:HEXISTS key field

功能:查看哈希表 key 中给定域 field 是否存在。

如果哈希表含有给定域,返回 1 。如果不含有给定域,或 key 不存在,返回 0 。

  • hincrby 与 hincrbyfloat

格式:HINCRBY key field increment

功能:为哈希表 key 中的域 field 的值加上增量 increment 。hincrby 命令只能增加整数值,而 hincrbyfloat 可以增加小数值。

增量也可以为负数,相当于对给定域进行减法操作。如果 key 不存在,一个新 的哈希表被创建并执行 HINCRBY 命令。如果域 field 不存在,那么在执行命令前,域的值被初始化为 0。对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。

  • hkeys 与 hvals

格式:HKEYS key 或 HVALS key

功能:返回哈希表 key 中的所有域/值。

当 key 不存在时,返回一个空表。

  • hlen

格式:HLEN key

功能:返回哈希表 key 中域的数量。

当 key 不存在时,返回 0 。

  • hstrlen

格式:HSTRLEN key field

功能:返回哈希表 key 中, 与给定域 field 相关联的值的字符串长度(string length)。

如果给定的键或者域不存在, 那么命令返回 0 。

应用场景

Hash 型 Value 非常适合存储对象数据。key 为对象名称,value 为描述对象属性的 Map,对对象属性的修改在 Redis 中就可直接完成。其不像 String 型 Value 存储对象,对象需要序列化,例如序列化为 JSON 串,对对象属性值的修改需要先反序列化为对象后再修改, 修改后再序列化为 JSON 串后写入到 Redis。

5. List 型 Value 操作命令

Redis 存储数据的 Value 可以是一个 String 列表类型数据。即该列表中的每个元素均为 String 类型数据。列表中的数据会按照插入顺序进行排序。不过,该列表的底层实际是一个无头节点的双向链表,所以对列表表头与表尾的操作性能较高,但对中间元素的插入与删除的操作的性能相对较差。

  • lpush/rpush

格式:LPUSH key value [value …] 或 RPUSH key value [value …]

功能:将一个或多个值 value 插入到列表 key 的表头/表尾(表头在左表尾在右)

如果有多个 value 值,对于 lpush 来说,各个 value 会按从左到右的顺序依次插 入到表头;对于 rpush 来说,各个 value 会按从左到右的顺序依次插入到表尾。如果 key 不存在,一个空列表会被创建并执行操作。当 key 存在但不是列表类型时,返回一个 错误。执行成功时返回列表的长度。

7a6a05d723908f9262a1b0cfcaab5e5d.png

  • llen

格式:LLEN key

功能:返回列表 key 的长度。

如果 key 不存在,则 key 被解释为一个空列表,返回 0 。如果 key 不是列表 类型,返回一个错误。

  • lindex

格式:LINDEX key index

功能:返回列表 key 中,下标为 index 的元素。列表从 0 开始计数。

如果 index 参数的值不在列表的区间范围内(out of range),返回 nil 。

  • lset

格式:LSET key index value

功能:将列表 key 下标为 index 的元素的值设置为 value 。

  • lrange

格式:LRANGE key start stop

功能:返回列表 key 中指定区间[start, stop]内的元素,即包含两个端点。

List 的下标从 0 开始,即以 0 表示列表的第一个元素,以 -1 表示列表的最后一个元素,-2 表示列表的倒数第二个元素,以此类推。超出范围的下标值不会引起错误。如果 start 下标比列表的最大下标 还要大,那么 LRANGE 返回一个空列表。如果 stop 下标比最大下标还要大,Redis 将 stop 的值设置为最大下标。

  • lpushx 与 rpushx

格式:LPUSHX key value 或 RPUSHX key value

功能:将值 value 插入到列表 key 的表头/表尾,当且仅当 key 存在并且是一个列表。

当 key 不存在时,命令什么也不做。若执行成功,则输出表的长度。

  • linsert

格式:LINSERT key BEFORE|AFTER pivot value

功能:将值 value 插入到列表 key 当中,位于元素 pivot 之前或之后。

当 pivot 元素不存在于列表中时,不执行任何操作,返回-1;当 key 不存在时,key 被视为空列表,不执行任何操作,返回 0;如果 key 不是列表类型,返回一个错误;如果命令执行成功,返回插入操作完成之后,列表的长度。

  • lpop / rpop

格式:LPOP key [count] 或 RPOP key [count]

功能:从列表 key 的表头/表尾移除 count 个元素,并返回移除的元素。count 默认值 1

当 key 不存在时,返回 nil

  • blpop / brpop

格式:BLPOP key [key …] timeout 或 BRPOP key [key …] timeout

功能:BLPOP/BRPOP 是列表的阻塞式(blocking)弹出命令。当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP/BRPOP 命令阻塞,直到等待 timeout 超时或发现可弹出元素为止。当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。timeout 为阻塞时长, 单位为秒,其值若为 0,则表示只要没有可弹出元素,则一直阻塞。

假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长。反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。

  • rpoplpush

格式:RPOPLPUSH source destination

功能:在一个原子时间内,将列表 source 中的最后一个元素(尾元素)弹出返回给客户端。并将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。

如果 source 不存在,值 nil 被返回,并且不执行其他动作。如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转(rotation)操作。

  • brpoplpush

格式:BRPOPLPUSH source destination timeout

功能:BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本,

  • lrem

格式:LREM key count value

功能:根据参数 count 的值,移除列表中与参数 value 相等的元素。

  1. count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
  2. count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的 绝对值。
  3. count = 0 : 移除表中所有与 value 相等的值。

返回被移除元素的数量。当 key 不存在时, LREM 命令返回 0 ,因为不存在 的 key 被视作空表(empty list)。

  • ltrim

格式:LTRIM key start stop

功能:对一个列表进行裁剪(trim),只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一 个元素,以 1 表示列表的第二个元素,以此类推。也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。当 key 不是列表类型时,返回一个错误。如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要 大,或者 start > stop , LTRIM 返回一个空列表,因为 LTRIM 已经将整个列表清空。 如果 stop 下标比 end 下标还要大,Redis 将 stop 的值设置为 end 。

应用场景

Value 为 List 类型的应用场景很多,主要是通过构建不同的数据结构来实现相应的业务功能。

通过 lpush + lpop 或 rpush + rpop 可以实现栈数据结构效果:先进后出。

  1. 队列

通过 lpush + rpop 或 rpush + lpop 可以实现队列数据结构效果:先进先出。

  1. 阻塞式消息队列

通过 lpush + brpop 可以实现阻塞式消息队列效果。作为消息生产者的客户端使用 lpush 从列表左侧插入数据,作为消息消费者的多个客户端使用 brpop 阻塞式“抢占”列表尾部数 据进行消费,保证了消费的负载均衡与高可用性。brpop 的 timeout 设置为 0,表示只要没有数据可弹出,就永久阻塞。

  1. 动态有限集合

通过 lpush + ltrim 或 rpush + ltrim 可以实现有限集合。通过 lpush 从列表左侧向列表中添加数据,通过 ltrim 保持集合的动态有限性。像企业的末位淘汰、学校的重点班等动态管理,都可通过这 种动态有限集合来实现。

6. Set 型 Value 操作命令

Redis 存储数据的 Value 可以是一个 Set 集合,且集合中的每一个元素均 String 类型。

Redis 中的 Set 集合与 Java 中的 Set 集合的实现相似

  • sadd

格式:SADD key member [member …]

功能:将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。

假如 key 不存在,则创建一个只包含 member 元素作成员的集合。当 key 不是集合类型时,返回一个错误。

  • smembers

格式:SMEMBERS key

功能:返回集合 key 中的所有成员。

不存在的 key 被视为空集合。若 key 中包含大量元素,则该命令可能会阻塞 Redis 服务。所以生产环境中一般不使用该命令,而使用 sscan 命令代替。

  • scard

格式:SCARD key

功能:返回 Set 集合的长度

当 key 不存在时,返回 0 。

  • sismember

格式:SISMEMBER key member

功能:判断 member 元素是否集合 key 的成员。

如果 member 元素是集合的成员,返回 1 。如果 member 元素不是集合的成 员,或 key 不存在,返回 0 。

  • smove

格式:SMOVE source destination member

功能:将 member 元素从 source 集合移动到 destination 集合。

如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 0 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去,返回 1。当 destination 集合已经包含 member 元素时, SMOVE 命令只是简单地将 source 集合中的 member 元素删除。当 source 或 destination 不是集合类型时,返回一个错误。

  • srem

格式:SREM key member [member …]

功能:移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略,且返回成功移除的元素个数。

当 key 不是集合类型,返回一个错误。

  • srandmember

格式:SRANDMEMBER key [count]

功能:返回集合中的 count 个随机元素。count 默认值为 1。

若 count 为正数,且小于集合长度,那么返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合长度,那么返回整个集合。如果 count 为负数,那么返回一个包含 count 绝对值个元素的数组,但数组中的元素可能会出现重复。

  • spop

格式:SPOP key [count]

功能:移除并返回集合中的 count 个随机元素。count 必须为正数,且默认值为 1。

如果 count 大于等于集合长度,那么移除并返回整个集合。

  • sdiff / sdiffstore

格式:SDIFF key [key …] 或 SDIFFSTORE destination key [key …]

功能:返回第一个集合与其它集合之间的差集。

这两个命令的不同之处在于,sdiffstore 不仅能够显示差集,还能将差集存储到指 定的集合 destination 中。如果 destination 集合已经存在,则将其覆盖。不存在的 key 被视为空集。

  • sinter / sinterstore

格式:SINTER key [key …] 或 SINTERSTORE destination key [key …]

功能:返回多个集合间的交集。

  • sunion / sunionstore

格式:SUNION key [key …] 或 SUNIONSTORE destination key [key …]

功能:返回多个集合间的并集。

应用场景

  1. 动态黑白名单

例如某服务器中要设置用于访问控制的黑名单。如果直接将黑名单写入服务器的配置文件,那么存在的问题是,无法动态修改黑名单。此时可以将黑名单直接写入 Redis,只要有客户端来访问服务器,服务器在获取到客户端IP后先从 Redis的黑名单中查看是否存在该 IP,如果存在,则拒绝访问,否则访问通过。

  1. 有限随机数

有限随机数是指返回的随机数是基于某一集合范围内的随机数,例如抽奖、随机选人。 通过 spop 或 srandmember 可以实现从指定集合中随机选出元素。

  1. 用户画像

社交平台、电商平台等各种需要用户注册登录的平台,会根据用户提供的资料与用户使用习惯,为每个用户进行画像,即为每个用户定义很多可以反映该用户特征的标签,这些标签就可以使用 sadd 添加到该用户对应的集合中。这些标签具有无序、不重复特征。

同时平台还可以使用 sinter/sinterstore 根据用户画像间的交集进行好友推荐、商品推荐、 客户推荐等。

7. 有序 Set 型 Value 操作命令

Redis 存储数据的 Value 可以是一个有序 Set,这个有序 Set 中的每个元素均 String 类型。有序 Set 与 Set 的不同之处是,有序 Set 中的每一个元素都有一个分值 score,Redis 会根据 score 的值对集合进行由小到大的排序。其与 Set 集合要求相同,元素不能重复,但元素的 score 可以重复。由于该类型的所有命令均是字母 z 开头,所以该 Set 也称为 ZSet。

  • zadd
1
ZADD key score member [[score member] [score member] ...]

功能:将一个或多个 member 元素及其 score 值加入到有序集 key 中的适当位置。

score 值可以是整数值或双精度浮点数。如果 key 不存在,则创建一个空的有序集并执行 ZADD 操作。当 key 存在但不是有序集类型时,返回一个错误。如果命令执行成功,则返回被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。 若写入的 member 值已经存在,但 score 值不同,则新的 score 值将覆盖老 score。

  • zrange 与 zrevrange
1
2
ZRANGE key start stop [WITHSCORES]
ZREVRANGE key start stop [WITHSCORES]

功能:返回有序集 key 中,指定区间内的成员。zrange 命令会按 score 值递增排序,zrevrange命令会按score递减排序。具有相同 score 值的成员按字典序/逆字典序排列。可以通过使用 WITHSCORES 选项,来让成员和它的 score 值一并返回。

若 key 中指定范围内包含大量元素,则该命令可能会阻塞 Redis 服务。所以生产环 境中如果要查询有序集合中的所有元素,一般不使用该命令,而使用 zscan 命令代替。

  • zrangebyscore 与 zrevrangebyscore
1
2
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

功能:返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增/递减次序排列。具有相同 score 值的成员按字典序/逆字典序排列。可选的 LIMIT 参数指定返回结果的数量及区间(就像 SQL 中的 SELECT LIMIT offset, count ),注意当 offset 很大时,定位 offset 的操作可能需要遍历整 个有序集,此过程效率可能会较低。

min 和 max 的取值是正负无穷大的。默认情况下,区间的取值使用闭区间 (小 于等于或大于等于),也可以通过给参数前增加左括号“(”来使用可选的开区间 (小于或 大于)。

  • zcard

格式:ZCARD key

功能:返回集合的长度

当 key 不存在时,返回 0

  • zcount

格式:ZCOUNT key min max

返回有序集 key 中,score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。

  • zscore

格式:ZSCORE key member

功能:返回有序集 key 中,成员 member 的 score 值。

如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。

  • zincrby

格式:ZINCRBY key increment member

功能:为有序集 key 的成员 member 的 score 值加上增量 increment 。increment 值可以是整数值或双精度浮点数。

可以通过传递一个负数值 increment ,让 score 减去相应的值。当 key 不存在, 或 member 不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member 。当 key 不是有序集类型时,返回一个错误。命令执行成功,则返回 member 成员的新 score 值。

  • zrank 与 zrevrank

格式:ZRANK key member 或 ZREVRANK key member

功能:返回有序集 key 中成员 member 的排名。zrank 命令会按 score 值递增排序, zrevrank 命令会按 score 递减排序。

score 值最小的成员排名为 0 。如果 member 不是有序集 key 的成员,返回 nil 。

  • zrem

格式:ZREM key member [member …]

功能:移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。

当 key 存在但不是有序集类型时,返回一个错误。执行成功,则返回被成功移除的成员的数量,不包括被忽略的成员。

  • zremrangebyrank
1
ZREMRANGEBYRANK key start stop

功能:移除有序集 key 中,指定排名(rank)区间内的所有成员。

排名区间分别以下标参数 start 和 stop 指出,包含 start 和 stop 在内。排名区间参数从 0 开始,即 0 表示排名第一的成员,以此类推。 也可以使用负数表示,-1 表示最后一个成员以此类推。命令执行成功,则返回被移除成员的数量。

  • zremrangebyscore
1
ZREMRANGEBYSCORE key min max

功能:移除有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。

命令执行成功,则返回被移除成员的数量。min和max支持无穷大,“-inf”表示负无穷,“+inf”表示正无穷。

  • zrangebylex
1
ZRANGEBYLEX key min max [LIMIT offset count]

功能:该命令仅适用于集合中所有成员都具有相同分值的情况。当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的字典序(lexicographical ordering)来进行排序。即这个命令返回给定集合中元素值介于 min 和 max 之间的成员。如果有序集合里面的成员带有不同的分值, 那么命令的执行结果与 zrange key 效果相同。

合法的 min 和 max 参数必须包含左小括号“(”或左中括号“[”,其中左小括号“(”表示开区间,而左中括号“[”则表示闭区间。min 或 max 也可使用特殊字符“+”和“-”,分别表示正无穷大与负无穷大。

  • zlexcount

格式:ZLEXCOUNT key min max

功能:该命令仅适用于集合中所有成员都具有相同分值的情况。该命令返回该集合中元素值本身(而非 score 值)介于 min 和 max 范围内的元素数量。

  • zremrangebylex

格式:ZREMRANGEBYLEX key min max

功能:该命令仅适用于集合中所有成员都具有相同分值的情况。该命令会移除该集合中元素值本身介于 min 和 max 范围内的所有元素。

应用场景

有序 Set 最为典型的应用场景就是排行榜,例如音乐、视频平台中根据播放量进行排序的排行榜;电商平台根据用户评价或销售量进行排序的排行榜等。将播放量作为 score,将作品 id 作为 member,将用户评价积分或销售量作为 score,将商家 id 作为 member。使用 zincrby 增加排序 score,使用 zrevrange 获取 Top 前几名,使用 zrevrank 查询当前排名,使用 zscore 查询当前排序 score 等。

8. benchmark 测试工具

在Redis安装完毕后会自动安装一个redis-benchmark测试工具,用于测试 Redis 的性能。查看到其用法:

389fce4e1ceb36a4a62dee0adf4bafad.png

常用的 options:

  • -h:指定要测试的 Redis 的 IP,若为本机,则可省略
  • -p:指定要测试的 Redis 的 port,若为 6379,则可省略
  • -c:指定模拟有客户端的数量,默认值为 50
  • -n:指定这些客户端发出的请求的总量,默认值为 100000
  • -d:指定测试 get/set 命令时其操作的 value 的数据长度,单位字节,默认值为 3。在测试其它命令时该指定没有用处。
  • -t:指定要测试的命令,多个命令使用逗号分隔,不能有空格
  • -q:指定仅给出总述性报告

测试结果分析:

该测试工具会逐个测试所有 Redis 命令,每个命令都会给出一份测试报告,每个测试报告由四部分构成:

  1. 测试环境报告

92237527e68bdc417c2b17dcad98a23d.png

  1. 延迟百分比分布

这是按照百分比进行的统计报告:每完成一次剩余测试量的 50%就给出一个统计数据。

1cc6d1d6727bc0df83ec616393926a81.png

  1. 延迟的累积分布

这是按照时间间隔统计的报告:基本是每 0.1 毫秒统计一次。

cf2e5c0a591cc1ee92a310c4045c6bb3.png

  1. 总述报告

0668e4a3ae820f7b4ba5060c8b0f2565.png

9. 简单动态字符串 SDS

Redis 中 Value 的基础数据类型都是字符串。如 Hash 型 Value 的 field 与 value 的类型、List 型、Set 型、ZSet 型 Value 的元素的类型等都是字符串。虽然 Redis 是使用标准 C 语言开发的,但并没有直接使用 C 语言中传统的字符串表示,而是自定义了一 种字符串。这种字符串本身的结构比较简单,但功能却非常强大,称为简单动态字符串, Simple Dynamic String,简称 SDS。

d7c68ffd2265488fcd23137582a43e59.png

其中 embstr 就表示该数据底层使用SDS字符串存储,但是 Redis 中的所有字符串并不都是 SDS,也会出现 C 字符串。C 字符串只会出现在字符串“字面常量”中,该字符串不可能发生变更,如控制台输出的 ‘zhangsan’

SDS 结构

SDS 不同于 C 字符串。C 字符串本身是一个以双引号括起来,以空字符’\0’结尾的字符序 列。但 SDS 是一个结构体,定义在 Redis 安装目录下的 src/sds.h 中:

1
2
3
4
5
6
7
8
struct sdshdr {
// 字节数组,用于保存字符串
char buf[];
// buf[]中已使用字节数量,称为 SDS 的长度
int len;
// buf[]中尚未使用的字节数量
int free;
}

执行 SET country “China”命令时,键 country 与值”China”都是 SDS 类型的,只不过一个是 SDS 的变量,一个是 SDS 的字面常量。”China”在内存中的结构如下:

93932fc21a02a4c65c5085a88e4dd6e2.png

通过以上结构可以看出,SDS 的 buf 值实际是一个 C 字符串,包含空字符’\0’共占 6 个字节。但 SDS 的 len 是不包含空字符’\0’的。

SDS 的优势

C 字符串使用 Len+1 长度的字符数组来表示实际长度为 Len 的字符串,字符数组最后以空字符’\0’结尾,表示字符串结束。这种结构简单,但不能满足 Redis 对字符串功能性、安全性及高效性等的要求。

防止”字符串长度获取”性能瓶颈

对于 C 字符串,若要获取其长度,则必须要通过遍历整个字符串才可获取到的。对于超长字符串的遍历,会成为系统的性能瓶颈。

SDS 结构体中直接就存放着字符串的长度数据,所以对于获取字符串长度需要 消耗的系统性能,与字符串本身长度是无关的,不会成为 Redis 的性能瓶颈。

保障二进制安全

C 字符串中只能包含符合某种编码格式的字符,例如 ASCII、UTF-8 等,并且除了字符串末尾外,其它位置是不能包含空字符’\0’的,否则该字符串就会被程序误解为提前结束。而在图片、音频等二进制数据中以空字符’\0’作为分隔符的情况又是很常见的。

SDS 不是以空字符’\0’作为字符串结束标志的,其是通过 len 属性来判断字符串是否结束的。所以,对于程序处理 SDS 中的字符串数据,无需对数据做任何限制、过滤、假设,只需读取即可。数据写入的是什么,读到的就是什么。

减少内存再分配次数

C语言字符串需要添加元素时,底层是创建新数组来存储(类似Java中的String),为了防止后面的数据被覆盖,会对内存进行再分配。

SDS 采用了空间预分配策略与惰性空间释放策略来避免内存再分配问题

  • 空间预分配策略:每次 SDS 进行空间扩展时,程序不但为其分配所需的空间,还会为其分配额外的未使用空间,以减少内存再分配次数。而额外分配的未使用空间大小取决于空间扩展后 SDS 的 len 属性值。
    • 如果 len 属性值小于 1M,那么分配的未使用空间 free 的大小与 len 属性值相同。
    • 如果 len 属性值大于等于 1M ,那么分配的未使用空间 free 的大小固定是 1M。
  • 惰性空间释放策略:SDS 字符串长度如果缩短,那么多出的未使用空间将暂时不释放,而是增加到 free 中。以使后期扩展 SDS 时减少内存再分配次数。如果要释放 SDS 的未使用空间,则可通过 sdsRemoveFreeSpace()函数来释放。

兼容C函数

Redis 中提供了很多的 SDS 的 API,以方便用户对 Redis 进行二次开发。为了能够兼容 C 函数,SDS 的底层数组 buf[]中的字符串仍以空字符’\0’结尾。

常用的 SDS 操作函数

函数 功能描述
sdsnew() 使用指定的 C 字符串创建一个 SDS
sdsempty() 创建一个不包含任何字符串数据的 SDS
sdsdup() 创建一个指定 SDS 的副本
sdsfree() 释放指定的 SDS
sdsclear() 清空指定 SDS 的字符串内容
sdslen() 获取指定 SDS 的已使用空间 len 值
sdsavail() 获取指定 SDS 的未使用空间 free 值
sdsMakeRoomFor() 使指定的 SDS 的 free 空间增加指定的大小
sdsRemoveFreeSpace() 释放指定 SDS 的 free 空间
sdscat() 将指定的 C 字符串拼接到指定 SDS 字符串末尾
sdscatsds() 将指定的 SDS 的字符串拼接到另一个指定 SDS 字符串末尾
sdscpy() 将指定的 C 字符串复制到指定的 SDS 中,覆盖原 SDS 字符串内容
sdsgrouzero() 扩展 SDS 字符串到指定长度。这个扩展是使用空字符’\0’填充
sdsrange() 截取指定 SDS 中指定范围内的字符串
sdstrim() 在指定 SDS 中删除所有指定 C 字符串中出现的所有字符
sdsemp() 对比两个给定的 SDS 字符串是否相同
sdstolow() 将指定 SDS 字符串中的所有字母变为小写
sdstoupper() 将指定 SDS 字符串中的所有字母变为大写

10. 集合的底层实现原理

Redis 中对于 Set 类型的底层实现,直接采用了 hashTable。但对于 Hash、ZSet、List 集合的底层实现进行了特殊的设计,使其保证了 Redis 的高性能。

Hash与ZSet

对于Hash与ZSet集合,其底层的实现实际有两种:压缩列表zipList,与跳跃列表skipList。

用户写入不同的数据,系统会自动使用不同的实现。同时满足以配置文件 redis.conf 中相关集合元素数量阈值与元素大小阈值两个条件。就使用压缩列表 zipList,只要有一个条件不满足就使用跳跃列表 skipList。

b2244a1e09b3d0007f7224665d8469f7.png

集合元素个数不能大于默认值:128 字节,且每个集合元素大小都小于 默认值:64 字节

zipList

zipList,通常称为压缩列表,是一个经过特殊编码的用于存储字符串或整数的双向链表。其底层数据结构由三部分构成:head、entries 与 end。这三部分在内存上是连续存放的。

  1. head 由三部分构成:

    • zlbytes:占 4 个字节,用于存放 zipList 列表整体数据结构所占的字节数,包括 zlbytes 本身的长度。
    • zltail:占 4 个字节,用于存放 zipList 中最后一个 entry 在整个数据结构中的偏移量。该数据的存在可以快速定位列表的尾 entry 位置,以方便操作。
    • zllen:占 2 字节,用于存放列表包含的 entry 个数。由于其只有 16 位,所以 zipList 最多可以含有的 entry 个数为 2^16 -1 = 65535 个。
  2. entries:真正的列表,由很多的列表元素 entry 构成。由于不同的元素类型、数值的不同,从而导致每个 entry 的长度不同,每个 entry 由三部分构成:

    • prevlength:该部分用于记录上一个 entry 的长度,以实现逆序遍历。默认长度为 1 字节, 只要上一个 entry 的长度<254 字节,prevlength 就占 1 字节,否则其会自动扩展为 3 字节长度。
    • encoding:该部分用于标志后面的 data 的具体类型。如果 data 为整数类型,encoding 固定长度为 1 字节。如果 data 为字符串类型,则 encoding 长度可能会是 1 字节、2 字节或 5 字节。data 字符串不同的长度,对应着不同的 encoding 长度。
    • data:真正存储的数据。数据类型只能是整数类型或字符串类型。不同的数据占用的字节长度不同。
  3. end:end 只包含一部分,称为 zlend。占 1 个字节,值固定为 255,即二进制位为全 1,表示 一个 zipList 列表的结束。

5ab9e2f274ce8d67d1093fe321163f96.png

对于 ziplist,实现复杂,为了逆序遍历,每个 entry 中包含前一个 entry 的长度,这样会导致在 ziplist 中间修改或者插入 entry 时需要进行级联更新。

除了需要改变 entres 的结构,还需要改变后一个元素(以及修改元素) head 中的 zltail

在高并发的写操作场景下会极度降低 Redis 的性能。

在 Redis 7.0 中,已经将 zipList 全部替换为了 listPack,但为了兼容性,在配置中也保留 了 zipList 的相关属性。

listPack

listPack 也是一个经过特殊编码的用于存储字符串或整数的双向链表。其底层数据结构 也由三部分构成:head、entries 与 end,且这三部分在内存上也是连续存放的。

  1. head 由两部分构成:
    • totalBytes:占 4 个字节,用于存放 listPack 列表整体数据结构所占的字节数,包括 totalBytes 本身的长度。
    • elemNum:占 2 字节,用于存放列表包含的 entry 个数。其意义与 zipList 中 zllen 的相同。
  2. entries :最大的变化就是没有了记录前一个 entry 长度的 prevlength,而增加了记录当前 entry 长度的 element-total-len。
  • encoding:该部分用于标志后面的 data 的具体类型。如果 data 为整数类型,encoding 长度可能会是 1、2、3、4、5 或 9 字节。不同的字节长度,其标识位不同。如果 data 为字符串类型,则 encoding 长度可能会是 1、2 或 5 字节。data 字符串不同的长度,对应着不同的 encoding 长度。
  • data
  • element-total-len:该部分用于记录当前 entry 的长度,用于实现逆序遍历。由于其特殊的记录方式,使其本身占有的字节数据可能会是 1、2、3、4 或 5 字节。

skipList

skipList,跳跃列表,简称跳表,是一种随机化的数据结构,基于并联的链表,实现简单, 查找效率较高。

简单来说就是随机多加了一些指针,在有序链表能够快速定位数据

简单实现(非随机):

为了提升查找效率,在偶数结点上增加一个指针,让其指向下一个偶数结点。这样所有偶数结点就连成了一个新的链表(简称高层链表),当然,高层链表包含的节点个数只是原来链表的一半。此时再想查找某个数据时,先沿着高层链表进行查找。当遇到第一个比待查数据大的节点时,立即从该大节点的前一个节点回到原链表中进行查找。

对链表分层级的方式从原理上看确实提升了查找效率,但在实际操作时就出现了问题:由于固定序号的元素拥有固定层级,所以列表元素出现增加或删除的情况下,会导致列表整体元素层级大调整,但这样势必会大大降低系统性能。

这就体现了随机的好处

List

quickList

quickList,快速列表,quickList 本身是一个双向无循环链表,它的每一个节点都是一个 zipList。从Redis3.2版本开始,对于List的底层实现,使用quickList替代了zipList 和 linkedList。

quickList 本质上是 zipList 和 linkedList 的混合体。其将 linkedList 按段切分,每一段使用 zipList 来紧凑存储若干真正的数据元素,多个 zipList 之间使用双向指针串接起来。当然,对于每个 zipList 中最多可存放多大容量的数据元素,在配置文件中通过 list-max-ziplist-size 属性可以指定。

  1. 检索操作:

quickList 由一个个的 zipList 构成,每个 zipList 的 zllen 中记录的就是当前 zipList 中包含的 entry 的个数,即包含的真正数据元素的个数。根据要检索元素的 index,从 quickList 的头节点开始,逐个对 zipList 的 zllen 做 sum 求和,直到找到第一个求和后 sum 大于 index 的 zipList,那么要检索的这个元素就在这个 zipList 中。

  1. 插入:

由于 zipList 是有大小限制的,所以在 quickList 中插入一个元素在逻辑上相对就比较复杂一些。假设要插入的元素的大小为 insertBytes,而查找到的插入位置所在的 zipList 当前的大小为 zlBytes,那么具体可分为下面几种情况:

情况一:当 insertBytes + zlBytes <= list-max-ziplist-size 时,直接插入到 zipList 中相应位置 即可

情况二:

当 insertBytes + zlBytes > list-max-ziplist-size,且插入的位置位于该 zipList 的首部位置,此时需要查看该 zipList 的前一个 zipList 的大小 prev_zlBytes。

  • 若 insertBytes + prev_zlBytes<= list-max-ziplist-size 时,直接将元素插入到前一个 zipList 的尾部位置即可
  • 若 insertBytes + prev_zlBytes> list-max-ziplist-size 时,直接将元素自己构建为一个新 的 zipList,并连入 quickList 中

情况三:

当 insertBytes + zlBytes > list-max-ziplist-size,且插入的位置位于该 zipList 的尾部位置,此时需要查看该 zipList 的后一个 zipList 的大小 next_zlBytes。

  • 若 insertBytes + next_zlBytes<= list-max-ziplist-size 时,直接将元素插入到后一个 zipList 的头部位置即可
  • 若 insertBytes + next_zlBytes> list-max-ziplist-size 时,直接将元素自己构建为一个新的 zipList,并连入 quickList 中

情况四:当 insertBytes + zlBytes > list-max-ziplist-size,且插入的位置位于该 zipList 的中间位置,则将当前 zipList 分割为两个 zipList 连接入 quickList 中,然后将元素插入到分割后的前面 zipList 的尾部位置

  1. 删除操作

对于删除操作,只需要注意一点,在相应的 zipList 中删除元素后,该 zipList 中是否还有元素。如果没有其它元素了,则将该 zipList 删除,将其前后两个 zipList 相连接。

11. BitMap 操作命令

BitMap 是 Redis 2.2.0 版本中引入的一种新的数据类型。该数据类型本质上就是一个仅包含 0 和 1 的二进制字符串。而其所有相关命令都是对这个字符串二进制位的操作。用于描述该字符串的属性有三个:key、offset、bitValue。

key:BitMap 是 Redis 的 key-value 中的一种 Value 的数据类型,所以该 Value 一定有其对 应的 key。

offset:每个 BitMap 数据都是一个字符串,字符串中的每个字符都有其对应的索引,该索引从 0 开始计数。该索引就称为每个字符在该 BitMap 中的偏移量 offset。这个 offset 的值的范围是[0,2^32 -1],近43亿

bitValue:每个 BitMap 数据中都是一个仅包含 0 和 1 的二进制字符串,每个 offset 位上的字符就称为该位的值 bitValue。bitValue 的值非 0 即 1。

  1. setbit

格式:SETBIT key offset value

功能:为给定 key 的BitMap 数据的 offset 位置设置值为 value。其返回值为修改前该 offset 位置的 bitValue

对于原 BitMap 字符串中不存在的 offset 进行赋值,字符串会自动伸展以确保它 可以将 value 保存在指定的 offset 上,空白位置以 0 填充。需要注意的是,对使用较大 offset 的 SETBIT 操作来说,内存分配过程可能造成 Redis 服务器被阻塞。

  1. getbit

格式:GETBIT key offset

功能:对 key 所储存的 BitMap 字符串值,获取指定 offset 偏移量上的位值 bitValue。

当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 。

  1. bitcount

格式:BITCOUNT key [start] [end]

功能:统计给定字符串中被设置为 1 的 bit 位的数量。一般情况下,统计的范围是给定的整个 BitMap 字符串。但也可以通过指定额外的 start 或 end 参数,实现仅对指定字节范围内字符串进行统计,包括 start 和 end 在内。注意,这里的 start 与 end 的单位是字节,不是 bit,并且从 0 开始计数

  1. bitpos

格式:BITPOS key bit [start] [end]

功能:返回 key 指定的 BitMap 中第一个值为指定值 bit(非 0 即 1) 的二进制位的位置。在默认情况下, 命令将检测整个 BitMap,但用户也可以通过可选的 start 参数和 end 参数指定要检测的范围。

  1. bitop
1
格式:BITOP operation destkey key [key …]

功能:对一个或多个 BitMap 字符串 key 进行二进制位操作,并将结果保存到 destkey 上。

operation 可以是 AND 、 OR 、 NOT 、 XOR

除了 NOT 操作之外,其他操作都可以接受一个或多个 BitMap 作为输入。

除了 NOT 操作外,其他对一个 BitMap 的操作其实就是一个复制。

如果参与运算的多个 BitMap 长度不同,较短的 BitMap 会以 0 作为补充位与较长 BitMap 运算,且运算结果长度与较长 BitMap 的相同。

应用场景

由于 offset 的取值范围很大,所以其一般应用于大数据量的二值性统计。例如平台活跃用户统计(访问或未访问)、支持率统计(支持或不支持)、员工考勤统计(上班或未上班)、图像二值化(黑或白)等。

12. HyperLogLog 操作命令

HyperLogLog 是 Redis 2.8.9 版本中引入的一种新的数据类型,其意义是 hyperlog log,超级日志记录。该数据类型可以简单理解为一个 set 集合,集合元素为字符串。但实际上 HyperLogLog 是一种基数计数概率算法,通过该算法可以利用极小的内存完成独立总数的统 计。其所有相关命令都是对这个“set 集合”的操作。

HyperLogLog 算法是一个纯数学算法

  1. pfadd

格式:PFADD key element [element …]

功能:将任意数量的元素添加到指定的 HyperLogLog 集合里面。如果内部存储被修改了返回 1,否则返回 0。

  1. pfcount

格式:PFCOUNT key [key …]

功能:该命令作用于单个 key 时,返回给定 key 的 HyperLogLog 集合的近似基数;该命 令作用于多个 key 时,返回所有给定 key 的 HyperLogLog 集合的并集的近似基数;如果 key 不存在,则返回 0。

  1. pfmerge

应用场景

HyperLogLog 可对数据量超级庞大的日志数据做不精确的去重计数统计。当然,这个不 精确的度在 Redis 官方给出的误差是 0.81%。这个误差对于大多数超大数据量场景是被允许的。对于平台上每个页面每天的 UV 数据,非常适合使用 HyperLogLog 进行记录。

pv是页面浏览量;uv是客户端数量

13. Geospatial 操作命令

Geospatial,地理空间。

Redis 在 3.2 版本中引入了 Geospatial 这种新的数据类型。该类型本质上仍是一种集合, 只不过集合元素比较特殊,是一种由三部分构成的数据结构,这种数据结构称为空间元素:

  • 经度:longitude。有效经度为[-180,180]。正的表示东经,负的表示西经。
  • 纬度:latitude。有效纬度为[-85.05112878,85.05112878]。正的表示北纬,负的表示南纬。
  • 位置名称:为该经纬度所标注的位置所命名的名称,也称为该 Geospatial 集合的空间元 素名称。

通过该类型可以设置、查询某地理位置的经纬度,查询某范围内的空间元素,计算两空间元素间的距离等。

  1. geoadd
1
GEOADD key longitude latitude member [longitude latitude member …]

功能:将一到多个空间元素添加到指定的空间集合中。

当用户尝试输入一个超出范围的经度或者纬度时,该命令会返回一个错误。

  1. geopos

格式:GEOPOS key member [member …]

功能:从指定的地理空间中返回指定元素的位置,即经纬度。

因为该命令接受可变数量元素作为输入,所以即使用户只给定了一个元素,命令也会返回数组。

  1. geodist

格式:GEODIST key member1 member2 [unit]

功能:返回两个给定位置之间的距离。其中 unit 必须是:m(米,默认)、km(千米)、mi(英里)、ft(英尺)

如果两个位置之间的其中一个不存在, 那么命令返回空值。另外,在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。

  1. geohash

格式:GEOHASH key member [member …]

功能:返回一个或多个位置元素的 Geohash 值。

GeoHash 是一种地址编码方法。他能够把二维的空间经纬度数据编码成一个字符串。该值主要用于底层应用或者调试, 实际中的作用并不大。

  1. georadius
1
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

功能:以给定的经纬度为中心,返回指定地理空间中包含的所有位置元素中,与中心距离不超过给定半径的元素。返回时还可携带额外的信息:

  • WITHDIST :在返回位置元素的同时,将位置元素与中心之间的距离也一并返回。距离的单位和用户给定的范围单位保持一致。
  • WITHCOORD :将位置元素的经维度也一并返回。
  • WITHHASH:将位置元素的 Geohash 也一并返回,不过这个 hash 以整数形式表示

命令默认返回未排序的位置元素。 通过以下两个参数,用户可以指定被返回位置元素的排序方式:

  • ASC :根据中心的位置,按照从近到远的方式返回位置元素。
  • DESC :根据中心的位置,按照从远到近的方式返回位置元素。

在默认情况下, 该命令会返回所有匹配的位置元素。虽然用户可以使用 COUNT 选项去获取前 N 个匹配元素,但因为命令在内部可能会需要对所有被匹配的元素进行处理,所以在对一个非常大的区域进行搜索时,即使使用 COUNT 选 项去获取少量元素,该命令的执行速度也可能会非常慢。

  1. georadiusbymember
1
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

功能:这个命令和 GEORADIUS 命令一样,都可以找出位于指定范围内的元素,但该命令的中心点是由位置元素形式给定的,而不是像 GEORADIUS 那样,使用输入的经纬度来指定中心点。

返回结果中也是包含中心点位置元素的

应用场景

Geospatial 的意义是地理位置,所以其主要应用地理位置相关的计算。例如钉钉中的“签到” 功能等。

14. 发布/订阅命令

发布/订阅,即 pub/sub,是一种消息通信模式:发布者也称为消息生产者,生产和发送 消息到存储系统;订阅者也称为消息消费者,从存储系统接收和消费消息。整个消息发布者、 订阅者与存储系统称为消息系统。

Redis 构成的消 息系统中,发布/订阅的消息都是以频道 Channel 分类的。

  1. subscribe

格式:SUBSCRIBE channel [channel …]

Redis 客户端通过一个 subscribe 命令可以同时订阅任意数量的频道。在输出了订阅了主题后,命令处于阻塞状态,等待相关频道的消息。

  1. psubscribe

格式:PSUBSCRIBE pattern [pattern …]

功能:订阅一个或多个符合给定模式的频道。

这里的模式只能使用通配符 *。例如,it* 可以匹配所有以 it 开头的频道,像 it.news、 it.blog、it.tweets 等;news.*可以匹配所有以 news.开头的频道,像 news.global.today、 news.it 等。

  1. publish

格式:PUBLISH channel message

功能:Redis 客户端退订指定的频道。

如果没有频道被指定,也就是一个无参数的 UNSUBSCRIBE 命令被执行,那么客户端使用 SUBSCRIBE 命令订阅的所有频道都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的频道。

  1. punsubscribe

格式:PUNSUBSCRIBE [pattern [pattern …]]

功能:退订一个或多个符合给定模式的频道。

这里的模式只能使用通配符 *。如果没有频道被指定,客户端将退订所有订阅的频道。

  1. pubsub

格式:PUBSUB <subcommand> [argument [argument …]]

功能:PUBSUB 是一个查看订阅与发布系统状态的内省命令集,它由数个不同格式的子命令组成

  • pubsub channels

格式:PUBSUB CHANNELS [pattern]

功能:列出当前所有的活跃频道。活跃频道指的是那些至少有一个订阅者的频道。

如果不给出 pattern 参数,将会列出订阅/发布系统中的所有活跃频道。如果给出 pattern 参数,那么只列出和给定模式 pattern 相匹配的那些活跃频道。pattern 中只能使用通配符*。

  • pubsub numsub

格式:PUBSUB NUMSUB [channel-1 … channel-N]

功能:返回给定频道的订阅者数量。不给定任何频道则返回一个空列表。

  • pubsub numpat

格式:PUBSUB NUMPAT

功能:查询当前 Redis 所有客户端订阅的所有频道模式的数量总和

15. Redis 事务

Redis 的事务的本质是一组命令的批处理。这组命令在执行过程中会被顺序地、一次性全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会被中断。

事务特性:

  • Redis 的事务仅保证了数据的一致性,不具有像 DBMS 一样的 ACID 特性。
  • 这组命令中的某些命令的执行失败不会影响其它命令的执行,不会引发回滚。即不具备原子性。
  • 这组命令通过乐观锁机制实现了简单的隔离性。没有复杂的隔离级别。
  • 这组命令的执行结果是被写入到内存的,是否持久取决于 Redis 的持久化策略,与事务无关。

事务实现

  1. multi:开启事务
  2. exec:执行事务
  3. discard:取消事务

78872cfb3a5d701f267f30f5fd69ebe3.png

Redis 事务异常处理

语法错误:

当事务中的命令出现语法错误时,整个事务在 exec 执行时会被取消。也就是所有指令都不执行

执行异常:

如果事务中的命令没有语法错误,但在执行过程中出现异常,该异常不会影响其它命令的执行。

a9c6c606aa88a68ea2e695c1c6eeaecb.png

Redis 事务隔离机制

在并发场景下可能会出现多个客户端对同一个数据进行修改的情况。为了解决这种情况造成的问题,Redis 事务通过乐观锁机制(添加版本号)实现了多线程下的执行隔离。

  • watch

82f5ad336720032ba812d9153a760ed7.png

由于添加了乐观锁,另一个客户端在此期间先修改了age,导致此客户端事务执行失败

上一篇:
缓存组件
下一篇:
Redis系列第一章