作者:
逍遥Sean
简介:一个主修Java的Web网站\游戏服务器后端开发者
主页:https://blog.csdn.net/Ureliable
觉得博主文章不错的话,可以三连支持一下~ 如有需要我的支持,请私信或评论留言!
前言:
如果需要在linux环境中搭建一个redis服务参考:
Linux环境下搭建使用缓存中间件Redis
如果需要掌握redis的常考知识点参考:
缓存中间件Redis必知必会
RDB:Redis DataBase
,在指定的时间间隔内将内存中的数据集快照写⼊磁盘,实际操作过程是fork⼀个⼦进程,先将数据集写⼊临时⽂件,写⼊成功后,再替换之前的⽂件,⽤⼆进制压缩存储。
优点:
- 整个Redis数据库将只包含⼀个⽂件
dump.rdb
,⽅便持久化。- 容灾性好,
⽅便备份
。性能最⼤化
,fork ⼦进程来完成写操作,让主进程继续处理命令,所以是 IO 最⼤化。使⽤单独⼦ 进程来进⾏持久化,主进程不会进⾏任何 IO 操作,保证了 redis 的⾼性能- 相对于数据集⼤时,⽐ AOF 的
启动效率更⾼
。
缺点:
- 数据
安全性低
。RDB 是间隔⼀段时间进⾏持久化,如果持久化之间 redis 发⽣故障,会发⽣数据丢 失。所以这种⽅式更适合数据要求不严谨的时候)- 由于RDB是通过fork⼦进程来协助完成数据持久化⼯作的,因此,如果当数据集较⼤时,可能会
导致整个服务器停⽌服务
⼏百毫秒,甚⾄是1秒钟。
AOF:Append Only File
,以⽇志的形式记录服务器所处理的每⼀个写、删除操作,查询操作不会记录,以⽂本的⽅式记录,可以打开⽂件看到详细的操作记录
优点:
数据安全
,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也 是异步完成的,其效率也是⾮常⾼的,所差的是⼀旦系统出现宕机现象,那么这⼀秒钟之内修改的数据将会丢失。⽽每修改同步,我们可以将其视为同步持久化,即每次发⽣的数据变化都会被⽴即 记录到磁盘中。。- 通过 append 模式写⽂件,
即使中途服务器宕机也不会破坏已经存在的内容
,可以通过 redischeck-aof ⼯具解决数据⼀致性问题。- AOF 机制的
rewrite 模式
。定期对AOF⽂件进⾏重写
,以达到压缩的⽬的
缺点:
- AOF ⽂件⽐ RDB
⽂件⼤,且恢复速度慢
。- 数据集⼤的时候,⽐ rdb 启动效率低。
运⾏效率没有RDB⾼
AOF⽂件⽐RDB更新频率⾼,优先使⽤AOF还原数据,AOF⽐RDB更安全也更⼤,RDB性能⽐AOF好,如果两个都配了优先加载AOF
Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
惰性过期:只有当
访问⼀个key时,才会判断该key是否已过期
,过期则清除。该策略可以最⼤化地节省CPU资源,却对内存⾮常不友好。极端情况可能出现⼤量的过期key没有再次被访问,从⽽不 会被清除,占⽤⼤量内存。
定期过期:每隔⼀定的时间
,会扫描⼀定数量的数据库的expires字典中⼀定数量的key,并清除其中已过期的key
。该策略是⼀个折中⽅案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有 键。)
Redis中同时使⽤了惰性过期和定期过期两种过期策略。
1、事务开始
MULTI
命令的执⾏,标识着⼀个事务的开始。MULTI命令会将客户端状态的 flags 属性中打开 REDIS_MULTI标识来完成的。
2、命令⼊队
当⼀个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执⾏不同的操作。如果客户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的⼀个,⽴即执⾏这个命令,否则将命令放⼊⼀个事务队列⾥⾯,然后向客户端返回QUEUED 回复
- 如果客户端发送的命令为 EXEC、DISCARD、WATCH、MULTI 四个命令的其中⼀个,那么服务 器⽴即执⾏这个命令。
- 如果客户端发送的是四个命令以外的其他命令,那么服务器并不⽴即执⾏这个命令。⾸先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的flags 属性关闭 REDIS_MULTI 标识,并且返回错误信息给客户端。如果正确,将这个命令放⼊⼀个事务队列⾥⾯,然后向客户端返回QUEUED 回复
事务队列是按照FIFO的⽅式保存⼊队的命令
3、事务执⾏ 客户端发送 EXEC 命令,服务器执⾏ EXEC 命令逻辑。
- 如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者 REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执⾏。
- 否则客户端处于事务状态(flags 有 REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然后执⾏事务队列中的所有命令,最后将返回结果全部返回给客户端;
redis不⽀持事务回滚机制,但是它会检查每⼀个事务中的命令是否错误。
Redis 事务不⽀持检查那些程序员⾃⼰逻辑错误。例如对 String 类型的数据库键执⾏对 HashMap 类型 的操作!
WATCH
命令是⼀个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)⾏为。可以监控⼀个或多个键,⼀旦其中有⼀个键被修改(或删除),之后的事务就不会执⾏,监控⼀直持续到EXEC 命令。MULTI
命令⽤于开启⼀个事务,它总是返回OK。MULTI执⾏之后,客户端可以继续向服务器发送任意多条命令,这些命令不会⽴即被执⾏,⽽是被放到⼀个队列中,当 EXEC命令被调⽤时,所有队列 中的命令才会被执⾏。EXEC
:执⾏所有事务块内的命令。返回事务块内所有命令的返回值,按命令执⾏的先后顺序排列。 当操作被打断时,返回空值 nil 。通过调⽤DISCARD,客户端可以清空事务队列,并放弃执⾏事务, 并且客户端会从事务状态中退出。UNWATCH
命令可以取消watch对所有key的监控。
通过执⾏slaveof
命令或设置slaveof选项,让⼀个服务器去复制另⼀个服务器的数据。主数据库可以进⾏读写操作,当写操作导致数据变化时会⾃动将数据同步给从数据库。⽽从数据库⼀般是只读的,并接受主数据库同步过来的数据。⼀个主数据库可以拥有多个从数据库,⽽⼀个从数据库只能拥有⼀个主数据库。
全量复制:
bgsave命令fork⼦进程进⾏RDB持久化
,该过程是⾮常消耗CPU、内存(⻚表复制)、硬盘IO的RDB⽂件发送给从节点
,对主从节点的带宽都会带来很⼤的消耗清空⽼数据
、载⼊新RDB⽂件
的过程是阻塞的,⽆法响应客户端的命令;如果从节点执⾏bgrewriteaof,也会带来额外的消耗部分复制:
复制偏移量
:执⾏复制的双⽅,主从节点,分别会维护⼀个复制偏移量offset
复制积压缓冲区
:主节点内部维护了⼀个固定⻓度的、先进先出(FIFO)队列 作为复制积压缓冲区,服务器运⾏ID(runid)
:每个Redis节点,都有其运⾏ID,运⾏ID由节点在启动时⾃动⽣成,主节点会将⾃⼰的运⾏ID发送给从节点,从节点会将主节点的运⾏ID存起来。 从节点Redis断开重连的时候,就是根据运⾏ID来判断同步的进度:
setnx
来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁redis操作的原⼦性
锁过期
,所以需要额外的⼀个看⻔狗定时任务来监听锁是否需要续约redis节点挂掉
后的情况,所以需要采⽤红锁的⽅式来同时向N/2+1个节点申请锁,都申请到了才证明获取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到Redis的主从复制是提⾼Redis的可靠性的有效措施,主从复制的流程如下:
主从库间会先建⽴连接
,为全量复制做准备主库将所有数据同步给从库
。从库收到数据后,在本地完成数据加载,这个过程依赖于内存快照RDB主库会把第⼆阶段执⾏过程中新收到的写命令,再发送给从库
。具体的操作是,当主库完成RDB⽂件发送后,就会把此时replocation buffer中修改操作发送给从库,从库再执⾏这些操作。这样⼀来,主从库就实现同步了实现增量同步
Redis提供了三种集群策略:
主从模式
:这种模式⽐较简单,主库可以读写,并且会和从库进⾏数据同步,这种模式下,客户端直接连主库或某个从库,但是但主库或从库宕机后,客户端需要⼿动修改IP,另外,这种模式也⽐较难进⾏扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能⽀持特⼤数据量哨兵模式
:这种模式在主从的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕机,然后在从库中选择⼀个库作为进的主库,另外哨兵也可以做集群,从⽽可以保证但某⼀个哨兵节点宕机后,还有其他哨兵节点可以继续⼯作,这种模式可以⽐较好的保证Redis集群的⾼可⽤,但是仍然不能很好的解决Redis的容量上限问题。Cluster模式
:Cluster模式是⽤得⽐较多的模式,它⽀持多主多从,这种模式会按照key进⾏槽位的分配,可以使得不同的key分散到不同的主节点上,利⽤这种模式可以使得整个集群⽀持更⼤的数据Redis分布式锁底层是如何实现的?Redis主从复制的核⼼原理Redis集群策略容量,同时每个主节点可以拥有⾃⼰的多个从节点,如果该主节点宕机,会从它的从节点中选举⼀个新的主节点。缓存中存放的⼤多都是热点数据,⽬的就是防⽌请求可以直接从缓存中获取到数据,⽽不⽤访问Mysql。
雪崩
:如果缓存中某⼀时刻⼤批热点数据同时过期,那么就可能导致⼤量请求直接访问Mysql了
,解决办法就是在过期时间上增加⼀点随机值,另外如果搭建⼀个⾼可⽤的Redis集群也是防⽌缓存雪崩的有效⼿段击穿
:和缓存雪崩类似,缓存雪崩是⼤批热点数据失效,⽽缓存击穿是指某⼀个热点key突然失效,也导致了⼤量请求直接访问Mysql
数据库,这就是缓存击穿,解决⽅案就是考虑这个热点key不设过期时间穿透
:假如某⼀时刻访问redis的⼤量key都在redis中不存在
(⽐如⿊客故意伪造⼀些乱七⼋糟的key),那么也会给数据造成压⼒,这就是缓存穿透,解决⽅案是使⽤布隆过滤器,它的作⽤就是如果它认为⼀个key不存在,那么这个key就肯定不存在,所以可以在缓存之前加⼀层布隆过滤器来拦截不存在的key延时双删
,步骤是:先删除Redis
缓存数据,再更新Mysql
,延迟⼏百毫秒再删除Redis
缓存数据,这样就算在更新Mysql
时,有其他线程读了Mysql,把⽼数据读到了Redis中,那么也会被删除掉,从⽽把数据保持⼀致RDB:Redis DataBase 将某⼀个时刻的内存快照(Snapshot),以⼆进制的⽅式写⼊磁盘。
⼿动触发:
save
命令,使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在⽣产环境⼀定要慎⽤bgsave
命令,fork出⼀个⼦进程执⾏持久化,主进程只在fork过程中有短暂的阻塞,⼦进程创建之后,主进程就可以响应客户端请求了⾃动触发:
save m n
:在 m 秒内,如果有 n 个键发⽣改变,则⾃动触发持久化,通过bgsave执⾏,如果设置多个、只要满⾜其⼀就会触发,配置⽂件有默认配置(可以注释掉)flushall
:⽤于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会清空RDB⽂件,同时也会⽣成dump.rdb、内容为空主从同步:全量同步时会⾃动触发bgsave
命令,⽣成rdb发送给从节点
优点:
- 整个Redis数据库将只包含⼀个⽂件 dump.rdb,⽅便持久化。
- 容灾性好,⽅便备份。
- 性能最⼤化,fork ⼦进程来完成写操作,让主进程继续处理命令,所以是 IO 最⼤化。使⽤单独⼦进程来进⾏持久化,主进程不会进⾏任何 IO 操作,保证了 redis 的⾼性能
- 相对于数据集⼤时,⽐ AOF的启动效率更⾼。
缺点:
- 数据安全性低。RDB 是间隔⼀段时间进⾏持久化,如果持久化之间 redis 发⽣故障,会发⽣数据丢失。所以这种⽅式更适合数据要求不严谨的时候)
- 由于RDB是通过fork⼦进程来协助完成数据持久化⼯作的,因此,如果当数据集较⼤时,可能会导致整个服务器停⽌服务⼏百毫秒,甚⾄是1秒钟。会占⽤cpu
AOF:Append Only File
以⽇志的形式记录服务器所处理的每⼀个写、删除操作,查询操作不会记录,以⽂本的⽅式记录,可以打开⽂件看到详细的操作记录,调操作系统命令进程刷盘
优点:
- 数据安全
- 通过 append 模式写⽂件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redischeck-aof ⼯具解决数据⼀致性问题。
- AOF 机制的 rewrite 模式。定期对AOF⽂件进⾏重写,以达到压缩的⽬的
缺点:
- AOF ⽂件⽐ RDB ⽂件⼤,且恢复速度慢。
- 数据集⼤的时候,⽐ rdb 启动效率低。
- 运⾏效率没有RDB⾼
对⽐:
1. AOF⽂件⽐RDB更新频率⾼,优先使⽤AOF还原数据。AOF⽐RDB更安全也更⼤
2. RDB性能⽐AOF好
3. 如果两个都配了优先加载AOF
Redis基于Reactor模式开发了⽹络事件处理器、⽂件事件处理器fileeventhandler。它是单线程的, 所以 Redis才叫做单线程的模型,它采⽤IO多路复⽤机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。可以实现⾼性能的⽹络通信模型,⼜可以跟内部其他单线程的模块进⾏对接,保证了 Redis内部的线程模型的简单性。
⽂件事件处理器的结构包含4个部分:多个Socket、IO多路复⽤程序、⽂件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)。
多个 Socket 可能并发的产⽣不同的事件,IO多路复⽤程序会监听多个 Socket,会将 Socket 放⼊⼀个队列中排队,每次从队列中有序、同步取出⼀个 Socket 给事件分派器,事件分派器把 Socket 给对应的事件处理器。
然后⼀个 Socket 的事件处理完之后,IO多路复⽤程序才会将队列中的下⼀个 Socket 给事件分派器。⽂件事件分派器会根据每个 Socket 当前产⽣的事件,来选择对应的事件处理器来处理。
单线程快的原因:
1. 纯内存操作
2. 核⼼是基于⾮阻塞的IO多路复⽤机制
3. 单线程反⽽避免了多线程的频繁上下⽂切换带来的性能问题