1.Redis到底是单线程还是多线程
Redis6.0之前的单线程是指网络IO和键值对读写时一个线程来完成。
Redis6.0之后引入的多线程是指网络请求过程采用了多线程,而键值对读写的命令仍然是单线程处理的,所以Redis是并发安全的。
也就是说,只有网络请求模块和数据操作模块是单线程的,而其他的持久化、集群数据同步等是多线程的。
2.Redis单线程为什么会这么快
- 命令执行是基于内存操作的,一条命令在内存里操作的时间只有几十纳秒。
- 命令的执行是单线程操作,没有线程切换的开销。
- 基于IO多路复用机制,来提升Redis的IO利用率。
- 高效的数据结构,全局hash表以及多种高效的数据结构。比如跳表,压缩列表等。
3.Redis底层数据是如何用跳表来存储的
Redis的数据结构有一个Zset,是一个有序集合,它的数据结构是压缩列表或者跳表。
跳表,存储有序元素的时候,是把元素都放入到链表中,然后按照有序集合里面元素的分值从小到大,从左到右进行排序。链表的插入元素是比较快的。
将有序链表改造为支持近似“折半查找”的算法,可以快速的插入、删除、查找。
4.Redis的持久化机制
Redis的持久化有两种,一种是RDB,一种是AOF。
RDB
将某一刻的内存快照,以二进制的方式写入磁盘。
手动触发:
- save命令,使用Redis处于阻塞状态(指的是redis的主进程),直到RDB持久化完成,才会响应其他客户端发来的命令,我们的读写请求都会等待RDB完成。所以在生产环境一定要慎用。
- bgsave命令,通过主进程fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程再被创建后,主进程就可以响应客户端的请求了。主进程的写时拷贝策略来保证父子进程的数据共享,把父进程写的命令拷贝出一个副本,在副本中修改这个数据。此时子进程读的数据还是原来的数据。
自动触发
save m n
:在m秒内,如果有个键发生改变则自动出发持久化,通过bgsave执行flushall
:用于清空redis所有数据库,flushdb清空当前redis所在库数据,会清空RDB文件。- 主从同步:全量同步时会自动触发bgsave命令,生成rdb发送给从节点。
优点:
- 整个redis数据库只包含一个文件
dump.rdb
,方便持久化 - 容灾性好,方便备份
- 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以IO最大化。
- 相对于数据集较大时,比AOF的启动效率高。
缺点:
- 数据安全性低,RDB是间隔一段时间进行持久化,如果持久化之间redis,会导致数据丢。
- 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至1秒。
AOF
以日志的形式记录服务器锁处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作 记录,调操作系统命令进程刷盘。RDB保存的是数据,AOF保存的是增删改查指令。
- 所有的写命令追加到AOF缓冲中。
- AOF缓冲区根据对应策略向硬盘进行同步操作。
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
- 当Redis重启时,可以加载AOF文件进行数据恢复。
同步策略
- 每秒同步:异步完成,效率非常高,一旦出现宕机,那么一秒中内修改的数据会丢失。
- 每修改同步,同步持久化,每次发生的数据变化都会被立刻记录到磁盘中,最多丢失一条。
- 不同步,有操作系统控制,可能丢失较多的数据。
优点:
- 数据安全
- 通过append模式写文件,即使中途服务器宕机也不会破坏已存在文件的内容,可以通过redis-check-aof工具解决数据一致性问题
- AOF机制rewirte模式。定期对AOF文件进行重写,以达到压缩的目的。
缺点:
- AOF文件比RDB文件大,且恢复速度慢
- 数据集较大的时候,比rdb启动效率慢
- 运行效率没RDB高
5.Redis的过期key删除策略
Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期就是指当Redis中缓存的ey过期了,Redis如何处理。
惰性过期:只有当访问一个key时,才会判断key是否已过期,过期则清除。虽然该策略可以最大化节省CPU资源,但是对内存的压力非常大。极端情况下可能出现大量的过期key没有再被访问,不清除,占用大量内存
定期过期:每隔一段时间会扫描一定数量的数据库的expires中字典一定数量的key,并清除其中已过期的key。这种策略是一种折中方案,使CPU和内存资源达到最优的平衡效果。
Redis同时使用了惰性过期和定时过期两种过期策略。
6.Redis集群方案
哨兵模式
哨兵模式是主从模式的升级。哨兵是redis集群中一个非常重要的组件,主要的功能如下
- 集群监控:负责监控redis的主节点和从节点是否工作正常。
- 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
- 故障专业:如果主节点挂了,会自动转移到从节点上。
- 配置中心:如果故障转移发生了,通知管理员。
哨兵用于实现redis集群的高可用,本身也是分布式,也可以做集群。作为一个哨兵集群去运行,互相协同工作。
特点:
- 故障转移时,判断一个主节点是否宕机了(判断主节点是主观下线的还是客观下线的),会存在两种判断,需要大部分哨兵都同意才行,涉及到了分布式选举。
- 即使部分哨兵节点挂了,哨兵集群还是能正常工作的。
- 哨兵通常需要3个+,保证自身的健壮性。
- 哨兵+redis主从的部署架构,是不能够保证数据的丢失问题,只能保证redis集群的高可用性。
Redis Cluster
Redis Cluster是服务端的一种分辨技术。3.0版本后开始正式提供。采用slot(槽)的概念,一共分成16384
个槽。redis连接的任意一个节点,都会把这个请求发送到正确的节点上执行。
redis集群上有多少个节点,这个槽就分布在这些节点上,组成一个环,这些节点互相提供服务,所谓的主从也是互为主从。
比如说上面有5000个槽位,比如说有一个节点a是从节点,这个a节点会复制这5000个槽位在这个节点上面,这是主从,保证数据的安全性。这个a节点同时又是主节点,除了备份其他的5000个节点上,自己本身还有5000个槽位,这些5000个槽位又是其他节点来提供备份。
- 通过哈希的方式,将数据分片,将key均匀的分散到每个槽上,再来判断槽在那个节点上。
- 每份数据分会存储在互为主从的多个节点上。
- 数据先写入主节点,再同步到从节点。
- 同一分片的数据的多个节点不保证强一致性。
- 读数据时,当客户操作的key没有分配在该节点上,redis会转向指令,指向正确的节点。
- 扩容时需要把旧节点的数据迁移一部分到新节点上。
优点:
- 无中心架构,支持动态扩容,对业务透明。
- 具备哨兵的监控和自动的故障转移能力。
- 客户端不需要连接集群中的所有节点,连接一个可用的即可
- 高性能。
缺点:
- 运维也很复杂,因为扩容需要移动槽
- 只能使用redis中的0号数据库(redis中有11个数据库)
- 不支持批量操作
- 分布式逻辑和存储模块耦合等。
7.缓存击穿、缓存穿透、缓存雪崩
缓存击穿
缓存击穿是指缓存中没有但数据库有的数据(一般都是缓存过期)。这时由于并发用户太多,同时读缓存没读到数据,又去数据库读取,引起数据库压力增加。和雪崩不同的是,击穿只是并发的查找一个数据,雪崩是不同的过期了。
解决方案
- 设置热点数据永不过期
- 加互斥锁,与雪崩的效果一样。
缓存穿透
缓存穿透是指缓存和数据库中都没有数据,导致所有的请求都落在数据库上,导致数据库短时间内承受大量请求而崩掉。缓存穿透一般来自恶意攻击。
解决方案
- 接口层增加校验,如用户权限校验,id做基础校验,比如
id<=0
。 - 从缓存中取不到数据,在数据库中也没取到,这时可以将key - value存为null,那么针对这同一个key的攻击就无效。
- 布隆过滤器。
缓存雪崩
缓存雪崩是指缓存同一时间大面积失效,所以后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案
- 缓存数据的过期时间随机设置,防止同一时间内大量数据过期的现象发生。
- 给每一个缓存数据增加响应的缓存标记,记录缓存是否失效,如果失效了,就更新缓存数据,但是比较耗费CPU性能。
- 缓存预热,把热点数据先放到缓存中,在开启服务器。
- 互斥锁,查数据库时,把缓存的键锁起来,其他同一key的请求阻塞,然后等第一个查出来后,在放到缓存中其他阻塞的请求再去从缓存中拿。
8.Redis分布式锁实现
- 首先利用setnx来保证:如果key不存在才能获取到锁,如果key存在,则获取不到锁。
- 然后还要利用lua脚本来保证多个redis操作的原子性。
- 同时还要考虑到锁过期,所以需要额外的一个看门狗定时任务来监听锁是否需要续约。
- 同时还要考虑到redis节点挂掉后的情况,所以需要采用红锁的方式来同时向N/2+1个节点申请锁,都申请到了才证明获取锁成功,这样就算其中某个redis节点挂掉了,锁也不能被其他客户端获取到。
9. Redis主从复制的核心原理
- 集群启动时,主从车间会先建立连接,为全量复制做准备
- 主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载,这个过程依赖于内存快照RDB
- 在主库将数据同步给从库的过程中,主库不会阻塞,仍然可以正常接收请求。否则,redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。为了保证主从库的数据一致性,主库会在内存中用专门的replication buffer,记录RDB文件生成收到的所有写操作。
- 最后,也就是第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成RDB文件发送后,就会把此时replocation buffer中修改操作发送给从库,从库再执行这些操作。这样一来,主从库就实现同步了。
- 后续主库和从库都可以处理客户端读操作,写操作只能交给主库处理,主库接收到写操作后,还会将写操作发送给从库,实现增量同步