为什么单线程的Redis这么快
(1).redis是基于内存的,内存的读写速度非常快;
(2).redis是单线程的,省去了很多上下文切换线程的时间;
上下文切换就是cpu在多线程之间进行轮流执行(枪战cpu资源),
而redis单线程的,因此避免了繁琐的多线程上下文切换。
(3).redis使用多路复用技术,可以处理并发的连接;
多路-指的是多个socket连接,复用-指的是复用一个线程。
redis数据淘汰策略(6种)
淘汰策略
1. volatile-lru:使用LRU算法进行数据淘汰(淘汰上次使用时间最早的,且使用次数
最少的key),只淘汰设定了有效期的key.(从已设置过期时间的数据集
(server.db[i].expires)中挑选最近最少使用的数据淘汰)
2. volatile-ttl:淘汰剩余有效期最短的key.(从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰)
3. volatile-random:随机淘汰数据,只淘汰设定了有效期的key.(从已设置过期时间的数据集(server.db[i].expires)中任意选择数据 淘汰)
4. allkeys-lru:使用LRU算法进行数据淘汰,所有的key都可以被淘汰.(从数据集
(server.db[i].dict)中挑选最近最少使用的数据淘汰)
5. allkeys-random:随机淘汰数据,所有的key都可以被淘汰(从数据集(server.db[i].dict)中任意选择数据淘汰).
6. no-enviction(不 驱逐):禁止驱逐数据 (默认)
最好为Redis指定一种有效的数据淘汰策略以配合maxmemory设置,避免在内存使用满后
发生写入失败的情况。一般来说,推荐使用的策略是volatile-lru,并辨识Redis中保存
的数据的重要性。对于那些重要的,绝对不能丢弃的数据(如配置类数据等),应不设置
有效期,这样Redis就永远不会淘汰这些数据。对于那些相对不是那么重要的,并且能够热
加载的数据(比如缓存最近登录的用户信息,当在Redis中找不到时,程序会去DB中读
取),可以设置上有效期,这样在内存不够时Redis就会淘汰这部分数据。
配置方法:
maxmemory-policy volatile-lru #默认是no-eviction,即不进行数据淘汰
淘汰机制的注意事项:
1,版本不同,默认的策略不同
在2.8.13的版本里,默认是noeviction,在3.2.3版本里默认是volatile-lru
2,volatile-lru、volatile-ttl、volatile-random这几种只对设置了过期时间的
key有效,不会淘汰没有设置过期时间的key
3,与是否设置持久化无关
4,测试volatile-lru测量,在插入有过期时间的key时,如果达到了maxmemory值,就会对老的key进行淘汰,插入新值
5,如果设置为no-eviction或者key没有过期时间,当达到最大内存值时就会直接报错
内存管理与数据淘汰机制
物理内存(Physical memory)是相对于虚拟内存而言的。物理内存指通过物理内存条而
获得的内存空间,而虚拟内存则是指将硬盘的一块区域划分来作为内存。内存主要作用是
在计算机运行时为操作系统和各种程序提供临时储存。常见的物理内存规格有256M、
512M、1G、2G等,现如今随着计算机硬件的发展,已经出现4G、8G甚至更高容量的内存规
格。当物理内存不足时,可以用虚拟内存代替。
Swap分区在系统的物理内存(这里应该是运行内存)不够用的时候,把物理内存中的一部分
空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有
什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,
再从Swap分区中恢复保存的数据到内存中。
最大内存设置
os:操作系统(英语:Operating System,简称OS)
默认情况下,在32位OS中,Redis最大使用3GB的内存,在64位OS中则没有限制。
在使用Redis时,应该对数据占用的最大空间有一个基本准确的预估,并为Redis设定最大使用的内存。否则在64位OS中Redis会无限制地占用内存(当物理内存被占满后会使用swap空间),容易引发各种各样的问题。
通过如下配置控制Redis使用的最大内存:
maxmemory 100mb
在内存占用达到了maxmemory后,再向Redis写入数据时,Redis会:
•根据配置的数据淘汰策略尝试淘汰数据,释放空间
•如果没有数据可以淘汰,或者没有配置数据淘汰策略,那么Redis会对所有写请求返回错误,但读请求仍然可以正常执行
在为Redis设置maxmemory时,需要注意:
•如果采用了Redis的主从同步,主节点向从节点同步数据时,会占用掉一部分内存空间,如果maxmemory过于接近主机的可用内存,导致数据同步时内存不足。所以设置的maxmemory不要过于接近主机可用的内存,留出一部分预留用作主从同步。
淘汰算法
近似 LRU(Least Recently Used)算法:
Redis 使用的并不是完全 LRU 算法。不使用 LRU 算法,是为了节省内存,Redis 采用的是随机LRU算法,Redis 为每一个 key 增加了一个24 bit的字段,用来记录这个 key 最后一次被访问的时间戳。
注意 Redis 的 LRU 淘汰策略是懒惰处理,也就是不会主动执行淘汰策略,当 Redis 执行写操作时,发现内存超出 maxmemory,就会执行 LRU 淘汰算法。这个算法就是随机采样出5(默认值)个 key,然后移除最旧的 key,如果移除后内存还是超出 maxmemory,那就继续随机采样淘汰,直到内存低于 maxmemory 为止。
如何采样就是看 maxmemory-policy 的配置,如果是 allkeys 就是从所有的 key 字典中随机,如果是 volatile 就从带过期时间的 key 字典中随机。每次采样多少个 key 看的是 maxmemory_samples 的配置,默认为 5。
LFU:
Redis 4.0 里引入了一个新的淘汰策略 —— LFU(Least Frequently Used) 模式,个人认为它比 LRU 更加优秀。
LFU 表示按最近的访问频率进行淘汰,它比 LRU 更加精准地表示了一个 key 被访问的热度。
如果一个 key 长时间不被访问,只是刚刚偶然被用户访问了一下,那么在使用 LRU 算法下它是不容易被淘汰的,因为 LRU 算法认为当前这个 key 是很热的。而 LFU 是需要追踪最近一段时间的访问频率,如果某个 key 只是偶然被访问一次是不足以变得很热的,它需要在近期一段时间内被访问很多次才有机会被认为很热。
Redis高可用方案
redis主从复制(Replication-Sentinel模式)
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节
点),但一个从节点只能有一个主节点.
从库一般是只读的(特定情况也可以写,通过参数slave-read-only指定)
主节点不但负责数据读写,还负责把写命令同步给从节点,写命令的发送过程是异步完
成,也就是说主节点处理完写命令后立即返回客户度,并不等待从节点复制完成。
优势:
读写分离
容灾备份
### Sentinel(哨兵)机制
```css
原理:当Master宕机的时候,Sentinel会选举出新的Master,并根据Sentinel中
client-reconfig-script脚本配置的内容,去动态修改VIP(虚拟IP),将VIP(虚拟IP)
指向新的Master。我们的客户端就连向指定的VIP即可!
作用:
1.监控:Sentinel 会不断的检查主服务器和从服务器是否正常运行。
2.通知:当被监控的某个Redis服务器出现问题,Sentinel通过API脚本向管理员或者其
他的应用程序发送通知。
3.自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,
它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点,并且将其他的从
节点指向新的主节点。当Master不能正常操作时哨兵会开始一次故障转移。
它会将失效的Master的其中一个Slave升级为新的Master,并让其他Slave改为复制新的Master。当客户端试图连接失效的Master时,集群会向客户端显示新的Master的地址。
Master和Slave切换后,Master的redis.conf、Slave的reids.conf和senisentinel的sentinel.conf配置文件的内容都会相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换。
主从原理(主从同步有同步和命令传播 2 个步骤)
主从同步实际分 2 种情况:
初次复制:从服务器第一次复制当前主服务器所有数据
断线后重复制:处于命令传播阶段的主从服务器,因为网络问题而中断复制,从服务器通
过自动重连,重新连接上主服务器并继续复制。
从 2.8 版本开始,在出现断线后重复制情况时,主服务器会根据复制偏移量、复制积压缓冲区和 run id,来确定执行完整重同步还是部分重同步
2.8 版本使用 psync 命令来代替 sync 命令去执行同步操作。目的是为了解决同步(sync 命令)的低效操作
sync 与psync(将sync 优化为psync)
全量复制是 Redis 最早支持的复制方式,也是主从第一次建立复制时必须经历的的阶
段。触发全量复制的命令是 sync 和 psync。之前说过,这两个命令的分水岭版本是
2.8,redis 2.8 之前使用 sync 只能执行全量不同, 2.8 之后同时支持全量同步
和部分同步。
psync 具有完整重同步和部分重同步两种模式:
完整重同步:用于初次复制情况,执行过程同 sync,在这不赘述了。
部分重同步:用于断线后重复制情况,如果满足一定条件,主服务器
只需要将断线期间执行的写命令发送给从服务器即可。
因此很明显,当主从同步出现断线后重复制的情况,psync 的部分重同步模式可以解决
sync 的低效情况。
psync 命令需要 3 个组件支持:
1、主从节点各自复制偏移量
2、主节点复制积压缓冲区
3、主节点运行 ID
命令格式为psync{runId}{offset}
主从节点各自复制偏移量(offset):
1、参与复制的主从节点都会维护自身的复制偏移量。
2、主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info
replication 中的 masterreploffset 指标中。
3、从节点每秒钟上报自身的的复制偏移量给主节点,因此主节点也会保存从节点
的复制偏移量。
4、从节点在接收到主节点发送的命令后,也会累加自身的偏移量,统计信息在 info
replication 中。
5、通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。
主节点复制积压缓冲区:
1、复制积压缓冲区是一个保存在主节点的一个固定长度的先进先出的队列。默认大小
1MB。
2、这个队列在 slave 连接时创建。当主服务器进行命令传播时,不但会把命令发送给从节点,也会写入复制缓冲区。
3、他的作用就是用于部分复制和复制命令丢失的数据补救。通过 info replication 可
以看到相关信息。
:
因此复制积压缓冲区的构造是这样的:
当从服务器向主服务器发送 psync 命令时,还需要将自己的复制偏移量带上,主服务器
就可以通过这个复制偏移量和复制积压缓冲区的偏移量进行对比。
若复制积压缓冲区存在从服务器的复制偏移量 + 1 后的数据,则进行部分重同步,否则进
行完整重同步。
主节点运行 ID(run id):
1、每个 redis 启动的时候,都会生成一个 40 位的运行 ID。
2、运行 ID 的主要作用是用来识别 Redis 节点。如果使用 ip+port 的方式,那么如
果主节点重启修改了 RDB/AOF 数据,从节点再基于偏移量进行复制将是不安全的。所以,当运行 id 变化后,从节点将进行全量复制。也就是说,redis 重启后,默认从节点
会进行全量复制。
运行 id 是在进行初次复制时,主服务器将会将自己的运行 id 发送给从服务器,让其保
存起来。当从服务器断线重连后,从服务器会将这个运行 id 发送给刚连接上的主服务
器。若当前服务器的运行 id 与之相同,说明从服务器断线前复制的服务器就是当前服务
器,主服务器可以尝试执行部分同步;若不同则说明从服务器断线前复制的服务器不是当
前服务器,主服务器直接执行完整重同步。
如果在重启时不改变运行 ID 呢:
1、可以通过 debug reload 命令重新加载 RDB 并保持运行 ID 不变。从而有效的避免
不必要的全量复制。
2、他的缺点则是:debug reload 命令会阻塞当前 Redis 节点主线程,因此对于大数
据量的主节点或者无法容忍阻塞的节点,需要谨慎使用。一般通过故障转移机制可以解决
这个问题。
1.1.全量复制(全量同步)
BGSAVE与LASTSAVE命令
BGSAVE命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的
Redis进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。
客户端对Redis服务器下达bgsave命令时,Redis会fork出一个子进程进行RDB文件的生
成。当RDB生成完毕后,子进程再反馈给主进程。fork子进程时也会阻塞,不过正常情况
下fork过程都非常快的。
客户端可以通过 LASTSAVE 命令查看相关信息,判断 BGSAVE 命令是否执行成功。
PSYNC执行过程:
1.Slave接受从客户端发送过来的SLAVEOF命令。
2.当前服务器判断自己是否保存Master runid是否是第一次复制。
3.如果是第一次复制那么当前服务器向Master发送PSYNC ? -1命令,主动请求Master进行全量同步。
4.如果已经父之过Master,那么当前从服务器向Master发送PSYNC runid offset命令。
5.Master接收到PSYNC 命令后首先判断runid是否和本机的id一致,如果runid和本机id不一致则返回+FULLRESYNC runid offset命令执行全量同步操作,当前服务器会将runid保存起来,在下次发送PSUNC时使用。
6.如果判断runid和本机id一致,Master则会再次判断offset偏移量和本机的偏移量相差有没有超过复制积压缓冲区大小,如果没有那么就给Slave发送CONTINUE,此时Slave只需要等待Master传回失去连接期间丢失的命令;
Redis的全量同步主要分为三个阶段:
同步快照阶段:Master创建并发送快照给Slave,Slave再入快照并解析。Master同时将此阶段产生的新的命令写入到积压缓冲区中。
同步写缓冲阶段:Master向Slave同步存储在缓冲区的写操作命令。
同步增量阶段:Master向SLave同步写操作命令。
Redis的全量同步详细过程:
1、从节点发送 psync 命令(spync ? -1)
2、主节点根据命令返回 FULLRESYNC
3、从节点记录主节点 ID 和 offset
4、主节点 bgsave 并生成 RDB 到本地
5、主节点发送 RBD 文件到从节点
6、从节点收到 RDB 文件并加载到内存中
7、主节点在从节点接受数据的期间,在一个缓冲区中记录从现在开始执行的写命令,当从节点加载 RDB 完毕,再发送过去。(如果从节点花费时间过长,将导致缓冲区溢出,最后全量同步失败)
8、从节点清空数据后加载 RDB 文件,如果 RDB 文件很大,这一步操作仍然耗时,如
果此时客户端访问,将导致数据不一致,可以使用配置slave-server-stale-data 关闭.
9、从节点成功加载完 RBD 后,如果开启了 AOF,会立刻做 bgrewriteaof。
注意:
1、如过 RDB 文件大于 6GB,并且是千兆网卡,Redis 的默认超时机制(60 秒),会导
致全量复制失败。可以通过调大 repl-timeout 参数来解决此问题。
2、Redis 虽然支持无盘复制,即直接通过网络发送给从节点,但功能不是很完善,生产环境慎用。
1.2.增量复制(部分复制)(增量同步)(命令传播)
Redis增量同步主要是指Slave完成初始化开始正常工作时,Master发生的写操作同步到
Slave的过程。通常情况下,Master没执行一个写命令就会想Slave发送相同的写命令,
然后Slave接受并执行。主节点不但负责数据读写,还负责把写命令同步给从节点,写命令
的发送过程是异步完成,也就是说主节点处理完写命令后立即返回客户度,并不等待从节
点复制完成。
当从节点正在复制主节点时,如果出现网络闪断和其他异常,从节点会让主节点补发丢失
的命令数据,主节点只需要将复制缓冲区的数据发送到从节点就能够保证数据的一致性,
相比较全量复制,成本小很多。
1、当从节点出现网络中断,超过了 repl-timeout 时间,主节点就会中断复制
连接。
2、主节点会将请求的数据写入到“复制积压缓冲区”,默认 1MB。
3、当从节点恢复,重新连接上主节点,从节点会将 offset 和主节点 id 发送到
主节点
4、主节点校验后,如果偏移量的数后的数据在缓冲区中,就发送 cuntinue 响应
—— 表示可以进行部分复制
5、主节点将缓冲区的数据发送到从节点,保证主从复制进行正常状态。
主从优势和不足
优势:
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
避免单点故障通常的做法是将数据复制多个副本保存在不同的服务器上,这样即使有其中一台服务器出现故障,其他服务器依然可以继续提供服务。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;
实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提
供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服
务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis
服务器的并发量。
缺点:
(1)主从切换的过程中会丢数据
(2)Redis只能单点写,不能水平扩容(增加服务器数量),只能垂直扩展(提高单机配置)
心跳检测
当完成了同步之后,主从服务器就会进入命令传播阶段,此时从服务器会以每秒 1 次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset> ,其中 replication_offset 是从服务器当前的复制偏移量
发送这个命令主要有三个作用:
检测主从服务器的网络状态
辅助实现 min-slaves 选项
检测命令丢失(若丢失,主服务器会将丢失的写命令重新发给从服务器)
Redis-Cluster模式
Redis-Cluster原理(采用虚拟哈希槽 分片方式)
客户端与Redis节点直连,不需要中间Proxy层,直接连接任意一个Master节点
根据公式HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,然后
Redis会去相应的节点进行操作
Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,计算公式:slot = CRC16(key) & 16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。
Redis 虚拟槽分区的特点:
解耦数据和节点之间的关系,简化了节点扩容和收缩难度。
节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据
支持节点、槽和键之间的映射查询,用于数据路由,在线集群伸缩等场景。
Redis 集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容。可以说,槽是 Redis 集群管理数据的基本单位,集群伸缩就是槽和数据在节点之间的移动。
优势与不足
优点:
(1)无需Sentinel哨兵监控,如果Master挂了,Redis Cluster内部自动将Slave切换
Master
(2)可以进行水平扩容
(3)支持自动化迁移,当出现某个Slave宕机了,那么就只有Master了,这时候的高可用
性就无法很好的保证了,万一Master也宕机了,咋办呢? 针对这种情况,如果说其他
Master有多余的Slave ,集群自动把多余的Slave迁移到没有Slave的Master 中。
缺点:
(1)批量操作是个坑
(2)资源隔离性较差,容易出现相互影响的情况。
槽位映射表:
连接Redis-cluster时,会指定很多Ip+端口,
但使用JedisCluster连接时,只要传递的众多地址中有一个可使用就可以,
当连接成功时,会获取cluster node的映射关系,并保存到本地.同时会为
每个节点创建一个JedisPool.当我们要执行一个set命令时,会根据key值使用
crc16算法获取槽位,
然后根据映射关系获取应该发送的节点.
执行命令
1.成功
2.失败:
失败可以能是由于槽迁移导致的,此时会返回重定向错误,
jedisCluster会捕获异常,然后重新获取
映射关系,并重新执行
故障转移:
1.故障发现: 通过ping/pong消息实现故障发现
2.故障恢复
1.检查资格:
1.检查从节点与主节点的断开时间
时间上超过cluster-node-timeout * cluster-replica-factor 乘积值,则取消称为主节点的资格
2.选择偏移量最大的(即选择与主节点相比,数据完整度最高的)
2.替换主节点:
1.当前从节点取消复制, 变为主节点(slaveof no one)
2.撤销以前主节点的槽位,给新的主节点
3.向集群广播消息,表明已经替换了故障节点