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

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命令实现对地理位置信息的删除

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解决ArcGIS导出的svg格式的图片插入Word后的字体问题

背景 在ArcGIS中设置字体为Times New Roman,但导入Word后字体转为等线。 ArcGIS中的Layout 导入Word​​​​​​ 原因分析 Word无法识别嵌入进SVG格式文件中的字体。 解决方案 在Export Layer窗口中,将Embed fonts取消勾选,Convert cha…

【新闻】全球热钱,正在流入新加坡 这个夏天有点猛,油价看涨? 普华永道已丢了六成“A股大客户”

新加坡成为全球投资焦点,吸引大量并购活动。预计经济增长2.4%,股指上涨8%。未来可期待更多国际投资涌入。 近期,新加坡成为全球投资者的焦点,吸引了大量的并购和投资活动。 据报道,2024年第二季度,新加坡…

前端项目vue3/React使用pako库解压缩后端返回gzip数据

pako仓库地址:https://github.com/nodeca/pako 文档地址:pako 2.1.0 API documentation 外部接口返回一个直播消息或者图片数据是经过zip压缩的,前端需要把这个数据解压缩之后才可以使用,这样可以大大降低网络数据传输的内容&…

Android Studio中HAXM安装失败的解决方案(HAXM installation failed)

文章目录 错误示例Hyper-VWindows SandboxWindows Hypervisor Platform(Windows 虚拟化监控程序平台) 出现原因解决方法虚拟机平台方案一方案二方案三 错误示例 表明HAXM (Hardware Accelerated Execution Manager)安装失败了。HAXM是一个硬件辅助虚拟化…

C++基础编程100题-015 OpenJudge-1.3-13 反向输出一个三位数

更多资源请关注纽扣编程微信公众号 http://noi.openjudge.cn/ch0103/13/ 描述 将一个三位数反向输出。 输入 一个三位数n。 输出 反向输出n。 样例输入 100样例输出 001参考程序 #include<bits/stdc.h> using namespace std;int main(){int n;cin>>n;cou…

【TB作品】MSP430G2553,单片机,口袋板, 烘箱温度控制器

题3 烘箱温度控制器 设计一个基于MSP430的温度控制器&#xff0c;满足如下技术指标&#xff1a; &#xff08;1&#xff09;1KW 电炉加热&#xff0c;最度温度为110℃ &#xff08;2&#xff09;恒温箱温度可设定&#xff0c;温度控制误差≦2℃ &#xff08;3&#xff09;实时显…

基于Langchain-chatchat搭建本地智能知识问答系统

基于Langchain-chatchat搭建本地智能 搭建本地智能知识问答系统&#xff1a;基于Langchain-chatchat的实践指南引言项目概述环境安装Anacondapip 项目安装步骤大语言模型&#xff08;LLM&#xff09;的重要性结语 搭建本地智能知识问答系统&#xff1a;基于Langchain-chatchat的…

记录Gstreamer的uridecodebin可以自动选择硬解码器

记录&#xff1a; uridecodebin3 和uridecodebin优先硬解码 这两个插件&#xff0c;本来是负责动态选择合适的解码器来处理特定的媒体流&#xff0c;使用案例&#xff1a; gst-launch-1.0 uridecodebin urirtsp://192.168.1.120:8554/test ! glimagesink -v gst-launch-1.0 …

汇聚荣做拼多多运营,是新手怎么做?

作为电商领域的一颗新星&#xff0c;拼多多以其独特的商业模式迅速崛起&#xff0c;吸引了众多商家和消费者的目光。对于新手来说&#xff0c;如何在拼多多平台上开展运营活动&#xff0c;成为了许多初入电商领域的人们关心的问题。本文将围绕如何做好拼多多运营这一核心内容&a…

【ARM】MDK工程切换高版本的编译器后出现error A1137E报错

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决工程从Compiler 5切换到Compiler 6进行编译时出现一些非语法问题上的报错。 2、 问题场景 对于一些使用Compiler 5进行编译的工程&#xff0c;要切换到Compiler 6进行编译的时候&#xff0c;原本无任何报错警告…

部署企业级AI知识库最重要的是什么?✍

随着人工智能技术的迅猛发展&#xff0c;企业级AI知识库成为提升企业管理效率和信息获取能力的重要工具。那么&#xff0c;在部署企业级AI知识库时&#xff0c;最重要的是什么呢&#xff1f;本文将从数据质量、系统可扩展性、用户体验以及智能化这四个关键方面进行详细分析。 …

计算机专业课面试常见问题-计算机网络篇

目录 1. 计算机网络分为哪 5 层&#xff1f; 2. TCP 协议简述&#xff1f; 3. TCP 和 UDP 的区别&#xff1f;->不同的应用场景&#xff1f; 4. 从浏览器输入网址到显示页…

Ant Design Vue Upload 自定义上传 customRequest,这一篇很详细

Upload 常用属性和方法 示例上传接口 # 接口文档 url https://www.mocky.io/api/main/upload 头部 x-token: xxx 参数 file: File // 上传的文件 flag: xxx // 上传的标识// 文件上传 api 函数简单封装 export const uploadApi ({ file }) > {const formData new Fo…

Java中Collection的成员及其特点

Collection集合 list集合系列 ArrarList集合 底层基于数组来实现 查询速度快&#xff08;根据索引查询数据&#xff09; 删除效率低&#xff08;可能需要把后面很多的数据往后移&#xff09; 添加效率…

CesiumJS【Basic】- #016 多边形面渲染“花了”的问题

文章目录 多边形面渲染“花了”的问题1 目标2 问题代码3 修正后代码4 总结多边形面渲染“花了”的问题 1 目标 解决多边形的面“花了”的问题 2 问题代码 使用Cesium.PerInstanceColorAppearance渲染后出现色斑 import * as Cesium from "cesium";const viewer …

文化财经wh6boll带macd多空转折点提示指标公式源码

文化财经wh6boll带macd多空转折点提示指标公式源码&#xff1a; DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); MACD:2*(DIFF-DEA); MID:MA(CLOSE,26);//求N个周期的收盘价均线&#xff0c;称为布林通道中轨 TMP2:STD(CLOSE,26);//求M个周期内的收盘价的标准差 …

惊天大瓜姬圈天莱女明星出轨风波

#惊天大瓜&#xff01;姬圈天菜女明星出轨风波#近日&#xff0c;娱乐圈掀起了一场前所未有的风暴&#xff01;狗仔队放出重磅消息&#xff0c;直指某位姬圈天菜级别的女明星深陷出轨泥潭。消息一出&#xff0c;引发了网友们的热议和猜测&#xff0c;究竟这位神秘的女明星是谁&a…

第N8周:seq2seq翻译实战-Pytorch复现

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 一、前期准备 from __future__ import unicode_literals, print_function, division from io import open import unicodedata import s…

DataGrip 2024 po for Mac 数据库管理工具解

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

怎么实现微信支付?

微信小程序中微信支付&#xff08;前端流程&#xff09; 微信支付前准备工作 微信公众平台绑定商户号 微信支付平台配置好后端信息支付前要有用户的openid 1. 客户端点击支付按钮 在用户点击支付按钮时&#xff0c;触发支付流程。 // 绑定支付按钮点击事件 function onPayB…