深入浅出Redis(十一):Redis四种高级数据结构:Geosptial、Hypeloglog、Bitmap、Bloom Filter布隆过滤器

简介: 深入浅出Redis(十一):Redis四种高级数据结构:Geosptial、Hypeloglog、Bitmap、Bloom Filter布隆过滤器

引言

Redis提供丰富的数据结构来解决各种场景下的问题,前段时间的一篇文章深入浅出Redis(一):对象与数据结构已经深入浅出的说明Redis中的常用基础对象与数据结构

本篇文章将作为那篇文章的补充,深入浅出的解析另外四种数据结构:Geospatial、Hyperloglog、Bitmap以及Bloom Filter布隆过滤器

Geospatial

Geospatial 是一种能够解决地理空间相关场景下的数据结构,它提供的命令能够容易实现两地距离、附近的人等功能

Geospatial 使用GeoHash算法,底层实现使用zset对象 因此也可以使用Zset命令

geoadd 添加

geoadd key 经度 纬度 名称

将指定的地理空间位置(纬度、经度、名称)添加到指定的key中(可添加多个)

有效的经度从-180度到180度

有效的纬度从-85.05112878度到85.05112878度

超出上述经纬度会报错

 127.0.0.1:6379> geoadd china:city 116.23 40.22 beijing 
 (integer) 1
 127.0.0.1:6379> geoadd china:city 121.48 31.40 shanghai 113.88 22.55 shenzhen 104.10 30.65 chendu
 (integer) 3

geopos 获取

geopos key 成员名...

从key里返回所有给定位置元素的位置(经度和纬度)

 127.0.0.1:6379> geopos china:city beijing shenzhen
 1) 1) "116.23000055551528931"
    2) "40.2200010338739844"
 2) 1) "113.87999922037124634"
    2) "22.5500010475923105"

geodist 位置间的距离

geodist key member1 member2 [unit]

返回两个给定位置之间的距离

单位:(默认米):m 表示单位为米,km表示单位为千米,mi 表示单位为英里,ft 表示单位为英尺

 127.0.0.1:6379> geodist china:city beijing shenzhen
 "1977782.5112"
 127.0.0.1:6379> geodist china:city beijing shenzhen km
 "1977.7825"

georadius 以某点为中心,查找周围x半径之内的member(附近的人)

georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [count]

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素

withcoord:获得经纬度坐标

withdist:找到的元素距离中心点的距离

count:限制查到的个数

 #以经纬度 110,30为中心,半径1500km的范围内的成员
 127.0.0.1:6379> georadius china:city 110 30 1500 km 
 1) "chendu"
 2) "shenzhen"
 3) "shanghai"
 4) "beijing"
 #以经纬度 110,30为中心,半径1500km的范围内的成员 列出成员坐标经纬度
 127.0.0.1:6379> georadius china:city 110 30 1500 km withcoord
 1) 1) "chendu"
    2) 1) "104.09999996423721313"
       2) "30.6499990746355806"
 2) 1) "shenzhen"
    2) 1) "113.87999922037124634"
       2) "22.5500010475923105"
 3) 1) "shanghai"
    2) 1) "121.48000091314315796"
       2) "31.40000025319353938"
 4) 1) "beijing"
    2) 1) "116.23000055551528931"
       2) "40.2200010338739844"
  #以经纬度 110,30为中心,半径1500km的范围内的成员 列出成员坐标经纬度和成员到中心直线距离
 127.0.0.1:6379> georadius china:city 110 30 1500 km withcoord withdist
 1) 1) "chendu"
    2) "570.9717"
    3) 1) "104.09999996423721313"
       2) "30.6499990746355806"
 2) 1) "shenzhen"
    2) "914.3335"
    3) 1) "113.87999922037124634"
       2) "22.5500010475923105"
 3) 1) "shanghai"
    2) "1108.3830"
    3) 1) "121.48000091314315796"
       2) "31.40000025319353938"
 4) 1) "beijing"
    2) "1269.3568"
    3) 1) "116.23000055551528931"
       2) "40.2200010338739844"
 #以经纬度 110,30为中心,半径1500km的范围内的成员 列出成员坐标经纬度 限制只查询一个(直线距离最近的)      
 127.0.0.1:6379> georadius china:city 110 30 1500 km withcoord count 1
 1) 1) "chendu"
    2) 1) "104.09999996423721313"
       2) "30.6499990746355806"
  #以经纬度 110,30为中心,半径1500km的范围内的成员 列出成员坐标经纬度 限制只查询俩个(直线距离最近的)  
 127.0.0.1:6379> georadius china:city 110 30 1500 km withcoord count 2
 1) 1) "chendu"
    2) 1) "104.09999996423721313"
       2) "30.6499990746355806"
 2) 1) "shenzhen"
    2) 1) "113.87999922037124634"
       2) "22.5500010475923105"

georadiusbymember (与georadius类似,它是以member为中心的)

georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [count]

这个命令和 georadius命令一样, 都可以找出位于指定范围内的元素, 但是 georadiusbymember 的中心点是由给定的位置元素决定的

 #以beijing为中心 1500km为半径 查找的成员(会查到自己)
 127.0.0.1:6379> georadiusbymember china:city beijing 1500 km
 1) "shanghai"
 2) "beijing"

geohash

geohash key member...

该命令将返回11个字符的Geohash字符串

 # 深圳与成都
 127.0.0.1:6379> geohash china:city shenzhen chendu
 1) "ws0br3hgk20"
 2) "wm6n2gem1v0"
 ​
 # 东莞与深圳
 127.0.0.1:6379> geohash china:city dongguan shenzhen
 1) "ws0fuqwjpn0"
 2) "ws0br3hgk20"

将二维经纬度转化为一维字符串,如果俩个成员距离越近,字符串就会越相似

通过Zset指令来操作geo(因为geo底层实现原理就是Zset)

 127.0.0.1:6379> zrange china:city 0 -1
 1) "chendu"
 2) "shenzhen"
 3) "dongguan"
 4) "shanghai"
 5) "beijing"
 127.0.0.1:6379> zrem china:city dongguan
 (integer) 1
 127.0.0.1:6379> zrange china:city 0 -1
 1) "chendu"
 2) "shenzhen"
 3) "shanghai"
 4) "beijing"

Hyperloglog

在数据量特别大的情况下,想要统计数量可以选择用哈希表实现的set存储(能够去重),但是哈希表是空间换时间的数据结构,这种情况下会浪费大量空间

hyperloglog使用基数统计算法,用固定且少量的空间,能够实现统计计数,但缺点是有0.81%的错误率

A={1,2,4,5,6,1}

B={1,2,4,5,6}

基数=5(不重复的元素)

命令

  • 存: pfadd key element...将指定元素添加到hyperloglog
  • 读: pfcount key...统计hyperloglog中的基数数量(一个hyperloglog时)或并集数量(多个hyperloglog时)
  • 合并:pfmerge destkey sourcekey...将多个hyperloglog合并成一个hyperloglog并返回并集数量
 127.0.0.1:6379> pfadd mykey a b c d e f g 
 (integer) 1
 127.0.0.1:6379> pfcount mykey
 (integer) 7
 127.0.0.1:6379> pfadd mykey2 a b c d e f g h i j
 (integer) 1
 127.0.0.1:6379> pfcount mykey mykey2
 (integer) 10
 127.0.0.1:6379> pfmerge newkey mykey2  mykey
 OK
 127.0.0.1:6379> pfcount newkey
 (integer) 10

Bitmap

Bitmap使用位数组中的二进制来进行 状态统计 (只有 0 1)

Bitmap能够有效的大数据量下进行只有俩个状态的统计

比如:统计只有俩个状态的用户信息(活跃,不活跃。登录,未登录。打卡,未打卡)

使用

  • setbit key offset value设置key在offset处的bit值(0/1)
  • getbit key offset 获得key在offset处的bit值(0/1)
  • bitcount key 统计key中有多少位1
  • 模拟电影是否被点播情况 key->日期 offset->(电影ID)value->(0为未点播,1为点播)
  • 统计每天某部电影是否被点播 getbit 日期 电影ID
  • 统计每天有多少部电影被点播 bitcount 日期
  • 统计每周/月/年有多少部电影被点播 bitop or 每周日期 记录值后可统计每月,每年 或
  • 统计年度哪部电影没被点播 (为0时没被点播)
 127.0.0.1:6379> setbit 1130 1 1 #11月30日 1号电影 被点播
 (integer) 0
 127.0.0.1:6379> setbit 1130 2 1 #11月30日 2号电影 被点播
 (integer) 0
 127.0.0.1:6379> setbit 1130 3 1 #11月30日 3号电影 被点播
 (integer) 0
 127.0.0.1:6379> setbit 1201 4 1 #12月1日 4号电影 被点播
 (integer) 0
 127.0.0.1:6379> setbit 1201 5 1 #12月1日 5号电影 被点播
 (integer) 0
 127.0.0.1:6379> setbit 1201 1 1 #12月1日 1号电影 被点播
 (integer) 0
 ​
 127.0.0.1:6379> bitcount 1130 #11月30日 3部电影被点播
 (integer) 3
 127.0.0.1:6379> bitcount 1201 #12月1日 3部电影被点播
 (integer) 3
 127.0.0.1:6379> bitop and 1130-1201 1130 1200 #统计11月30日 与 12月1日 都点播了的电影
 (integer) 1
 127.0.0.1:6379> getbit 1130-1201 1 #2日都被点播的电影是1号电影
 (integer) 1
 127.0.0.1:6379> bitcount 1130-1201
 (integer) 1

原理

位数组使用sds来实现,sds是二进制安全的,sds存储时逆序存储位数组,逆序存储在扩容时不用修改老数据

(不了解sds的同学可以先看这篇文章深入浅出Redis(一):对象与数据结构

setbit :先计算len是否需要扩容,再计算偏移量在哪个字节上,接着计算偏移量在哪个位上,修改那个位的值并返回旧的值

getbit :计算偏移量在哪个字节上,接着计算偏移量在哪个位上,再获取

bittop :and、or、xor都是新建sds 每个字节做位操作结果放入新建sds中 ;not 则是直接取反

bitcount : 数据量小于128位时使用查表,大于128位时使用每次循环4次 swar,每次swar可以计算32位;swar是将32位分成2bit一组与01 (计算低位)再右移一位继续与运算(计算高位),再依次分为4位、8位、16位一组依次操作(jdk的integer.bitcount也是swar算法)

Bloom Filter

布隆过滤器能够使用少量的空间来判断某个元素是否存在于集合中,但存在一定的误判率(不在集合中保存元素)

布隆过滤器适合在大数据场景下,允许一定误判的快速判断元素是否存在集合中

Bloom Filter用于判断元素是否重复在集合中,不保存元素数据,节省空间,有一定误差

原理

Bloom Filter由位数组和多个hash函数组成

image.png

添加:将Key经过多个hash函数得到的索引,在位数组对应索引上设置为1

判断是否在集合中:将Key经过多个hash函数得到的索引,查看位数组对应索引上值是否为1,为1则可能存在(该索引上设置为1还有可能是添加其他Key设置的),如果值为0,那么该Key一定不存在集合中

布隆过滤器的误判率与空间大小有关,空间越小就越容易导致误判

使用

安装布隆过滤器插件

 #下载
 wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
 #解压
 tar -zxvf v1.1.1.tar.gz
 cd rebloom-1.1.1
 #编译
 make

使用 bf.add添加 bf.exists判断是否存在集合中

 127.0.0.1:6379> bf.add bloomfilter cl
 (integer) 1
 127.0.0.1:6379> bf.add bloomfilter tcl
 (integer) 1
 127.0.0.1:6379> bf.exists bloomfilter cl
 (integer) 1
 127.0.0.1:6379> bf.exists bloomfilter cl1
 (integer) 0
 127.0.0.1:6379> bf.exists bloomfilter tcl
 (integer) 1

总结

本篇文章深入浅出的解析Redis中四种高级数据结构的使用、适用场景以及原理

Geospatial 使用Geohash算法以及zset对象实现,适用于计算地理空间的场景

Hypeloglog 使用少量固定空间以及基数统计算法,适用于大数据情况下能接收微小出错的统计场景

Bitmap 使用sds实现的位数组,sds逆序存储位数组扩容时不用修改旧数据,适用于大数据情况下只有两个状态的统计场景

Bloom Filter 使用位数组与多个哈希函数实现,适用于在大数据情况下且能接收微小出错的判断元素是否存在集合的场景


相关文章
|
5月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
352 86
|
5月前
|
存储 消息中间件 NoSQL
Redis数据结构:别小看这5把“瑞士军刀”,用好了性能飙升!
Redis提供5种基础数据结构及多种高级结构,如String、Hash、List、Set、ZSet,底层通过SDS、跳表等实现高效操作。灵活运用可解决缓存、计数、消息队列、排行榜等问题,结合Bitmap、HyperLogLog、GEO更可应对签到、UV统计、地理位置等场景,是高性能应用的核心利器。
|
5月前
|
存储 缓存 NoSQL
Redis基础命令与数据结构概览
Redis是一个功能强大的键值存储系统,提供了丰富的数据结构以及相应的操作命令来满足现代应用程序对于高速读写和灵活数据处理的需求。通过掌握这些基础命令,开发者能够高效地对Redis进行操作,实现数据存储和管理的高性能方案。
165 12
|
5月前
|
存储 消息中间件 NoSQL
【Redis】常用数据结构之List篇:从常用命令到典型使用场景
本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。
|
9月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
4月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
5月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
269 1
Redis专题-实战篇二-商户查询缓存
|
4月前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
9月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
1285 0

热门文章

最新文章