Redis HyperLogLog

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
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 的工作过程:
redis 127.0.0.1:6379> PFADD w3ckey “redis”

1) (integer) 1

redis 127.0.0.1:6379> PFADD w3ckey “mongodb”

1) (integer) 1

redis 127.0.0.1:6379> PFADD w3ckey “mysql”

1) (integer) 1

redis 127.0.0.1:6379> PFCOUNT w3ckey

(integer) 3
Redis HyperLogLog 命令
下表列出了 redis HyperLogLog 的基本命令:
序号 命令及描述
1 PFADD key element [element …]
添加指定元素到 HyperLogLog 中。
2 PFCOUNT key [key …]
返回给定 HyperLogLog 的基数估算值。
3 PFMERGE destkey sourcekey [sourcekey …]
将多个 HyperLogLog 合并为一个 HyperLogLog

package com.hong.api.redis;

import redis.clients.jedis.Jedis;

public class JedisUserVisitTest {
    private Jedis jedis = null;

    public JedisUserVisitTest(String host, int port) {
        jedis = new Jedis(host, port);
    }

    public void setHyperLogLog(String key, String value) {
        jedis.pfadd(key, value);
    }

    public void setHyperLogLog(String key, String[] values) {
        jedis.pfadd(key, values);
    }

    public Jedis getJedis() {
        return jedis;
    }

    public void setJedis(Jedis jedis) {
        this.jedis = jedis;
    }

    public static void main(String[] args) {
        JedisUserVisitTest jedisUserVisitTest = new JedisUserVisitTest("192.168.109.133", 6379);
        Gen3 rand = new Gen3();
        int size = 1000000 + 1;
        String[] arr = new String[size];

        int top = 0;
        for (long i = 0; i < 50000000000L; ++i) {
            arr[top++] = rand.next() + "";

            if (top == size) {
                top = 0;
                jedisUserVisitTest.setHyperLogLog("visit", arr);
            }
        }

        if (top != size) {
            top = 0;
            jedisUserVisitTest.setHyperLogLog("visit", arr);
        }

        System.out.println(jedisUserVisitTest.getJedis().pfcount("visit"));
    }
}
package com.hong.api.redis;

public class Gen3 {
    private long[] state = new long[624];
    private int left = 1;

    public Gen3() {
        for (int j = 1; j < 624; j++) {
            state[j] = (1812433253L * (state[j - 1] ^ (state[j - 1] >> 30)) + j);
            state[j] &= 0xfffffffffL;
        }
    }

    private static final long twist(long u, long v) {
        return (((u & 0x80000000L) | (v & 0x7fffffffL)) >> 1) ^ ((v & 1) == 1 ? 0x9908b0dfL : 0);
    }

    public void next_state() {
        int p = 0;
        left = 624;
        for (int j = 228; --j > 0; p++)
            state[p] = state[p + 397] ^ twist(state[p], state[p + 1]);

        for (int j = 397; --j > 0; p++)
            state[p] = state[p - 227] ^ twist(state[p], state[p + 1]);

        state[p] = state[p - 227] ^ twist(state[p], state[0]);
    }

    public long next() {
        if (--left == 0)
            next_state();
        return state[624 - left];
    }

    public static void main(String[] args) {
        Gen3 rand = new Gen3();
        long a = System.currentTimeMillis();
        long max = 0;
        for (long i = 0; i < 50000000000L; i++) {
            // System.out.println(rand.next());
            long ans = rand.next();
            if (ans > max)
                max = ans;
        }
        long b = System.currentTimeMillis();
        System.out.println(b - a);
        System.out.println(max);
    }
}

你可能感兴趣的:(Redis HyperLogLog)