一、 Redis分布式锁**
1、分布式锁
redis:setnx key value 获取锁
del key 释放锁
2、如何避免死锁?
死锁产生原因: 针对其中一个服务获取锁资源,此刻服务宕机,那么锁住的key 一直在redis,此刻将会产生死锁
解决:设置过期时间,超过这个时间就删除key
1)set key value ex n nx 设置 n秒的过期时间
以上解决方式引起另一个问题【锁过期和释放其他服务锁】:
假如A业务复杂n秒内并未执行完,锁就自动被redis释放了,此刻B获取锁,服务B执行自己业务,服务A在执行到到第12秒时执行完,那么服务A会去释放锁,此时释放的是B刚获得的锁
3、锁过期如何处理?
Redisson(Redis SDK客户端)中,自动续时功能:另起守护线程,监测服务在锁快要过期时间内业务是否处理完成,没有完成,自动续过期时间
4、释放其他服务的锁如何解决?
每个服务在设置key的时候,带上自己服务的唯一标识,这样在删除的时候只删除自己服务之前添加key就可以了;
如果需要先查看锁是否本服务添加,则要先get,再del,这样无法保证原子性,可以通过lua脚本,两个操作合并成一个操作,既可以保证原则性
以上单redis情况基本能实现分布式锁功能,但是集群环境就会有影响:
比如 当主从节点中,主机宕机,主从切换过程中,旧主节点数据并未完全同步到新的主节点,而锁还在旧主节点,这样其他服务就可以从新的主节点获取锁,并加锁成功,此刻A和B服务将可以操作公共资源
5、redLock(不建议使用,成本大,建议用业务兜底)
需要至少5个实例(官方推荐),且每个实例都是master,不需要从库和哨兵。
实现流程
1、客户端先获取当前时间戳T1
2、客户端依次向5个master实例发起加锁命令,且每个请求都会设置超时时间(毫秒级,注意:不是锁的超时时间),如果某一个master实例由于网络等原因导致加锁失败,则立即想下一个master实例申请加锁。
3、当客户端加锁成功的请求大于等于3个时,且再次获取当前时间戳T2,
当时间戳T2 - 时间戳T1 < 锁的过期时间。则客户端加锁成功,否则失败。
4、加锁成功,开始操作公共资源,进行后续业务操作
5、加锁失败,向所有redis节点发送锁释放命令
6、分布式中NPC问题?
N:网络延迟 P:集成暂停(GC) C:时钟漂移
二 、Zookeeper分布式锁
1、zookeeper优缺点
优点:1、不用设置过期时间
2、时间监听机制,加锁失败后,可以等待锁释放
缺点:1、性能不如redis
2、网络不稳定时,可能会有多个节点同时获锁问题,如:节点1由于网络波动,导致zk将其删除【临时节点客户端关闭会自动删除】,刚好节点2获得锁,name节点1和节点2 都会获得锁
2、redis优缺点
1) 优点:性能比较好,支持天然高并发
2) 缺点:获取锁失败后,得轮询的去获取锁
大多数情况下无法保证数据强一致性
三、redis缓存击穿,缓存穿透,缓存雪崩
1、缓存击穿(数据库有):某一时刻热点数据失效,大量请求打到数据库上
- 单机:设置过期时间,业务耗时长,开启守护线程监听失效时间,重置时间,双机:redlock
2、缓存穿透:缓存穿透(数据库无):查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询
额外开辟set,用来专门存储key(bit),节省内存容量,且避免穿透
布隆过滤器
3、缓存雪崩:大面积的key同时过期,导致大量并发打到数据库
- 分散key的过期时间
- 业务读取数据库时间设置短暂的随机延迟时间
四、redis持久化:RDB和AOF
1、RDB:
指定间隔时间内将内存中的数据集快照写入磁盘,恢复时按照快照文件直接读取到内存,,可以手动或者自动触发
1.1备份如何执行?
redis会单独户创建fork子进程进行持久化,将数据写入临时文件,等持久化过程都结束,再讲临时文件替换之前持久化好的文件。针对数据恢复完整性要求不高,RDB更加高效,缺点是最后一次持久化数据可能丢失
1.2手动触发
Save和bgSave命令可以触发RDB持久化
1)save持久化会阻塞redis服务,知道RDB持久化完成,不建议使用
2)bgsave:redis会进程执行fork操作创建子进程,RDB持久化由子进程负责,不会阻塞redis服务进程,阻塞也只是发生在fork进程阶段,一般时间很短
1.3 自动触发(bgsave)
1)配置文件设置相关配置:save m n 标识m秒内被修改过n次时,自动触发bgsave
2)主从节点全量复制时,主节点自动执行bgsave操作,将RDB发送从节点
3)执行debug reload命令时
4)执行shutdown命令,且没有开启AOF持久化时
1.4 RDB优缺点
1)优点:RDB文件是二进制压缩文件,是某个时间点的全部数据库快照,恢复速度比AOF快,适合备份,全量复制,灾难恢复场景
2)缺点:
i:执行bgsave操作都要执行fork,属于重量级操作,频繁执行成本高,无法做到实时持久化,或者秒级持久化
II:redis版本更替块,不同格式RDB版本不一定兼容
2、AOF【写后日志】:
每次写命令追加写入日志中,当需要恢复数据时重新执行AOF文件中的命令就可以了。AOF解决了数据持久化的实时性,也是目前主流的Redis持久化方式。
2.1 写后日志优缺点?
-
避免额外的检查开销:不存在错误命令
-
不会阻塞当前的写操作
-
存在风险:
1)如果命令完成,写日志前宕机,则会丢失数据
2)主线程写磁盘压力大,导致写盘满,阻塞后续操作
2.2如何实现AOF?
AOF日志记录Redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。
- 命令追加 当AOF持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。
- 文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入AOF文件中,Redis提供了三种写回策略:
2.3 AOF重写会阻塞主进程么?
AOf重写过程由后台进程bgrewriteaof完成,fork会将主线程的内存拷贝一份给bgwriteAof子进程,包含了数据库最新数据,然后子进程在不影响主进程情况下,将拷贝数据记入重写日志,故不阻塞主线程
2.4 AOF何时会重写?
- auto-aof-rewrite-min-size:表示运行AOF重写时文件的最小大小,默认为64MB。
- auto-aof-rewrite-percentage:这个值的计算方式是,当前aof文件大小和上一次重写后aof文件大小的差值,再除以上一次重写后aof文件大小。也就是当前aof文件比上一次重写后aof文件的增量大小,和上一次重写后aof文件大小的比值。
五、 主从如何实现数据一致
1、全量复制、基于长连接的命令传播、增量复制
执行replicaof 命令形成主从关系,复制数据;
1)第一阶段:从发送psync ? -1 [psync runid offset],与主库建立连接,协商同步 【注:第一次还不不知道主库的复制id,复制offset首次为-1】
主库接收到从库的psync命令后,发送fullresync {runnid} {offset},返回从库
2)第二阶段:主库将rdb文件中的所有数据同步给从库,从库接收数据后,本地完成数据加载【bgsave命令执行】
3)第三阶段:第二阶段执行过程中新收到的命令【replication_buffer中新修改的操作】,在rdb文件发送后,同步到从库
2、分担主库全局复制压力方式?主从级联【主-从-从模式】
replicaof 从库ip 端口,
3、主从库断连解决方案?
主库会将锻炼期间的写操作写入replication_buffer,同时也写入replication_backlog_buffer缓冲区【环形缓冲区】
主库记录自己写到的位置,从库记录自己走读的位置,master_repl_offset【主库写操作位置】,slave_repl_offset从苦苦读操作位置,当主从恢复连接是,从库向主库发送psync ,把slave_repl_offset发送主库,主库将master_repl_offset和slave_repl_offset之间的操作同步从库就行,
注意:主库写锁速度大于从库速度将会导致数据被覆盖
解决办法:调整repl_backlog_size=缓冲空间*2
缓冲空间=主库写入速度操作大小-主从网络传输速度操作大小
六、主库挂了,如何不间断服务?
1、哨兵监控:定期向主从库发送ping命令,检测是否仍在线状态,如果没有响应判断为主观下线
当哨兵数有n/2+1,且大于等于配置文件中的quorum则认定主库客观下线,
2、选主库:筛选+打分
筛选: 判断从库在线状态,网络连接状态【down-after-miliseconds*10 主从锻炼的最大连接超时时间】
打分:1)优先级最高的从库得分高【slave-priority】
2)与旧主库同步程度最接近的从库得分高
3)id号小的从库得分高
3、通知
哨兵将新连接信息发送其他从库,让他他们执行replicaof命令和新主库建立连接,并进行数据复制,
同时将新新主库信息通知客户端,将请求操作发送到新主库
七、哨兵挂了,主库还能切换么?
1、哨兵之间如何相互发现?
哨兵和主库建立起连接 【sentinel monitor mastername ip 端口号 quorum】,在主库发布自己连接信息,或者订阅其他哨兵的连接信息,这样他们就可以彼此建立连接
2、哨兵如何知道从库ip地址端口号?
哨兵向主库发送info命令,主库介绍到命令将从库里列表返回哨兵,哨兵获取列表信息,同从库建立连接,并且对从库监控
3、如何决定哪个哨兵执行主从切换
通过投票选举leader方式
4、客户端如何知道新主库的连接信息?
客户端从哨兵订阅主库下线判断,新主库选定,从库重新配置等相关频道消息
八、redis变慢方法检查:
1、获取redis在当前环境下的基线性能
./redis-cli --intrinsic-latency 120
2、是否适用慢查询命令,
-
**当你需要执行排序、交集、并集操作时,**可以在客户端完成,而不要用 SORT、SUNION、SINTER 这些命令,以免拖慢 Redis 实例。
-
因为 KEYS 命令需要遍历存储的键值对,所以操作延时高
- 用其他高效命令代替。比如说,如果你需要返回一个 SET 中的所有成员时,不要使用 SMEMBERS 命令,而是要使用 SSCAN 多次迭代返回,避免一次返回大量数据,造成线程阻塞。
3、是否对过期 key 设置了相同的过期时间?对于批量删除的 key,可以在每个 key 的过期时间上加一个随机数,避免同时删除。
4、是否存在 bigkey? 对于 bigkey 的删除操作,如果你的 Redis 是 4.0 及以上的版本,可以直接利用异步线程机制减少主线程阻塞;如果是 Redis 4.0 以前的版本,可以使用 SCAN 命令迭代删除;对于 bigkey 的集合查询和聚合操作,可以使用 SCAN 命令在客户端完成。
5、Redis AOF 配置级别是什么?业务层面是否的确需要这一可靠性级别?如果我们需要高性能,同时也允许数据丢失,可以将配置项 no-appendfsync-on-rewrite 设置为 yes,避免 AOF 重写和 fsync 竞争磁盘 IO 资源,导致 Redis 延迟增加。当然, 如果既需要高性能又需要高可靠性,最好使用高速固态盘作为 AOF 日志的写入盘。
6、Redis 实例的内存使用是否过大?发生 swap 了吗?如果是的话,就增加机器内存,或者是使用 Redis 集群,分摊单机 Redis 的键值对数量和内存压力。同时,要避免出现 Redis 和其他内存需求大的应用共享机器的情况。
7、在 Redis 实例的运行环境中,是否启用了透明大页机制?如果是的话,直接关闭内存大页机制就行了。
8、是否运行了 Redis 主从集群?如果是的话,把主库实例的数据量大小控制在 2~4GB,以免主从复制时,从库因加载大的 RDB 文件而阻塞。
9、是否使用了多核 CPU 或 NUMA 架构的机器运行 Redis 实例?使用多核 CPU 时,可以给 Redis 实例绑定物理核;使用 NUMA 架构时,注意把 Redis 实例和网络中断处理程序运行在同一个 CPU Socket 上。