redis相关内容

一、RESP 协议

1)、特征:在TCP 层、二进制安全、基于请求-响应模式、简单易懂

2)、5种结构:单行字符串(第一个字节为 +)、错误消息(第一个字节为 -)、整型数字(第一个字节为 :,后面跟数数的字符串)、多行字符串(第一个字节为 $,后面跟字符串的长度)、数组(第一个字节为 *,后面跟数组的长度)

二、事务

redis事务是通过 MULTI、EXEC、DISCARD、WATCH、UNWATCH 命令来实现的

事务特性:

1)、redis会将多个命令一次性的按照顺序执行,执行事务期间不会因为其他命令中断事务执行

2)、redis事务只保证隔离性(单线程执行保证隔离性)和一致性(事务执行完才可以持久化数据,保证了一致性),不能保证原子性(多个命令执行时一个失败了其他命令还会正常执行)和持久性(RDB 和 AOF 两种数据持久模式,同步数据有时间间隔会导致不能持久化)

三、主要特性

1)支持缓存过期设置

2)、支持数值自增减 incr decr

3)、支持 lua 脚本,保证命令原子性

4)、单线程处理缓存,线程安全

四、雪崩原因

1)、key过期

2)、redis服务重启,导致数据丢失

2.1)、RDB 方式持久化有问题

2.2)、AOF 方式持久化在一定条件下也有问题,比如 everysec 或者 no 情况下

3)、redis 内存满了,缓存失效策略会移除key

解决方案(本质就是限流,防止大量请求同时访问数据库):

1)、加锁控制,这种方式简单粗暴,但是没有充分利用系统性能

2)、使用 semaphore 信号量限流 + 容错降级(降低预期期望)

缓存雪崩解决方案:

1)、信号量控制并发:对数据库访问限流

2)、容错降级:返回自定义信息

3)、redis集群:解决内存不足问题

五、redis 数据主从同步原理

1)、首次同步时,slave 发送 pysnc ? -1 命令给 master,master 进行 BGSAVE 命令生成 RDB 文件快照

2)、master 将 RDB 文件、serverId 和 offset 发送给 slave,slave 本地保存快照并全量同步 RDB 的所有命令,并记录 serverid 和 offset

3)、slave 发送 pysnc serverId offset 命令给 master,master 判断 serverId 是否和当前 master 的serverId是否一样,不一样的话,通知 slave 执行全量同步

4)、serverId 一样的话,再判断 offset 是否一致,一致的话不需要做同步了,否则 master 判断 offset 之后的数据是否还存在缓冲区中,如果有则进行部分重同步,否则执行全量重同步

六、redis 哨兵实现原理

1)定时任务检测 master 节点是否下线

1.1)、每1秒每个哨兵会向master、所有slave 和 其他哨兵节点发送 ping 包来做心跳检测

1.2)、每2秒每个哨兵会向 redis 的 _sentinel_:hello 频道发送自己对 master 的故障判断以及自己的节点信息,并且其他哨兵都会订阅这个频道消息来了解其他哨兵节点的信息以及对master的判断

1.3)、每10秒每个哨兵会向 master 和所有 slave 发送 info repliaction 命令来获取 master 信息(ip、端口、服务器 run_id、role 等) 和 所有 slave 的信息(ip、端口、服务器 run_id、role、slave_priority)

2)、master 下线

2.1)、通过定时任务1的心跳机制哨兵可以判断 master 是否故障,如果故障了会主观认为 master sdown 了

2.2)、判断 master 故障的哨兵会向其他所有的哨兵发送 sentinel is-master-down-addr 命令询问它们对 master 故障的判断,如果认为 master 故障的哨兵数达到了 quorum (一般设置为 【sentinelNumber / 2 + 1】) 数时,则 master 确实可能故障了,sdown(主观下线) 变为了 odown(客观下线)

3)、故障转移哨兵 leader 选举

3.1)、master 故障之后,每个哨兵都会向其他哨兵发送 sentinel is-master-down-addr 命令请求当选 leader

3.2)、收到请求的哨兵如果没有将唯一一张投票投出去,则会同意请求哨兵当选 leader,否则拒绝

3.3)、哨兵如果发现自己的得票数 >= max(quorum,sentinelNumber / 2 + 1),则它当选为 leader

3.4)、如果这一轮没有选举出 leader,则会开始下一轮的选举

4)、选举出 redis master(故障转移)

哨兵 leader 保存了 master 的所有 slave 信息,则会按照如下规则从 slave列表 中选择新的 master

4.1)、过滤掉主观下线的节点

4.2)、选择 slave_priority 最高的节点,如果有则返回,否则继续选举

4.3)、选择出 offset 最大的节点,因为 offset 越大说明数据复制的越完整,如果有就返回,否则继续选举

4.4)、选择 run_id 最小的节点

5)、更新主从状态

5.1)、通过 slaveof no one 命令将选出来的 slave 节点成为 master

5.2)、通过 slaveof 命令让其他 slave 或者下线的 master 成为新的 master 的 slave

七、客户端连接 master 原理

示例代码:

 String masterName = "mymaster"; 
 Set sentinels = new HashSet<>(); 
 sentinels.add("192.168.92.128:26379"); 
 sentinels.add("192.168.92.128:26380"); 
 sentinels.add("192.168.92.128:26381"); 

 JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); //初始化过程做了很多工作 
 Jedis jedis = pool.getResource(); 
 jedis.set("key1", "value1"); 
 pool.close(); 

1)、jedis 客户端提供了对哨兵查询操作的支持,只需要提供 masterName 和哨兵节点集合,构造 JedisSentinelPool 对象,然后通过 JedisSentinelPool.getResource() 方法获取redis连接进行redis操作

2)、在  JedisSentinelPool 的构造方法中实现了连接 master 的相关逻辑,具体逻辑如下

2.1)、遍历所有的哨兵,获取 master 节点信息

具体做法:通过 哨兵节点 + masterName 获取 master 信息,即调用哨兵节点的 sentinel get-master-addr-by-name masterName 命令,获取到 master 信息后就停止遍历哨兵

2.2)、增加对哨兵的监听,发生故障时客户端便会受到哨兵对的通知,从而完成 master 的切换

具体做法:利用 redis 的发布订阅功能,为每一个哨兵开启一个单独的线程,订阅哨兵节点的 switch-master 频道,当收到消息时初始化redis连接池

八、持久化方式

RDB:redis默认方式,redis 主进程 fork(此时会阻塞客户端请求)出一个子进程,子进程将内存中的所有数据写入到临时文件,临时文件创建好之后替换久的 dump.rdb 文件,这个过程中只有子进程负责io操作,主进程仍然可以负责客户端请求,效率比较高

过期key对RDB没有任何影响:

1)、持久化key之前,会检查key是否过期,过期的key不会持久化到 dump.rdb 中

2)、RDB文件数据恢复到redis内存中时会检查key是否过期,过期的key不会加载到内存中(这里指master,slave则会加载包含过期的所有key)

优点:

1)、恢复数据时 RDB 方式比 AOF 快

2)、同步数据过程中不会阻塞客户端请求,效率高

3)、dump.rdb 是一个非常紧凑的文件,相比 AOF 来说占用空间小,非常适合备份和灾难性恢复数据

缺点:

1)、容易丢失数据

2)、fork 子进程时会将内存数据复制到子进程中,如果内存数据过大,则会阻塞客户端请求时间较长

AOF:redis 会将所有写操作以日志的方式追加到 aof 文件中,会阻塞客户端请求,重启时将 aof 文件中的所有命令执行一遍恢复数据。通过 appendonly yes 来开启

过期key对AOF没有任何影响:

1)、key过期后,还没有被删除时,则过期的key(没有发生任何修改命令)不会持久化到 aof 文件中,如果key删除了,则会向aof文件中追加一条del命令

2)、aof重写时,会先判断key是否过期,过期的key不会重写到aof文件中

持久化策略:appendfsync always、appendfsync everysec、appendfsync no

优点:

1)、持久化策略可以减少数据丢失

2)、aof 文件过大时会对 aof 文件重写,重写后的aof文件包含了恢复当前数据集所需的最小命令集合

缺点:

1)、追加 aof 文件内容时会阻塞客户端请求

2)、同体量数据时 aof 文件较大 

AOF 数据重写原理:当 aof 文件过大时,redis 会 fork 出一个新进程将文件重写,新进程会复制内存中的所有数据(没有读取旧aof文件,由于文件过大耗时),将内存数据会以命令的方式写入aof临时文件,写好之后替换旧的aof文件。

触发机制:redis会记录上次重写时的aof文件大小,默认配置是当 aof 文件是上次重写 aof 文件的一倍并且大于 64M 时会触发重写


数据恢复:重启redis时,如果 dump.rdb 和 aof 同时存在,redis 会自动读取 aof 文件来恢复。AOF 恢复时将 aof 文件中的所有命令执行一遍恢复数据,RDB 恢复时将 dump.rdb 文件直接读取到内存中做数据恢复

九、redis 到底快在哪?

1)、完全基于内存操作,数据结构类似 hashmap 的 key value 结构,采用的是数组+链表的数据结构,查询和更新的操作时间复杂度都是o(1)

2)、采用单线程处理请求,避免了多线程切换带来的CPU损耗,同时也避免了加锁导致性能下降

3)、采用IO多路复用(select,poll,epoll)模型,linux 只支持 epoll 方法,多路指的是多个socket连接,复用指的是复用同一个线程

4)、数据结构(string、list、hash、set、zset或者 sorted set)简单,对数据操作也简单,redis的数据结构是专门设计的

5)、使用底层模型不同,它们之间底层实现方式以及客户端之间通信的应用协议不一样,redis构建了VM机制,因为一般的系统调用系统的函数,会浪费一定的时间去请求

IO多路复用模型使用了Reactor设计模式,用户线程轮询IO操作状态的工作统一交给了 Reactor 的 handle_events 事件循环处理,当用户线程注册过事件处理器之后就可以继续执行其他的工作,而 Reactor 负责调用内核的 select 方法检查 scoket 状态,当有 socket 被激活时,则通知用户线程(或者调用用户线程的回调函数),执行 handle_event 进行数据的读取和处理工作。由于调用 select 函数时会阻塞线程,因此IO多路复用模型也被称为 异步阻塞IO 模型

十、五种数据结构

对于redis来说,所有的 key 都是字符串

redis 的 key 、value 如果是字符串,大小限制都是 512M(代码写死的,不可配置)

1)、string

redis 最基本的数据类型,key-value 一一对应

常用命令:get、set、del、incr、decr

适合场景:缓存、计数器 和 session 存储

2)、hash(哈希)

一个 Mapmap 结构,value 值本身又是一个 map 结构,如 value={{field1,value1},......fieldN,valueN}}

常用命令:hget、hset、hdel

适合场景:缓存,相比string更节省空间,能直观的维护对象缓存信息

3)、list(链表)

list 是有序的双端(不是双向)链表,value 可以重复,头和尾都可以插入删除数据

常用命令:lrange

lpush + lpop = stack(栈)

lpush + rpop = queue(队列)

lpush + ltrim = capped collection(有限集合)

lpush + brpop = message queue(消息队列)

适合场景:timeline -> 微博的时间轴,lpush 加入时间轴,展示最新列表

4)、set

集合类型也是用来保存多个字符串的元素,和 list 有以下三点不同:

1)、set 中不允许有重复的元素

2)、set 中的元素是无序的,不能通过索引查找元素

3)、可以取多个集合取交集、并集、差集等

常用命令:sadd、sset、srem、scard、smembers、sismember

适合场景:用户添加标签、用户点赞、收藏、关注

5)、zset 有序集合

zset 除了有 set 的特性外,自己独有的就是zset 中的元素是可以排序的,它给每个元素设置一个score(分数可以重复)作为排序的依据

常用命令:zadd、zrange、zscore

适合场景:排行榜,音乐、帖子、视频等做排行榜,榜单可以按照用户的关注数、更新时间、字数等打分做排行

11、六种数据淘汰策略

noeviction:不删除策略,达到最大内存限制时,直接返回错误信息

allkeys-lru(LRU-> less recently used):回收最近最少使用的key

volatile-lru(LRU-> less recently used):回收设有过期时间的最近最少使用的key

allkeys-random(Random):随机回收key

volatile-random(Random):随机回收设有过期时间的key

volatile-ttl:回收设有过期时间并且存活时间(TTL)最短的key

如果数据分为热数据和冷数据,推荐使用 allkeys-lru

如果数据访问频率差不多的话,推荐使用 allkeys-random

如果需要根据TTL来筛选需要删除的key,推荐使用 volatile-ttl

redis 使用的是近似LRU算法,并不是完全LRU算法

近似LRU算法:抽取少量的样本(最近最少使用的数据),然后删除其中最少使用的数据

12、redis 的过期策略

redis 默认使用的是惰性删除和定期删除的过期策略

1)、定时删除:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除

2)、惰性删除:key过期的时候不删除,每次从数据库进行get或者setnx等操作时去检查是否过期,若过期,则删除

3)、定期删除:每隔一段时间执行一次删除(在redis.conf配置文件设置hz(默认是10(10~500,建议最大设置为100),即每隔100ms清除一次过期key),即1s刷新的频率)过期key操作,对指定个数个库的每一个库随机删除小于等于指定个数(默认是每个库检查20个key)的过期key,如果定期删除操作已经达到指定时长,则会直接退出定期删除

13、缓存和数据库数据不一致问题

缓存入口太多,单独使用缓存管理服务器,canal server 监听mysql的binlog日志,同步缓存

你可能感兴趣的:(redis)