分布式锁
-
setnx lock:lock1 true
- lock:lock1:
通过 锁的前缀lock,
和锁的名字 lock1 组合成key - setnx:
如果不存在就设置 lock:lock1 为true,
否则就返回失败,
这里如果设置成功,则是抢占到锁。
- lock:lock1:
这种设置会有一点问题,
抢占到锁的程序必须要在处理完任务后释放锁,
但是如果这个程序崩溃了,
导致没有及时释放锁,
那么就会导致这个锁一直被占用,
所以我们要加个超时时间,
于是setnx lock:lock1 true
expire lock:lock1 5
不过这样还是有个问题,
因为抢占锁和设置超时不是原子性的,
所以会出现,
抢占锁成功了,
但是设置超时的时候,
程序崩溃,一样会出现死锁问题,
所以Redis2.8提供了:set lock:lock1 true ex 5 nx
这样设置是不是就完美了呢?
假设抢占锁成功,
但是执行了5s还是没完成任务,
这个时候锁被释放了~~~
那么怎么解决呢?
这个其实没有太好的办法,
但是我们可以这样设置锁,set lock:lock1 随机数 ex 5 nx
抢占锁的程序生成一个随机数,
作为该锁的一个标识,
完成任务后,要去验证下这个随机数是否还是自己生成的,
如果不是,任务就回滚。
但是因为我们无法保证这个验证的原子性,
所以并不能完全保证锁的正确性。
非精准去重 HyperLogLog
在一些大数据量的去重中可以使用到,
比如统计UV,
我们最简单的想法肯定是用一个Set保存 uid,
然后计数就可以了,
但是当uid 数量特别多的时候,
这种方式就有点不划算了,
这时候我们其实可以考虑下 HyperLogLog,
HyperLogLog 提供不精确的去重计数方案,
虽然不精确但是也不是非常不精确,
标准误差是 0.81%,
这样的精确度已经可以满足 UV 的 统计需求了。
使用如下:pfadd key user1
pfcount key
通过 pfadd 将用户id加入到 key 集合
通过 pfcount 来计算 key 集合里面有多少用户数
HyperLogLog还提供了pfmerge
指令,
可以将两个key合并,
这种场景其实也比较多,
比如 渠道1 有个用户计数,
渠道2也有个,
那么我要统计两个渠道的和,
就可以用的到了。
布隆过滤器
布隆过滤器相信应该很多人都知道,
Redis也是提供了相关的支持。
布隆过滤器是什么
简单的来说就是一个Set容器,
不过其去重是不精准的,
好处是存再多的数据,其体积也不大-
布隆过滤器原理
布隆过滤器有一个很长的全零数组 arr,
和 N多个 Hash算法 H1 H2 ... Hn,
当一个 key 被添加进来,
我们就通过这 N 个Hash算法算出N个值并与 数组arr长度取模,
得出N个值 N1,N2,Nn,
那么 arr 对应的位置置为 1,
当我们要判断 key2 是不是已经添加过,
我们也通过上面的方式,
看下一下数组 arr 各个位置上是不是都是1,
只要有一个是0,
我们就认为没有添加过,否则就是添加过。很简单的逻辑,
也可以看出这种算法是有一定的局限的,
极端情况下,
填入的key足够多,
可以把数组所有位置都置为1,
那么任何key都会被判定为已添加。
所以布隆过滤器要求我们:
Hash算法 得算的比较均匀,
数组得足够长,至少要比加入的元素个数要长。当然不管怎么样,其都不是保证100%精准的,
他只能保证,
如果判定不存在,那就是不存在,
但是如果判定存在,其实不一定存在 -
布隆过滤器的使用
bf.add key u1
添加元素bf.exists key u1
查询元素是否存在bf.madd key u1 u2
批量添加bf.mexists key u1 u2
批量检查
值得注意的是,
我们要注意 error_rate 和 initial_size:- error_rate:期望的错误率
- initial_size: 期望放入的元素个数
这两个值会极大影响布隆过滤器的使用效果,
具体原因不赘述了,
其设置方式如下bf.reserve key 0.01 100
bf.reserve 名字 error_rate initial_size