1.Geospecial
1.介绍,是什么
Redis的Geospatial(地理空间)功能是一组专门用于处理地理位置数据的命令,基于Redis的有序集合(Sorted Set)数据结构实现。 它允许你存储地理坐标(经度和纬度),并执行各种基于位置的计算和查询。
# GEOADD:添加一个或多个地理空间位置到指定的key中
GEOADD cities 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
# GEOPOS:获取一个或多个位置的地理坐标
GEOPOS cities "Palermo" "Catania"
# GEODIST:计算两个位置之间的距离
GEODIST cities "Palermo" "Catania" km
# GEORADIUS:以给定的经纬度为中心,查找指定半径内的位置
GEORADIUS cities 15 37 100 km
# GEORADIUSBYMEMBER:以给定的成员为中心,查找指定半径内的位置
GEORADIUSBYMEMBER cities "Palermo" 200 km
# GEOHASH:获取一个或多个位置的Geohash值
GEOHASH cities "Palermo"
2.应用场景
- 附近的人/地点搜索:
- 查找用户周围5公里内的餐厅
- 显示附近的加油站或ATM机
- 社交应用中查找附近的朋友
- 配送和物流
- 查找用户周围5公里内的餐厅
- 显示附近的加油站或ATM机
3.底层的数据结构
Geospecial的底层数据结构是Zset(有序集合)
所有 GEO 命令实际上都是操作一个特殊的 Sorted Set:
Key 对应 Sorted Set 的名称
Member 是地点名称(如"北京"、"上海")
Score 是经过 Geohash 编码后的 52 位整数值
Redis 使用 Geohash 将二维的经纬度转换为一维的分数(score):
经纬度标准化:
经度范围 [-180,180] → 映射到 [0,2^32]
纬度范围 [-90,90] → 映射到 [0,2^32]
交替编码:
将经度和纬度的二进制位交替排列
例如:经度比特1 → 纬度比特1 → 经度比特2 → 纬度比特2 → ...
生成52位整数:
Redis 使用 52 位整数表示(对应 double 类型的精度)
26位来自经度,26位来自纬度
例如存储坐标(经度13.361389,纬度38.115556):
1.标准化坐标
2.转换为二进制表示
3.交替组合经度和纬度比特
4.生成最终的52位整数作为Sorted Set的score
为什么选择这种设计?
高效的范围查询:
Geohash 具有"前缀相似性":附近的位置有相似的hash值
使用ZRANGEBYSCORE可以高效查询附近位置
内存效率: 相比单独存储经纬度,使用一个score更节省空间 复用已有的Sorted Set数据结构
计算优化: 距离计算时可以直接解码出原始坐标 使用Haversine公式计算实际距离
2.Hyperloglog
1.介绍,是什么
HyperLogLog 是 Redis 提供的一种概率性基数统计数据结构,用于高效地估计一个集合中不重复元素的数量(基数统计)。
核心特点:
极低的内存占用:统计上亿级别的基数只需约 12KB 内存
固定内存消耗:无论统计多少元素,内存占用基本恒定
概率性算法:有约 0.81% 的标准误差
支持合并:多个 HyperLogLog 可以合并计算总基数
2.应用场景
1.大规模的uv统计
- 网站uv统计
- 文章阅读量
- 广告曝光 2.大数据去重
- 搜索引擎:统计不同搜索查询的数量
- 电商平台:统计不同搜索关键词的数量
- 日志分析:统计不同错误类型的出现次数
3.底层的数据结构
有两种编码格式来优化不同场景下的存储
基本工作流程
1.哈希计算:对输入元素执行64位哈希(Redis使用MurmurHash64A)如果是稀疏矩阵,则使用32位哈希
2.寄存器选择:前14位作为寄存器索引(2^14=16384个寄存器)
3.前导零计数:计算剩余50位中第一个1出现的位置
4.寄存器更新:如果新值大于当前寄存器值,则更新
3.Bitmap
1.介绍,是什么
Redis的Bitmap实际上是基于String类型实现的,将字符串视为一个由二进制位组成的数组,每个位置可以存储0或1,用来处理二进制的数据
2.应用场景
- 横向统计 某一个用户存储多个状态值(不推荐)
签到场景为例,用户的id为key,时间为offset偏移值,value为1代表签到,0代表未签到,但是这样维护起来很麻烦,每个用户都要维护一个bitmap - 纵向统计 每个key值作为业务标识,offset为用户的uid (推荐)
比如短视频平台,对每个视频的标识为key,然后uid作为偏移量,value为1代表该用户喜欢该视频,0代表不喜欢该视频 - 实现一个简单的布隆过滤器(利用位数组的特性)
3.底层的数据结构
底层数据结构是String
假设执行
SETBIT mybitmap 10 1
内存中的实际存储情况
字节偏移量:0 1
bit布局: 00000000 01000000
bit索引: 0-7 8-15
第10位被设为1
自动扩展机制:
当设置的offset超过当前字符串长度时,Redis会自动扩展字符串并用0填充间隙
扩展是以字节为单位的,即使只设置一个bit也可能导致多个字节的分配
内存预分配:
Redis采用预分配策略减少内存重分配次数
每次扩展时可能会分配比实际需要更多的空间
稀疏位图处理:
对于非常大的offset,Redis会分配中间所有必要的字节
这可能导致稀疏位图实际占用内存比预期大
与普通String的异同
| 特性 | Bitmap视角 | String视角 |
|---|---|---|
| 存储内容 | 二进制位数组 | 字节数组 |
| 访问方式 | 按bit偏移量 | 按字节偏移量 |
| 扩展单位 | bit(实际扩展字节) | 字节 |
| 典型操作 | 位运算 | 追加、截取等 |