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是一個不錯的選擇。

相關文章
相關標籤/搜索