写点什么

大数据 -41 Redis 类型集合 (2) bitmap geohash Z 阶曲线 Base32

作者:武子康
  • 2025-07-15
    美国
  • 本文字数:4222 字

    阅读完需:约 14 分钟

大数据-41 Redis 类型集合(2) bitmap geohash Z阶曲线 Base32

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI 篇持续更新中!(长期更新)

AI 炼丹日志-30-新发布【1T 万亿】参数量大模型!Kimi‑K2 开源大模型解读与实践,持续打造实用 AI 工具指南!📐🤖

💻 Java 篇正式开启!(300 篇)

目前 2025 年 07 月 10 日更新到:Java-68 深入浅出 分布式服务 Netty 实现自定义 RPC 附详细代码 MyBatis 已完结,Spring 已完结,Nginx 已完结,Tomcat 已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300 篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT 案例 详解


章节内容

上一节我们完成了如下的内容:


  • string 类型

  • list 类型

  • set 类型

  • sortedset (zset) 类型

  • hash 类型

背景介绍

这里是三台公网云服务器,每台 2C4G,搭建一个大数据的学习环境,供我学习。之前已经在 VM 虚拟机上搭建过一次,但是没留下笔记,这次趁着前几天薅羊毛的 3 台机器,赶紧尝试在公网上搭建体验一下。


  • 2C4G 编号 h121

  • 2C4G 编号 h122

  • 2C2G 编号 h123


bitmap 类型

基本概念

bitmap(位图)是一种使用二进制位(bit)来表示数据的数据结构,每个 bit 位都可以表示某个元素对应的值或状态。在 bitmap 中,key 通常对应元素本身的标识符,而 value 则是由多个 bit 位组成的二进制串。

工作原理

bitmap 通过以下方式工作:


  1. 每个元素对应 bitmap 中的一个 bit 位

  2. 该 bit 位的值(0 或 1)表示元素的状态或存在性

  3. 多个 bit 位可以组合成更复杂的状态表示

存储优势

bitmap 具有显著的存储空间优势:


  • 传统方式存储 10,000 个用户 ID 需要约 40KB(假设每个 ID4 字节)

  • 使用 bitmap 存储同样规模的用户在线状态仅需约 1.25KB(10,000 位÷8 位/字节)

  • 存储空间节省可达 97%以上

典型应用场景

  1. 用户签到系统:每个用户每天对应一个 bit 位,1 表示已签到

  2. 活跃用户统计:记录用户 ID 对应的活跃状态

  3. 布隆过滤器:实现高效的元素存在性检测

  4. 权限控制:用不同 bit 位表示不同权限

  5. 去重统计:快速统计不重复元素数量

操作示例

常见的 bitmap 操作包括:


  • SETBIT:设置某个 bit 位的值

  • GETBIT:获取某个 bit 位的值

  • BITCOUNT:统计值为 1 的 bit 位数

  • BITOP:对多个 bitmap 进行位运算(AND/OR/XOR/NOT)

实现方式

不同系统实现 bitmap 的方式:


  1. Redis:提供原生 bitmap 数据类型和相关操作命令

  2. MySQL:可以使用 BINARY 或 VARBINARY 类型模拟

  3. 编程语言:Java 的 BitSet,C++的 bitset 等

注意事项

使用 bitmap 时需要考虑:


  1. 稀疏数据可能导致空间浪费

  2. 不支持删除操作(只能将 bit 位置 0)

  3. 大 bitmap 可能需要分片处理

  4. 位操作可能带来额外的 CPU 开销


bitmap 特别适合处理大规模布尔值集合的场景,在保证高性能的同时显著降低存储需求。

常见操作

应用场景

  • 用户每月签到

  • 统计活跃用户

  • 用户在线状态查询


统计用户签到的信息,可以通过这种方法:


127.0.0.1:6379> setbit user:sign:1000 20240101 1 # bitmap 1是签到 0是没有(integer) 0127.0.0.1:6379> setbit user:sign:1000 20240102 1(integer) 0127.0.0.1:6379> getbit user:sign:1000 20240101(integer) 1127.0.0.1:6379> getbit user:sign:1000 20240103(integer) 0127.0.0.1:6379> 127.0.0.1:6379> bitcount user:sign:1000 # 获取用户签到次数(integer) 2127.0.0.1:6379> 
复制代码


对于统计在线用户数量:


127.0.0.1:6379> setbit 20240101 1000 1(integer) 0127.0.0.1:6379> setbit 20240101 1001 1(integer) 0127.0.0.1:6379> setbit 20240101 1002 1(integer) 0127.0.0.1:6379> bitcount 20240101(integer) 3127.0.0.1:6379> 
复制代码

geo 空间类型

Redis 的 geo 空间类型是专门设计用来高效处理地理位置信息的特殊数据类型,它实现了一套完整的基于地理位置查询的系统。该功能主要通过以下核心技术实现:


  1. Z 阶曲线(Z-order curve)

  2. 一种将多维数据映射到一维的方法,通过将二维的经纬度坐标编码为一维的 z 值

  3. 这种空间填充曲线能保持地理位置的邻近性

  4. 例如:相邻的地理位置在 Z 阶曲线上也会保持临近关系

  5. Base32 编码

  6. 采用 32 个字符(0-9 和 b-z)的编码方案

  7. 将 Z 阶曲线计算出的数值转换为更紧凑的字符串表示

  8. 例如:一个经纬度坐标可能被编码为"wx4g0b"这样的字符串

  9. Geohash 算法

  10. 结合了 Z 阶曲线和 Base32 编码的地理位置编码方法

  11. 将地球表面划分为网格,每个网格用唯一的字符串标识

  12. 前缀越长的 geohash 表示的地理范围越小

  13. 例如:"wx4g0"比"wx4"表示更精确的位置范围


实际应用场景包括:


  • 附近的人或地点查询(如查找 5 公里内的餐厅)

  • 地理位置缓存(如存储用户最新位置)

  • 地理围栏(如判断用户是否进入特定区域)


Redis 提供了 GEOADD、GEODIST、GEORADIUS 等命令来操作 geo 数据类型,支持高效的地理位置存储和查询。例如,可以用 GEOADD 添加位置点,然后用 GEORADIUS 查询指定半径范围内的所有点。

Z 阶曲线

Z 阶曲线(Z-order curve)是一种将多维空间映射到一维空间的空间填充曲线,由数学家 Guy Macdonald Morton 在 1966 年提出。这种曲线因其独特的"Z"字形遍历方式而得名,在计算机科学和地理信息系统等领域有广泛应用。

基本原理

  1. 坐标转换

  2. 首先将 X 轴和 Y 轴的十进制坐标分别转换为二进制形式

  3. 例如:坐标(3,5)转换为二进制就是(011,101)

  4. 交叉编码

  5. 将两个二进制数进行位交叉操作(位交织)

  6. 从最低位开始,交替取 X 和 Y 的二进制位

  7. 例如:(3,5) -> 0(来自 X)1(来自 Y)1(来自 X)0(来自 Y)1(来自 X)1(来自 Y) -> 011011

  8. 编码转换

  9. 将得到的交叉二进制码转换为十进制

  10. 011011(二进制) = 27(十进制)

具体步骤示例

假设我们有以下二维坐标点:(1,2)、(1,3)、(2,2)、(2,3)、(3,2)、(3,3)、(4,2)、(4,3)


  1. 转换为二进制:

  2. X:01,01,10,10,11,11,100,100

  3. Y:10,11,10,11,10,11,10,11

  4. 进行位交叉:

  5. (1,2) -> 0(X)1(Y)1(X)0(Y) -> 0110

  6. (1,3) -> 0(X)1(Y)1(X)1(Y) -> 0111

  7. (2,2) -> 1(X)0(Y)0(X)0(Y) -> 1000

  8. 以此类推...

  9. 转换为十进制:

  10. 0110=6, 0111=7, 1000=8, 1001=9, 1010=10, 1011=11, 1100=12, 1101=13

  11. 按编码值排序连接:

  12. 6,7,8,9,10,11,12,13

应用场景

  1. 空间索引

  2. 在数据库中对多维数据进行索引

  3. 例如 GIS 系统中的地图数据存储

  4. 图像处理

  5. 用于图像压缩和存储

  6. 将二维像素阵列转换为一维序列

  7. 并行计算

  8. 优化数据访问的局部性

  9. 提高缓存命中率

  10. 科学计算

  11. 处理多维数据集

  12. 如流体动力学模拟中的网格处理

特点

  1. 保持局部性

  2. 空间上邻近的点在一维编码上也较为接近

  3. 简单高效

  4. 编码解码过程只需要位操作

  5. 可扩展性

  6. 可以扩展到更高维度(3D,4D 等)

  7. 非完美连续性

  8. 有时会在某些位置产生较大的跳跃


Z 阶曲线为处理多维数据提供了一种简单而有效的方法,特别适合需要将多维空间映射到一维存储或处理的场景。通过这种转换,可以保持数据的一定空间局部性,从而提高各种算法的效率。


Base32 编码

Base32 是一种二进制到文本的编码方案,主要用于将任意二进制数据转换为可打印的 ASCII 字符序列。这种编码机制在多种场景中得到应用,如数据存储、URL 安全传输、数字证书等。

编码原理

  1. 数据分组:将原始二进制数据按照每 5 个位(bit)为一组进行分割。如果数据总位数不是 5 的倍数,需要进行补位处理。

  2. 字符映射:每组 5 位二进制值(范围 0-31)对应一个 Base32 字母表中的字符。标准的 Base32 字母表包含以下 32 个字符:


   ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
复制代码


  1. 补位处理

  2. 如果最后不足 5 位,在右侧补 0 使达到 5 位

  3. 如果原始数据字节数不是 5 的倍数,需要在编码结果末尾添加相应数量的"="作为填充字符

编码示例

例如,编码二进制数据"01100011"(ASCII 字符"c")的过程:


  1. 补位为"01100 01100"(最后 3 位补两个 0)

  2. 分别对应十进制 12 和 12

  3. 查字母表得到'M'和'M'

  4. 由于原始只有 1 字节(8 位),需要补"="直到总长度是 8 的倍数

  5. 最终编码结果为"MM======"

应用场景

  1. DNS 系统:DNSSEC 使用 Base32 编码处理域名密钥

  2. OTP 认证:双因素认证中的一次性密码常用 Base32 编码

  3. 文件哈希:某些系统使用 Base32 表示文件校验值

  4. URL 安全传输:比 Base64 更适合 URL 传输,因为它不使用易混淆字符

与其他编码比较

相对于 Base64:


  • 优点:不使用大小写字母,避免大小写敏感问题;不使用特殊字符,更适合 URL 传输

  • 缺点:数据膨胀率更高(原始数据增加 60%,而 Base64 为 33%)


相对于十六进制:


  • 更紧凑(5 位表示一个字符,而不是 4 位)

  • 可读性更好


geohash 算法

GeoHash 是一种地理位置信息编码方法,经过 GeoHash 映射后,地球上任意位置的经纬度就可以编码为一个较短的字符串。Redis 中的经纬度使用 52 位的整数进行编码,放进 ZSet 中,Score 是 GeoHash 的 52 位整数值。在使用 Geo 查询时,其内部对应的操作其实是 ZSet 操作,通过 ZSet 的 Score 排序就可以得到附近的坐标。

常见操作

应用场景

  • 记录地理位置

  • 计算距离

  • 附近的人


127.0.0.1:6379> geoadd user:addr 111.11 44.44 ww 112.22 43.33 kk 111.33 33.44 zz # 添加坐标地址(integer) 3127.0.0.1:6379> geohash user:addr ww zz # 获取geo编码结果1) "wrzhb65cf80"2) "wmznjrs4150"127.0.0.1:6379> geopos user:addr ww # 获取坐标1) 1) "111.11000150442123413"   2) "44.43999999347073526"127.0.0.1:6379> geodist user:addr ww zz # 获取坐标的距离"1223636.0233"127.0.0.1:6379> geodist user:addr ww kk"152182.4560"127.0.0.1:6379> geodist user:addr ww kk km # 获取坐标的距离 KM"152.1825"127.0.0.1:6379> 
复制代码

Stream 类型

stream 是 5.0 版本后新增的数据结构,可用于持久化消息队列。


  • 消息 ID 序列化生成

  • 消息遍历

  • 消息的阻塞和非阻塞读取

  • 消息的分组消费

  • 未完成消息的处理

  • 消息队列监控


每个 Stream 都有唯一的名称,它就是 Redis 的 Key。

常见操作

应用场景

消息队列


127.0.0.1:6379> xadd topic:001 * name wzk age 18 # topic 写入数据"1720514702080-0"127.0.0.1:6379> xadd topic:001 * name ww age 20"1720514716118-0"127.0.0.1:6379> xadd topic:001 * name zz age 20"1720514722040-0"127.0.0.1:6379> xadd topic:001 * name kk age 10"1720514728559-0"127.0.0.1:6379> xrange topic:001 - + # 遍历topic 查看当前的数据1) 1) "1720514702080-0"   2) 1) "name"      2) "wzk"      3) "age"      4) "18"2) 1) "1720514716118-0"   2) 1) "name"      2) "ww"      3) "age"      4) "20"3) 1) "1720514722040-0"   2) 1) "name"      2) "zz"      3) "age"      4) "20"4) 1) "1720514728559-0"   2) 1) "name"      2) "kk"      3) "age"      4) "10"127.0.0.1:6379> xread COUNT 1 streams topic:001 0 # 消费topic的数据1) 1) "topic:001"   2) 1) 1) "1720514702080-0"         2) 1) "name"            2) "wzk"            3) "age"            4) "18"127.0.0.1:6379> 
复制代码


发布于: 刚刚阅读数: 2
用户头像

武子康

关注

永远好奇 无限进步 2019-04-14 加入

Hi, I'm Zikang,好奇心驱动的探索者 | INTJ / INFJ 我热爱探索一切值得深究的事物。对技术、成长、效率、认知、人生有着持续的好奇心和行动力。 坚信「飞轮效应」,相信每一次微小的积累,终将带来深远的改变。

评论

发布
暂无评论
大数据-41 Redis 类型集合(2) bitmap geohash Z阶曲线 Base32_Java_武子康_InfoQ写作社区