redis类型解析汇总
- 介绍
- 数据类型简介
- 主要数据类型:
- 衍生类型:
- 字符串(String)
- 底层设计原理
- 图例
- 设计优势
- 字符串使用方法
- 设置字符串值
- 获取字符串值
- 获取和设置部分字符串
- 获取字符串长度
- 追加字符串
- 设置新值并返回旧值
- 递增/递减
- 同时设置多个键值对
- 同时获取多个键的值
- 设置字符串值并带有过期时间
- 自增字符串值为浮点数
- 哈希(Hash)
- 底层设计原理
- 哈希表
- 哈希冲突解决方法
- 图例
- 哈希使用方法
- 设置指定key
- 获取指定key
- 删除指定key
- 获取指定key字段数量
- 获取指定key中所有字段
- 获取指定key对应所有值
- 获取指定key对应所有字段和值
- 若field不存在时设置
- 批量设置
- 对指定key中指定字段值增加数值(int值)
- 字段判空
- 迭代指定key
- 获取指定key指定字段值长度
- 批量返回指定key值的字段值
- 对指定key中指定字段值增加数值(float值)
- 若field不存在时设置(存在NX/XX参数时)
- 批量移除字段的TTL
- 列表(List)
- 底层设计原理
- 图例
- 列表使用方法
- 列表头插
- 列表尾插
- 移除头部元素
- 移除尾部元素
- 获取指定索引元素
- 获取列表长度
- 获取指定范围元素
- 移除指定数量元素
- 设置指定索引元素
- 裁剪指定范围元素
- 阻塞模式头移
- 阻塞模式尾移
- 指定元素位置插入元素
- 集合(Set)
- 底层设计原理
- 图例
- 集合使用方法
- 添加成员
- 移除成员
- 获取所有成员
- 检查是否为集合成员
- 获取集合元素数量
- 获取移除的成员
- 获取指定数量成员
- 转移集合成员
- 返回集合交集
- 获取集合并集
- 获取集合差集
- 计算并保存集合交集
- 计算并保存集合并集
- 计算并保存集合差集
- 有序集合(Sorted Set)
- 底层设计原理
- 跳跃表(Skip List)
- 哈希表(Hash Table)
- 图例
- 有序集合使用方法
- 添加成员
- 删除成员
- 查询成员个数
- 查询成员的分数
- 按排名查询成员
- 按分数范围查询成员
- 按分数范围删除成员
- 增加成员的分数
- 查看排名范围内的成员和分数
- 逆序按排名查询成员
- 逆序按分数范围查询成员
- 删除排名范围内的成员
- 获取有序集合中在字典范围内成员数量
- 按字典范围查询成员
- 按字典范围删除成员
- 求集合的并集
- 求集合的交集
- 求集合的并集和
- 位图(BitMap)
- 底层设计原理
- 应用场景
- 图例
- 位图使用方法
- 设置位图中的位
- 获取位图中的位
- 获取位图的长度
- 求位图之间的逻辑操作
- 设置位图的长度
- 获取位图指定范围内的所有位
- 设置位图指定范围内的所有位
- 获取位图指定范围内的所有位,并对它们进行位运算
- 超级日志结构(HyperLogLog)
- 底层设计原理
- 应用场景
- 图例
- HyperLogLog使用方法
- 添加元素到HyperLogLog
- 获取HyperLogLog的基数估计
- 合并多个HyperLogLog
- 其他说明
- 注意事项
- 地理位置(GeoSpatial)
- 底层设计原理
- 应用场景
- 图例
- GeoSpatial使用方法
- 添加地理位置及其经纬度信息
- 计算两个地理位置之间的距离
- 获取地理位置的Geohash值
- 获取地理位置的经纬度信息
- 根据指定地理位置查询范围内的其他地理位置
- 根据指定的地理位置成员查询范围内的其他地理位置
- 从节点上执行地理位置查询
- 统计给定范围内的地理位置数量
- 通过复杂的过滤器查询符合条件的地理位置
- 执行GEOSEARCH查询并将结果存储到另一个GeoSpatial结构中
- 流(Stream)
- 底层设计原理
- 应用场景
- 图例
- Stream使用方法
- 创建Stream
- 读取Stream
- 消费者组管理
- 获取Stream信息
- 删除Stream中的条目
- 使用示例
- 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
- HSET key field value
- 设置哈希 key 中的字段 field 的值为 value。
HSET user:1001 username "alice"
获取指定key
- HGET key field
- 获取哈希 key 中字段 field 的值。
HGET user:1001 username
删除指定key
- HDEL key field [field …]
- 删除哈希 key 中的一个或多个字段。
HDEL user:1001 username
获取指定key字段数量
- HLEN key
- 返回哈希 key 中字段的数量。
HLEN user:1001
获取指定key中所有字段
- HKEYS key
- 返回哈希 key 中所有的字段。
HKEYS user:1001
获取指定key对应所有值
- HVALS key
- 返回哈希 key 中所有的值。
HVALS user:1001
获取指定key对应所有字段和值
- 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不存在时设置
- HSETNX key field value
- 当字段 field 不存在时,设置哈希 key 中的字段 field 的值为 value。
HSETNX user:1001 username "alice"
批量设置
- HMSET key field value [field value …]
- 同时设置多个字段的值。
HMSET user:1001 username "alice" email "alice@example.com" age "30"
对指定key中指定字段值增加数值(int值)
- HINCRBY key field increment
- 将哈希 key 中的字段 field 的值增加 increment。
HINCRBY user:1001 age 1
字段判空
- HEXISTS key field
- 判断哈希 key 中是否存在字段 field。
HEXISTS user:1001 username
迭代指定key
- HSCAN key cursor [MATCH pattern] [COUNT count]
- 迭代哈希 key 中的键值对。
HSCAN user:1001 0
获取指定key指定字段值长度
- HSTRLEN key field
- 返回哈希 key 中字段 field 的值的长度。
HSTRLEN user:1001 username
批量返回指定key值的字段值
- HMGET key field [field …]
- 返回哈希 key 中一个或多个字段的值。
HMGET user:1001 username email
对指定key中指定字段值增加数值(float值)
- HINCRBYFLOAT key field increment
- 将哈希 key 中的字段 field 的值增加 increment(浮点数)。
HINCRBYFLOAT user:1001 score 0.5
若field不存在时设置(存在NX/XX参数时)
- HSET key field value [NX|XX]
- 当指定 NX 或 XX 参数时,只在字段不存在或者已存在时才设置值。
HSET user:1001 username "alice" NX
批量移除字段的TTL
- HPERSIST key field [field …]
- 移除哈希 key 中一个或多个字段的生存时间(TTL)。
HPERSIST user:1001 username
列表(List)
底层设计原理
List数据结构是一个双向链表(双端链表)实现的,支持在列表的两端进行快速的插入(push)和删除(pop)操作。这种实现保证了在头部和尾部进行插入和删除的时间复杂度都是O(1)
图例
// TODO
列表使用方法
列表头插
- LPUSH key value [value …]
- 将一个或多个值插入到列表头部。
LPUSH mylist "world"
LPUSH mylist "hello"
列表尾插
- RPUSH key value [value …]
- 将一个或多个值插入到列表尾部。
RPUSH mylist "foo"
RPUSH mylist "bar"
移除头部元素
- LPOP key
- 移除并返回列表头部的元素。
LPOP mylist
移除尾部元素
- RPOP key
- 移除并返回列表尾部的元素。
RPOP mylist
获取指定索引元素
- LINDEX key index
- 返回列表中指定索引位置的元素。
LINDEX mylist 0
获取列表长度
- LLEN key
- 返回列表的长度。
LLEN mylist
获取指定范围元素
- LRANGE key start stop
- 返回列表中指定范围内的元素。
LRANGE mylist 0 2
移除指定数量元素
- LREM key count value
- 移除列表中指定数量的元素。
LREM mylist 2 "hello"
设置指定索引元素
- LSET key index value
- 设置列表中指定索引位置的元素值。
LSET mylist 1 "new_value"
裁剪指定范围元素
- LTRIM key start stop
- 对列表进行裁剪,保留指定范围内的元素。
LTRIM mylist 0 2
阻塞模式头移
- BLPOP key [key …] timeout
- 从列表头部弹出元素,如果列表为空则阻塞并等待元素的到来。
BLPOP mylist 10
阻塞模式尾移
- BRPOP key [key …] timeout
- 从列表尾部弹出元素,如果列表为空则阻塞并等待元素的到来。
BRPOP mylist 10
指定元素位置插入元素
- LINSERT key BEFORE|AFTER pivot value
- 在列表中指定元素 pivot 的前面或后面插入元素 value。
LINSERT mylist BEFORE "world" "new_value"
集合(Set)
底层设计原理
- 每个集合成员(如 member1, member2, member3)被映射到哈希表的一个桶中。
- 桶内的数据结构可以是链表或其他形式,用于处理哈希冲突,但每个键值对的键都是成员的值,值则是一个常量(NULL)。
- Set 数据结构非常适合用于存储唯一值的场景,例如标签、用户 ID 等,并且能够通过交集、并集、差集等操作,支持更复杂的数据处理需求
图例
// TODO
集合使用方法
添加成员
- SADD key member [member …]
- 向集合添加一个或多个成员。
SADD myset "member1"
SADD myset "member2"
移除成员
- SREM key member [member …]
- 从集合中移除一个或多个成员。
SREM myset "member1"
获取所有成员
- SMEMBERS key
- 返回集合中的所有成员。
SMEMBERS myset
检查是否为集合成员
- SISMEMBER key member
- 检查 member 是否是集合 key 的成员。
SISMEMBER myset "member1"
获取集合元素数量
- SCARD key
- 返回集合的基数(集合中元素的数量)。
SCARD myset
获取移除的成员
- SPOP key [count]
- 移除并返回集合中的一个或多个随机元素。
SPOP myset
获取指定数量成员
- SRANDMEMBER key [count]
- 返回集合中一个或多个随机元素(不移除元素)。
SRANDMEMBER myset 2
转移集合成员
- SMOVE source destination member
- 将 member 从 source 集合移动到 destination 集合。
SMOVE myset myotherset "member1"
返回集合交集
- SINTER key [key …]
- 返回多个集合的交集。
SINTER myset myotherset
获取集合并集
- SUNION key [key …]
- 返回多个集合的并集。
SUNION myset myotherset
获取集合差集
- SDIFF key [key …]
- 返回多个集合的差集。
SDIFF myset myotherset
计算并保存集合交集
- SINTERSTORE destination key [key …]
- 计算多个集合的交集,并将结果存储到新的集合中。
SINTERSTORE newset myset1 myset2
计算并保存集合并集
- SUNIONSTORE destination key [key …]
- 计算多个集合的并集,并将结果存储到新的集合中。
SUNIONSTORE newset myset1 myset2
计算并保存集合差集
- SDIFFSTORE destination key [key …]
- 计算多个集合的差集,并将结果存储到新的集合中。
SDIFFSTORE newset myset1 myset2
有序集合(Sorted Set)
底层设计原理
有序集合是一种特殊的数据结构,它类似于集合(Set),但每个成员都关联了一个分数(score),该分数用于对成员进行排序。有序集合的底层设计原理主要涉及两个核心点:跳跃表(SkipList)和哈希表(Hash Table)。
跳跃表(Skip List)
跳跃表是一种数据结构,用于在有序元素的集合中快速查找某个元素。它通过层级结构来实现快速查找,每一层都是元素的一个有序子集,最底层包含所有元素。在跳跃表中,每个节点包含一个指向下一个节点的指针,以及一个指向同一层下一个节点的指针。
在Redis中,有序集合的底层实现就是通过跳跃表来实现的。每个成员在跳跃表中都是一个节点,节点中包含了成员本身的值和分数,以及指向下一个节点和下一层节点的指针。通过跳跃表,Redis可以实现有序集合的快速查找和排序功能。
哈希表(Hash Table)
除了跳跃表,有序集合在底层还使用了哈希表来存储成员和分数之间的映射关系。哈希表是一种以键值对形式存储数据的数据结构,可以快速查找和访问数据。
在Redis中,有序集合使用哈希表来存储成员和分数之间的映射关系,这样可以在跳跃表中快速定位到某个成员的节点,然后通过哈希表找到成员对应的分数。哈希表的使用使得在有序集合中查找某个成员的分数变得高效。
图例
// TODO
有序集合使用方法
添加成员
- ZADD key score member [score member …]
- 将一个或多个成员及其对应的分数添加到有序集合中。
ZADD myset 1 "member1" 2 "member2"
删除成员
- ZREM key member [member …]
- 从有序集合中删除一个或多个成员。
ZREM myset "member1"
查询成员个数
- ZCARD key
- 获取有序集合中成员的数量。
ZCARD myset
查询成员的分数
- ZSCORE key member
- 获取指定成员的分数。
ZSCORE myset "member1"
按排名查询成员
- ZRANK key member
- 获取指定成员在有序集合中的排名(从0开始)。
ZRANK myset "member1"
按分数范围查询成员
- ZRANGE key start stop [WITHSCORES]
- 按分数从小到大的顺序,获取指定范围内的成员。
ZRANGE myset 0 1 WITHSCORES
按分数范围删除成员
- ZREMRANGEBYSCORE key min max
- 删除分数在指定范围内的成员。
ZREMRANGEBYSCORE myset 0 5
增加成员的分数
- ZINCRBY key increment member
- 给指定成员的分数增加增量increment。
ZINCRBY myset 2 "member1"
查看排名范围内的成员和分数
- ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
- 按分数范围获取成员及其分数,支持分页。
ZRANGEBYSCORE myset 0 10 WITHSCORES LIMIT 0 2
逆序按排名查询成员
- ZREVRANK key member
- 获取指定成员在有序集合中的逆序排名(从0开始)。
ZREVRANK myset "member1"
逆序按分数范围查询成员
- ZREVRANGE key start stop [WITHSCORES]
- 按分数从大到小的顺序,获取指定范围内的成员。
ZREVRANGE myset 0 1 WITHSCORES
删除排名范围内的成员
- ZREMRANGEBYRANK key start stop
- 删除排名在指定范围内的成员。
ZREMRANGEBYRANK myset 0 1
获取有序集合中在字典范围内成员数量
- ZLEXCOUNT key min max
- 获取有序集合中在字典范围内(字典区间使用"-“和”+"表示)的成员数量。
ZLEXCOUNT myset "-" "+"
按字典范围查询成员
- ZRANGEBYLEX key min max [LIMIT offset count]
- 按字典范围获取成员。
ZRANGEBYLEX myset "-" "[m" LIMIT 0 2
按字典范围删除成员
- ZREMRANGEBYLEX key min max
- 删除字典范围内的成员。
ZREMRANGEBYLEX myset "-" "[m"
求集合的并集
- ZUNIONSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
- 计算给定有序集合的并集,并将结果存储在新的有序集合中。
ZUNIONSTORE dest 2 set1 set2 WEIGHTS 1 2
求集合的交集
- ZINTERSTORE destination numkeys key [key …] [WEIGHTS weight [weight …]] [AGGREGATE SUM|MIN|MAX]
- 计算给定有序集合的交集,并将结果存储在新的有序集合中。
ZINTERSTORE dest 2 set1 set2
求集合的并集和
- 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
位图使用方法
设置位图中的位
- SETBIT key offset value
- 将位图key中偏移量为offset的位的值设置为value(0或1)。
SETBIT mybitmap 0 1 # 将mybitmap中偏移量为0的位设置为1
获取位图中的位
- GETBIT key offset
- 获取位图key中偏移量为offset的位的值。
GETBIT mybitmap 0 # 获取mybitmap中偏移量为0的位的值
获取位图的长度
- BITCOUNT key [start end]
- 统计位图key中值为1的位的数量。
BITCOUNT mybitmap # 统计mybitmap中所有值为1的位的数量
求位图之间的逻辑操作
- BITOP operation destkey key [key …]
- 对一个或多个位图执行位运算,并将结果保存到destkey中。operation可以是AND、OR、XOR、NOT等。
BITOP AND destbitmap mybitmap1 mybitmap2 # 将mybitmap1和mybitmap2按位进行AND操作,并将结果存储到destbitmap中
设置位图的长度
- BITFIELD key [GET type offset] [SET type offset value]
- 对位图进行更加复杂的操作,可以用于获取和设置指定偏移量的位。
BITFIELD mybitmap GET u4 0 # 获取mybitmap中偏移量为0的4位无符号整数
BITFIELD mybitmap SET u4 0 10 # 设置mybitmap中偏移量为0的4位无符号整数为10
获取位图指定范围内的所有位
- BITFIELD key [GET type offset] [GET type offset] …
- 可以一次性获取多个不同偏移量的位的值。
BITFIELD mybitmap GET u4 0 GET u4 4 # 获取mybitmap中偏移量0和4的4位无符号整数
设置位图指定范围内的所有位
- BITFIELD key [SET type offset value] [SET type offset value] …
- 可以一次性设置多个不同偏移量的位的值。
BITFIELD mybitmap SET u4 0 10 SET u4 4 5 # 设置mybitmap中偏移量0和4的4位无符号整数分别为10和5
获取位图指定范围内的所有位,并对它们进行位运算
- BITFIELD key [GET type offset] [GET type offset] … [operation type] [operation value]
- 可以在获取位的同时,对获取到的位进行位运算。
BITFIELD mybitmap GET u4 0 GET u4 4 OR 5 # 获取mybitmap中偏移量0和4的4位无符号整数,并对它们进行OR运算(假设5是一个值)
超级日志结构(HyperLogLog)
HyperLogLog(简称HLL)是一种用于近似计数的数据结构,主要用于解决集合的基数估计问题。它可以在极小的内存消耗下,对大型数据集合的基数进行估计
底层设计原理
1. 基数估计:
- HyperLogLog利用一种概率性算法来估计集合的基数(即不重复元素的数量),它的基本原理是通过对数据进行哈希映射,统计哈希值前导0的个数来估计基数。
2. 哈希函数:
- HyperLogLog使用一种特殊的哈希函数,将元素映射到一个固定长度的二进制字符串中。这个哈希函数的设计对结果的准确性有很大影响。
3. 桶与计数器:
- HyperLogLog将哈希值分配到多个桶中,并在每个桶中维护一个计数器,用于记录对应桶中哈希值前导0的最大长度。
4. 合并计数器:
- 当多个HyperLogLog数据结构需要合并时,采用的是取各个桶中计数器的最大值作为合并后的结果,这样可以保证合并后的结果不会低于原始数据结构的估计值。
应用场景
HyperLogLog在Redis中的应用场景包括但不限于:
1. 独立用户数量统计:
- 可以用于统计网站或应用的独立访客数量,以便进行用户行为分析和精准营销。
2. 基数估计:
- 可以用于统计大数据集合的基数,例如网页访问量、搜索关键词数量等。
3. 数据去重:
- 可以用于判断数据集合中是否存在重复元素,帮助进行数据去重处理。
图例
// TODO
HyperLogLog使用方法
添加元素到HyperLogLog
- PFADD key element [element …]
- 将一个或多个元素添加到HyperLogLog中。
PFADD myloglog user1 user2 user3 # 将user1, user2, user3添加到名为myloglog的HyperLogLog中
获取HyperLogLog的基数估计
- PFCOUNT key [key …]
- 获取一个或多个HyperLogLog的基数估计值。
PFCOUNT myloglog # 获取名为myloglog的HyperLogLog的基数估计值
合并多个HyperLogLog
- 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使用方法
添加地理位置及其经纬度信息
- 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
计算两个地理位置之间的距离
- GEODIST key member1 member2 [unit]
- key: GeoSpatial的键名。
- member1, member2: 要计算距离的两个地理位置的名称或标识符。
- unit (可选): 距离单位,默认为m(米),可选km(千米)、mi(英里)、ft(英尺)。
GEODIST cities Beijing Shanghai km
获取地理位置的Geohash值
- GEOHASH key member [member …]
- key: GeoSpatial的键名。
- member: 要获取Geohash的地理位置的名称或标识符。
GEOHASH cities Beijing Shanghai
获取地理位置的经纬度信息
- GEOPOS key member [member …]
- key: GeoSpatial的键名。
- member: 要获取经纬度的地理位置的名称或标识符。
GEOPOS cities Beijing
根据指定地理位置查询范围内的其他地理位置
- 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
根据指定的地理位置成员查询范围内的其他地理位置
- GEORADIUSBYMEMBER key member radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
参数与GEORADIUS类似,不同之处在于使用成员名称而不是经纬度来指定中心点。
GEORADIUSBYMEMBER cities Beijing 1000 km WITHCOORD
从节点上执行地理位置查询
- 只读版本的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
统计给定范围内的地理位置数量
- GEOCOUNT key longitude latitude radius unit
- key: GeoSpatial的键名。
- longitude, latitude: 中心点的经度和纬度。
- radius: 统计范围的半径。
- unit: 单位,可以是m(米)、km(千米)、mi(英里)、ft(英尺)。
GEOCOUNT cities 116.405285 39.904989 1000 km
通过复杂的过滤器查询符合条件的地理位置
- 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结构中
- 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中添加新条目:
- 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 未完待续