系列文章目录
文章目录
一、数据结构
1.字符串
不直接用C语言的字符串,是因为
- O(n)获取字符串长度
- 扩容复杂
- 特殊字符无法处理
Redis的字符串结构有效解决了上述问题。
2.链表
redis链表底层实现是双向链表
3.哈希表
用一对key和value存储数据。C语言自身没有哈希表,Redis自己实现了哈希表。
Redis采用拉链法作为哈希表的实现。
哈希表的负载因子为散列表内元素个数/散列表的长度。
如果redis空闲,负载因子大于等于1就执行rehash。
redis在执行备份,一般就等负载因子大于等于5时rehash。
4.集合
分为普通集合和整数集合
- 普通集合就是对哈希表的封装
- 整数集合是redis独创的集合。是有序数组,所以用二分查找。
5.有序集合
跳表是参考二叉树给链表添加父节点的思想。
Zset实现微博热词TopK排序。
二、持久化
- Redis是内存性数据库
- 优点:内存读取速度快
- 缺点:数据易丢失
- 解决方法:RDB、AOF
1.RDB
把目前redis内存中的数据,生成一个快照(RBD文件),保存在硬盘中,如果发生事故,redis可以通过RBD文件,进行文件读取,将数据重新载入内存中。
RDB触发条件:
- 手动触发:save命令-主线程执行rdbsave函数,服务器进程阻塞,不能处理其他请求
bgsave命令-会fork一个子进程,去执行rdbsave函数,主线程可以处理其他请求
-自动触发:配置文件中写入save m n,表示当m秒内发生n次变化时,会自动执行bgsave。
2.AOF
记录之后所有对Redis数据进行修改的操作。
发生事故后,可以通过重新执行AOF文件,修改命令全部执行一遍,数据恢复。
- AOF重写与回复:创建一个新的AOF文件,以替代原有的冗余AOF文件。并且新的AOF与老AOF文件没有关系,由当前Redis数据状态生成。
- AOF触发条件:手动触发-bgrewriteaof,自动触发-配置文件中appendonly yes开启
自动触发的写入策略:
- Always:每个命令完成后,都会写入磁盘文件
- Everysec:每秒写回,每过1秒,把上一秒的命令写入AOF文件中。
- No:将操作命令写入缓存区,由操作系统决定何时写入AOF文件中。
默认策略是Everysec。
三、缓存
1.缓存淘汰
- 先进先出FIFO
可以理解为一个队列 - 最近最少使用LRU
一种实现思路双向链表:双向链表+哈希表
- 最不经常使用LFU
redis一般使用LRU算法。
2.过期删除
- 主动删除-设置删除间隔时间,指定时间后进行主动删除。优点-设置合理间隔后不会使内存超标。缺点:当redis较忙时,碰巧遇到删除点,会加重服务器负担。
- 惰性删除-取值时会检查该数据是否过期,过期则删除,否则返回。优点-服务器运算资源小。缺点-容易造成资源浪费。
redis一般采用定期删除,即主动删除+惰性删除。
3.缓存一致
核心思想是当缓存的数据有更新值了,不是更新缓存数据,而是删除缓存数据。
model1:先删除再修改。
采用延时双删策略,解决问题。
model2:先更新再删除
Read/Write Through
核心点在于应用把缓存作为主要的数据读取方式,避免缓存击穿。
Write Behind
- 方式一:将更新步骤放到消息队列中,异步执行。
- 方式二:利用主从复制。
4.缓存击穿
查询某个数据的值,但该值在缓存中不存在,在数据库中有。
若对于某个不存在的缓存突然存在大量请求,那么请求会直接作用到数据库中,后端数据库可能会宕机。
解决方法:
- 设置热点数据永不过期
- 热点数据后台启动一个异步线程,重新把数据回填缓存层。
5.缓存穿透
缓存查不到,数据库也查不到。所有的请求都会直接作用到数据库中,属于非法请求。
解决方法:
- 拦截非法查询请求
- 缓存空对象
- 布隆过滤器-输出查询数据,通过一系列hash函数,判断对应位置是否全为1.如果是,则很大概率就是有效请求,若有一位为0,则一定是无效请求。
6.缓存血崩
大量缓存失效,大量的请求作用到数据库上,导致宕机。(多点的缓存击穿)
解决方法:
- 减少并发量(Mysql)
- 设置热点数据永不过期
- 分析失效时间,尽量让失效时间分散
- 缓存预热,根据情况分析,将热数据直接加载到缓存系统
四、集群
1.主从复制
- 主库从库需要进行握手信息验证。
- 握手完成后,从库向主库发送PSYN命令,即同步命令,开启数据同步过程,并发送主库ID,复制的进度偏移量offset。
- 根据从库发送的信息,进行逻辑判断,并告诉从库,是进行全量复制还是断线后重复制。
全量复制:主库执行bgsave命令,生成rdb文件,发送给从库进行数据同步。
命令传播阶段:主库将数据库更新命令发送给从库,从库进行数据更新。
断线后重复制:根据服务器运行id、复制偏移量、以及复制积压缓冲区进行从库与主库断线重连后的复制。
2.哨兵
新建哨兵组,对主库从库统一进行监控,如果主库坏了,哨兵组进行投票,从从库中重新选出主库。
从本质上,哨兵是一个不提供数据服务的redis服务器。
每个机器都会向哨兵一定时间发送信息(心跳)。哨兵如果一定时间收不到这个信息,就默认这个机器断联了。
- 如果某个哨兵发现主库无法连接,会通知其他哨兵尝试连接主库。若存在过半哨兵确认无法连接,则标记该主库为客观下线。
- 通过选举协议,从所有烧饼中选择一个节点,主持新主库的选举工作,选取完成后,对被选中的从库发送slaveof no one,该从库就变成主库。
- 同时向其他从库发送新主库的ip地址与端口。
3.集群
Redis提供的分布式数据库解决方案
- 自动将数据切分给多个节点存储
- 即使这些节点一部分宕机,也可以执行部分数据操作。
分区策略:采用虚拟槽,所有键通过CRC16校验函数,对16384取模,决定数据分配到哪个槽位。每个redis的cluster节点负责一部分分槽数据的存储。并且该节点可以结合之前降到的主从复制模式,将分配给它的数据进行复制。
查询策略:每个节点都会存储整个集群节点信息,也成为了元信息。
某个节点存储的元数据包括,以该节点为第一视角下:
- 各节点的槽数据
- 各节点的master和slave状态
- 各节点是否存活
元数据信息传播-gossip协议,具体过程:
- 周期性地向周围K个邻近节点传播信息。
扩容缩容:扩容一般是数据过多需要添加节点,缩容是数据过少需要减少节点,在此过程中数据需要前移。
3.1集群总结
哈希Slot的优缺点:
- 优点:将Redis的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。
- 缺点:每个Node承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重。
Redis集群的逻辑一句话说就是先Hash分逻辑节点,然后每个逻辑节点内部都是主从。
扩展并发读就添加Slave,扩展并发写都添加Master,扩容也是添加Master(Master的写能力与Disk大小是整个Redis集群存储容量的瓶颈)。