redis使用文件事件处理器,是单线程的,它采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理
命令到达服务端以后不会立即执行,而是进入一个队列中,然后I/O多路复用程序通过队列向文件事件分派器传送socket
redis时间过期策略:
定期删除+惰性删除
定期删除:默认每隔100ms随机抽取过期的key进行删除,防止过期key太多占用内存
惰性删除:在查询的时候会判断是否已经过期,如果已经过期则删除
内存淘汰机制:当内存使用达到maxmemory时,触发主动清理策略 相关文章:https://www.cnblogs.com/zjoch/p/11149278.html
redis的持久化:
1、快照快照(snapshotting)持久化(RDB)存储某个时间点的副本,并且可以对快照进行备份,redis默认采用
2、只追加文件AOF(append-only file)持久化 可以通过 appendonly yes 指令开启 三种不同的AOF持久化方式:
1、每执行一条命令,都会写入硬盘中的AOF文件
2、每1s同步一次
3、让操作系统决定何时同步
优缺点:rdb快照文件比aof文件小一些,但是可能存在丢数据的问题
redis事物:
可以通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能
将多个命令打包,一次性,按顺序执行,在执行期间,不会中断事物去执行其他客户端的命令请求
具有原子性,一致性,隔离性,和持久性
缓存穿透:
大量请求的key不在redis中, 直接打到数据库上
解决办法:做好参数校验,不合法的参数直接抛异常
缓存雪崩:
缓存在同一时刻大面积失效,导致后续请求都落到了数据库上,造成数据库短时间内承受大量请求
原因:redis故障,或者一些访问量大的key在某一时刻大面积失效
解决办法:1、redis不可用: 1、采用redis集群,2、限流
2、热点缓存失效:1、设置不同的失效时间,比如随机设置缓存的失效时间 2、缓存永不失效
分布式锁
两种,setnx+lua 或者set key value px mill nx,nx表示if not exist 核心实现如下
- 获取锁(unique_value可以是UUID等)
SET resource_name unique_value NX PX 30000
- 释放锁(lua脚本中,一定要比较value,防止误解锁)
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
3大要点:1、set命令要用 set key value milliseconds nx; 2、value要具有唯一性 3、释放锁时要验证value值,不能误解锁
但是这个只能作用在一个redis节点上,因此基于分布式环境提出了一种更高级的分布式锁算法:Redlock,大体就是获取每个集群实例的锁,只有一半以上获取锁成功时,才认为成功,并且锁的有效时间为,锁有效时间-获取锁的时间-漂移时间
1、获取当前unix时间,以毫秒为单位
2、依次尝试从5个实例,使用相同的key和具有唯一性的value(比如uuid)获取锁,当向redis获取锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间,这样可以避免redis已经挂掉的情况下还在等待响应结果,如果响应超时,则继续请求另外的redis
3、如果取到了锁,则key的有效时间为有效时间减去获取锁所使用的时间,如果获取锁失败(没有在至少n/2 +1 个redis上获取锁或者取锁时间超过了有效时间),则客户端应该在所有的实例上进行解锁
相关代码:
// 实现分布式锁非常重要的一点就是set的value要具有唯一性
protected final UUID id = UUID.randomUUID();
String getLockName(long threadId) {
return id + ":" + threadId;
}
RFuture tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
// 获取锁时需要在redis实例上执行的lua命令
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
// 首先分布式锁的KEY不能存在,如果确实不存在,那么执行hset命令(hset REDLOCK_KEY uuid+threadId 1),并通过pexpire设置失效时间(也是锁的租约时间)
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 如果分布式锁的KEY已经存在,并且value也匹配,表示是当前线程持有的锁,那么重入次数加1,并且设置失效时间
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 获取分布式锁的KEY的失效时间毫秒数
"return redis.call('pttl', KEYS[1]);",
// 这三个参数分别对应KEYS[1],ARGV[1]和ARGV[2]
Collections.
https://www.cnblogs.com/rgcLOVEyaya/p/RGC_LOVE_YAYA_1003days.html