第3章 小功能大用处-Bitmaps、HyperLogLog、GEO

news2024/10/5 18:27:42

1.Bitmaps
1.1数据结构模型
现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位,例
如“big”字符串是由3个字节组成,但实际在计算机存储时将其用二进制表
示,“big”分别对应的ASCII码分别是98、105、103,对应的二进制分别是
01100010、01101001和01100111,如下图所示。
在这里插入图片描述
Redis提供了Bitmaps这个“数据结构”可以实现对位的操作。把数据结构加上引号主要因为:

  • Bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作。
  • Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符
    串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的
    每个单元只能存储0和1,数组的下标在Bitmaps中叫做偏移量
    在这里插入图片描述
    1.2命令
    1.2.1设置值:setbit key offset value
    时间复杂度:O(1)
    设置键的第offset个位的值(从0算起)
    假设现在有20个用户,userid=0,5,11,15,19的用户对网站进行了访问,那么当前Bitmaps初始化结果如下图所示
    在这里插入图片描述
127.0.0.1:6379> setbit unique:users:2016-04-05 0 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 5 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 11 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 15 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 19 1
(integer) 0

在第一次初始化Bitmaps时,假如偏移量非常大,那么整个初始化过程执行会比较慢,可能会造成Redis的阻塞。
1.2.2.获取值:gitbit key offset//获取键的第offset位的值(从0开始算)
时间复杂度:O(1)
操作获取id=8的用户是否在2016-04-05这天访问过,返回0说明没有访问:

127.0.0.1:6379> getbit unique:users:2016-04-05 8
(integer) 0

由于offset=1000000根本就不存在,所以返回结果也是0:

127.0.0.1:6379> getbit unique:users:2016-04-05 1000000
(integer) 0

1.2.3.获取Bitmaps指定范围值为1的个数:bitcount [start][end]
时间复杂度:O(N)
下面操作计算2016-04-05这天的独立访问用户数量:

127.0.0.1:6379> bitcount unique:users:2016-04-05
(integer) 5

[start]和[end]代表起始和结束字节数,下面操作计算用户id在第1个字节到第3个字节之间的独立访问用户数,对应的用户id是11,15,19。

127.0.0.1:6379> bitcount unique:users:2016-04-05 1 3
(integer) 3

1.2.4Bitmaps间的运算:bitop op destkey key[key…]
时间复杂度:O(N)
bitop是一个复合操作,它可以做多个Bitmaps的and(交集)、or(并
集)、not(非)、xor(异或)操作并将结果保存在destkey中。假设2016-
04-04访问网站的userid=1,2,5,9,如下图所示。
在这里插入图片描述
and(交集)
下面操作计算出2016-04-04和2016-04-03两天都访问过网站的用户数量

127.0.0.1:6379> bitop and unique:users:and:2016-04-04_03 unique: users:2016-04-03
unique:users:2016-04-03
(integer) 2
127.0.0.1:6379> bitcount unique:users:and:2016-04-04_03
(integer) 2

在这里插入图片描述
or(并集)
如果想算出2016-04-04和2016-04-03任意一天都访问过网站的用户数量
(例如月活跃就是类似这种),可以使用or求并集,具体命令如下:

127.0.0.1:6379> bitop or unique:users:or:2016-04-04_03 unique:
users:2016-04-03 unique:users:2016-04-03
(integer) 2
127.0.0.1:6379> bitcount unique:users:or:2016-04-04_03
(integer) 6

not(非)

127.0.0.1:6379> bitop not unique:users:not:2016-04-04 unique:users:2016-04-04
(integer) 2
127.0.0.1:6379> bitcount unique:users:not:2016-04-04
(integer) 12

因为unique:users:2016-04-04共有2字节,取非只取2字节内的。
xor(异或)

127.0.0.1:6379> bitop xor unique:users:xor:2016-04-03_04 unique:users:2016-04-03 unique:users:2016-04-04
(integer) 2
127.0.0.1:6379> bitcount unique:users:xor:2016-04-03_04
(integer) 4

1.2.5计算Bitmaps中第一个值为targetBit的偏移量
bitpos key targetBit [start] [end]
时间复杂度:O(N)
下面操作计算2016-04-04当前访问网站的最小用户id:

127.0.0.1:6379> bitpos unique:users:2016-04-04 1
(integer) 1

除此之外,bitops有两个选项[start]和[end],分别代表起始字节和结束字
节,例如计算第0个字节到第1个字节之间,第一个值为0的偏移量

127.0.0.1:6379> bitpos unique:users:2016-04-04 0 0 1
(integer) 0

1.3Bitmaps分析
假设网站有1亿用户,每天独立访问的用户有5千万,如果每天用集合类型和Bitmaps分别存储活跃用户可以得到表3-3。
在这里插入图片描述
很明显,这种情况下使用Bitmaps能节省很多的内存空间,尤其是随着时间推移节省的内存还是非常可观的。
但Bitmaps并不是万金油,假如该网站每天的独立访问用户很少,例如只有10万(大量的僵尸用户),那么两者的对比如表3-5所示,很显然,这时候使用Bitmaps就不太合适了,因为基本上大部分位都是0。
在这里插入图片描述
2.HyperLogLog
HyperLogLog并不是一种新的数据结构(实际类型为字符串类型),而是一种基数算法,通过HyperLogLog可以利用极小的内存空间完成独立总数的统计,数据集可以是IP、Email、ID等。HyperLogLog提供了3个命令:pfadd、pfcount、pfmerge。
例如2016-03-06的访问用户是uuid-1、uuid-2、uuid-3、uuid-4,2016-03-05的访问用户是uuid-4、uuid-5、uuid-6、uuid-7。
在这里插入图片描述
2.1添加
pfadd key element [element …] //pfadd用于向HyperLogLog添加元素,如果添加成功返回1:
时间复杂度:O(1)

127.0.0.1:6379> pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
(integer) 1
127.0.0.1:6379> pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
(integer) 0
127.0.0.1:6379> pfcount 2016_03_06:unique:ids
(integer) 4

2.2计算独立用户数
pfcount key [key …] //pfcount用于计算一个或多个HyperLogLog的独立总数
时间复杂度:O(1),使用单个键调用时,平均常数时间非常小。O(N),其中N是键的个数,当调用多个键时,常数次数要大得多。

127.0.0.1:6379> pfadd 2016_03_05:unique:ids "uuid-4" "uuid-5" "uuid-6" "uuid-7"
(integer) 1
127.0.0.1:6379> pfcount 2016_03_05:unique:ids 2016_03_06:unique:ids
(integer) 7

2.3合并
pfmerge destkey sourcekey [sourcekey …] //pfmerge求多个HyperLogLog的并集并赋值给destkey
时间复杂度:O(N),合并N个hyperloglog,但是常数时间很高。
例如要计算2016年3月5日和3月6日的访问独立用户数,可以看到最终独立用户数是7:

127.0.0.1:6379> pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
(integer) 1
127.0.0.1:6379> pfadd 2016_03_05:unique:ids "uuid-4" "uuid-5" "uuid-6" "uuid-7"
(integer) 1
127.0.0.1:6379> pfmerge 2016_03_05_06:unique:ids 2016_03_05:unique:ids
2016_03_06:unique:ids
OK
127.0.0.1:6379> pfcount 2016_03_05_06:unique:ids
(integer) 7

2.4.100万个用户放到HyperLogLog和set中的内存对比:
2.4.1.HyperLogLog:
下面使用shell脚本向HyperLogLog插入100万个id,插入前记录一下redis-cli端执行info memory:

127.0.0.1:6379> info memory
# Memory
used_memory:835144
used_memory_human:815.57K
......

在shell窗口执行下面shell命令

...向2016_05_01:unique:ids插入100万个用户,每次插入1000条:
elements=""
key="2016_05_01:unique:ids"
for i in `seq 1 1000000`
227
do
	elements="${elements} uuid-"${i}
	if [[ $((i%1000)) == 0 ]];
	then
		redis-cli  -a paassword pfadd ${key} ${elements}
		elements=""
	fi
done

当上述代码执行完成后,可以看到内存只增加了15K左右:

127.0.0.1:6379> info memory
# Memory
used_memory:850616
used_memory_human:830.68K
......

但是,同时可以看到pfcount的执行结果并不是100万:

127.0.0.1:6379> pfcount 2016_05_01:unique:ids
(integer) 1009838

2.4.2.set
可以对100万个uuid使用集合类型进行测试,代码如下:

elements=""
key="2016_05_01:unique:ids:set"
for i in `seq 1 1000000`
do
	elements="${elements} "${i}
	if [[ $((i%1000)) == 0 ]];
	then
		redis-cli -a password sadd ${key} ${elements}
		elements=""
	fi
done

当上述代码执行完成后,可以看到内存使用了84MB:

127.0.0.1:6379> info memory
# Memory
used_memory:88702680
used_memory_human:84.59M
......

但独立用户数为100万:

127.0.0.1:6379> scard 2016_05_01:unique:ids:set
(integer) 1000000

表3-6列出了使用集合类型和HperLogLog统计百万级用户的占用空间对比。
在这里插入图片描述
可以看到,HyperLogLog内存占用量小得惊人,但是用如此小空间来估算如此巨大的数据,必然不是100%的正确,其中一定存在误差率。Redis官方给出的数字是0.81%的失误率。
HyperLogLog内存占用量非常小,但是存在错误率,开发者在进行数据结构选型时只需要确认如下两条即可:

  • 只为了计算独立总数,不需要获取单条数据。
  • 可以容忍一定误差率,毕竟HyperLogLog在内存的占用量上有很大的优势
    2.5GEO
    Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需要实现这些功能的开发者来说是一大福音。
    2.5.1增加地理位置信息
    geoadd key [NX|XX] [CH] longitude latitude member [longitude latitude member …]
  • XX: 只更新已经存在的元素。永远不要添加元素。
  • NX: 不要更新已经存在的元素。总是添加新元素。
  • XX和NX选项互斥。
  • CH: 将返回值从添加的新元素数修改为更改的元素总数(CH是changed的缩写)。更改的元素是添加的新元素和坐标已更新的现有元素。因此,在命令行中指定的具有与过去相同分数的元素不会被计算在内。注意:通常,GEOADD的返回值只计算添加的新元素的数量。
  • longitude、latitude、member分别是该地理位置的经度、纬度、成员,
    时间复杂度:O(log(N)) ,对于添加的每一项,其中N是排序集中元素的个数。
127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing 117.12 39.08 tianjin
(integer) 2

2.5.2.获取地理位置信息
geopos key member [member …]
时间复杂度:O(1)

127.0.0.1:6379> geopos cities:locations tianjin
1) 1) "117.12000042200088501"
2) "39.0800000535766543"

2.5.3.获取两个地理位置的距离。
geodist key member1 member2 [m|km|ft|mi] //[米|公里|英里|尺]
时间复杂度:O(1)

127.0.0.1:6379> geodist cities:locations tianjin beijing km
"89.2061"

2.5.4.获取指定位置范围内的地理信息位置集合
georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist][withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist][withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadius和georadiusbymember两个命令的作用是一样的,都是以一个地理位置为中心算出指定半径内的其他地理信息位置,不同的是georadius命令的中心位置给出了具体的经纬度,georadiusbymember只需给出成员即可。其中radiusm|km|ft|mi是必需参数,指定了半径(带单位),这两个命令有很多可选参数,如下所示:

  • withcoord:返回结果中包含经纬度。
  • withdist:返回结果中包含离中心节点位置的距离。
  • withhash:返回结果中包含geohash,有关geohash后面介绍。
  • COUNT count:指定返回结果的数量。
  • asc|desc:返回结果按照离中心节点的距离做升序或者降序。
  • store key:将返回结果的地理位置信息保存到指定键。
  • storedist key:将返回结果离中心节点的距离保存到指定键。
    时间复杂度:O(N+log(M)) N为圆心和半径划定的圆形区域边界框内的元素个数,M为索引内的项数。
127.0.0.1:6379> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"
   2) "190.4424"
   3) 1) "13.36138933897018433"
      2) "38.11555639549629859"
2) 1) "Catania"
   2) "56.4413"
   3) 1) "15.08726745843887329"
      2) "37.50266842333162032"
127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"

2.5.5.获取geohash
geohash key member [member …]
时间复杂度:O(1)

127.0.0.1:6379> geohash cities:locations beijing
1) "wx4ww02w070"
127.0.0.1:6379> type cities:locations
zset

geohash有如下特点:

  • GEO的数据类型为zset,Redis将所有地理位置信息的geohash存放在zset中。
  • 字符串越长,表示的位置更精确,表3-8给出了字符串长度对应的精度,例如geohash长度为9时,精度在2米左右
    在这里插入图片描述
  • 两个字符串越相似,它们之间的距离越近,Redis利用字符串前缀匹配算法实现相关的命令。
  • geohash编码和经纬度是可以相互转换的。
  • Redis正是使用有序集合并结合geohash的特性实现了GEO的若干命令。
    2.5.6.删除地理位置信息
    zrem key member
    GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1855457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMware ESXi 8.0U2c macOS Unlocker OEM BIOS Huawei (华为) FusionServer 定制版

VMware ESXi 8.0U2c macOS Unlocker & OEM BIOS Huawei (华为) FusionServer 定制版 ESXi 8.0U2 标准版,Dell (戴尔)、HPE (慧与)、Lenovo (联想)、Inspur (浪潮)、Cisco (思科)、Hitachi (日立)、Fujitsu (富士通)、NEC (日电)、Huawei (华为)、xFusion (超聚…

HTML(16)——边距问题

清楚默认样式 很多标签都有默认的样式,往往我们不需要这些样式,就需要清楚默认样式 写法: 用通配符选择器,选择所有标签,清除所有内外边距选中所有的选择器清楚 *{ margin:0; padding:0; } 盒子模型——元素溢出 作…

OpenCV颜色检测

OpenCV颜色检测 前言策略分析根据颜色检测目标对象相关链接 前言 绿幕技术是一种经典的视频编辑技术,可以用于将人物置于不同的背景中。例如在电影制作中,技术的关键在于演员不能身着特定颜色的衣服(比如绿色),站在只有绿色的背景前。然后&a…

数据库原理与安全复习笔记(未完待续)

1 概念 产生与发展:人工管理阶段 → \to → 文件系统阶段 → \to → 数据库系统阶段。 数据库系统特点:数据的管理者(DBMS);数据结构化;数据共享性高,冗余度低,易于扩充&#xff…

【Linux系列】tree 命令的实用指南

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

前端:Nuxt3 + Vuetify3 + Element Plus + 添加常用插件

想要开发一个网站,并且支持SEO搜索,当然离不开我们的 Nuxt ,那通过本篇文章让我们一起了解一下。如果构建一个Nuxt项目 安装 Nuxt3,创建项目 安装nuxt3, 需要node v18.10.0,大家记得查看自己的node版本。…

[保姆级教程]uniapp小程序获取右上角胶囊位置信息

文章目录 导文使用uni.getMenuButtonBoundingClientRect();方法实现完整案例 隐藏默认导航栏&#xff1a;全局隐藏当前页面隐藏 导文 uniapp小程序获取右上角胶囊位置信息 使用uni.getMenuButtonBoundingClientRect();方法实现 <script>const menuButtonInfo uni.getMe…

校园设施物联网信息化改造

随着物联网技术的发展越来越成熟&#xff0c;它不断地与人们的日常生活和工作深入融合&#xff0c;推动着社会的进步。其中物联网系统集成在高校实践课程中可以应用到许多项目&#xff0c;如环境气象检测、花卉种植信息化监管、水质信息化监管、校园设施物联网信息化改造、停车…

内卷时代!程序员如何突破35岁的宿命?

大家好&#xff0c;我是码农先森。 曾经梦想仗剑走天涯&#xff0c;如今却在写字楼里安家。他乡容不下灵魂&#xff0c;家乡容不下肉体&#xff0c;还面临着35岁被毕业&#xff0c;这难道就是程序员的宿命&#xff1f;大环境我们无法改变&#xff0c;但我认为至少能改变自己。…

6.20作业

1.已知网址www.hqyj.com截取出网址的每一个部分(要求&#xff0c;该网址不能存入文件中) echo www.hqyj.com | cut -d "." -f "1,2,3" 2.整理思维导图 3.将配置桥接网络的过程整理成文档&#xff0c;发csdn

H4020 12V24V36V40V1A 同步降压芯片IC Buck-DCDC 低功耗,高效率 100%占空比

H4020是一款12V24V36V40V1A的同步降压&#xff08;Buck&#xff09;DC-DC转换器&#xff0c;专为需要高效率、低功耗和精确电压/电流控制的应用而设计。它内置了高压MOSFET&#xff0c;支持宽范围的输入电压&#xff08;5V-36V&#xff09;&#xff0c;并能提供高达1A的持续输出…

WIN Semis揭幕耐湿砷化镓pHEMT技术

​犹如为无线通信领域注入了一股清新的活力。这项技术不仅支持E频带&#xff0c;更在晶圆级上筑起了一道坚固的防潮屏障&#xff0c;满足了对严苛环境条件的bHAST挑战。今日&#xff0c;WIN半导体公司正式公布了0.1m pHEMT技术PP10-29的测试版&#xff0c;预示着通信领域的新篇…

《web程序设计》课程大作业,XX地旅游景点网站【IDEA下JSP(前后端)+MySQL技术】

背景&#xff1a; 《web程序设计》课程大作业要求 一、课程目标&#xff1a;课程教学目的是让学生能够全面了解和掌握目前国内比较流行的交互式网页制作的理论知识与开发技术&#xff0c;能开发制作出有一定实用性的交互式网站&#xff0c;为将来继续学习和就业打下坚实基础。…

Linux如何远程访问?

远程访问是现代计算机网络中非常重要的一个功能&#xff0c;它允许用户通过网络连接到远程计算机&#xff0c;并在远程计算机上执行操作。对于使用Linux操作系统的用户来说&#xff0c;Linux远程访问是非常常见的需求。本文将介绍如何实现Linux远程访问&#xff0c;并简要介绍一…

写一个坏越的个人天地(二)

小红书上搜了下博客,感觉好像没有让自己喜欢的。昨天刚好学了点grid布局,来试试 菜单栏直接使用el-menu 下边布局就用grid局部了,这块初步想法是轮播+你的天气和我的天气+自我介绍 天气的话,这边要先找一下有没有天气的api 我这边百度搜了个聚合的api,一天可以免费调用5…

云原生容器技术入门:Docker、K8s技术的基本原理和用途

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《未来已来&#xff1a;云原生之旅》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、容器技术概述 1、什么是容器技术 2、容器技术的历史与发展 3…

统信UOS1070上配置文件管理器默认属性03

原文链接&#xff1a;统信UOS1070上配置文件管理器默认属性03 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在统信UOS 1070上配置文件管理器默认属性的第三篇文章——配置工作区、侧边栏及高级设置。通过这些配置&#xff0c;您可以更好地组织和管理文件&…

基于matlab的高斯滤波与图像去噪

1 高斯滤波原理 1.1 原理 高斯滤波是一种线性平滑滤波技术&#xff0c;主要用于消除图像中的高斯噪声。它的工作原理可以理解为对整幅图像进行加权平均的过程&#xff0c;即每个像素点的值都由其本身和邻域内的其他像素值经过加权平均后得到。 高斯滤波实质上是一种信号的滤…

Apache Tomcat 10.1.25 新版本发布 java 应用服务器

Tomcat 是一个小型的轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试 JSP 程序的首选。对于一个初学者来说&#xff0c;可以这样认为&#xff0c;当在一台机器上配置好 Apache 服务器&#xff0c;可利用它响应对 H…

POLYGON Horror Carnival - Low Poly 3D Art by Synty

465 个独特的预设模型 一个正在运行的摩天轮和旋转木马 包括10个示例脚本,让嘉年华栩栩如生 ◼ 描述◼ 欢迎来到恐怖嘉年华。这个地方曾经有诱人的音乐,现在却有着令人不安的旋律,暗示着其中令人不安的惊喜。 这场险恶的盛会的真正核心在于演示场景。它使用3D低多边形资源构…