| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> Redis中的Bitmaps、HyperLogLog、Geospatial -> 正文阅读 |
|
[大数据]Redis中的Bitmaps、HyperLogLog、Geospatial |
目录 Bitmaps在移动应用的业务场景中,我们需要保存这样的信息:一个 key 关联了一个数据集合。
通常情况下,我们面临的用户数量以及访问量都是巨大的,比如百万、千万级别的用户数量,或者千万级别、甚至亿级别的访问信息。 所以,我们必须要选择能够非常高效地统计大量数据(例如亿级)的集合类型。 如何选择集合类型假如我们在判断用户是否登陆的场景中使用 Redis 的 String 类型实现(key -> userId,value -> 0 表示下线,1 - 登陆),假如存储 100 万个用户的登陆状态,如果以字符串的形式存储,就需要存储 100 万个字符串了,内存开销太大。
合理地使用操作位能够有效地提高内存使用率和开发效率。java中位与操作是最快的,在Redis中同样道理,Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:
?判断用户登录状态Bitmap 提供了 只需要一个 key为login?表示存储用户登陆状态集合数据, 将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 setbit <key> <offset>设置对应KEY值的偏移量的值 第一步:1代表用户已经登陆
第二步:检查该用户是否登陆,返回值 1 表示已登录
第三步:退出,将 offset 对应的 value 设置成 0
?小结只需要一个 bit 位就能表示 0 和 1。在统计海量数据的时候将大大减少内存占用。 HyperLogLog在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站PV( PageView页面访问量) ,可以使用Redis的incr、incrby 轻松实现。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。 但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。 什么是基数比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。 应用场景?HyperLogLog 主要的应用场景就是进行基数统计。这个问题的应用场景其实是十分广泛的。例如:对于 Google 主页面而言,同一个账户可能会访问 Google 主页面多次。于是,在诸多的访问流水中,如何计算出 Google 主页面每天被多少个不同的账户访问过就是一个重要的问题。那么对于 Google 这种访问量巨大的网页而言,其实统计出有十亿 的访问量或者十亿零十万的访问量其实是没有太多的区别的,因此,在这种业务场景下,为了节省成本,其实可以只计算出一个大概的值,而没有必要计算出精准的值。 对于上面的场景,可以使用
命令pfadd <key> <element> [element ...] 添加指定元素到 HyperLogLog中,将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0。 ?pfcount<key> [key ..]计算HLL的近似基数,可以计算多个HLL ,比如用HLL存储每天的UV ,计算一周的UV可以使用7天的UV合并计算即可 ?pfmerge<destkey> <sourcekey> [sourcekey ..]将一个或多 个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来给并计算可得 ?比如在网站中我们有两个内容差不多的页面,运营说需要这两个页面的数据进行合并。其中页面的 UV 访问量也需要合并,那这个时候 GeospatialRedis 3.2中增加了对GEO类型的支持。GEO , Geographic ,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。 如何实现定位说到定位,很多人第一反应应该是,实时上报经纬度,数据库中提前存储好所有的经纬度,然后用上报的经纬度和数据库中的经纬度进行比较,计算出附近的人或共享单车。这种做法需要循环遍历,数据库中的数据量大,查询慢,效率低。 那么,这些app是如何做到既能够精确定位,又能够实时查询的呢?答案就是使用geohash。redis的"数据类型"geospatial就能计算出geohash。redis使用geohash技术将实时上报的精度和纬度,通过一定的算法转化成最长12个字符的字符串,两个位置的经纬度计算的字符串的前缀越相同,则两个位置离得越近。这样一来就可以通过数据库的like加上geohash的前几位模糊查询数据库的数据了。比如ofo共享单车,数据库中用一张表t_bike专门存储ofo的每一辆车的编号no、经度longitude 、纬度latitude、geohash等字段,当每一辆车上报自己的经纬度时,同时计算一个geohash存到表中;当用户要用车时,上报用户的实时位置的经纬度,并计算一个hash值,比如hash=efgrtv98fjng,那么可以使用:
就可以找到附近有多少车了。like后面使用的hash位数越多,查找的范围越准确。 Geohash技术geohash技术就是将经纬度转换成最长12个字符的字符串,同时两个位置越近,生成的字符串的前缀越一致。这是如何实现的呢? 例如,东方明珠的经纬度,东经121.506377,北纬31.245105。 下面就以东方明珠为例,简单说一下如何将这两个经纬度计算成一个hash字符串的。 geohash的计算1.使用二分法生成二进制 将纬度(-90,90)分成两个区间,(-90,0)和(0,90),如果目标纬度落在左边区间则记为0,否则记为1;再将目标纬度所在的那个区间在通过二分法分成两个相等的区间,如果目标纬度落在左边区间则记为0,否则记为1,以此类推。 同样的,将经度(-180,180)也通过这种方式计算。 最终,经度和纬度计算后,分别得到一个由0和1组成的二进制。 假如,东方明珠的经纬度计算后,得到两个二进制位:
?2.合并二进制 将上面的两个二进制按照“偶数位放经度,奇数位放纬度”的原则,从0位开始数起,合并成一个二进制。 可以理解成将纬度向后移动一位,然后将两行压成一行。
3.二进制转换成十进制 把上面合并后的60位二进制,按照从左往右,每5位划分成1个组,如果最后一组如果不足5位就用0补齐到5位。分组后所示:
将上面的每组二进制分别转成十进制:
?4.十进制转base32字符串 使用base32编码表,将每个十进制数替换成编码表中的字符,获得一个字符串。 ?转化后的字符串:
这就是模拟东方明珠的经纬度生产的geohash的值(不是真实值)。 geohash的精度hash的字符串长1位-12位,对应精度的级别1-12级。字符串越长,位置越精确。 geohash的边界问题比如现在是y1,根据geohash算法定位到y1所在的矩形块,返回附近的点就会得到y2。但是我们发现实际情况是,x, z虽然不在我们所在的那个矩形区域,但是x, z显然离我们更近。这就是geohash的边界问题,就是如果我们刚好处在矩形的边界处,那么离我们最近的点不一定是和我们在同一个矩形框中的点,很可能是旁边的矩形框中的某些点离我们更近。 ?其实,就是将该区块上下左右以及四个对角的8个区块的hash都计算一遍,分别计算和自己之间的距离,找到最近的一个。因为这是的数据量已经非常小了,计算周边的8个块也很快。 Redis对地理位置的支持可以通过编程的方式实现上面的计算过程,当然也可以直接使用redis计算geohash。 redis中的geospatial本质是使用sorted set存储的。使用的也是geohash技术。经度二进制和纬度二进制,通过上面介绍的“偶数位放经度,奇数位放纬度”的原则,形成一个独特的52位二进制。sorted set存储每一个成员时,会给每一个成员一个分数用于排序,分数值是一个双精度的64位浮点型数字字符串,它能包括的整数范围是 实际使用时,可以提前将一份各地区的经纬度表格,通过redis命令导入到redis内存中,然后可以通过相关的redis命令计算每个地区的geohash,并且可以搜索指定范围内,redis中存在的地区有哪些,同时也可以计算两个经纬度之间的距离。 指令操作1、geoadd<key> < longitude> <latitude> <member> [longitude latitude member..]添加地理位置(经度,纬度,名称)。 2、geopos <key><name> 获取制定城市的坐标 3、geodist <key> <member1> <member2> [m]|km|ft|mi ]获取两个位置 之间的直线距离,不指定单位参数,默认用米? ?4、georadius <key>< longitude> <latitude>radius m|km|ft|mi 以给定的经纬度为中心 ,找出某一半径内的元素 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/20 14:38:56- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |