传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。
命令 | 描述 |
---|---|
pfadd key element1 [elememt2..] |
添加指定元素到 HyperLogLog 中 |
pfcount key [key] |
返回给定 HyperLogLog 的基数估算值。 |
pfmerge mergekey sourcekey [sourcekey..] |
将多个 HyperLogLog 合并为一个 HyperLogLog |
# 添加元素和统计个数
127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素
(integer) 1
127.0.0.1:6379> type myelemx # hyperloglog底层使用String
string
127.0.0.1:6379> PFCOUNT myelemx # 估算myelemx的基数
(integer) 11
127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s
(integer) 1
127.0.0.1:6379> PFCOUNT myelemy
(integer) 11
# 合并
127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz
OK
127.0.0.1:6379> PFCOUNT myelemz # 估算基数
(integer) 17
opsForHyperLogLog().add(pfKey, i)
-添加数据opsForHyperLogLog().size(pfKey)
-统计数据的基数/**
* 测试对HyperlogLog的操作
*/
@Test
public void testHyperLogLog() {
// 添加100 000个不重复的数、100 000个重复的数-共20万个数
String pfKey = "test:hll:01";
for (int i = 0; i < 100000; i++) {
redisTemplate.opsForHyperLogLog().add(pfKey, i);
}
for (int i = 0; i < 100000; i++) {
int r = (int)(Math.random() * 100000);
redisTemplate.opsForHyperLogLog().add(pfKey, r);
}
// 统计指定key中所有不重复的基数个数
long size = redisTemplate.opsForHyperLogLog().size(pfKey);
System.out.println(size);
}
99565
opsForHyperLogLog().union(unionKey, pfKey2, pfKey3, pfKey4)
/**
* 合并数据-并统计合并后的基数
*/
@Test
public void testHyperLogLogUnion() {
String pfKey2 = "test:hll:02";
String pfKey3 = "test:hll:03";
String pfKey4 = "test:hll:04";
for (int i = 0; i < 10000; i++) {
redisTemplate.opsForHyperLogLog().add(pfKey2, i);
}
for (int i = 5000; i < 15000; i++) {
redisTemplate.opsForHyperLogLog().add(pfKey3, i);
}
for (int i = 10000; i < 20000; i++) {
redisTemplate.opsForHyperLogLog().add(pfKey4, i);
}
// 合并三组数
String unionKey = "test:hll:union";
redisTemplate.opsForHyperLogLog().union(unionKey, pfKey2, pfKey3, pfKey4);
// 统计合并后的基数
long size = redisTemplate.opsForHyperLogLog().size(unionKey);
System.out.println(size);
}
19891
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),
在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用
Bitmaps!
命令 | 描述 |
---|---|
setbit key offset value |
为指定key的offset位设置值 |
getbit key offset |
获取offset位的值 |
bitcount key [start end] |
统计字符串被设置为1的bit数,也可以指定统计范围按字节 |
bitop operration destkey key[key..] |
对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 |
BITPOS key bit [start] [end] |
返回字符串里面第一个被设置为1或者0的bit位。start和end只能按字节,不能按位 |
# 添加数据和获取数据
127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1 # 设置sign的第2位为 1 不设置默认 是0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> type sign # 底层也是String类型
string
127.0.0.1:6379> getbit sign 2 # 获取第2位的数值
(integer) 1
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 4 # 未设置默认是0-及false
(integer) 0
# 统计数据中为1的个数-即为true的个数
127.0.0.1:6379> BITCOUNT sign # 统计sign中为1的位数
(integer) 4
opsForValue().setBit(bitKey, 1, true)
- 设置状态opsForValue().getBit(bitKey, 0))
- 查询状态redisConnection.bitCount(bitKey.getBytes())
- 统计状态为true的个数/**
* 测试对BitMaps的操作
* 记录-查询和统计
*/
@Test
public void testBitMap() {
String bitKey = "test:bit:01";
// 记录数据状态-默认false
redisTemplate.opsForValue().setBit(bitKey, 1, true);
redisTemplate.opsForValue().setBit(bitKey, 4, true);
redisTemplate.opsForValue().setBit(bitKey, 7, true);
// 查询
System.out.println(redisTemplate.opsForValue().getBit(bitKey, 0));
System.out.println(redisTemplate.opsForValue().getBit(bitKey, 1));
System.out.println(redisTemplate.opsForValue().getBit(bitKey, 2));
// 统计
Object execute = redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
return redisConnection.bitCount(bitKey.getBytes());
}
});
System.out.println(execute);
}
false
true
false
3
connection.bitOp()
- 位运算@Nullable
Long bitOp(RedisStringCommands.BitOperation var1, byte[] var2, byte[]... var3);
OR运算
connection.bitOp(RedisStringCommands.BitOperation.OR,
bitKeyOR.getBytes(), bitKey2.getBytes(), bitKey3.getBytes(), bitKey4.getBytes());
/**
* OR运算
* 统计3组数据的布尔值, 并对这3组数据做OR运算.
*/
@Test
public void testBitMapOperation() {
String bitKey2 = "test:bm:02";
redisTemplate.opsForValue().setBit(bitKey2, 0, true);
redisTemplate.opsForValue().setBit(bitKey2, 1, true);
redisTemplate.opsForValue().setBit(bitKey2, 2, true);
String bitKey3 = "test:bm:03";
redisTemplate.opsForValue().setBit(bitKey3, 2, true);
redisTemplate.opsForValue().setBit(bitKey3, 3, true);
redisTemplate.opsForValue().setBit(bitKey3, 4, true);
String bitKey4 = "test:bm:04";
redisTemplate.opsForValue().setBit(bitKey4, 4, true);
redisTemplate.opsForValue().setBit(bitKey4, 5, true);
redisTemplate.opsForValue().setBit(bitKey4, 6, true);
// 合并处理
String bitKeyOR = "test:bm:or";
Object obj = redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.bitOp(RedisStringCommands.BitOperation.OR,
bitKeyOR.getBytes(), bitKey2.getBytes(), bitKey3.getBytes(), bitKey4.getBytes());
return connection.bitCount(bitKeyOR.getBytes());
}
});
System.out.println(obj); // 统计的个数
// 合并后,每位的状态
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 0));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 1));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 2));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 3));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 4));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 5));
System.out.println(redisTemplate.opsForValue().getBit(bitKeyOR, 6));
}
7
true
true
true
true
true
true
true