3.redis命令(续)

140 阅读12分钟

3.11 BitMap 操作命令

3.11.1 BitMap 简介

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

  • key:BitMap 是 Redis 的 key-value 中的一种 Value 的数据类型,所以该 Value 一定有其对应的 key。
  • offset:每个 BitMap 数据都是一个字符串,字符串中的每个字符都有其对应的索引,称为每个字符在该 BitMap 中的偏移量 offset,从 0 开始计数。这个 offset 的值的范围是[0,2^32 -1]
  • bitValue:每个 BitMap 数据中都是一个仅包含 0 和 1 的二进制字符串,每个 offset 位上的字符就称为该位的值 bitValue。bitValue 的值非 0 即 1。
3.11.2 setbit

格式:SETBIT key offset value

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

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

3.11.3 getbit

格式:GETBIT key offset

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

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

3.11.4 bitcount

格式:BITCOUNT key [start] [end]

统计给定字符串中被设置为 1 的 bit 位的数量。指定的 start 或 end,实现对指定字节范围内字符串进行统计,包括 start 和 end 在内。注意,这里的 start 与 end 的单位是字节,不是 bit,并且从 0 开始计数。

start 和 end 参数都可以使用负数值。对于不存在的 key 被当成是空字符串来处理

3.11.5 bitpos

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

返回 key 指定的 BitMap 中第一个值为指定 bit 的二进制位的位置。 pos,即 position。

start 与 end 的意义与 bitcount 命令中的相同。

3.11.6 bitop

格式:BITOP operation destkey key [key …]

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

operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:

  • BITOP AND destkey key [key ...] :对一个或多个 BitMap 执行按位与操作
  • 其他的类似

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

3.11.7 bitmap和set的取舍

一个平台要统计日活跃用户数量。

如果使用 Set 来统计,只需上线一个用户,就将其用户 ID 写入 Set 集合即可,最后只需统计出 Set 集合中的元素个数即可完成统计。即 Set 集合占用内存的大小与上线用户数量成正比。假设用户 ID 为 m 位 bit 位,当前活跃用户数量为 n,则该 Set 集合的大小最少应该是 m*n 字节

如果使用 BitMap,则需要先定义出一个 BitMap,其占有的 bit 位至少为注册用户数量。只需上线一个用户,就使其中一个 bit 位置 1,最后只需统计出 BitMap 中 1 的个数即可完成统计。即 BitMap 占用内存的大小与注册用户数量成正比,与上线用户数量无关。假设平台具有注册用户数量为 N,则 BitMap 的长度至少为 N 个 bit 位,即 N/8 字节。

何时使用 BitMap 更合适? 令 m*n 字节 = N/8 字节,即 n = N/(8*m) 时,使用 Set 集合与使用 BitMap 所占内存大小相同。以淘宝为例,其用户 ID 长度为 11 位(m),其注册用户数量为 8 亿(N),当活跃用户数量为 8 亿/(8*11) = 0.09 亿 = 900 万,使用 Set 与 BitMap 占用的内存是相等的。但淘宝的日均活跃用户数量为 8 千万,所以淘宝使用 BitMap 更合适。

3.12 HyperLogLog 操作命令

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

HyperLogLog 算法是一个纯数学算法,我们这里不做研究。

3.12.1 pfadd

格式:PFADD key element [element …]

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

3.12.2 pfcount

格式:PFCOUNT key [key …]

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

3.12.3 pfmerge

格式:PFMERGE destkey sourcekey [sourcekey …]

将多个 HyperLogLog 集合合并为一个 HyperLogLog 集合,并存储到 destkey 中,合并后的 HyperLogLog 的基数接近于所有 sourcekey 的 HyperLogLog 集合的并集。

3.12.4 应用场景

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

3.13 Geospatial 操作命令

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

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

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

3.13.1 geoadd

格式:GEOADD key longitude latitude member [longitude latitude member …]

将一到多个空间元素添加到指定的空间集合中。当用户尝试输入一个超出范围的经度或者纬度时,该命令会返回一个错误。

3.13.2 geopos

格式:GEOPOS key member [member …]

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

3.13.3 geodist

格式:GEODIST key member1 member2 [unit]

返回两个给定位置之间的距离。其中 unit 必须是以下单位中的一种:

  • m :米,默认
  • km :千米
  • mi :英里
  • ft:英尺

如果两个位置有一个不存在,返回空值。在计算距离时会假设地球为完美的球形,这一假设最大会造成 0.5% 的误差。

3.13.4 geohash

格式:GEOHASH key member [member …]

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

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

3.13.5 georadius

格式: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 选项去获取少量元素,该命令的执行速度也可能会非常慢。

3.13.6 georadiusbymember

格式:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

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

3.14 发布/订阅命令

3.14.1 消息系统

image.png

发布/订阅,即 pub/sub,是一种消息通信模式:生产者生产和发送 息到存储系统;订阅者从存储系统接收和消费消息。存储系统可以是文件系统 FS、消息中间件 MQ、数据管理系统 DBMS,也可以是 Redis。整个消息发布者、订阅者与存储系统称为消息系统。

消息系统中的订阅者订阅了某类消息后,只要存储系统中存在该类消息,其就可不断的接收并消费这些消息。当存储系统中没有该消息后,订阅者的接收、消费阻塞。当发布者将消息写入到存储系统后,会立即唤醒订阅者。当存储系统放满时,不同的发布者具有不同的处理方式:有的会阻塞发布者的发布,等待可用的存储空间;有的则会将多余的消息丢失。

当然,不同的消息系统消息的发布/订阅方式也是不同的。例如 RocketMQ、Kafka 等消息中间件构成的消息系统中,发布/订阅的消息都是以主题 Topic 分类的。而 Redis 构成的消息系统中,发布/订阅的消息都是以频道 Channel 分类的。

3.14.2 subscribe

格式:SUBSCRIBE channel [channel …]

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

3.14.3 psubscribe

格式:PSUBSCRIBE pattern [pattern …]

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

这里的模式只能使用通配符 *。例如,it* 可以匹配所有以 it 开头的频道

3.14.4 publish

格式:PUBLISH channel message

Redis 客户端通过 publish 命令可以发布一个频道的消息。返回值为接收到该消息的订阅者数量。

3.14.5 unsubscribe

格式:UNSUBSCRIBE [channel [channel …]]

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

如果没有参数,那么客户端退订所有频道。命令会返回一个信息,告知客户端所有被退订的频道。

3.14.6 punsubscribe

格式:PUNSUBSCRIBE [pattern [pattern …]]

退订一个或多个符合给定模式的频道。不指定模式则退订所有频道

3.14.7 pubsub

格式:PUBSUB [argument [argument …]]

查看订阅与发布系统状态的内省命令集,它由数个不同格式的子命令组成,下面分别介绍这些子命令的用法。

(1) pubsub channels

格式:PUBSUB CHANNELS [pattern]

列出当前所有的活跃频道(至少有一个订阅者的频道)。

(2) pubsub numsub

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

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

(3) pubsub numpat

格式:PUBSUB NUMPAT

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

3.15 Redis 事务

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

3.15.1 Redis 事务特性

Redis 的事务仅保证了数据的一致性,不具有像 DBMS 一样的 ACID 特性。

  • 这组命令中的某些命令的执行失败不会影响其它命令的执行,不会引发回滚。即不具备原子性。
  • 这组命令通过乐观锁机制实现了简单的隔离性。没有复杂的隔离级别。
  • 这组命令的执行结果是被写入到内存的,是否持久取决于 Redis 的持久化策略,与事务无关。
3.15.2 Redis 事务实现
  • multi:开启事务
  • exec:执行事务
  • discard:取消事务
3.15.3 Redis 事务异常处理

(1) 语法错误:当事务中的命令出现语法错误时,整个事务在 exec 执行时会被取消。

(2) 执行异常:在执行过程中出现异常,该异常不会影响其它命令的执行。 image.png

3.15.4 Redis 事务隔离机制

(1) 为什么需要隔离机制

在并发场景下可能会出现多个客户端对同一个数据进行修改的情况。

(2) 隔离的实现

Redis 通过 watch 命令再配合事务实现了多线程下的执行隔离。

image.png

以上两个客户端执行的时间顺序为:

image.png

(3) 实现原理

  1. 当客户端A对 key 执行了 watch 后,系统为该 key 添加一个 version 并初始化。例如初值为 1.0。
  2. 此后客户端A将对该 key 的修改语句写入事务命令队列中,虽未执行,但其将该 key 的 value 值与 version 进行了读取并保存到了当前客户端缓存。
  3. 此后客户端B对该 key 的值进行了修改,不仅修改 key 的 value 本身,同时增加 version 的值,例如使其 version 变为了 2.0,并将该 version 记录到了该 key 信息中。
  4. 此后客户端A执行 exec,开始执行事务中的命令。不过,其在执行到对该 key 进行修改的命令时,首先对当前客户端缓存中保存的 version 值与当前 key 信息中的 version 值。如果缓存 version 小于 key 的 version,则说明客户端缓存的 key 的 value 已经过时,该写操作如果执行可能会破坏数据的一致性。所以该写操作不执行。