先来官网说明:
The SET command supports a set of options that modify its behavior:
- EX seconds – Set the specified expire time, in seconds.
- PX milliseconds – Set the specified expire time, in milliseconds.
- NX – Only set the key if it does not already exist.
- XX – Only set the key if it already exist.
- KEEPTTL – Retain the time to live associated with the key.
Note: Since the SET command options can replace SETNX, SETEX, PSETEX, it is possible that in future versions of Redis these three commands will be deprecated and finally removed.
按照官网说明,哪些setnx,setex,psetex后面都不要用了
一般是先获取锁,然后根据结果来决定是否改修改时间,现在这些可以一条指令完成,无需事务
set lock:Qbit true ex 5 nx
上面的加锁只是保证了当加锁者挂了之后锁会被自动释放,但问题是如果加锁者没有挂而是执行太久导致锁被释放,然后这个锁被第二个线程获取,那么锁会被第一个加锁者释放么?如果第一个加锁者使用DEL那么结果是会被释放的,为了解决这个问题,需要给锁加个标识(随机数),这样大家就只释放自己的锁,同时也可以感知到自己的超时,从而报错.书中给出的方案如下(lua)
tag =random.nextint()
if redis.set(key,tag,nx=True,ex=5):
do_something()
redis.delifequals(key,tag)
# delifequals
if redis.call("get",KEYS[1])==ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
spring的方案没有考虑这个问题,这里附上老版spring的解决方案
local lockClientId = redis.call('GET', KEYS[1])
if lockClientId == ARGV[1] then
redis.call('PEXPIRE', KEYS[1], ARGV[2])
return true
elseif not lockClientId then
redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
return true
end
return false
上面的lockClientId是一个uuid需要记下来.
新版的spring采用了set ex nx的语法,关键代码如下
RedisSerializer<String> serializer = RedisLockRegistry.this.redisTemplate.getStringSerializer();
byte[][] actualArgs = new byte[][] {
serializer.serialize(constructLockKey()),
RedisLockRegistry.this.lockSerializer.serialize(RedisLock.this),
serializer.serialize("NX"),
serializer.serialize("EX"),
serializer.serialize(String.valueOf(expireAfter))
};
return connection.execute("SET", actualArgs) != null;
注意这里nx在ex前面,也是可以的
这个命令也许用的不少,但是官网上还是提到不少骚操作.
按照官网介绍Redis是跟随系统时间来处理的,所以系统时间的更改就相当于时间的流逝.所以官网支持当迁移Redis的存储文件时,如果两个系统的时钟不一致,会导致一些key的失效,同样,如果修改了系统时间也会导致key失效,下面是原文说明:
Even running instances will always check the computer clock, so for instance if you set a key with a time to live of 1000 seconds, and then set your computer time 2000 seconds in the future, the key will be expired immediately, instead of lasting for 1000 seconds.
按照官方文档,很多对key的操作(但不是所有)是不会引起过期时间的改变,所以它不像Web开发中的Session那样可以自动延期.具体这些那些指令,我建议用到的时候再去翻官网
首先,由于Master和Replica都有过期时间,所以即使Replica没有等到Master的DEL指令,也不会返回过期的Key,其次这样保证了Master挂掉后,新的Master也能继续DEL过期的key
理论上,一个client在master加锁成功,然后master还没来得及告诉replica的时候自己就挂了,然后replica称为新的master后又会接受加锁,这样就会出现问题,于是出现了分布式锁
其简单原理是向无关联的redis发送**set(key,value,nx=true,ex=xxx)指令,大多数成功就认为加锁成功,当释放时需要向所有节点删除.这样大量的io必然带来性能下降
大多数人都知道有passive和active两种策略,其中passive确保了不会读到过期数据,而active则为了节省空间,官方提供了一个trivial probabilistic algorithm,其过程如下
Specifically this is what Redis does 10 times per second:
- Test 20 random keys from the set of keys with an associated expire.
- Delete all the keys found expired.
- If more than 25% of keys were expired, start again from step 1.
可以看出来,这种算法在垃圾回收消耗和内存使用中做出了一种平衡,并且做了一个统计上的假设.
另外主节点才会执行active策略,并将DEL指令写入AOF从而让从节点执行
对于批量设置了大量的key,如果仅仅考虑自动回收,难么把超时时间设置为不一样的随机数,避免同时过期.
这里说下Redis的Approx LRU,它是给所有key加了个最后一次被访问的时间戳,当触发被动回收(内存超了)就随机找几个(默认是5),干掉一个,看下内存超了没,超了就继续.到了Redis3.0有了一个淘汰池,就是把候选人和淘汰池里的再比较一次,有了一次获取缓刑的机会.
这是一个优化的回收的机制,内部会根据key大小来决定是否异步处理
事务在遇到指令执行失败后,后面的指令还会继续执行…Redis的事务根本不具备原子性,而仅仅是满足了事务隔离性中的串行化–当前事务有着不被其他事务打断的权利
管道的本质…客户端通过改变了读写的顺序带来的性能的巨大提升
所以一般事务会结合pipeline一起使用
watch是用在事务开启后,提交前防止数据被别人修改导致不一致,所以一般顺序是:
一旦multi后就不能watch了