Redis是什么
Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。**
redis 为什么快:
1.基于内存没数据存储运算都在内存中
2.存储是 KV 模式,数据结构都是自己设计的
3.IO复用和非阻塞IO, IO服用来监听多个socket客户端,一个线程处理多个请求,避免上下文切换和竞争
4.工作线程是单线程(串行 快)
高可用:
持久化,主从复制,哨兵,集群
membercache :只有string 数据向计算移动
1.redis数据类型
Redis 7.0之后后 Listpack 取代了 ziplist
redis字符串 (String) (动态字符串SDS)
有四个字段 : 长度,大小,类型,数据 char[]
编码 :int emstr (嵌入式string,长度小于44,分配一块连续的内存空间) raw (大于44长度字符串,分配两块内存,sds,一块redisObject)
(浮点型会转成字符串)
有5个数据结构
sdshdr5. (2^5=32byte)
sdshdr8. (2 ^ 8=256byte)
sdshdr16. (2 ^ 16=65536byte=64KB)
sdshdr32. (2 ^ 32byte=4GB)
sdshdr64,2的64次方byte=17179869184G用于存储不同的长度的字符串。
优点:计算长度大小为 O(1),封装了管理内存,(会惰性释放空间)
string实现分布式锁:
SETNX
格式: setnx key value
将key 的值设为value ,若给定的 key已经存在,则SETNX不做任何动作。
del 删除锁
setnx 的问题:
- 无法自动释放锁
如果持有锁的进程崩溃或未能主动释放锁,可能导致死锁。解决方案通常是使用 SET 命令加选项 NX 和 PX(或 EX)设置过期时间。 - 不可重入:同一个客户端不能多次获取同一个锁。
- 可能的锁误删 如果锁超时过期,可能导致误删其他客户端持有的锁。.
- 没有失败重试
解决方案: redlock(红锁)
多实例加锁: 在多个独立 Redis 实例上尝试加锁,只要多数实例成功即可,增加了容错能力。
失败重试:如果加锁失败,可以在短暂等待后重试,增加获取锁的机会。
原子操作:使用 SET 命令的 NX 和 PX 参数,确保设置锁和过期时间是原子操作
加锁时间校验:确保加锁总时间小于锁的过期时间,防止因超时导致锁误删。
redis列表 (List) (quicklist = 双端链表 + 压缩列表ziplist)
数据结构 :
Quicklist 是一个双端链表, 这个双端链表的节点 是ziplist
最大2^32 -1
对两端的操作性能高,操作中间节点性能低
操作分为 L(从左边) R(从右边)
应用场景:一个key,多个val ,发布订阅场景
redis哈希表 (Hash) (哈希表 + 压缩列表ziplist)
数据结构:哈希表 + 压缩列表ziplist:
小于64byte的数据存ziplist ,大于64字节 的存哈希表
时间复杂度:O(1)
K V(value为一个键值对)
应用场景:购物车
redis集合 (Set) (哈希表 + 整数集合intset)O1
数据结构:
整数类型使用 整数集合intset来存
非整数 使用 哈希表(数组+链表)来存
整数数组: 底层 为 int_8[] 数组,存储的是 int_16(被编码过)
无序集合 ,无重复,通过哈希实现 O(1),求交集,并集等操作多
应用:抽奖,社交,点赞,可能认识的人
redis有序集合 (ZSet) (跳表skiplist + 压缩列表ziplist)(logn)
数据结构
元素数量小于128 ,大于64byte 使用 ziplist
当元素个数 大于 128 或者 元素 的大小大于64字byte 使用跳表
有序集合,每个元素关联一个score分数值来排序
应用: 热点,排行榜
redis地理空间 (GEO)
用来存储地理位置信息,经纬度。
操作:添加坐标,获取坐标,计算距离,获取范围内的做标记集合
应用:附件的人,地图,外卖等
redis基数统计 (HyperlogLog)
用于做基数统计,只会计算结果,不会存储数据,比较省空间,
应用:统计网站uv,pv ,活跃用户数量,统计关键字,等统计场景
redis位图 (bitmap)
由 0 1 状态表示的二进制数组 多用于签到 或判断Y/N 的场景,是否点击
有点:省空间,一个字节有8个比特位
底层 string实现,最大 2^32
redis流 (Stream)
主要用于MQ:支持消息持久化,支持全局唯一ID,支持ack消息确认,支持消费者组
结构:是一个消息链表,将消息都穿起来,每个消息有唯一id
消费者组:同个消费组可以订阅多个消费者
游标:用来确认那些消息被消费了(类似offset)
消费者pending_ids : 记录消息是否会ack 是否被确认
redis位域 (bitfield)
可以将一个string看做一个比特数组,对任意偏移量进行访问
可以一次性操作多个比特位域(多个连续的比特位)
用处:位域修改,溢出控制
Redis底层数据结构
SkipList跳表
实现: 跳表是一个实现二分查找的有序链表, 对链表做了多级的索引,每层都是对数据二分做索引
解决什么问题 :链表查询效率低
思想 :空间换时间
时间复杂度 : O(log n)
空间复杂度:O(n)

ZipList 压缩列表
1它是由连续内存块组成的顺序型数据结构
2.ziplist是一个经过特殊编码的双向链表,它不存储指针是存储上一个节点长度和当前节点长度
3.节约内存,是一种时间换空间的思想。只用在字段个数少,字段值小的场景里面
ziplist结构
Ziplist 头部: 链表长度,节点数量,偏移量 中间 :节点 ;尾部: 有 0xff 结束符
LIstPack 紧凑列表(redis7.0之后)
Listpack 对比 ziplist 的优势 ?
Ziplist连锁更新:插入一个较大的元素时候,会导致 ziplist连锁更新,因为每个节点都要保存前节点的长度(大于255保存长度的字段会变大)
Listpack做法: 将接节点长度记录本节点的尾部
LIstpack结构 :
头部: 链表长度,节点数量, 中间 :节点 ;尾部: 有 0xff 结束符
redis命令
1 Key的命令:
keys * 查看当前库所有的key
exists key 判断某个key是否存在
type key 查看你的key是什么类型
del key 删除指定的key数据
unlink Key 非阻塞删除,仅仅将keys从keyspace元数据中删除,真正的删除会在后续异步
ttl key 查看还有多少秒过期,-1表示永不过期,-2表示己过期
expire key 秒钟 为给定的key设置过期时间
move key dbindex [0-15] 将当前数据库的key移动到给定的数据库 当中
select dbindex 切换数据库【0-15】,默认为 0
dbsize 查看当前数据库key的数量
flushdb 清空当前库
flushall 通杀全部库
Redis持久化
为什么需要持久化,内存断电数据会丢失,为了保存数据至磁盘
RDB(redis数据库)(默认方式)
以指定时间为间隔存储数据的快照,并写入磁盘,存储为 dump.rdb 文件
恢复:将dump.rdb 移动到 redis安装目录 ,重启redis即可
RDB优缺点:
优点:性能高:父进程不会进行磁盘IO,由子进程完成(fork内核命令快),适合备份和容灾。
缺点:可能会丢失数据,还未到写RDB文件的时间间隔,断电了; 数据量过大 IO和 fork 会降低性能
时间间隔:
Redis 6.0和以前: 15分钟有1次变更,5分钟有10个key变更,1分钟有1w 个key 变更,都会写RDB
Redis6.2和7.0 以后: 一小时有1次key变更,5分钟有300次key变更,1分钟内有1w次变更,会写RDB
可已通过修改redis.conf 配置文件 来修改时间间隔
手动触发:
SAVE :会阻塞当前Redis服务器,知道持久化完成
BGSAVE :fork一个子进程来进行持久化,不阻塞redis正常请求
AOF(append only file)
以日志文件的形式记录redis的写指令,保存为 appendonly.AOF 文件
重启redis会将AOF文件命令执行一遍以完成数据的恢复。
Redis默认不开启AOF 。开启命令:appendonly yes
AOF优缺点
优点:可靠性高,有多种回写策略,everysec只会丢失1秒的数据,文件错误有修复命令redis-check-AOF —fix,有重写机制防文件大
缺点:AOF文件 比等效的 RDB文件要大,速度也更慢(RDB使用fork内核命令)
AOF 工作流程
1.redis 命令 先进入 AOF缓冲区(内存中),到达一定数量再统一写入AOF文件
2.AOF缓冲 有三种写回策略来将命令写入磁盘中的AOF文件
3.为了避免AOF文件膨胀,会根据规则进行AOF命令 的合并 称为:AOF重写,从而压缩AOF文件
三种写回策略
everysec :默认写回策略 ,每隔一秒将缓冲区的命令写回磁盘,折中策略,宕机会丢失1秒的数据
Always:同步回写,每个写命令执行完毕立刻写回磁盘。可靠性高,但每个命令都写磁盘,IO次数多
No:只写到AOF缓冲区,由操作系统决定什么时候写回磁盘,性能好,宕机但容易丢数据
AOF重写 (AOFRW)
解决AOF文件越来越大的问题
当AOF文件大小达到设定的峰值,redis自动启动AOF重写机制,只保留用于恢复数据的最小指令集合。
触发机制 :1.文件膨胀到上次重写的两倍 ,并且 大小到达64M
手动触发:berewriteaof 命令
重写原理:只保留最后数据版本的命令 ,并不对原文件进行操作,而是读取redis现有数据,重新生成命令来代替原来AOF文件。
重写过程:
1 .创建重写子进程,将现有AOF文件放入一个临时文件
2 .主进程将 redis命令写入缓冲区中,并写入原有AOF文件
3 .子进程将缓冲区的命令追加到 新的AOF文件
4 .新AOF文件 替代旧AOF文件
MP-AOF实现(redis7.0新特性)
将AOF文件拆分成多个AOF文件,分为三种类型
BASE:基础AOF文件,一般通过子进程重写产生,只有一个
INCR:增量AOF文件
HISTORY:历史AOF文件,每次AOFRW(重写)完成后,history文件 会被删除
清单文件:记录AOF文件清单
AOF文件异常修复命令 :redis-check-AOF —fix
RDB-AOF混合持久化(AOF优先级高)
1.可以同时开启 ,同时启动的话 优先使用 AOF
2.同时开启,重启只会加载AOF,aof不存在 找RDB,RDB也没有,则恢复失败
混合模式:
AOF做数据恢复,增量持久化, RDB镜像做全局持久化
RDB做头部,AOF追加做增量
纯缓存模式only
关闭所有 持久化方案
场景:对性能要求高,需要做高速缓存
关闭RDB: save “ ” 空字符串
关闭AOF: appendonly no
关闭后 更可以使用命令来生成 rab 和AOF文件 (save bgsave bgrewriteAOF)
Redis事务
事务可以一次性执行多条命令,本质是一个命令集合,将命令放入一个队列,一个事务中的命令会被按顺序串行执行,而不会被其他命令打断插入
特性:
1.隔离性: redis为单线程,所以在事务执行完毕前是不回去执行其他的命令的
2.没有隔离级别 :事务提交前 任何命令不会被执行,会将命令放进队列 顺序执行
3.不保证原子性:执行一半无法进行回滚
4.排他性:redis保证食物内的命令顺序执行,不被其他命令插入
命令 :
MULTI :开启一个事务
EXEC :执行事务中的所有命令
DISARD:取消事务,放弃事务中的命令
WATCH key 监控 key如果事务执行前这个key被改动,则取消当前事务(类似锁) 事务提交,或者redis重启都是取消监控
UNWATCH 取消对所有key的监控
Redis 管道(pipelinling)
管道可以一次性发送多命令给服务端,服务端处理完成,将处理结果一次性返回,减少了redis 服务端和客户端的通信次数,从而降低延迟。
实现原理是队列,保证多条命令的顺序
用处 :解决执行redis命令时,需要多次客户端和服务器端交互的 问题,(RTT:数据往返于两端的时间)
使用方法 :
将命令写入文件中,通过redis pipe 来执行 例:cat xxx.txt | redis -cli - a 111111 - -pipe
管道 和原生 批处理命令的 区别 mset mget
1;原生批处理命令时 原子性的 管道不是
2.管道可移植性一次性执行不同数据类型
3.管道式服务端和客户端共同完成的,原生命令是服务端实现的
事务和管道区别 :
1.事务执行会阻塞其他操作,管道不会
2.事务是一条一条发送到服务端,通过exec执行,管道是一次性发送
Redis 主从复制(replica)
支撑Redis高可用 和容灾备份
能干嘛:
读写分离
容灾恢复
数据备份
水平扩容支持高并发
配从库,不配主库
主从关系
从库保存 主库的 ip和端口
命令
Info replication 查看复制节点的主从关系和配置信息
replicatof 主库ip 端口 :配置主库,写进redis.conf配置文件中
Slaveof 主库ip 端口 :重新绑定一个主库
Slaveof no one 当前节点停止与其他节点的同步 转为主库
主从复制原理和工作流程
1.slave启动,第一次连接到master后会发送一个sync 命令,首次连接master会清除自己的数据,全量复制master数据
2.master 使用 RDB 将数据 复制到slave节点,slave拿到RDB文件后将文件加载进内存中
3.心跳包保持主从联系和通信,默认为 10秒发一次 :repl-ping-replica-period 10
4.增量复制:master继续将新收到的修改命令依次发给slave,完成数据同步
5.从机下线,重连后,会重传数据,根据主从复制的偏移量 offset,master将offset之后的数据同步给slave
主从复制缺点:
1.复制延迟:写操作都是先在master上更新,再同步至slave ,避免不了有延迟
2.master挂了:默认不会自己选主
Redis 哨兵(sentinel)
哨兵作用:
监控 master,和salve节点,如果故障的话 根据投票数将从库设为主库,保证稳定性
哨兵配置:
1.哨兵节点 应有多个,设为 奇数 个(需要投票,偶数可能出现同票数)
2.各个哨兵节点的配置保持一致
3.哨兵 + 主从复制,并不能保证 无故障
哨兵运行原理和流程:
1 SDOWN(主观下线):单个哨兵主观发现 ping不到 master 了,一定时间没得到回复,即达到了SDOWN的条件(默认30秒)
2 ODOWN(客观下线):多数哨兵确认master 节点挂了,则达到了ODOWN的条件
3 哨兵选举出一个哨兵leader:
4 哨兵leader 进行故障迁移,选举出新的master节点
Raft算法(选举哨兵leader)
基本思路:先到先得
master选举
1.某个slave被选为 新master :比较slave权限优先级—> 比较复制偏移量offset(复数据完整性) —>比较Run id 小的为 master(服务器运行id)
2.哨兵leader 对新master 执行 slaveof no one 变为 master节点
3.哨兵leader 将 旧 master 降级为 slave 节点
Redis集群
Redis集群是一个提供在多个Redis节点间共享数据的 程序集合。Redis集群支持多个Master
Redis集群作用
1.支持多个master,每个master可以挂多个slave读写分离,数据高可用,支持海量数据读写
2.redis集群自带故障转移机制,无需哨兵
3.客户端只需要连接集群中的一个节点即可
4.槽位slot负责分配到各个节点,对应的集群维护节点,操作,和数据
集权算法-分片-槽位slot
集群空间 被分为 16384 个槽位,最多16384个节点(建议最大1000)
Redi使用 哈希槽
分片
Redis集群将数据分散到多台机器上,集群中的每个redis实例都被认为是整体数据的一个分片
找到分片:对key 使用CRC16算法,并对总分片数取模,用确定性哈希算法,将key始终映射到同一个分片
分片和操作的优势
最大优势:方便扩缩容和数据分派查找
添加或者删除节点,都会移动槽位来解决,所以 增加或删除节点 都不会造成集群不可用。
Slot槽位映射
1.哈希取余分区 :让固定的命令落在同一台机器,分开数据 缺点:节点变更会重新计算映射
2.一致性哈希算法 :解决 节点变动问题
三个步骤 : 1.构建一致性哈希环 2。服务器IP节点映射 3.key到服务器的落键规则
问题:会导致数据倾斜 ,节点分布不均匀会导致数据倾斜
哈希槽分区
2 ^I4 = 16384 个哈希槽
解决数据倾斜
为什么是 16384 :
1 CRC16算法可获得 65536(2^16)个哈希值, 65536个会导致心跳包为8k ,16384为 2k
2.redis 集群节点数量不会超过1000个 16384个槽位够用
3,槽位越小 ,节点少的情况下,压缩配比高,容易传输
Redis线程模型
Redis 4之后慢慢支持多线程,redis6之后才有多线程IO
Redis 工作线程是单线程 ,但是其他功能 AOF,RDB ,异步删除,集群数据同步等,都是由其他线程完成的
工作线程是单线程原因,:redis瓶颈不在cpu,在内存和网络,单线程通过IO复用可以处理多个请求
Redis主线程工作流程
1.服务端和客户端连接,通过轮询方式将 socket 分配给IO线程
2.IO线程读取数据并解析 ,主线程阻塞,IO线程,等待IO线程完成客户端的请求(IO为多线程,等待很快)
3.主线程执行操作:主线程以单线程执行redis命令操作
4.主线程将结果放入缓冲区,主线程阻塞等待,IO线程线数据放入socket返回到客户端,主线程最后会清空队列,等待后续请求。
redis多路复用:epoll
Redis 缓存过期淘汰策略
1.立即删除
到期立刻删除,内存中数据为最新,用cpu性能换存储空间
缺点:会占用cpu时间,会给cpu调度压力,消耗性能
2.惰性删除
开启:lazyfree-lazy-evicition = yes
数据到达过期时间,不做处理,下次这个key被访问时,检测到已过期,删除数据,返回不存在
缺点:对内存不友好,不被访问到的key,会一直占用内存,对内存不友好
3.定期删除
每隔一段时间删除一次过期键,采用随机抽取的方式,抽取一部位数据进行检测
难点:1.定期删除间隔的控制,间隔太短耗费cpu,太长删除不够及时 。2 抽样的策略:抽样不合理会导致删除不及时,会有漏网之鱼
Redis缓存淘汰策略
页面置换算法
LRU :最近最少使用,法淘汰最长时间未被使用的页(时间)
LFU: 最近最不常用 ,淘汰一定时间内访问次数最少的页 (频率)
8种策略:(默认为第一种,不删,满了OOM)
1.noeviction:不会驱逐任何key,表示即使内存达到上限也不进存置换,所有能引起内存增加的命令都会返回error
2.allkeys-ru:对所有key使用LRU算法进行删除,优先删除掉最近最不经常使用的key,用以保存新数据
3. volatile-lru:对所有设置了过期时间的key使用LRU算法进行删除
4,allkeys-random:对所有key随机删除
5.volatile-random:对所有设置了过期时间的key随机删除
6. volatile-tt:删除马上要过期的key
7.allkeys-1fu:对所有key使用LFU算法进行删除
8.volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除
8种分类 :对所有key和过期key 使用:LRU,LFU,随机, 还有 过期全删(不删)
最常用的:(根据场景)
1.对所有key使用LRU
2.对所有key随机删除(场景:key只是用一次的场景)
3.删除马上要过期的key (会为所有key设置过期时间,业务场景精确)
使用建议 : 开惰性删除,尽量避免大Key
Redis 缓存预热 、缓存穿透、 缓存击穿、 缓存雪崩
缓存预热
首次加载时,mysql有数据,Redis无数据,初始访问的人会有延迟
解决方法: 1.人工加载 2.程序活中间件将数据加载进redis
缓存雪崩
发生原因
1.缓存中有大量key同时过期
2 Redis挂了
解决方案 :
1.缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
2.redis集群:主从的模式让redis继续提供服务,再通过aof + rdb 快速恢复
3.多缓存结合:本地缓存 + redis缓存
4.服务降级:限流 ,降级
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案
1.接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
2.回写增强:从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒
3.布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉 (谷歌 Guava)
缓存击穿
大量请求同一个Key,此时key过期失效,导致请求都落到了数据库上
失效原因: 1.过期被删除访问不到 2.被删除了
解决方案:
1.对于频繁的热点key,不设置过期时间,自己存过期时间,业务校验,或者调整过期策略 如:LRU,LFU
2.双检加锁:在第一个查询这个key的请求,用锁,锁住它,第一个数据,查不到,去做缓存,后面的请求就能查到缓存了
常见问题
Redis内存相关
1.redis最大内存 怎么看
配置文件:maxmemory ,默认是 0 byte ,代表不限制(64位),32位选 为3G
2.内存一般配置为最大内存的3/4
3.如何修改最大内存
修改配置文件 , 命令修改 set maxmemory
4.查看redis内存使用情况
info memory ; config get maxmemory
5.redis内存打满了怎么办
报错:内存溢出 OOM

Redis是一个高性能的内存数据库,支持多种数据结构如字符串、列表、哈希、集合、有序集合。其通过RDB快照和AOF日志实现持久化,确保数据安全。主从复制和哨兵系统提供高可用性,集群实现数据分片和故障恢复。Redis使用单线程模型,通过IO复用提高性能,并支持事务和管道操作。此外,还介绍了Redis的内存管理、过期策略和缓存策略,如LRU、LFU和随机删除。
1300

被折叠的 条评论
为什么被折叠?



