1、Redis是什么?
redis是由C语言开发的、开源的、高性能键值对的内存数据库,可用作缓存、消息中间件,支持10万QPS,工作线程为单线程,是线程安全的。
Redis是单线程的,但Redis为什么这么快?
- 完全基于内存,绝大部分操作都是纯内存操作,速度非常快。
- 数据结构简单,对数据操作也简单,Redis中的数据结构是专门经过设计的
- 采用单线程,避免了不必要的上下文切换和竞争条件,不用去考虑各种锁的问题,所以也就不存在加锁释放锁操作,更不会出现死锁而导致性能的消耗
- 使用多路IO复用模型,非阻塞IO;(多个网络连接复用一个线程)
- Redis构建了属于自己的VM机制
2、五大数据类型及使用场景
- String:k:v ,value不仅是string,也可以是数字。string类型是二进制安全的,可以包含任何数据、图片、序列化对象(最大512M)
常用命令:get、set、incr、decr、mset、mget、strlen、append
使用场景:计数器(incr,decr)、分布式锁(setnt,expire,del)、存放session(expire) - List:字符串列表;
常用命令:lpush、lpop、lrange、lrem、llen、ltrim
使用场景:消息队列(lpush,rpop)(单向栈、双向消息列队)
特点是在链表结构,两端插入数据效率更高 - Hash:一个键值对集合(ksy:value
value又是k:v
),hash特别适合用于对象存储。
常用命令:hget、hset、hgetall、hvals、hkeys、hdel、hlen
使用场景:存储对象信息(个人信息、详情页) - Set:集合(无序、不重复)
常用命令:sadd、spop、smembers、srem、sinter(交)、sunion(并)、sdiff(差)、sismembers、scard、smove
使用场景:
1、共同关注、二度好友(分片集群存在问题)
Cluster集群:HASH_SLOT=CRC16(key) mod 16384
Redis提供了一种Hash Tag的功能,在key中使用{}
括起key中的一部分,在进行 CRC16(key) mod 16384 的过程中,只会对{}内的字符串计算
2、随机展示
3、黑名单(sismembers) - Sorted Set(Zset):集合(有序、不重复)
常用命令:zadd、zrange、zscore、zrevrange、zrangebyscore
3、Redis持久化
dump.rdb(快照)
:在指定的时间间隔内,将内存的数据集快照写入磁盘
触发机制:
- 手动触发
- Save:会阻塞当前redis服务器,在执行sasve期间,redis不能执行其他命令,直到rdb过程完毕
- bgsave:flushall、shutdown,都会执行bgsave
- 自动触发(默认)
- save:900 1(900秒内至少有一个修改)
- save:300 10(300秒内至少有10个修改)
- save:60 10000(60秒内至少有1万个修改)
持久化过程:
redis进程执行fork操作创建子进程来进行持久化,会先将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,整个过程,主进程不进行任何IO操作,这也确保了redis的高性能
优缺点:
优点:
- rdb文件紧凑,全量复制,非常适合用于备份和灾难恢复
- 生成rdb文件时,redis会fork一个子进程来处理,主进程不需要进行任何磁盘IO操作
- RDB在恢复大量数据时的速度会比AOF要快
缺点:
RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,当进行持久化时,会开启一个子进程专门负责快照持久化,不能保证数据的完整性,fork子进程会占用一定内存空间
appendonly.aof
:会将每一次的写命令通过write函数追加到文件中,当服务重启时重新执行这些命令来恢复原始数据。
AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。
重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
触发机制:
- always:每次修改都同步(性能差,但是数据完整性好)
- everysec(默认):每秒同步,异步操作,宕机会有丢失数据的风险
- no:不同步,交由内核线程同步(大多数Linux操作系统是没30秒进行一次fsync)
优缺点
- AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
- AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
- AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
- 文件体积大,恢复速度慢
4、redis 8种淘汰策略
- noeviction:不删除策略,达到最大内存限制时,如需更多内存,直接返回错误信息
- allkeys-lru:从所有kv中优先淘汰使用最少的
- volatile-lru:从设置了过期时间的kv中,优先淘汰使用最少的
- allykeys-random:从所有kv中随机淘汰
- volatile-random:从设置了过期时间的kv中,随机淘汰
- volatile-ttl:从设置了过期时间的kv中,优先淘汰剩余时间短的
redis4.0新增两种:
- allkeys-lfu:从所有kv中通过统计访问频率,优先淘汰访问频率最少(即最不常使用的)
- volatile-lfu:从已设置过期时间的kv中,优先淘汰访问频率最少的
5、缓存击穿
缓存击穿是指单个key,非常热点,高并发的场景下集中在一个点访问,当这个key突然失效,持续的并发访问就会突破缓存,直接打到数据库上,导致数据库瞬间压力过大,有可能会拖垮数据库
解决方案:
- 设置热点数据永不过期
- 使用分布式锁,保证每一个key只有一个线程去查询后端服务
6、缓存穿透
缓存穿透是指查询一个缓存层和数据库都不存在的数据,高并发情况下,缓存无法命中,所有的请求还是会打到数据库上
解决方案:
- 布隆过滤器
布隆过滤器是一种数据结构,对所有可能存在的 - 在控制层先进行鉴权效验,不符合直接丢弃
- 缓存过期时间比较短的空对象
7、缓存雪崩
缓存层出现了问题
,比如同一个时间点,缓存中的“大批量”数据过期,正好赶上并发请求,所有请求打到数据库,数据库压力过大,甚至挂掉,然后有可能导致真个服务垮掉
解决方案:
- 缓存数据的过期时间时,加一个随机值
- 服务降级:通过加锁或者队列的方式来避免数据库的压力过大
- 缓存数据预热
- 搭建集群,保证redis高可用
8、什么是主从复制?
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
主从复制是高可用Redis的基础
9、主从复制的作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
- 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
10、复制原理
复制的过程步骤如下:
从节点执行 slaveof 命令
从节点只是保存了 slaveof 命令中主节点的信息,并没有立即发起复制
从节点内部的定时任务发现有主节点的信息,开始使用 socket 连接主节点
连接建立成功后,发送 ping 命令,希望得到 pong 命令响应,否则会进行重连
如果主节点设置了权限,那么就需要进行权限验证;如果验证失败,复制终止。
权限验证通过后,进行数据同步,这是耗时最长的操作,主节点将把所有的数据全部发送给从节点。
当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
数据间的同步
上面说的复制过程,其中有一个步骤是“同步数据集”,这个就是现在讲的‘数据间的同步’。
redis 同步有 2 个命令:
sync 和 psync,前者是 redis 2.8 之前的同步命令,后者是 redis 2.8 为了优化 sync 新设计的命令。我们会重点关注 2.8 的 psync 命令。
psync 命令需要 3 个组件支持:
主从节点各自复制偏移量
主节点复制积压缓冲区
主节点运行 ID
主从节点各自复制偏移量:
参与复制的主从节点都会维护自身的复制偏移量。
主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 masterreploffset 指标中。
从节点每秒钟上报自身的的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量。
从节点在接收到主节点发送的命令后,也会累加自身的偏移量,统计信息在 info replication 中。
通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。
主节点复制积压缓冲区:
复制积压缓冲区是一个保存在主节点的一个固定长度的先进先出的队列。默认大小 1MB。
这个队列在 slave 连接时创建。这时主节点响应写命令时,不但会把命令发送给从节点,也会写入复制缓冲区。
他的作用就是用于部分复制和复制命令丢失的数据补救。通过 info replication 可以看到相关信息。
主节点运行 ID:
每个 redis 启动的时候,都会生成一个 40 位的运行 ID。
运行 ID 的主要作用是用来识别 Redis 节点。如果使用 ip+port 的方式,那么如果主节点重启修改了 RDB/AOF 数据,从节点再基于偏移量进行复制将是不安全的。所以,当运行 id 变化后,从节点将进行全量复制。也就是说,redis 重启后,默认从节点会进行全量复制。
全量复制:
全量复制是 Redis 最早支持的复制方式,也是主从第一次建立复制时必须经历的的阶段。触发全量复制的命令是 sync 和 psync。之前说过,这两个命令的分水岭版本是 2.8,redis 2.8 之前使用 sync 只能执行全量不同,2.8 之后同时支持全量同步和部分同步。
步骤:
发送 psync 命令(spync ? -1)
主节点根据命令返回 FULLRESYNC
从节点记录主节点 ID 和 offset
主节点 bgsave 并保存 RDB 到本地
主节点发送 RBD 文件到从节点
从节点收到 RDB 文件并加载到内存中
主节点在从节点接受数据的期间,将新数据保存到“复制客户端缓冲区”,当从节点加载 RDB 完毕,再发送过去。(如果从节点花费时间过长,将导致缓冲区溢出,最后全量同步失败)
从节点清空数据后加载 RDB 文件,如果 RDB 文件很大,这一步操作仍然耗时,如果此时客户端访问,将导致数据不一致,可以使用配置slave-server-stale-data 关闭.
从节点成功加载完 RBD 后,如果开启了 AOF,会立刻做 bgrewriteaof。
以上加粗的部分是整个全量同步耗时的地方。
注意:
如过 RDB 文件大于 6GB,并且是千兆网卡,Redis 的默认超时机制(60 秒),会导致全量复制失败。可以通过调大 repl-timeout 参数来解决此问题。
Redis 虽然支持无盘复制,即直接通过网络发送给从节点,但功能不是很完善,生产环境慎用
增量复制:
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
Redis主从同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
11、哨兵模式
概述
Redis Sentinel 是一个分布式的架构,它本身也是一个独立的 Redis 节点,只不过它不存储数据,只支持部分命令,它能够自动完成故障发现和故障转移,并通知应用方,从而实现高可用。
主从切换技术的方法是:当主服务器宕机后,需要手动把一台服务器切换为主服务器,这就是需要人工干预,费时费力,还会造成一段时间内服务不可用,这是一种不推荐的方式,更多时候,我们优先考虑哨兵模式,Redis从2.8开始正式提供了==Sentinel(哨兵)==架构来解决这个问题
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,烧饼是一个独立的进程,作为进程,他会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行多个Redis实例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPHawbAh-1599132073516)(…/…/笔记/images/image-20200531151557742.png)]
哨兵的作用
-
监控(Monitoring)
:通过发送命令,哨兵(sentinel)会不断检查你的Master和Slave是否正常运作 -
通知(Nottification)
:当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。将slave自动切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,切换主机 故障转移
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此我们可以使用多个哨兵进行监控,各个哨兵之间还会互相监控,这样就形成了多哨兵模式
哨兵模式的工作原理
监控的工作流程如下:
- 哨兵发送info命令,并且保存所有哨兵状态主节点和从节点信息
- 主节点会记录redis实例信息,主节点记录的信息跟哨兵记录的信息看起来一样,实际是有区别的
- 哨兵会根据在主节点拿到从节点信息,给对应的从节点也发送info命令
- 接着哨兵2来了,同样的也会给主节点发送info指令,并且建立cmd连接
- 这时候哨兵2也会保存跟哨兵1一样的信息,只不过是保存的哨兵信息是两个
- 这时候为了每一个哨兵的信息都一致,他们之间建立了一个发布订阅,为了哨兵之间的信息长期对称,他们之间也会互发ping命令
- 当再来一个哨兵3时,也会做同样的事,给主节点和从节点发送info,并且跟哨兵1和哨兵2建立连接
通知的工作流程
sentinel会给主从的所有节点发送命令获取其状态,并且会把信息发布到哨兵的订阅里
故障迁移工作流程
主观下线:
哨兵(Sentinel)节点会每秒一次的频率向建立了命令连接的实例发送PING命令,如果在down-after-milliseconds
毫秒内没有做出有效响应包括 (PONG/LOADING/MASTERDOWN)以外的响应,哨兵就会将该实例在本结构体中的状态标记为SRI_S_DOWN主观下线
客观下线:
当一个哨兵节点发现主节点处于主观下线状态时,会向其他的哨兵节点发出询问该节点是不是已经主观下线了。如果超过配置参数quorum
个节点认为是主观下线 时,该哨兵节点就会将自己维护的结构体中该主节点标记为SRI_O_DOWN客观下线
leader选举
在认为主节点客观下线
的情况下,哨兵节点间会发起一次选举,命令还是上面的命令SENTINEL is-master-down-by-addr
,只是run_id
这次会将自己的run_id
带进去,希望接受者将自己设置哨兵代表。直到有一个sentinel的票数如果超过总的sentinel半数以上,此哨兵当选,当选的哨兵去所有节点中找出一个节点作为主节点,正常情况下,哪个 sentinel节点最先确认master客观下线,哪个sentinel节点就会成为执行故障转移的 leader
挑选主节点的规则:
- 先排除掉不在线的(主观下线的 slave)
- 哨兵代表会给所有子节点发送信息,然后排除掉响应慢的(大于等于5秒没有回复过sentinel节点ping响应的slave)
- 与原主节点断开时间最久的排除掉(与master失联超过
down-after- milliseconds * 10
秒的slave) - 然后根据优先级(从节点优先级,可配置,默认100)最低的从节点,如果有优先级相同的节点,进行下一步。注意如果这个值配置为0,则代表禁止该节点成为 master。)
- 判断offset偏移量(择复制偏移量最大的从节点(复制的最完整)
- 最后判断runid,论资排辈,创建时间最早的上位
故障迁移
- leader执行
SLAVEOF no one
对选出来的从节点设置成新的主节点 ,并确保在后续的INFO命令时,该节点返回状态为master - leader会向剩余的slave发送
SLAVEOF命令
,让他们成为新的主节点的slave - 将旧的主节点变成新的主节点的从节点,并对其保持关注
哨兵模式的优点
- 哨兵基于主从复制模式,所以继承了主从复制的全部优点
- 主从可以切换,故障可以转移,系统可用性更好
- 哨兵模式就是主从复制模式的升级,手动升级为自动,更加健壮
哨兵模式的缺点
- Redis不好横向扩展,集群容量一旦达到上限,在线扩容将十分麻烦
- 实现哨兵模式的配置其实很麻烦,里面有很多选择