Redis之bitmaps,hyperloglog,geo

redis三大功能

redis除了咱们经常使用的5中基本数据类型,在此基础上,还提供了一些特殊的功能模块。这里介绍如下三种:bitmaps,hyperloglog,geo。java

bitmaps

  • 概述git

    redis中的bitmaps(位图)不是实际的数据类型,而是在String类型上定义的一组面向位的操做。因为字符串是二进制安全blob,而且它们的最大长度为512 MB,所以它们适合设置最多2^32个不一样的位。位图的最大优点之一是它们在存储信息时一般能够节省大量空间。例如,在经过增量用户ID表示不一样用户的系统中,可使用仅512MB的存储器记住40亿用户的单个位信息redis

  • 使用场景算法

    活跃用户数统计:假设一个系统天天有100万独立用户登陆。数组

    需求1:统计每周的活跃用户数。安全

    需求2:统计一周天天都登陆了用户数。bash

    分析,须要存储每一个用户的每一天的登陆信息数据结构

  • 经常使用命令dom

    setbit key offset value
    bitop   在不一样的字符串之间执行逐位操做。提供的操做是AND,OR,XOR和NOT。
    bitcount  执行填充计数,报告设置为1的位数。
    bitpos   查找具备指定值0或1的第一个位。
    复制代码
  • 代码学习

    //TODO
    public class BitMapTest {
    
        public static void main(String[] args) {
            //链接本地的 Redis 服务
            Jedis jedis = new Jedis("localhost");
            //初始化id为0-99的签到状况
            IntStream.range(0, 100).forEach((id) -> {
                        jedis.setbit("星期" + id % 7, id, true);
                    }
            );
            //id的为10的用户,天天都登陆了的。
            for (int i = 0; i < 7; i++) {
                jedis.setbit("星期" + i , 10, true);
            }
    
            for (int i = 0; i < 7; i++) {
                System.out.println(String.format("星期%s 的活跃用户数为%s",i, jedis.bitcount("星期" + i)));
            }
            jedis.bitop(BitOP.OR, "week","星期0", "星期1", "星期2", "星期3", "星期4", "星期5", "星期6");
            System.out.println(String.format("本周的活跃用户数为%s",jedis.bitcount("week")));
    
            jedis.bitop(BitOP.AND, "week1","星期0", "星期1", "星期2", "星期3", "星期4", "星期5", "星期6");
            System.out.println(String.format("本周天天都登陆的用户数%s",jedis.bitcount("week1")));
        }
    }
    复制代码

  • 总结

    学习bitmap,咱们知道bitmap经过一个bit数组来存储特定数据的一种数据结构,每个bit位都能独立包含信息,bit是数据的最小存储单位,所以能大量节省空间。bitmap有一个很明显的优点是能够轻松合并多个统计结果,只须要对多个结果求与,或,异或等操做就能够。也能够大大减小存储内存,能够作个简单的计算,若是要统计1亿个数据的基数值,大约须要内存。

    数据类型 占用空间 储存的用户量 内存量
    set 32位 100000000 32*100000000/8/1024/1024≈ 381M
    bitMap 1位 100000000 100000000/8/1024/1024 ≈ 12M

hyperloglog

  • 概述

    什么是基数? 假设有个集合为(1,4,2,7,8,7)那么这个集合的基数为去重以后的元素个数,即为5。

    bitmap对于内存的节约量是显而易见的,但仍是不够。统计一个对象的基数值须要12M,若是统计10000个对象,就须要将近120G了,一样不能普遍用于大数据场景。

    redis中实现的HyperLogLog,只须要12K内存,在标准偏差0.81%的前提下,可以统计2^{64}个数据。HyperLogLog是一种几率算法,几率算法不直接存储数据集合自己,经过必定的几率统计方法预估基数值,这种方法能够大大节省内存,同时保证偏差控制在必定范围内。

  • 经常使用命令

    pfadd key val1 val2 ...  
    pfcount key  统计基数
    复制代码
  • 使用场景

    统计页面独立的UV,UV即对于某个页面,天天独立访问的用户数量,若是同一个用户屡次访问,只能算一次。

  • 代码

    public class HyperLogLogTest {
        public static void main(String[] args) {
            //链接本地的 Redis 服务
            Jedis jedis = new Jedis("localhost");
            //初始化id为0-99的签到状况
            IntStream.range(0, 10000).forEach((id) -> {
                String userId = UUID.randomUUID().toString();
                jedis.pfadd("datetime:page1",userId);
                    }
            );
            System.out.println("经过hyperloglog几率算法估算出的UV:"+jedis.pfcount("datetime:page1"));
        }
    }
    复制代码

  • 总结

    hyperloglog是一种几率算法,是经过局部推算总体的一种算法,在不存储元素的状况下,用计算集合的基数,可是有必定的偏差。若是偏差在业务容忍的范围内。那么这一种很是节省内存的高效算法。

geo

  • 概述

    在redis3.2版本,增长了Geo地理空间位置的计算功能。经过GEO咱们能够计算两个地理位置的距离,以及给定地理位置获取指定范围内的地理位置集合等

  • 经常使用命令(使用redis3.2以后的版本才有geo功能)

    一、GEOADD:增长某个地理位置的坐标 二、GEOPOS:获取某个地理位置的坐标 三、GEODIST:获取两个地理位置的距离 四、GEORADIUS:根据给定地理位置坐标获取指定范围内的地理位置集合 五、GEORADIUSBYMEMBER:根据给定地理位置获取指定范围内的地理位置集合 六、GEOHASH:获取某个地理位置的 geohash 值

  • 使用场景

    计算用户和商家的距离

  • 代码

    public class GeoTest {
        public static void main(String[] args) {
            //链接本地的 Redis 服务
            Jedis jedis = new Jedis("localhost");
            //初始化4个门店的位置
            Map<String, GeoCoordinate> map=new HashMap<>();
            map.put("成华新风路专营店",new GeoCoordinate(104.11117,30.6846));
            map.put("青羊区东门街营业厅",new GeoCoordinate(104.05983,30.66685));
            map.put("武侯区一环路南三段营业厅",new GeoCoordinate(104.0614,30.63354));
            map.put("金牛区三洞桥专营店",new GeoCoordinate(104.04903,30.67408));
            jedis.geoadd("shop",map);
            GeoRadiusParam param=GeoRadiusParam.geoRadiusParam()
                    .withDist()//返回距离
                    .withCoord() //返回经纬度
                    .sortAscending();//根据距离升序排序;
            double userLongitude=104.045181;//用户的经度
            double userLatitude=30.688663;//用户的纬度
            //查询距离用户10千米范围内的营业点
            List<GeoRadiusResponse> shop = jedis.georadius("shop", userLongitude, userLatitude, 10, GeoUnit.KM,param);
            for (GeoRadiusResponse geoRadiusResponse : shop) {
                System.out.println(String.format("用户距离 %s (经度:%s,纬度:%s) 有%s 公里 ",
                        geoRadiusResponse.getMemberByString(),
                        geoRadiusResponse.getCoordinate().getLongitude(),
                        geoRadiusResponse.getCoordinate().getLatitude(),
                        geoRadiusResponse.getDistance()));
            }
        }
    }
    复制代码

  • 总结

    redis的geo是经过 它只是假设地球是一个球体,由于使用的距离公式是Haversine公式。这个公式只是在应用于地球时的近似值,而地球不是一个完美的球体。在做为不是特别高的精度状况下,使用geo是一个不错的选择。

相关文章
相关标签/搜索