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

88 阅读11分钟

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

🚀 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) 0
127.0.0.1:6379> setbit user:sign:1000 20240102 1
(integer) 0
127.0.0.1:6379> getbit user:sign:1000 20240101
(integer) 1
127.0.0.1:6379> getbit user:sign:1000 20240103
(integer) 0
127.0.0.1:6379> 
127.0.0.1:6379> bitcount user:sign:1000 # 获取用户签到次数
(integer) 2
127.0.0.1:6379> 

对于统计在线用户数量:

127.0.0.1:6379> setbit 20240101 1000 1
(integer) 0
127.0.0.1:6379> setbit 20240101 1001 1
(integer) 0
127.0.0.1:6379> setbit 20240101 1002 1
(integer) 0
127.0.0.1:6379> bitcount 20240101
(integer) 3
127.0.0.1:6379> 

geo 空间类型

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

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

    • 一种将多维数据映射到一维的方法,通过将二维的经纬度坐标编码为一维的z值
    • 这种空间填充曲线能保持地理位置的邻近性
    • 例如:相邻的地理位置在Z阶曲线上也会保持临近关系
  2. Base32编码

    • 采用32个字符(0-9和b-z)的编码方案
    • 将Z阶曲线计算出的数值转换为更紧凑的字符串表示
    • 例如:一个经纬度坐标可能被编码为"wx4g0b"这样的字符串
  3. Geohash算法

    • 结合了Z阶曲线和Base32编码的地理位置编码方法
    • 将地球表面划分为网格,每个网格用唯一的字符串标识
    • 前缀越长的geohash表示的地理范围越小
    • 例如:"wx4g0"比"wx4"表示更精确的位置范围

实际应用场景包括:

  • 附近的人或地点查询(如查找5公里内的餐厅)
  • 地理位置缓存(如存储用户最新位置)
  • 地理围栏(如判断用户是否进入特定区域)

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

Z阶曲线

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

基本原理

  1. 坐标转换

    • 首先将X轴和Y轴的十进制坐标分别转换为二进制形式
    • 例如:坐标(3,5)转换为二进制就是(011,101)
  2. 交叉编码

    • 将两个二进制数进行位交叉操作(位交织)
    • 从最低位开始,交替取X和Y的二进制位
    • 例如:(3,5) -> 0(来自X)1(来自Y)1(来自X)0(来自Y)1(来自X)1(来自Y) -> 011011
  3. 编码转换

    • 将得到的交叉二进制码转换为十进制
    • 011011(二进制) = 27(十进制)

具体步骤示例

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

  1. 转换为二进制:

    • X:01,01,10,10,11,11,100,100
    • Y:10,11,10,11,10,11,10,11
  2. 进行位交叉:

    • (1,2) -> 0(X)1(Y)1(X)0(Y) -> 0110
    • (1,3) -> 0(X)1(Y)1(X)1(Y) -> 0111
    • (2,2) -> 1(X)0(Y)0(X)0(Y) -> 1000
    • 以此类推...
  3. 转换为十进制:

    • 0110=6, 0111=7, 1000=8, 1001=9, 1010=10, 1011=11, 1100=12, 1101=13
  4. 按编码值排序连接:

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

应用场景

  1. 空间索引

    • 在数据库中对多维数据进行索引
    • 例如GIS系统中的地图数据存储
  2. 图像处理

    • 用于图像压缩和存储
    • 将二维像素阵列转换为一维序列
  3. 并行计算

    • 优化数据访问的局部性
    • 提高缓存命中率
  4. 科学计算

    • 处理多维数据集
    • 如流体动力学模拟中的网格处理

特点

  1. 保持局部性

    • 空间上邻近的点在一维编码上也较为接近
  2. 简单高效

    • 编码解码过程只需要位操作
  3. 可扩展性

    • 可以扩展到更高维度(3D,4D等)
  4. 非完美连续性

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

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

Base32编码

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

编码原理

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

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

  3. 补位处理

    • 如果最后不足5位,在右侧补0使达到5位
    • 如果原始数据字节数不是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) 3
127.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>