美文网首页springcloudmaven面试
Redis的3个高级数据结构

Redis的3个高级数据结构

作者: 阿飞的博客 | 来源:发表于2018-08-05 18:03 被阅读329次

    平常我们我接触最多的是5个入门级数据结构:String,Hash,List,Set,Sorted Set。本文介绍3个高级数据结构:Bitmaps,Hyperloglogs,GEO。

    Bitmaps

    bitmaps不是一个真实的数据结构。而是String类型上的一组面向bit操作的集合。由于strings是二进制安全的blob,并且它们的最大长度是512m,所以bitmaps能最大设置2^32个不同的bit。

    bit操作被分为两组:

    • 恒定时间的单个bit操作,例如把某个bit设置为0或者1。或者获取某bit的值。
    • 对一组bit的操作。例如给定范围内bit统计(例如人口统计)。

    Bitmaps的最大优点就是存储信息时可以节省大量的空间。例如在一个系统中,不同的用户被一个增长的用户ID表示。40亿(2^32=4*1024*1024*1024≈40亿)用户只需要512M内存就能记住某种信息,例如用户是否登录过。

    Bits设置和获取通过SETBIT 和GETBIT 命令,用法如下:

    SETBIT key offset value
    GETBIT key offset
    

    使用实例:

    127.0.0.1:6380> setbit dupcheck 10 1
    (integer) 0
    127.0.0.1:6380> getbit dupcheck 10 
    (integer) 1
    

    SETBIT命令第一个参数是位编号,第二个参数是这个位的值,只能是0或者1。如果bit地址超过当前string长度,会自动增大string。

    Bitmaps示意图

    GETBIT命令指示返回指定位置bit的值。超过范围(寻址地址在目标key的string长度以外的位)的GETBIT总是返回0。三个操作bits组的命令如下:

    • BITOP 执行两个不同string的位操作.,包括AND,OR,XOR和NOT.
    • BITCOUNT 统计位的值为1的数量。
    • BITPOS 寻址第一个为0或者1的bit的位置(寻址第一个为1的bit的位置:bitpos dupcheck 1; 寻址第一个为0的bit的位置:bitpos dupcheck 0).

    bitmaps一般的使用场景:

    • 各种实时分析.
    • 存储与对象ID关联的节省空间并且高性能的布尔信息.

    例如,想象一下你想知道访问你的网站的用户的最长连续时间。你开始计算从0开始的天数,就是你的网站公开的那天,每次用户访问网站时通过SETBIT命令设置bit为1,可以简单的用当前时间减去初始时间并除以3600*24(结果就是你的网站公开的第几天)当做这个bit的位置。

    这种方法对于每个用户,都有存储每天的访问信息的一个很小的string字符串。通过BITCOUN就能轻易统计某个用户历史访问网站的天数。另外通过调用BITPOS命令,或者客户端获取并分析这个bitmap,就能计算出最长停留时间。

    HyperLogLogs

    HyperLogLog是用于计算唯一事物的概率数据结构(从技术上讲,这被称为估计集合的基数)。如果统计唯一项,项目越多,需要的内存就越多。因为需要记住过去已经看过的项,从而避免多次统计这些项。

    然而,有一组算法可以交换内存以获得精确度:在redis的实现中,您使用标准错误小于1%的估计度量结束。这个算法的神奇在于不再需要与需要统计的项相对应的内存,取而代之,使用的内存一直恒定不变。最坏的情况下只需要12k,就可以计算接近2^64个不同元素的基数。或者如果您的HyperLogLog(我们从现在开始简称它为HLL)已经看到的元素非常少,则需要的内存要要少得多。

    在redis中HLL是一个不同的数据结构,它被编码成Redis字符串。因此可以通过调用GET命令序列化一个HLL,也可以通过调用SET命令将其反序列化到redis服务器。

    HLL的API类似使用SETS数据结构做相同的任务,SETS结构中,通过SADD命令把每一个观察的元素添加到一个SET集合,用SCARD命令检查SET集合中元素的数量,集合里的元素都是唯一的,已经存在的元素不会被重复添加。

    而使用HLL时并不是真正添加项到HLL中(这一点和SETS结构差异很大),因为HLL的数据结构只包含一个不包含实际元素的状态,API是一样的:

    • PFADD命令用于添加一个新元素到统计中。
    • PFCOUNT命令用于获取到目前为止通过PFADD命令添加的唯一元素个数近似值。
    • PFMERGE命令执行多个HLL之间的联合操作。
    127.0.0.1:6380> PFADD hll a b c d d c
    (integer) 1
    127.0.0.1:6380> PFCOUNT hll
    (integer) 4
    127.0.0.1:6380> PFADD hll e
    (integer) 1
    127.0.0.1:6380> PFCOUNT hll
    (integer) 5
    
    

    PFMERGE命令说明:

    PFMERGE destkey sourcekey [sourcekey ...]
    Merge N different HyperLogLogs into a single one.
    

    用法(把hll1和hll2合并到hlls中):

    127.0.0.1:6380> PFADD hll1 1 2 3
    (integer) 1
    127.0.0.1:6380> PFADD hll2 3 4 5
    (integer) 1
    127.0.0.1:6380> PFMERGE hlls hll1 hll2
    OK
    127.0.0.1:6380> PFCOUNT hlls
    

    HLL数据结构的一个使用场景就是计算用户每天在搜索框中执行的唯一查询,即搜索页面UV统计。而Bitmaps则用于判断某个用户是否访问过搜索页面。这是它们用法的不同。

    GEO

    Redis的GEO特性在 Redis3.2版本中推出,这个功能可以将用户给定的地理位置(经度和纬度)信息储存起来,并对这些信息进行操作。GEO相关命令只有6个:

    • GEOADD:GEOADD key longitude latitude member [longitude latitude member ...],将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。例如:GEOADD city 113.501389 22.405556 shenzhen
    经纬度具体的限制,由EPSG:900913/EPSG:3785/OSGEO:41001规定如下:
    有效的经度从-180度到180度。
    有效的纬度从-85.05112878度到85.05112878度。
    当坐标位置超出上述指定范围时,该命令将会返回一个错误。
    
    • GEOHASH:GEOHASH key member [member ...],返回一个或多个位置元素的标准Geohash值,它可以在http://geohash.org/使用。查询例子:http://geohash.org/sqdtr74hyu0.(可以通过谷歌了解Geohash原理,或者戳Geohash基本原理:https://www.cnblogs.com/tgzhu/p/6204173.html)。

    • GEOPOS:GEOPOS key member [member ...],从key里返回所有给定位置元素的位置(经度和纬度)。

    • GEODIST:GEODIST key member1 member2 [unit],返回两个给定位置之间的距离。GEODIST命令在计算距离时会假设地球为完美的球形。在极限情况下,这一假设最大会造成0.5%的误差。

    指定单位的参数unit必须是以下单位的其中一个:
    m 表示单位为米(默认)。
    km 表示单位为千米。
    mi 表示单位为英里。
    ft 表示单位为英尺。
    
    • GEORADIUS:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count],以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。这个命令可以查询某城市的周边城市群。

    • GEORADIUSBYMEMBER:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count],这个命令和GEORADIUS命令一样,都可以找出位于指定范围内的元素,但是GEORADIUSBYMEMBER的中心点是由给定的位置元素决定的,而不是像 GEORADIUS那样,使用输入的经度和纬度来决定中心点。

    指定成员的位置被用作查询的中心。

    GEO的6个命令用法示例如下:

    redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
    (integer) 2
    
    redis> GEOHASH Sicily Palermo Catania
    1) "sqc8b49rny0"
    2) "sqdtr74hyu0"
    
    redis> GEOPOS Sicily Palermo Catania NonExisting
    1) 1) "13.361389338970184"
       2) "38.115556395496299"
    2) 1) "15.087267458438873"
       2) "37.50266842333162"
    3) (nil)
    
    redis> GEODIST Sicily Palermo Catania
    "166274.15156960039"
    
    redis> GEORADIUS Sicily 15 37 100 km
    1) "Catania"
    redis> GEORADIUS Sicily 15 37 200 km
    1) "Palermo"
    2) "Catania"
    
    redis> GEORADIUSBYMEMBER Sicily Agrigento 100 km
    1) "Agrigento"
    2) "Palermo"
    

    相关文章

      网友评论

      • Dovelol:飞哥,咨询几个问题,关于GEO相关功能,如果存入一系列的坐标,给定member想知道距离它最近的是哪个member,有什么好的计算方法么,看到底层存储的是个zset,那么zset的score具体存的是什么呢,可以利用zset的排序来计算距离信息吗。
      • BakerZhu:👍👍👍
      • 鋒Nic:飞神写得太好啦,补充两点:1.HyperLogLog内存占用量非常小,但是存在错误率;2.GEO删除地理位置信息的话采用zrem key member命令来实现对地理位置信息删除功能,因为GEO的底层实现就是zset数据结构,Redis是把所有地理位置信息的geohash存在zset。这两点飞神看看是否正确:interrobang::icecream:
        阿飞的博客:@鋒Nic 峰神棒棒哒,全对!!!😘

      本文标题:Redis的3个高级数据结构

      本文链接:https://www.haomeiwen.com/subject/qyobvftx.html