redis知识点
什么是redis
redis是一个基于内存的数据库,对数据的读写都在内存中完成,
因此读写速度非常快,常用于缓存,消息队列,分布式锁等场景。
除此之外,redis还支持事务,持久化,Lua脚本,多种集群方案,哨兵模式,切片集群,主从复制模式,发布/订阅模式,内存淘汰机制,过期删除机制。
redis和memcached有什么区别?
- 都基于内存的数据库,一般都用来做为缓存使用。
- 都有过期策略
- 两者性能都非常高
Redis和memcahed区别
支持类型不一样:
redis各种类型 set list zset Hash string
memcached 支持key value
持久化:
redis支持持久化
memcached不支持持久化
为什么redis作为mysql缓存?
- 具备高性能
- 具备高并发
- 支持的类型丰富
redis有哪几种常用类型?
string,hash,list,set,zset
string :可以存 字符串、整数、浮点数
应用场景:缓存对象,常规计数,分布式锁,共享session信息。
内部实现:
string类型的底层的数据结构主要是一个sds 简单动态字符串
list :一个链表,链表上每个字节都有一个字符串
消息队列
底层实现
双向链表,压缩列表实现的
在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表。
set :包含字符串的无序集合
聚合计算 点赞 共同关注 抽奖活动等等
底层实现
hash表和整数集合
hash 包含键值对的无序散列表
缓存对象,购物车实现等。
底层实现
listpack 哈希表
zset 和散列一样,用于存储键值对
排序场景、比如排行榜,排序等
底层实现
跳表和listpack
redis是单线程嘛?
redis单线程指的是
redis接收客户端消息,解析请求,进行数据读写操作,发送数据给客户端这个过程是单线程的。
redis程序不是单线程的。
单线程为什么快?
Redis 的大部分操作都在内存中完成。
Redis 采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题。
redis6.0之后对网络I/O采用了多线程处理。
对于命令的执行还是单线程来完成。
redis持久化
AOF 每执行一个写操作的命令,就把该命令以追加的形式写到文件里
RDB 将某一刻的内存数据,以二进制的形式写入磁盘
混合持久化方式
AOF实现过程
- redis执行完写的命令后,将命令追加到aof_buf缓冲区
- 通过write()系统调用,将aof_buf缓冲区的文件写入AOF文件里面,
此时数据拷贝到了内核缓冲区page cache中,等待写入硬盘 - 具体内核缓冲区什么时候写入硬盘,由内核决定。
redis有提供三种会写硬盘的策略:
always,everysec,no
Always,意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
Everysec,意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
AOF日志过大怎么办?
避免文件越来越大,提供了重写机制当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。
重写 AOF 日志的过程是怎样的?
重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的
为了解决这种数据不一致问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在创建 bgrewriteaof 子进程之后开始使用。
也就是说,在 bgrewriteaof 子进程执行 AOF 重写期间,主进程需要执行以下三个工作:
执行客户端发来的命令;
将执行后的写命令追加到 「AOF 缓冲区」;
将执行后的写命令追加到 「AOF 重写缓冲区」;
RDB快照是如何实现的?
AOF存的是操作命令,不是实际数据,当AOF恢复日志的时候,会重新将命令执行一遍,如果这时候日志很多,那么redis恢复操作就会很慢。
为了解决这个问题就有了快照RDB,RDB就是存储记录某个瞬间的内存数据,记录的是实际数据,而不是操作命令。直接将RDB文件读入内存,不像AOF还需要执行额外的命令,所以它在redis数据恢复的时候更快。
RDB也叫全量快照,会将所有快照都恢复,意思就是每次执行的时候会将内存里的数据记录到磁盘中,如果比较频繁的话也会影响redis性能,如果不频繁又会丢失数据。
redis有两种RDB快照执行方式
- 执行save,在主线程生成RDB文件,和执行命令操作在同一线程,这时如果写入时间过长会阻塞主线程。
- 执行bsave,由子线程完成RDB文件生成,避免了线程阻塞。
实际上执行bsave有几个条件
bsave < 900s mysql修改 >=1 至少一次修改
bsave < 300s mysql修改 >=10 至少十次修改
bsave < 60s mysql修改 >=1000 至少千次修改
redis在执行RDB快照时,可以执行增删改查命令吗?
当然可以,redis提供了COWC技术,Copy On Write Cow写的时候复制
执行上面的bsave的时候,是由fork子进程完成的,所以不影响主线程对表进行操作。
这时候会bsave会复制主线程的表来操作。
redis混合持久化是什么?
前面两种模式都有各自的优缺点
RDB 优缺点:数据恢复速度快,但是恢复的频率不好掌握,频率低,会丢数据,频率高,会影响性能。
AOF 优缺点:丢失数据少,数据恢复慢。
混合持久化结合了二者的优点。
保证了数据恢复快,丢失数据少,成年人我全都要懂得都懂。
redis混合持久化怎么工作的?
混合持久化的文件是这样的,AOF前半部分是RDB,后半部分是AOF。
优点:当redis重启前面是RDB就很快,加载完了子进程优执行AOF,让数据丢失更少。
缺点:版本兼容性差。 文件混合了两种写法,也会导致文件可读性差。
redis集群
要设计一个高可用的redis集群要从多个服务的节点来考虑,需要主从,哨兵等。
那么什么是主从?
主从简而言之一主多从,将主服务器的数据同步到多台从服务器里,且采用主从读写分离进行部署。
主服务器 权限 读写 从服务器负责读的内容。
所有写的数据都在主节点完成,同步到从节点上,保证了数据一致性。
这时,主从之间复制数据的命令是异步的。
所以当主服务器发送完成后,从服务器不一定执行完这个命令,数据无法保持强一致性。
哨兵是什么?
哨兵的作用是帮助redis宕机时,进行手动恢复。
切片集群是什么?
redis过载,缓存数据量过大,无法继续缓存,就要用到切片集群。提高redis的读写性能。
redis集群脑裂是什么?
意思就是出现两个主节点服务器,那为什么会出现这种情况?
就是主节点由于网络问题宕机了和哨兵监控失去了联系,哨兵检测不到,又重新拉了一个从节点作为主节点服务器。
这时候由于从节点会清空自己的缓冲区,所以导致数据丢失。
解决方案
直接将错误返回给客户端。
redis缓存过期淘汰策略
主要作用于redis的key,对key淘汰的一个方案。避免雪崩,穿透,击穿。
有几种策略
- 惰性删除 不主动删除,每次都检测数据库中的key,如果key过期就删除。
优点:只有访问的时候才检测key,所以对cpu调度很友好。
缺点:如果这个key过期一直没被访问,就会一直占用内存,对内存不友好。 - 定期删除 隔一段时间就取出一部分key检查,删除其中过期的Key。
如果过期key很多超过设定的数量,会再循环删除一次。
优点:减少了过期key对空间的占用。
缺点:不能确定执行频率,太频繁对CPU运行不友好,执行太少和惰性删除没什么区别。
那么又来了 我全都要 可以用这两种缓存方式结合。
redis内存满了,会发生什么?
那就要说道内存淘汰机制
主要有两种
直接返回错误:就是不提供redis服务,直接返回错误
淘汰一批数据:用一些算法来实现
LRU最近最少使用和LFU最近不常使用 这些概念OS里面都有不会的自己去看看
redis如何避免雪崩,击穿,穿透?
雪崩:大量key同一时间时效
如何避免
既然是大量key同一时间时效,那就将这些key的时效时间随机打散一下,过期时间就不会重复。
或者直接将热点数据设置不过期即可。
击穿:热点数据过期了,直接访问了数据库,数据库就被高并发数据冲垮宕机。
如何避免
用互斥锁。保证同一时间,只对一个业务进行请求缓存。没有读到的就返回空置或默认值
同样 热点数据不设置过期时间。过期前通知后台线程更新key。
穿透:找不到数据在redis和mysql中
业务操作失误,就是数据被删了。
被攻击了,黑客将数据删了。
布隆过滤器判断数据是否存在,不走数据库。不会让服务宕机。
redis常见缓存更新策略
Write Back 写回策略 写多场景
Cache Aside 旁路缓存策略 实际项目中使用 最常用策略 读多写少场景
Read/Write Through 读/写穿策略
写策略:1.更新数据库 2.删除缓存 必须这样 不然数据会出现不一致
读策略:1. 命中缓存,返回数据。2.没有命中缓存,从数据库中读取,写入缓存,返回。
redi如何实现分布式锁
set lock_key unique_value NX PX 50000
lock_key 就是 key 键;
unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;
NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
PX 50000 表示设置 lock_key 的过期时间为 50s,这是为了避免客户端发生异常而无法释放锁。
分布式锁
优点
- 性能高
- 实现方便 setnx方法
- 避免单点故障
缺点
超时时间不易设置,主从模式中数据是异步复制,分布式锁不可靠
那么如何保证可靠?
一个客户端对应多个redis申请加锁,有半数以上redis能够完成这个锁操作,就能成功完成锁操作。否则就加锁失败。
如何保证redis和Mysql数据一致
方案1:先更新数据库,再删除缓存 推荐方案
1.加锁,保证同一时间只运行一个请求更新缓存
2.更新完缓存后,给缓存上一个简短的过期时间,就算数据不一致也会短时间更新,也可以接受
如何保证它们都执行成功?
加入消息队列 进行重试缓存的删除
订阅mysql binlog再操作缓存 更新数据库的时候有一条binlog
它们都是异步的
方案2:先删除缓存,后更新数据库
如何保证数据一致性 延迟双删
#删除缓存
redis.delKey(name)
#更新数据库
db.update(name)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(name)
这里 睡眠时间不好定 还是用前一种方案较好
参考https://xiaolincoding.com