①Redis定义
Redis是C语言开发的一个开源的(遵循BSD协议)的高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种NoSQL(not only sql, 泛指非关系型数据库)的数据库。
Redis作为一个内存数据库,有以下优点:
- 性能优秀,数据存储在内存中,读写速度非常快,并支持并发10WQPS(QPS一般指每秒查询率);
- 单线程单进程(Redis6.0引入了多线程),是线程安全的,采用IO多路复用机制;
- 丰富的数据类型,支持string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)等;
- 支持数据持久化,可以将内存中的数据保存在磁盘中,重启时加载;
- 主从复制,支持哨兵模式,高可用;
- 可以用作分布式锁;
- 可以作为消息中间件使用,支持发布订阅。
②五种数据类型
-
String
是redis的基本数据类型,可以理解为何memcached一模一样的类型,一个key对应一个value。value不仅可以是String,也可以是数字。String 是二进制安全的,所以String类型可以包含任何数据,包括图片和序列化对象。String类型的最大存储512M。我理解的二进制安全的意思是:只关心二进制化的数据,不关心具体格式.只会严格的按照二进制的数据存取。不会妄图已某种特殊格式解析数据。
#String类型的实际应用场景: 计数器、粉丝数量、对象缓存存储
-
Hash
是键值(key-value)的集合。Redis的Hash是一个String的key-value的映射表。特别适合存储对象。#Hash的实际应用场景: 用户的信息等,适合存储经常变动的信息
-
List
是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部或者尾部。#List的实际应用场景: 消息排队!!消息队列、栈
-
Set
是String类型的无序集合。集合通过HashTable实现的。Set中的元素是没有顺序的,而且是没有重复的。#Set的实际应用场景: 共同好友,共同关注,推荐好友等
-
ZSet
也是String类型的集合,它和Set最大的区别就是ZSet是有序的集合。#ZSet的实际应用场景: 各种排行榜
三种数据类型:
-
HyperLogLog
适合用于网站访问数据统计- 采用一种基数算法,用于完成独立总数的统计。
- 占据空间小,无论统计多少数据,只占用12k空间
- 不精确的统计算法啊,标准误差为0.81%
-
Bitmap
可以用于统计用户信息,活跃、不活跃;登录、未登录;打卡;两个状态的都可以使用
③Redis为什么这么快(为什么是单线程而且还这么快)
为什么是单线程
Redis是单线程单进程的模型,因为Redis完全是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器的内存大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,所以Redis采用的是单线程。但是在Redis6.0后,Redis也引入了多线程。
为什么这么快
- Redis是完全基于内存的,绝大部分的操作是纯粹的内存操作,非常迅速。
- 数据结构简单,对数据的操作简单;
- 采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
- 使用多路复用IO模型,非阻塞IO。
④Redis和Memcached的区别
#先解释一下Memcached的定义:一个开源的、高性能的、具有分布式内存对象的缓存系统。
#本质上是一个内存key-value的缓存;
#协议简单,使用的是基于文本行的协议
#不支持数据的持久化,服务器关闭后数据全部丢失
#没有安全机制
- 存储方式:memcached会把数据全部存储在内存之中,断电后会全部丢失,数据不能超过内存大小;Redis有部分数据是存在硬盘上,能保证数据的持久化。
- 数据类型:memcached只支持key-value类型;而Redis支持5种基本类型和三种特殊类型的数据;
- value的大小:Redis可以达到1GB,而memcached只要1MB。
⑤淘汰策略
-
#volatile-lru 从已设置过期时间的KV集中优先对最近最少使用的数据淘汰
-
#volitile-ttl 从已设置过期时间的KV集中优先对剩余时间少的数据淘汰
-
#volitile-random 从已设置过期时间的KV集中随机数据淘汰
-
#allkeys-lru 从所有的KV集中优先对最近使用最少的数据淘汰
-
#allkeys-random 从所以的KV集中随机选择数据淘汰
-
#noeviction 不淘汰策略,若超过最大内存值,返回错误信息。
Redis4.0后加入了LFU(least frequency use,即最不经常使用)淘汰策略
#volitle-lfu 从已设置过期时间的KV集中优先对最近使用频率最低的数据淘汰
#allkeys-lfu 从所有的KV集中优先对最近使用频率最低的数据淘汰
⑥Redis的持久化
持久化机制
Redis为了保证效率,数据缓存在内存中,但是会周期性的把更新的数据写入磁盘或者把修改的操作写入追加到记录文件中,以保证数据的持久化。持久化的策略有两种:
- RDB:快照形式是直接把内存的数据保存到一个dump的文件中,定时保存;
- AOF:把所有的对Redis的服务器的修改命令都存到一个文件里,命令的集合。Redis默认是快照RDB的持久化方式。当Redis重启时,Redis会优先使用AOF文件来还原数据,因为SOF文件保存的数据集通常比RDB文件所保存的数据集更完整。
RDB的工作机制
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入一个临时文件中,等待持久化数据结束了,再用临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这几确保了极高的性能。如果需要进行大规模的数据的恢复,且对于数据恢复的完整性不是很敏感,RDB的方式比AOF的方式更高效。但是RDB最后一次的持久化数据可能会丢失。
优点:适合大规模的数据恢复、对数据的完整性要求不高。
缺点:需要一定的时间间隔进行操作!最后一次的持久化数据 容易丢失。fork进程的时候会占用一定的内存空间。
AOF的工作机制
以日志的形式记录每个操作,将Redis执行的所以指令记录下来(读操作不记录),只追加文件但不修改文件,Redis启动之初会读取该文件重构数据,换言之,Redis重启的话是根据日志文件的美容将写指令执行一次以完成数据的恢复工作。
⑦Redis的主从复制
主从复制的过程和原理:主从配置结合哨兵模式能够解决单点故障问题,从而提高Redis的高可用性。主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave 以读为主。默认情况下每个服务器都是主节点
复制过程:
- 从节点执行
slaveof [masterId] [masterPort]
,保存主节点信息; - 从节点中的的定时任务发现主节点信息,建立和主节点的
socker
的连接; - 从节点发送
Ping
信号,主节点返回Pong
,两边建立相互通信; - 通信建立后,主节点将所有的数据发送个从节点(数据同步);
- 主节点把当前数据同步给从节点后,便完成了复制的建立过程。接下来就是数据的传输,保证主从节点的数据一致性。
数据同步的过程:
-
Redis2.8之前是使用
sync [runId] [offset]
的同步命令,Redis2.8之后使用psync [runId] [offset]
d的命令。二者区别在于sync
命令仅仅支持全量复制,psync
支持全量和部分的复制。#runId 每个Redis节点启动都会有一个唯一的UUID,重启后会重新生成。 #offset 主从复制偏移量;当主节点有写入命令时,offset=offset+命令的字节长度。从节点在收到主节点发送的命令后,也会增加自己的offset,并把自己的offset发送给主节点。这样,主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点数据是否一致。
- 主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制。
-
主节点响应写命令时,不但会把命名发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。
部分复制的过程:
-
1 ) 当主从节点之间网络出现中断时,如果超过了 repl-timeout 时间,主节点会认为从节点故障并中断复制连接。
-
- 主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内部存在复制积压缓冲区( repl-backlog-buffer ),依然可以保存最近一段时间的写命令数据,默认最大缓存 1MB。
-
- 当主从节点网络恢复后,从节点会再次连上主节点。
-
- 当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们作为 psync 参数发送给主节点,要求进行补发复制操作。
-
- 主节点接到 psync 命令后首先核对参数 runId 是否与自身一致,如果一致,说明之前复制的是当前主节点;之后根据参数 offset 在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送 +CONTINUE 响应,表示可以进行部分复制。
-
- 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。
全量复制的过程:
1、从节点发送psync ? -1命令(因为第一次发送,不知道主节点的runId,所以为?,因为是第一次复制,所以offset=-1)。
2、主节点发现从节点是第一次复制,返回FULLRESYNC {runId} {offset},runId是主节点的runId,offset是主节点目前的offset。
3、从节点接收主节点信息后,保存到info中。
4、主节点在发送FULLRESYNC后,启动bgsave命令,生成RDB文件(数据持久化)。
5、主节点发送RDB文件给从节点。到从节点加载数据完成这段期间主节点的写命令放入缓冲区。
6、从节点清理自己的数据库数据。
7、从节点加载RDB文件,将数据保存到自己的数据库中。
8、如果从节点开启了AOF,从节点会异步重写AOF文件。
主从复制存在的问题:
- 一点主机节点宕机,需要从新选定主节点,全程需要人工干预,还需要命令所有的从节点去复制新的主节点;
- 主节点的读写和存储能力受单机的限制;
- 原生复制的弊端会在早期的版本中也会比较突出,例如:Redis复制中断后,从节点会发起psync。此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时,可能会造成毫秒或者秒级的差别。
⑧哨兵模式
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
哨兵模式主要包括主节点存活检测、主从运行情况检测、自动故障转移、主从切换。哨兵模式最小的配置是一主一从。
- 监控:不断检查主服务器和从服务器是否正常运行
- 通知:当被监控的某个Redis服务器出现问题,哨兵会通过API脚本向管理员或者其他应用程序发出通知
- 自动故障转移:当主节点不能正常工作,哨兵会开始一次的自动故障转移操作,它会从主节点的从节点中找一个升级为主节点,并将其余从节点指向新的主节点。
- 配置提供者:在哨兵模式下,客户端应用在初始化时连接的是哨兵节点集合,从中获取主节点的信息。
哨兵模式的工作原理:
- 每个哨兵节点都需要定期执行以下任务:每个哨兵以每秒一次的频率,向他所知道的服务器,从服务器以及其他的哨兵实例发送一个
PING
命令; - 如果一个实例距离最后一次的有效回复
PING
命令的时间超多down-after-milliseconds
所指定的时间,这个实例就会被哨兵标记为主观下线; - 如果一个主服务器被标记为主观下线,那么正在监视这个服务器所以的哨兵节点,要以每秒一次的频率确认主服务器进入了主观下线状态;
- 如果一个主服务器被标记为主观下线,并且有足够量的哨兵节点在指定时间范围内同意这一判断,此时,这个主服务器被标记为客观下线状态;
- 一般情况下,每个哨兵会以10秒一次的频率向它已知的所有主服务器和从服务器发送
INFO
命令,当一个主服务器被标记为客观下线,哨兵会向下线的主服务器的所有从服务器发送INFO
命令的频率,从10s/次调整到1s/次; - 哨兵和其他哨兵协商客观下线的主节点状态,如果处于
SDOWN
状态,则会投票选出新的主节点,将剩余从节点指向新的主节点进行数据复制; - 当没有足够数量的哨兵同意主服务器下线时,主服务器的客观下线状态就会被移除。当主服务器重新向哨兵的PING命令返回有效回复时,主服务器的主观下线状态就会被移除。
⑨Redis的雪崩和击穿
缓存穿透:
通俗讲就是查不到,用户查询一个数据,发现Redis的内存数据库中没有,也就是缓存没有命中,于是直接向持久层的数据库进行查询。当用户很多的时候,缓存都没有命中(秒杀),于是都去请求持久层数据库。这时持久层数据的压力就很大,此时的情况就是缓存穿透。
解决办法:在接口层增加校验,比如用户权限验证、参数校验等,不满足条件的直接返回。例如id做基础校验,id<=0的直接拦截。
高级方法:布隆过滤器-对所有可能查询的数据以hash的形式存储,在控制层先进行校验,不符合规则的丢弃,从而避免了对底层存储系统的查询压力;当存储层没有命中后,即使返回空的对象也将其缓存起来,同时设置一个过期时间,之后再访问这个数据就会在缓存 中获取,保护了持久层数据库。
缓存击穿:
是指一个key非常热点(类似微博热搜),在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决方法:
设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布
式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考
验很大。
缓存雪崩:
缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!
产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商
品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都
过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波
峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
解决方法:
redis****高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续
工作,其实就是搭建的集群。(异地多活!)
限流降级(在SpringCloud讲解过!)
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对
某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数
据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让
缓存失效的时间点尽量均匀。