Redis从生米煮成熟饭
转自:www.qianshan.tech
Redis为何选用单线程
单线程减少线程上下文切换和锁竞争。
网络IO模型采用IO多路复用,使用EPOLL注册读写事件通知,同步非阻塞。
Redis单线程如何发挥多核CPU优势
在单台服务器上运行多个redis实例。 使用taskset命令,将每个redis实例和cpu核心进行绑定
Redis分布式锁实现
setnx key value 如果key不存在,则创建并赋值。成功返回1 失败返回0。 同时设置过期时间: expire key seconds
也可使用set命令配合NX EX选项一次性设置 SET key value EX 10086 NX
缓存雪崩、缓存击穿、缓存穿透
缓存雪崩: 大量的KEY过期时间过于集中,导致瞬时很多缓存失效,由此可能导致数据库压力陡然升高。
解决方案: 将失效时间随机打乱,如在系统启动预热时设定一定程度上离散的过期时间。
缓存击穿: 缓存中某一个KEY过期失效,如果此时有大量请求过来无法命中缓存的KEY,缓存层像被凿开了一个口子一样流入大量数据库查询请求
解决方案:双重校验方式从数据库中读取数据到缓存。双重校验:第一层查询缓存失败后,进入临界区,保证同时只有一个请求线程读取数据库,进入临界区后再次尝试缓存,仍然没有命中则查询数据库。
缓存穿透: 外部请求不断查询一个系统中不存在的数据,服务无法命中缓存转而每次尝试从数据库中查询。
解决方案:
- 对查询结果为空key设置值为null的缓存,牺牲缓存空间换响应时间。
- 把所有非法的key映射到一个bitmap中,通过bitmap拦截。《布隆过滤器》原理
Redis如何删除过期key
-
主动删除:redis 默认每隔一定时间检查已过期key进行删除, 或者内存不足时触发主动删除机制
-
惰性删除:在有请求读写key时再检查key是否过期,过期则删除
Redis如何持久化
AOF
AOF(Append Only File ): 记录每次redis的写命令,如对一个key更新10次,记录10条写指令。可以设置每秒一次或者每个写动作发生后追加。 优点: 持久化频率高,异常down机时数据丢失少。 缺点: 文件大,恢复时相对耗时。
Redis默认使用RDB持久化,可同时开启AOF持久化,如下
appendonly yes
appendfilename "appendonly.aof"
AOF重写
AOF文件体积过大时会进行重写。重写的作用是用一条命令去记录键值对,代替之前记录该键值对的多个命令。如一个key自增100次,原文件记录了100条指令,重写后将原来的指令合并成为最新的一条,由此大大压缩AOF文件体积。
重写采用写时赋值(Copy On Write)原理,不直接操作原文件。
RDB
RDB:快照持久化。在特点时间点保存全量的数据信息。 优点: 恢复时直接将快照文件加载到内存,速度快。 缺点: 因为全量数据量大,持久化频率一般设置较低。异常关机时会丢失上次持久化到关机时刻的变更数据。
如RDB方式配置
save 900 1 #在900s内如果有1条数据被写入,则产生一次快照。
save 300 10#在300s内如果有10条数据被写入,则产生一次快照
save 60 10000 #在60s内如果有10000条数据被写入,则产生一次快照
stop-writes-on-bgsave-error yes # 如果为yes则表示,当备份进程出错的时候, 主进程就停止进行接受新的写入操作,这样是为了保护持久化的数据一致性的问题。
bgsave
也可通过basave命令手动触发快照的持久化。bgsave fork出子进程,采用写时复制机制写入。不阻塞主进程的工作(对比save命令)。
AOF和RDB可搭配同时使用,开启AOF时,redis启动默认从AOF日志文件中重建缓存。
Redis集群方案
- Sentinel: 哨兵模式,主要实现高可用。解决Redis原有的主从同步模式,当Master实例故障时导致整个集群无法工作的缺陷。Sentinel是一个独立运行的进程,检测Redis集群状态,如Master因故障下线时则在集群机器中发起选举选出新的Master节点。
- Cluster: 解决单台主机的内存容量有限。 Redis集群定义有16384个哈希槽,每个节点负责一段。每个key通过CRC16校验后对16384取模来决定放置哪个分片。
Redis和数据库不一致问题
数据库更新时需要触发缓存更新
如果先删除缓存再进行数据库更新, 可能在数据库更新之前,新的请求进来因为没有命中缓存从数据库中读取旧数据。 如果先进行数据库更新在删除缓存,可能出现数据库写入成功后删除缓存失败。导致缓存仍然是旧数据。
网上流传双删策略: 先删除缓存–> 数据库写入 ——》再次删除缓存。
好处是能数据更新前能预检查下redis工作状态,但是关键的问题:写入后删除缓存时失败的情况仍然可能存在。根本的解决方案应该在写入后删除失败时增加重试机制,或者回滚数据库更新。
Redis内存淘汰策略
Redis使用内存超过 maxmemory 配置大小时触发内存淘汰策略。
- noeviction:不删除策略,内存达到上限时直接返回错误信息
- allkeys-random: 针对所有的key,随机删除一部分
- allkeys-lru: 针对所有的key,优先删除最少使用的
- volatile-random: 针对设置了过期时间的key,随机删除一部分
- volatile-ttl: 针对设置了过期时间的key,优先删除最快过期的key