自学内容网 自学内容网

Redis 数据结构(三) HyperLogLog、位图、地理坐标

HyperLogLog 超日志,是一个专门计算集合的基数的概率算法。可以计算出集合的近似基数,标准误差为0.81%。它进行计算的内存是固定的(12kb。2的64次方)。

bitmap 位图,是由多个二进制位组成的数组,每个二进制位都有与之对应的偏移量,用户通过这些偏移量可以对位图中的一个或多个二进制位进行操作。它是在字符串的基础上实现的。

geo 地理坐标,用户可以将经纬度格式的地理坐标存储到Redis,并对这些坐标执行距离计算、范围操作等操作。其是在有序集合基础上实现的。

1 超日志 HyperLogLog

HyperLogLog 能够在占用极小内存的情况下估算一个集合中不重复元素的数量。所占用的内存是固定的(12kb),不会随着元素数量的变化而改变。

其主要用于需要统计大量唯一值数量但又对内存占用敏感的场景,如:统计网址一段时间的独立访客数量、估算广告的独立点击用户数。

添加

PFADD (可用于唯一判断)。

统计

PFCOUNT,其会将集合使用PFMERGE合并并存储到临时集合,操作完成后会将这个临时集合删除。

合并

PFMERGE。

表 HyperLogLog 相关操作命令

1.1 示例

某网址每日访客数量在10w以上,需求是,统计每日不同ip的访问数量。及判断某ip是否已访问过网站。

public class IpUniqueCounter {

    private final static Jedis jedis = RedisPool.getRedis();
    private final static String COUNTER_KEY = "ip_unique_counter_hyperLogLog";

    public static void addIp(String  ... ips) {
        jedis.pfadd(COUNTER_KEY,ips);
    }

    public static long count() {
        return jedis.pfcount(COUNTER_KEY);
    }

    public static boolean has(String ip) {
        return jedis.pfadd(COUNTER_KEY,ip) != 1;
    }

    public static void main(String[] args) {
        addIp("127.0.0.1","0.0.0.1","192.168.0.1");
        System.out.println(count());
        System.out.println(has("127.0.0.1"));
        System.out.println(has("127.0.0.2"));
        System.out.println(has("127.0.0.2"));
    }
}

2 位图 Bitmap

位图基于字符串数据类型构建,允许用户将字符串视为一系列的二进制位(bit),每个位可以是0或1.这种数据结构非常适合存储和操作大量布尔值状态的数据,具有高效的空间利用率和快速的位操作能力。

应用场景有:记录用户的签到情况、网站页面的访问次数,用户状态管理等。

设置与获取

GETBIT、SETBIT

统计

BITCOUNT,统计1的数量

查找

BITPOS 查找 0 或1首次出现的位置,可以指定范围。

位运算

BITOP。可以执行AND(逻辑并)、OR(逻辑或)、XOR(逻辑异或)、NOT(逻辑非)中的任意运算。

整数运算

BITFIELD,其子命令有SET、GET、INCRBY、OVERFLOW(用于处理溢出)。

表 位图相关操作命令

2.1 BITFIELD

用于处理整数相关运算。

命令格式:BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP | SAT | FAIL]

type: 指定数值的类型,需要以i或u位前缀,后跟位长度。例如u8, 表示无符号8位整数。i64,有符号64位整数。

value: 用于指定被设置的整数值,这个值的类型应该和type参数指定的类型一致。如果长度超过了type参数指定的类型,那么会将高位多出的长度阶段。例如 type指定位u4。 value位 17(1 0001),那么存储的值为0001。

offset: 设置起始偏移量,从0开始。可以是负数。 例如 5,表示从第6个二进制为开始。前面加上#号表示索引,表示第几个type指定的类型。 例如 type指定为 u4,那么#2 表示第3个4位无符号整数。

2.1.1 OVERFLOW

当使用SET 或 INCRBY子命令发生溢出时(即超出type指定的范围),可以在这些命令前面使用OVERFLOW 来指定溢出控制行为。

WRAP

回绕,默认值,向上溢出的整数值将从类型的最小值开始重新计算,向下溢出的整数值从类型的最大值重新计算。

SAT

饱和运算,向上溢出的整数值将被设置为类型最大值,向下溢出的整数则会被设置为类型的最小值。

FAIL

检测到计算会引发溢出时拒绝执行计算,并返回空值。

表 OVERFLOW子命令的可选参数处理溢出

2.2 示例

用户行为记录器:根据用户id记录用户是否执行某个操作,即在位图上,id 数值位上设置为1,表示该用户进行过操作。

public class ActionRecorder {

    private final static Jedis jedis = RedisPool.getRedis();
    private final static String ACTION_KEY = "action_recorder_bitmap";
    private final static String PERSON_ID_KEY = "action_recorder_person_id_string";

    public static long getPersonId(long num) {
        return jedis.incrBy(PERSON_ID_KEY,num);
    }

    public static void performBy(long userId) {
        jedis.setbit(ACTION_KEY,userId,true);
    }

    public static boolean isPerformedBy(long userId) {
        return jedis.getbit(ACTION_KEY,userId);
    }

    public static long countPerformed() {
        return jedis.bitcount(ACTION_KEY);
    }

    public static void main(String[] args) {
        performBy(getPersonId(1));
        performBy(getPersonId(5));
        performBy(getPersonId(5));

        System.out.println(isPerformedBy(1));
        System.out.println(isPerformedBy(0));
        System.out.println(isPerformedBy(6));
        System.out.println(isPerformedBy(11));
        System.out.println(isPerformedBy(12));
        System.out.println(countPerformed());
    }
}

布尔矩阵:由布尔值组成的矩阵,通常用于表示散列结构。

public class ZeroOneMatrix {

    private final static Jedis jedis = RedisPool.getRedis();
    private final static String MATRIX_KEY = "matrix_bitmap2";
    private final static long ROW_COUNT = 5;
    private final static long COL_COUNT = 6;

    private static long calculatePos(long row,long col) {
        if (row >= ROW_COUNT) throw new RuntimeException("行数超出界限");
        if (col >= COL_COUNT) throw new RuntimeException("列数超出界限");
        return row * COL_COUNT + col;
    }

    public static void setVal(long row,long col,boolean val) {
        jedis.setbit(MATRIX_KEY,calculatePos(row,col),val);
    }

    public static boolean getVal(long row,long col) {
        return jedis.getbit(MATRIX_KEY,calculatePos(row, col));
    }

    public static void show() {
        for (int i = 0; i < ROW_COUNT; i++) {
            StringBuilder sb = new StringBuilder();
            sb.append("row").append(i).append(":");
            for (int j = 0; j < COL_COUNT; j++) {
                sb.append(getVal(i,j)).append(" ");
            }
            System.out.println(sb);
        }
    }

    public static void main(String[] args) {
        setVal(0,4,true);
        setVal(1,5, true);
        setVal(4,0,true);
        show();
    }
}

3 地理坐标 GEO

本质是有序集合。每个元素包含三个属性:位置名称、经度、纬度。 经度和纬度会转换为一个hash值,这个相当于有序集合中的分值。

存储与获取

GEOADD、GEOPOS

距离

GEODIST

查找

GEORADIUS、GEORADIUSBYMEMBER(根据位置名称及半径范围查找)

hash值

GEOHASH

表 GEO 相关命令

3.1 示例

用户地理位置:用于存储用户位置(经纬度)信息,能计算用户直接的距离,用户周围的其他用户等。

public class GeoLocation {

    private final static Jedis jedis = RedisPool.getRedis();
    private final static String LOCATION_KEY = "location_geo";

    public static void setLocation(String city,double longitude,double latitude) {
        jedis.geoadd(LOCATION_KEY,longitude,latitude,city);
    }

    public static void showLocation(String city) {
        System.out.println(jedis.geopos(LOCATION_KEY,city));
    }

    public static void showDist(String city1,String city2) {
        System.out.println(jedis.geodist(LOCATION_KEY,city1,city2, GeoUnit.KM));
    }

    public static void nearby(String city,double radius) {
        System.out.println(jedis.georadiusByMember(LOCATION_KEY,city,radius,GeoUnit.KM));
    }

    public static void main(String[] args) {
        setLocation("北京",116.28,39.54);
        setLocation("上海",121.29,31.14);
        setLocation("广州",113.17,23.06);
        setLocation("深圳",114.03,22.34);

        showLocation("上海");
        showDist("北京","深圳");
        showDist("广州","深圳");
        showDist("深圳","深圳");

        nearby("深圳",200);
        nearby("深圳",1300);
    }
}


原文地址:https://blog.csdn.net/qq_25308331/article/details/144380562

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!