redis类型解析汇总

redis类型解析汇总

介绍

Redis(Remote Dictionary Server 远程字典服务器)是一个开源的内存数据库,它可以作为缓存、数据库和消息中间件使用

数据类型简介

主要数据类型:

1. 字符串(String):

  • Redis 最基本的数据类型,可以存储文本、整数或者二进制数据。
  • 支持基本的字符串操作,如设置、获取、追加等。

2. 哈希(Hash):

  • 类似于关联数组,适合存储对象。
  • 每个哈希可以存储多个字段和对应的值。

3. 列表(List):

  • 链表结构,支持从两端压入和弹出元素,适合存储有序的元素集合。
  • 可以用于实现队列、栈等数据结构。

4. 集合(Set):

  • 无序且唯一的元素集合,支持集合间的基本操作(交集、并集等)。
  • 可以用于存储不重复的元素集合,如用户标签、好友列表等。

5. 有序集合(Sorted Set):

  • 类似于集合,但每个元素关联一个分数,支持按分数排序。
  • 可以用于实现排行榜、范围查询等功能。

衍生类型:

1. 位图(BitMap):

  • Redis 提供了位操作命令,可以将字符串作为位数组来使用,支持位的设置、获取、计数等操作。
  • 适用于存储布尔型数据或者进行一些位运算操作。

2. 超级日志结构(HyperLogLog):

  • 用于进行基数统计,估算集合中不重复元素的数量。
  • 提供了一系列的命令用于添加元素、统计基数、合并 HyperLogLog 等。

3. 地理位置(GeoSpatial):

  • 通过经纬度坐标来存储地理位置信息,并支持查询两点之间的距离、查找附近的位置等功能。
  • 适用于实现地理位置服务、附近搜索等功能。

4. 流(Stream):

  • Redis 5.0 引入的新数据类型,类似于日志结构,支持添加、读取、消费消息等操作。
  • 可以用于实现消息队列、事件日志等应用场景。

字符串(String)

底层设计原理

在 Redis 中,字符串的底层结构是简单动态字符串(Simple Dynamic String,SDS)。SDS 是 Redis自己实现的一种字符串表示方式,相比于传统的 C 语言字符串,它具有以下优势,从而使得 Redis 中字符串操作更加高效

图例

// TODO 待补充

设计优势

1. 动态扩展:SDS 可以根据需要动态扩展字符串的长度,而不需要像 C 字符串那样每次都重新分配内存和拷贝数据,这样可以避免频繁的内存分配和释放,提高了性能。
2. 减少缓冲区溢出:SDS 在字符串末尾会额外存储一个字节的空间,用于存放空字符(‘\0’),这样即使字符串长度增加,也不会发生缓冲区溢出,提高了安全性。
3. 快速获取长度:SDS 结构中记录了字符串的长度信息,因此获取字符串长度的操作是 O(1) 的时间复杂度,而不像 C 字符串需要遍历整个字符串。
4. 二进制安全:SDS 可以存储任意二进制数据,而不仅限于文本数据,这使得 Redis 的字符串类型可以应对更多的应用场景,例如存储图片、序列化对象等。
5. 支持部分修改:SDS 支持在 O(1) 的时间复杂度内进行部分字符串的修改,比如替换某个位置的字符,这在某些应用场景下非常高效。

字符串使用方法

设置字符串值

  • SET key value [EX seconds] [PX milliseconds] [NX|XX]
    • 将字符串键 key 的值设置为 value。可以设置过期时间 EX seconds 或 PX milliseconds。
    • NX:仅在键不存在时设置值。
    • XX:仅在键已存在时设置值。
SET mykey "Hello"
SET mycounter 100 EX 3600 NX

获取字符串值

  • GET key
    • 获取键 key 的值。
GET mykey

获取和设置部分字符串

  • GETRANGE key start end

    • 返回键 key 中字符串值的子字符串,从 start 到 end 的位置(包括两端)。
  • SETRANGE key offset value

    • 用指定的 value 替换键 key 中字符串的部分内容,从 offset 开始。
GETRANGE mykey 0 3  # 返回 "Hell"
SETRANGE mykey 6 "Redis"  # 将 mykey 中索引为 6 开始的部分替换为 "Redis"

获取字符串长度

  • STRLEN key
    • 返回键 key 中字符串值的长度。
STRLEN mykey

追加字符串

  • APPEND key value
    • 将 value 追加到键 key 当前值的末尾,并返回新的字符串长度。
APPEND mykey ", welcome to Redis!"

设置新值并返回旧值

  • GETSET key value
    • 将键 key 的值设置为 value,并返回旧值。
GETSET mykey "New Value"

递增/递减

  • INCR key
    • 将键 key 中存储的数字值增加 1。
  • DECR key
    • 将键 key 中存储的数字值减少 1。
  • INCRBY key increment
    • 将键 key 中存储的数字值增加指定的 increment。
  • DECRBY key decrement
    • 将键 key 中存储的数字值减少指定的 decrement。
INCR mycounter
DECR mycounter
INCRBY mycounter 10
DECRBY mycounter 5

假设我们有一个键 mykey,它最初存储的值是 “Hello”。

同时设置多个键值对

  • MSET key1 value1 [key2 value2 …]
    • 同时设置多个键值对。
MSET key1 "value1" key2 "value2"

同时获取多个键的值

  • MGET key1 [key2 …]
    • 同时获取多个键的值。
MGET key1 key2

设置字符串值并带有过期时间

  • SETEX key seconds value
    • 设置键 key 的值为 value,并设置过期时间为 seconds 秒。
SETEX mykey 60 "Hello"  # 设置 mykey 的值为 "Hello",并设置过期时间为 60

自增字符串值为浮点数

  • INCRBYFLOAT key increment
    • 将键 key 中存储的数字值增加指定的浮点数 increment。
SET myfloat 10.5     
INCRBYFLOAT myfloat 0.1  # 将 myfloat 的值增加 0.1     

哈希(Hash)

底层设计原理

其实现主要包括两个部分:哈希表(Hash table)和哈希冲突解决方法(Collision resolution)

哈希表

Redis 中的哈希表实际上是一个数组(Array),数组的每个元素称为一个哈希桶(bucket)。每个桶中可以存储多个哈希节点(Hash node),每个节点包含一个键值对。

  • 数组大小和桶的数量: Redis 在创建哈希表时会预先分配一定大小的数组,数组的每个元素是一个桶。数组的大小会动态调整,以适应哈希表中存储的键值对数量变化。
  • 桶的选择: Redis 使用键的哈希值通过取模运算来决定存储在哪个桶中,例如 hash(key) % buckets_count。这样可以快速定位到存储和检索数据的位置

哈希冲突解决方法

当两个不同的键通过哈希函数计算得到相同的索引位置时,就会发生哈希冲突。Redis 使用链地址法(Separate chaining)来解决哈希冲突:

  • 链表存储: 每个桶中存储一个链表或者其他类似的数据结构(例如跳表),用于存放哈希冲突的键值对。这样即使发生冲突,也能通过遍历链表或者其他数据结构找到正确的键值对

图例

// TODO 待补充

哈希使用方法

设置指定key

  1. HSET key field value
  • 设置哈希 key 中的字段 field 的值为 value。
HSET user:1001 username "alice"

获取指定key

  1. HGET key field
  • 获取哈希 key 中字段 field 的值。
HGET user:1001 username

删除指定key

  1. HDEL key field [field …]
  • 删除哈希 key 中的一个或多个字段。
HDEL user:1001 username

获取指定key字段数量

  1. HLEN key
  • 返回哈希 key 中字段的数量。
HLEN user:1001

获取指定key中所有字段

  1. HKEYS key
  • 返回哈希 key 中所有的字段。
HKEYS user:1001

获取指定key对应所有值

  1. HVALS key
  • 返回哈希 key 中所有的值。
HVALS user:1001

获取指定key对应所有字段和值

  1. HGETALL key
  • 返回哈希 key 中的所有字段和值。
HGETALL user:1001

假设有一个用户信息存储在哈希中:

127.0.0.1:6379> HSET user:1001 username "alice"
(integer) 1
127.0.0.1:6379> HSET user:1001 email "alice@example.com"
(integer) 1
127.0.0.1:6379> HSET user:1001 age "30"
(integer) 1

可以使用以上方法来操作这个哈希数据结构:

127.0.0.1:6379> HGET user:1001 username
"alice"
127.0.0.1:6379> HGETALL user:1001
1) "username"
2) "alice"
3) "email"
4) "alice@example.com"
5) "age"
6) "30"
127.0.0.1:6379> HDEL user:1001 age
(integer) 1
127.0.0.1:6379> HLEN user:1001
(integer) 2
127.0.0.1:6379> HKEYS user:1001
1) "username"
2) "email"
127.0.0.1:6379> HVALS user:1001
1) "alice"
2) "alice@example.com"

若field不存在时设置

  1. HSETNX key field value
  • 当字段 field 不存在时,设置哈希 key 中的字段 field 的值为 value。
HSETNX user:1001 username "alice"

批量设置

  1. HMSET key field value [field value …]
  • 同时设置多个字段的值。
HMSET user:1001 username "alice" email "alice@example.com" age "30"

对指定key中指定字段值增加数值(int值)

  1. HINCRBY key field increment
  • 将哈希 key 中的字段 field 的值增加 increment。
HINCRBY user:1001 age 1

字段判空

  1. HEXISTS key field
  • 判断哈希 key 中是否存在字段 field。
HEXISTS user:1001 username

迭代指定key

  1. HSCAN key cursor [MATCH pattern] [COUNT count]
  • 迭代哈希 key 中的键值对。
HSCAN user:1001 0

获取指定key指定字段值长度

  1. HSTRLEN key field
  • 返回哈希 key 中字段 field 的值的长度。
HSTRLEN user:1001 username

批量返回指定key值的字段值

  1. HMGET key field [field …]
  • 返回哈希 key 中一个或多个字段的值。
HMGET user:1001 username email

对指定key中指定字段值增加数值(float值)

  1. HINCRBYFLOAT key field increment
  • 将哈希 key 中的字段 field 的值增加 increment(浮点数)。
HINCRBYFLOAT user:1001 score 0.5

若field不存在时设置(存在NX/XX参数时)

  1. HSET key field value [NX|XX]
  • 当指定 NX 或 XX 参数时,只在字段不存在或者已存在时才设置值。
HSET user:1001 username "alice" NX

批量移除字段的TTL

  1. HPERSIST key field [field …]
  • 移除哈希 key 中一个或多个字段的生存时间(TTL)。
HPERSIST user:1001 username

列表(List)

底层设计原理

List数据结构是一个双向链表(双端链表)实现的,支持在列表的两端进行快速的插入(push)和删除(pop)操作。这种实现保证了在头部和尾部进行插入和删除的时间复杂度都是O(1)

图例

// TODO

列表使用方法

列表头插

  1. LPUSH key value [value …]
  • 将一个或多个值插入到列表头部。
LPUSH mylist "world"
LPUSH mylist "hello"

列表尾插

  1. RPUSH key value [value …]
  • 将一个或多个值插入到列表尾部。
RPUSH mylist "foo"
RPUSH mylist "bar"

移除头部元素

  1. LPOP key
  • 移除并返回列表头部的元素。
LPOP mylist

移除尾部元素

  1. RPOP key
  • 移除并返回列表尾部的元素。
RPOP mylist

获取指定索引元素

  1. LINDEX key index
  • 返回列表中指定索引位置的元素。
LINDEX mylist 0

获取列表长度

  1. LLEN key
  • 返回列表的长度。
LLEN mylist

获取指定范围元素

  1. LRANGE key start stop
  • 返回列表中指定范围内的元素。
LRANGE mylist 0 2

移除指定数量元素

  1. LREM key count value
  • 移除列表中指定数量的元素。
LREM mylist 2 "hello"

设置指定索引元素

  1. LSET key index value
  • 设置列表中指定索引位置的元素值。
LSET mylist 1 "new_value"

裁剪指定范围元素

  1. LTRIM key start stop
  • 对列表进行裁剪,保留指定范围内的元素。
LTRIM mylist 0 2

阻塞模式头移

  1. BLPOP key [key …] timeout
  • 从列表头部弹出元素,如果列表为空则阻塞并等待元素的到来。
BLPOP mylist 10

阻塞模式尾移

  1. BRPOP key [key …] timeout
  • 从列表尾部弹出元素,如果列表为空则阻塞并等待元素的到来。
BRPOP mylist 10

指定元素位置插入元素

  1. LINSERT key BEFORE|AFTER pivot value
  • 在列表中指定元素 pivot 的前面或后面插入元素 value。
LINSERT mylist BEFORE "world" "new_value"

集合(Set)

底层设计原理

  • 每个集合成员(如 member1, member2, member3)被映射到哈希表的一个桶中。
  • 桶内的数据结构可以是链表或其他形式,用于处理哈希冲突,但每个键值对的键都是成员的值,值则是一个常量(NULL)。
  • Set 数据结构非常适合用于存储唯一值的场景,例如标签、用户 ID 等,并且能够通过交集、并集、差集等操作,支持更复杂的数据处理需求

图例

// TODO

集合使用方法

添加成员

  1. SADD key member [member …]
  • 向集合添加一个或多个成员。
SADD myset "member1"
SADD myset "member2"

移除成员

  1. SREM key member [member …]
  • 从集合中移除一个或多个成员。
SREM myset "member1"

获取所有成员

  1. SMEMBERS key
  • 返回集合中的所有成员。
SMEMBERS myset

检查是否为集合成员

  1. SISMEMBER key member
  • 检查 member 是否是集合 key 的成员。
SISMEMBER myset "member1"

获取集合元素数量

  1. SCARD key
  • 返回集合的基数(集合中元素的数量)。
SCARD myset

获取移除的成员

  1. SPOP key [count]
  • 移除并返回集合中的一个或多个随机元素。
SPOP myset

获取指定数量成员

  1. SRANDMEMBER key [count]
  • 返回集合中一个或多个随机元素(不移除元素)。
SRANDMEMBER myset 2

转移集合成员

  1. SMOVE source destination member
  • 将 member 从 source 集合移动到 destination 集合。
SMOVE myset myotherset "member1"

返回集合交集

  1. SINTER key [key …]
  • 返回多个集合的交集。
SINTER myset myotherset

获取集合并集

  1. SUNION key [key …]
  • 返回多个集合的并集。
SUNION myset myotherset

获取集合差集

  1. SDIFF key [key …]
  • 返回多个集合的差集。
SDIFF myset myotherset

计算并保存集合交集

  1. SINTERSTORE destination key [key …]
  • 计算多个集合的交集,并将结果存储到新的集合中。
SINTERSTORE newset myset1 myset2

计算并保存集合并集

  1. SUNIONSTORE destination key [key …]
  • 计算多个集合的并集,并将结果存储到新的集合中。
SUNIONSTORE newset myset1 myset2

计算并保存集合差集

  1. SDIFFSTORE destination key [key …]
  • 计算多个集合的差集,并将结果存储到新的集合中。
SDIFFSTORE newset myset1 myset2

有序集合(Sorted Set)

底层设计原理

有序集合是一种特殊的数据结构,它类似于集合(Set),但每个成员都关联了一个分数(score),该分数用于对成员进行排序。有序集合的底层设计原理主要涉及两个核心点:跳跃表(SkipList)和哈希表(Hash Table)。

跳跃表(Skip List)

跳跃表是一种数据结构,用于在有序元素的集合中快速查找某个元素。它通过层级结构来实现快速查找,每一层都是元素的一个有序子集,最底层包含所有元素。在跳跃表中,每个节点包含一个指向下一个节点的指针,以及一个指向同一层下一个节点的指针。
在Redis中,有序集合的底层实现就是通过跳跃表来实现的。每个成员在跳跃表中都是一个节点,节点中包含了成员本身的值和分数,以及指向下一个节点和下一层节点的指针。通过跳跃表,Redis可以实现有序集合的快速查找和排序功能。

哈希表(Hash Table)

除了跳跃表,有序集合在底层还使用了哈希表来存储成员和分数之间的映射关系。哈希表是一种以键值对形式存储数据的数据结构,可以快速查找和访问数据。
在Redis中,有序集合使用哈希表来存储成员和分数之间的映射关系,这样可以在跳跃表中快速定位到某个成员的节点,然后通过哈希表找到成员对应的分数。哈希表的使用使得在有序集合中查找某个成员的分数变得高效。

图例

// TODO

有序集合使用方法

添加成员

  1. ZADD key score member [score member …]
  • 将一个或多个成员及其对应的分数添加到有序集合中。
ZADD myset 1 "member1" 2 "member2"

删除成员

  1. ZREM key member [member …]
  • 从有序集合中删除一个或多个成员。
ZREM myset "member1"

查询成员个数

  1. ZCARD key
  • 获取有序集合中成员的数量。
ZCARD myset

查询成员的分数

  1. ZSCORE key member
  • 获取指定成员的分数。
ZSCORE myset "member1"

按排名查询成员

  1. ZRANK key member
  • 获取指定成员在有序集合中的排名(从0开始)。
ZRANK myset "member1"

按分数范围查询成员

  1. ZRANGE key start stop [WITHSCORES]
  • 按分数从小到大的顺序,获取指定范围内的成员。
ZRANGE myset 0 1 WITHSCORES

按分数范围删除成员

  1. ZREMRANGEBYSCORE key min max
  • 删除分数在指定范围内的成员。
ZREMRANGEBYSCORE myset 0 5

增加成员的分数

  1. ZINCRBY key increment member
  • 给指定成员的分数增加增量increment。
ZINCRBY myset 2 "member1"

查看排名范围内的成员和分数

  1. ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  • 按分数范围获取成员及其分数,支持分页。
ZRANGEBYSCORE myset 0 10 WITHSCORES LIMIT 0 2

逆序按排名查询成员

  1. ZREVRANK key member
  • 获取指定成员在有序集合中的逆序排名(从0开始)。
ZREVRANK myset "member1"

逆序按分数范围查询成员

  1. ZREVRANGE key start stop [WITHSCORES]
  • 按分数从大到小的顺序,获取指定范围内的成员。
ZREVRANGE myset 0 1 WITHSCORES

删除排名范围内的成员

  1. ZREMRANGEBYRANK key start stop
  • 删除排名在指定范围内的成员。
ZREMRANGEBYRANK myset 0 1

获取有序集合中在字典范围内成员数量

  1. ZLEXCOUNT key min max
  • 获取有序集合中在字典范围内(字典区间使用"-“和”+"表示)的成员数量。
ZLEXCOUNT myset "-" "+"

按字典范围查询成员

  1. ZRANGEBYLEX key min max [LIMIT offset count]
  • 按字典范围获取成员。
ZRANGEBYLEX myset "-" "[m" LIMIT 0 2

按字典范围删除成员

  1. ZREMRANGEBYLEX key min max
  • 删除字典范围内的成员。
ZREMRANGEBYLEX myset "-" "[m"

求集合的并集

  1. ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
  • 计算给定有序集合的并集,并将结果存储在新的有序集合中。
ZUNIONSTORE dest 2 set1 set2 WEIGHTS 1 2

求集合的交集

  1. ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
  • 计算给定有序集合的交集,并将结果存储在新的有序集合中。
ZINTERSTORE dest 2 set1 set2

求集合的并集和

  1. ZUNION key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
  • 计算给定有序集合的并集,并返回结果集。
ZUNION 2 set1 set2 WEIGHTS 1 2

位图(BitMap)

底层设计原理

位图是一种特殊的数据结构,它实际上是一个特定长度的字符串,字符串中的每个比特位都可以被设置或清除。这种设计使得Redis的位图非常节省内存,特别适合于需要高效存储大量布尔类型数据的场景

1. 存储结构:

  • Redis的位图是一个由字符串实现的位数组,其中每个比特位可以代表一个状态(通常是0或1)。
  • 位图的长度由用户定义,在Redis中可以使用命令 SETBIT 来设置位图的长度,如果超过当前长度,Redis会自动扩展。

2. 位操作命令:

  • Redis提供了一系列位操作的命令,包括设置位、清除位、查看位的值等,这些操作都是针对位图中的单个比特位进行的。

3. 内存优化:

  • 位图采用了非常紧凑的存储方式,每个比特位只占用1位(0或1),因此在存储大量布尔类型数据时非常节省内存。

应用场景

1. 用户在线状态:

  • 可以用一个位图来表示用户的在线状态,每个用户对应位图中的一个比特位,该位在用户在线时置为1,离线时置为0。这样可以高效地查询多个用户的在线状态。

2. 统计数据:

  • 可以用位图来记录用户的操作行为或者事件发生情况,比如记录用户每天的登录情况、签到情况等。通过位操作命令,可以快速统计和分析这些数据。

3. 布隆过滤器(Bloom Filter):

  • 布隆过滤器是一种快速判断一个元素是否在集合中的数据结构。Redis的位图可以用来实现简单的布隆过滤器,通过多个不同的哈希函数在位图中设置对应的比特位,来判断一个元素可能存在于集合中(不准确,但是高效)。

图例

// TODO

位图使用方法

设置位图中的位

  1. SETBIT key offset value
  • 将位图key中偏移量为offset的位的值设置为value(0或1)。
SETBIT mybitmap 0 1   # 将mybitmap中偏移量为0的位设置为1

获取位图中的位

  1. GETBIT key offset
  • 获取位图key中偏移量为offset的位的值。
GETBIT mybitmap 0   # 获取mybitmap中偏移量为0的位的值

获取位图的长度

  1. BITCOUNT key [start end]
  • 统计位图key中值为1的位的数量。
BITCOUNT mybitmap   # 统计mybitmap中所有值为1的位的数量

求位图之间的逻辑操作

  1. BITOP operation destkey key [key …]
  • 对一个或多个位图执行位运算,并将结果保存到destkey中。operation可以是AND、OR、XOR、NOT等。
BITOP AND destbitmap mybitmap1 mybitmap2   # 将mybitmap1和mybitmap2按位进行AND操作,并将结果存储到destbitmap中

设置位图的长度

  1. BITFIELD key [GET type offset] [SET type offset value]
  • 对位图进行更加复杂的操作,可以用于获取和设置指定偏移量的位。
BITFIELD mybitmap GET u4 0   # 获取mybitmap中偏移量为04位无符号整数
BITFIELD mybitmap SET u4 0 10   # 设置mybitmap中偏移量为04位无符号整数为10

获取位图指定范围内的所有位

  1. BITFIELD key [GET type offset] [GET type offset] …
  • 可以一次性获取多个不同偏移量的位的值。
BITFIELD mybitmap GET u4 0 GET u4 4   # 获取mybitmap中偏移量044位无符号整数

设置位图指定范围内的所有位

  1. BITFIELD key [SET type offset value] [SET type offset value] …
  • 可以一次性设置多个不同偏移量的位的值。
BITFIELD mybitmap SET u4 0 10 SET u4 4 5   # 设置mybitmap中偏移量044位无符号整数分别为105

获取位图指定范围内的所有位,并对它们进行位运算

  1. BITFIELD key [GET type offset] [GET type offset] … [operation type] [operation value]
  • 可以在获取位的同时,对获取到的位进行位运算。
BITFIELD mybitmap GET u4 0 GET u4 4 OR 5   # 获取mybitmap中偏移量044位无符号整数,并对它们进行OR运算(假设5是一个值)

超级日志结构(HyperLogLog)

HyperLogLog(简称HLL)是一种用于近似计数的数据结构,主要用于解决集合的基数估计问题。它可以在极小的内存消耗下,对大型数据集合的基数进行估计

底层设计原理

1. 基数估计:

  • HyperLogLog利用一种概率性算法来估计集合的基数(即不重复元素的数量),它的基本原理是通过对数据进行哈希映射,统计哈希值前导0的个数来估计基数。

2. 哈希函数:

  • HyperLogLog使用一种特殊的哈希函数,将元素映射到一个固定长度的二进制字符串中。这个哈希函数的设计对结果的准确性有很大影响。

3. 桶与计数器:

  • HyperLogLog将哈希值分配到多个桶中,并在每个桶中维护一个计数器,用于记录对应桶中哈希值前导0的最大长度。

4. 合并计数器:

  • 当多个HyperLogLog数据结构需要合并时,采用的是取各个桶中计数器的最大值作为合并后的结果,这样可以保证合并后的结果不会低于原始数据结构的估计值。

应用场景

HyperLogLog在Redis中的应用场景包括但不限于:
1. 独立用户数量统计:

  • 可以用于统计网站或应用的独立访客数量,以便进行用户行为分析和精准营销。

2. 基数估计:

  • 可以用于统计大数据集合的基数,例如网页访问量、搜索关键词数量等。

3. 数据去重:

  • 可以用于判断数据集合中是否存在重复元素,帮助进行数据去重处理。

图例

// TODO

HyperLogLog使用方法

添加元素到HyperLogLog

  1. PFADD key element [element …]
  • 将一个或多个元素添加到HyperLogLog中。
PFADD myloglog user1 user2 user3   # 将user1, user2, user3添加到名为myloglog的HyperLogLog中

获取HyperLogLog的基数估计

  1. PFCOUNT key [key …]
  • 获取一个或多个HyperLogLog的基数估计值。
PFCOUNT myloglog   # 获取名为myloglog的HyperLogLog的基数估计值

合并多个HyperLogLog

  1. PFMERGE destkey sourcekey [sourcekey …]
  • 将多个HyperLogLog合并为一个,并将结果存储到destkey中。
PFMERGE mergedlog myloglog1 myloglog2   # 将myloglog1和myloglog2合并为一个HyperLogLog,并将结果存储到mergedlog中

其他说明

1. HyperLogLog与其他数据结构结合使用:

  • 可以将HyperLogLog的结果与其他数据结构(如Sorted Set或Hash)结合使用,以实现更复杂的统计和分析功能。

2. 内存消耗与精度权衡:

  • HyperLogLog通过牺牲一定的精度来换取极小的内存消耗,但在实际使用中,需要根据具体的应用场景权衡精度和内存消耗。

注意事项

1. 基数估计误差:

  • HyperLogLog在进行基数估计时存在一定的误差,这个误差通常在1-2%之间,但在合适的场景下可以接受。

2. 数据存储和备份:

  • 在使用HyperLogLog时,需要考虑数据的持久化存储和备份策略,以防止数据丢失或出现不一致情况。

3. 数据更新和删除:

  • HyperLogLog结构本身不支持删除单个元素或更新元素的操作,因此需要在设计时考虑如何处理数据的更新和删除需求。

地理位置(GeoSpatial)

GeoSpatial(地理空间)数据结构是一种用于存储地理位置坐标的数据结构,主要包括地理位置的经度和纬度信息。它提供了高效的空间查询和距离计算功能,适用于需要基于地理位置进行检索和分析的应用场景

底层设计原理

1. 数据存储:

  • GeoSpatial在Redis内部使用有序集合(Sorted Set)来存储地理位置信息。有序集合的成员是地理位置的名称或标识符,而分数(score)则是地理位置的经纬度信息。

2. 地理位置编码:

  • 地理位置的经纬度信息通过一种特定的编码方式存储在有序集合中,通常使用WGS84坐标系的经度和纬度作为分数。Redis使用的是双精度浮点数来表示经纬度。

3. 空间索引:

  • Redis使用基于Geohash的空间索引来加速地理位置的查询。Geohash是一种将地理位置编码为字符串的方法,具有天然的空间特性,相近的地理位置在Geohash编码上也会比较接近。

4. 距离计算:

  • GeoSpatial支持根据地理位置计算两点之间的距离,通常使用的是欧氏距离或球面距离(例如大圆距离)来度量地理空间的距离。

5. 查询优化:

  • Redis通过有序集合的分数范围查询功能,可以高效地进行地理位置范围内的查询,比如获取某个位置周围一定距离内的其他位置。

应用场景

1. 位置推荐与附近搜索:

  • 许多应用需要根据用户当前位置或指定地点,推荐附近的商家、服务或活动。使用GeoSpatial可以高效地找到某个地理位置附近的其他位置,比如附近的餐馆、酒店或公园。

2. 地理围栏与地理提醒:

  • 通过设置地理围栏(Geofence),可以监控用户是否进入或离开某个特定的地理区域。这在位置服务和地理提醒应用中非常有用,如出租车调度、安全警报系统等。

3. 路径规划与导航:

  • 根据多个地理位置点,可以进行路径规划和导航功能。例如,从起点到终点之间的最佳路线,或者找到途径特定地点的路线。

4. 地理统计分析:

  • 分析地理位置数据可以提供有价值的商业洞察,如不同地区的用户分布、热门区域的访问频率等。这对于市场营销、城市规划或资源分配有重要意义。

5. 地理位置的实时更新与管理:

  • 对于需要实时更新的场景,如共享经济平台(共享单车、共享汽车)、实时定位服务(快递追踪、司机位置追踪)等,GeoSpatial提供了高效的数据存储和查询能力。

6. 地理位置相关的社交网络功能:

  • 在社交网络中,根据用户的地理位置推荐附近的朋友、活动或社区,增强用户交互和社交体验。

图例

// TODO

GeoSpatial使用方法

添加地理位置及其经纬度信息

  1. GEOADD key longitude latitude member [longitude latitude member …]
  • key: GeoSpatial的键名。
  • longitude, latitude: 地理位置的经度和纬度。
  • member: 地理位置的名称或标识符。
GEOADD cities 116.405285 39.904989 Beijing 121.472644 31.231706 Shanghai

计算两个地理位置之间的距离

  1. GEODIST key member1 member2 [unit]
  • key: GeoSpatial的键名。
  • member1, member2: 要计算距离的两个地理位置的名称或标识符。
  • unit (可选): 距离单位,默认为m(米),可选km(千米)、mi(英里)、ft(英尺)。
GEODIST cities Beijing Shanghai km

获取地理位置的Geohash值

  1. GEOHASH key member [member …]
  • key: GeoSpatial的键名。
  • member: 要获取Geohash的地理位置的名称或标识符。
GEOHASH cities Beijing Shanghai

获取地理位置的经纬度信息

  1. GEOPOS key member [member …]
  • key: GeoSpatial的键名。
  • member: 要获取经纬度的地理位置的名称或标识符。
GEOPOS cities Beijing

根据指定地理位置查询范围内的其他地理位置

  1. GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
  • key: GeoSpatial的键名。
  • longitude, latitude: 中心点的经度和纬度。
  • radius: 查询半径。
  • unit: 单位,可以是m(米)、km(千米)、mi(英里)、ft(英尺)。
  • 可选参数包括WITHCOORD(返回地理位置的经纬度)、WITHDIST(返回与中心点的距离)、WITHHASH(返回地理位置的Geohash)、COUNT(返回结果的数量)、ASC|DESC(结果排序)、STORE(将结果存储到另一个GeoSpatial结构)等。
GEORADIUS cities 116.405285 39.904989 1000 km WITHDIST

根据指定的地理位置成员查询范围内的其他地理位置

  1. GEORADIUSBYMEMBER key member radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
    参数与GEORADIUS类似,不同之处在于使用成员名称而不是经纬度来指定中心点。
GEORADIUSBYMEMBER cities Beijing 1000 km WITHCOORD

从节点上执行地理位置查询

  1. 只读版本的GEORADIUS与GEORADIUSBYMEMBER用于在从节点上执行地理位置查询。
    示例应用
    假设有一个名为cities的GeoSpatial结构存储了几个城市的地理位置信息:
GEOADD cities 116.405285 39.904989 Beijing
GEOADD cities 121.472644 31.231706 Shanghai

可以通过以下示例展示如何使用各种操作:

# 计算北京和上海之间的距离
GEODIST cities Beijing Shanghai km

# 获取北京的Geohash值
GEOHASH cities Beijing

# 获取上海的经纬度信息
GEOPOS cities Shanghai

# 查找距离北京1000公里范围内的其他城市及距离
GEORADIUS cities 116.405285 39.904989 1000 km WITHDIST

统计给定范围内的地理位置数量

  1. GEOCOUNT key longitude latitude radius unit
  • key: GeoSpatial的键名。
  • longitude, latitude: 中心点的经度和纬度。
  • radius: 统计范围的半径。
  • unit: 单位,可以是m(米)、km(千米)、mi(英里)、ft(英尺)。
GEOCOUNT cities 116.405285 39.904989 1000 km

通过复杂的过滤器查询符合条件的地理位置

  1. GEOSEARCH key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius unit] [BYBOX width height unit] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
  • key: GeoSpatial的键名。
  • 可选参数包括从成员开始、从经纬度开始、通过半径、通过矩形框、返回经纬度、返回距离、返回Geohash、结果数量、排序、存储等。
GEOSEARCH cities BYRADIUS 1000 km FROMMEMBER Beijing WITHDIST

执行GEOSEARCH查询并将结果存储到另一个GeoSpatial结构中

  1. GEOSEARCHSTORE destkey [key] [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius unit] [BYBOX width height unit] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
  • destkey: 存储结果的目标GeoSpatial的键名。
  • 其他参数与GEOSEARCH类似。
    GEOSEARCHSTORE result_cities BYRADIUS 1000 km FROMMEMBER Beijing WITHCOORD
    应用示例
    假设继续使用名为cities的GeoSpatial结构:
GEOADD cities 116.405285 39.904989 Beijing
GEOADD cities 121.472644 31.231706 Shanghai

展示如何使用这些新命令:

# 统计距离北京1000公里范围内的城市数量
GEOCOUNT cities 116.405285 39.904989 1000 km

# 按半径查询距离北京1000公里范围内的城市及距离
GEOSEARCH cities BYRADIUS 1000 km FROMMEMBER Beijing WITHDIST

# 执行查询并将结果存储到另一个GeoSpatial结构中
GEOSEARCHSTORE result_cities BYRADIUS 1000 km FROMMEMBER Beijing WITHCOORD

这些命令扩展了Redis GeoSpatial在处理地理位置数据时的灵活性和功能性,适用于更复杂和精细化的地理空间数据分析和查询需求。

流(Stream)

Stream是一种高性能、持久化的数据结构,主要用于处理消息队列和日志系统,Stream通过简单而强大的命令集提供了高效的消息传输和处理方式,适用于实时数据处理、日志收集、消息队列等多种应用场景。使用Redis
Stream,可以轻松地实现消息的存储、传输和消费,并通过消费者组管理确保消息的有序处理和可靠传递

底层设计原理

1. 日志结构存储:

  • Stream基于日志(log)结构存储数据。每个Stream内部实际上是一个由多个条目(entry)组成的有序日志,每个条目都有唯一的ID,称为条目ID。

2. 条目ID生成:

  • 每个新添加到Stream的条目都会自动生成一个唯一的全局递增的ID。这个ID包含了一个64位的毫秒时间戳和一个序列号,确保了每个条目在Stream内的顺序和全局唯一性。

3. 内存结构与持久化:

  • Stream的条目可以部分保存在内存中,以保证高性能的写入和读取操作。Redis会根据配置的策略将部分或全部Stream数据持久化到磁盘上。

4. 消费者组:

  • Stream支持消费者组(consumer group),这是一种消费Stream消息的方式。每个消费者组可以有多个消费者,消费者可以独立或并行地处理消息,而无需担心重复消费或消息丢失。

5. 消费位置管理:

  • 每个消费者组中的消费者会记录自己消费的位置(offset),这个位置信息存储在Redis中。消费者组内的消费者可以通过ACK机制告知Redis已经处理完特定的消息,Redis根据ACK来更新消费位置,以确保消息不会被重复消费。

应用场景

1. 消息队列:

  • Stream可以作为高性能的消息队列使用,特别是在需要持久化、顺序保证和可靠传输的场景中,比如任务队列、实时通知等。

2. 日志系统:

  • 由于Stream内部是有序的日志结构,并且支持消费者组和持久化存储,因此非常适合作为日志收集和处理系统的基础。可以用于实时日志分析、事件溯源等场景。

3. 实时数据处理:

  • Stream的特性使其非常适合处理实时生成的数据流,比如实时监控、数据管道、实时分析等应用。

4. 消息发布订阅:

  • Stream可以替代传统的发布订阅(Pub/Sub)模式,提供更多的持久化和顺序保证。这对于需要持久订阅历史消息的应用非常有用。

5. 事件驱动架构:

  • 在微服务架构或事件驱动架构中,Stream可以作为事件消息的主要传输和存储方式,支持各种事件驱动的应用场景。

图例

// TODO

Stream使用方法

创建Stream

使用 XADD 命令向Stream中添加新条目:

  1. XADD mystream MAXLEN ~100 * field1 value1 field2 value2 …
  • mystream: Stream 的键名。
  • MAXLEN ~100: 可选参数,设置Stream的最大长度(条目数)。~ 表示不精确,即允许一定程度的超出。
  • *: 自动分配一个唯一的ID。
  • field1 value1 field2 value2 …: 条目的字段和值。
XADD mystream * sensor_id 1001 temperature 37.5 timestamp 1624142935.01234

读取Stream

使用 XREAD 命令从Stream中读取条目:
2. XREAD COUNT 10 STREAMS mystream 0

  • COUNT 10: 可选参数,指定读取的条目数。
  • STREAMS mystream 0: 0 表示从Stream中读取所有未读条目。
XREAD COUNT 5 STREAMS mystream 0

消费者组管理

创建消费者组:

XGROUP CREATE mystream mygroup $

读取和确认消息:

XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
XACK mystream mygroup <message-id>

获取Stream信息

XINFO STREAM mystream

删除Stream中的条目

XDEL mystream <message-id>

使用示例

假设要创建一个名为 mystream 的Stream,并进行基本的操作:
1. 创建Stream并添加条目:

XADD mystream * sensor_id 1001 temperature 37.5 timestamp
1624142935.01234 XADD mystream * sensor_id 1002 temperature 36.8 timestamp 1624142945.01567

2. 读取Stream中的条目:

XREAD COUNT 2 STREAMS mystream 0

3. 创建消费者组并消费消息:
- 创建消费者组:

XGROUP CREATE mystream mygroup $

- 消费消息:

XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
XACK mystream mygroup <message-id>

4. 获取Stream信息:

XINFO STREAM mystream

5. 删除条目:

XDEL mystream <message-id>

redis特性和优势

高性能:Redis 数据存储在内存中,操作非常快速。
持久化:支持多种持久化方式,可以将数据持久化到磁盘,保证数据安全。
复制和高可用:支持主从复制和 Sentinel 或 Cluster 方式的高可用解决方案。
事务:支持事务,可以批量执行一系列命令,保证原子性。
发布/订阅:支持发布与订阅模式,用于消息传递和通知。
丰富的功能扩展:通过 Lua 脚本和插件机制,可以扩展 Redis 的功能。

redis使用场景总结

缓存:作为高速缓存存储常用数据,加速访问速度。
会话存储:存储用户会话信息,实现无状态服务。
计数器:实现实时计数功能,如网站访问次数、商品销量等。
排行榜:通过有序集合存储用户积分和排名信息。
消息队列:利用列表和发布/订阅功能实现异步消息传递。

// TODO 未完待续

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值