Redis深度探险知识总结
1.数据结构
- String:get/set,mget,incr,expire,setnx,setex
- list: rpush/lpush,lpop/rpop,llen,lrange,ltrim
- hash
- set
- hset
高级数据结构 - bitmaps
- HyperLogLog
- 布隆过滤器
- GEO
- PubSub
- Stream
2. 分布式锁
并发保证数据原子性(操作不会被线程调度打断)
关键命令 setnx,del
死锁
触发条件:当del没有被调用
解决方案:
- setnx和expire可以一起执行(超时锁会过期,就释放了锁)
- 乐观锁(设置一个随机数)–>释放时校验随机数是否一致
3.延时队列
基于list数据结构(本质时linkedlist)
流程: 将操作放入list队列中,处理完成再获取消息
队列空了怎么办(陷入pop死循环)
- 线程睡一会(多个操作会导致反应过慢)
- 延迟队列(将请求放入延时队列,过一会再执行)
- 抛出异常,让用户等一会
4.限流
简单限流
使用zset的score值对时间戳进行排序
可以使用pipeline提高效率
漏斗限流
最常用的一种限流方式
Redis插件 Redis-Cell
解释:如漏斗一样,堵住会变满,放开就下流。漏嘴流水速率小于灌水的速率,漏斗会变满,需要暂停并腾空漏斗。
5.堵塞
这keys *堵塞
缺点
- 没有分页
- 时间复杂度为On,容易卡顿
SCAN
- 不会堵塞
- 可分页
- 提供匹配功能
- 有游标
- 结果会重复
- 结束游标值为0
遍历顺序:高位进位加法
定位大key(一般为hash结构)
命令: redis-cli -h 127.0.0.1 -p 7001 --bigkeys -i 0.1
6.IO模型
- 单线程
- 内存数据库
- 多路复用(读写事件多路处理)
- 非阻塞IO
- 套接字
- 指令队列(每个客户端套接字关联一个指令队列,顺序处理,先到先服务)
- 响应队列(每个客户端套接字关联一个响应队列,可以将结果返回,队列为空时弹出连接,证明客户端已经没有工作)
- 定时任务
7.RESP协议
- 字符串 +开头
- 多行字符串 $开头
- 整数 :: 开头
- 错误消息 - 开头
- 数组 * 开头
AOF持久化就是记录的是写操作 ,可进去查看
8.持久化
快照
原理:Redis单线程不仅需要负责线上请求还要负责IO操作,严重拖垮服务器性能,需要同时负责请求和IO操作,Redis使用COW(Copy on write)来实现
fork(多进程):
调用glib函数创建一个子进程负责IO操作,共享父进程内存中的代码段和数据段。父进程就负责请求和对内存的修改
为什么叫快照:
子进程看到的数据没有变化,所以叫快照,可以安心写入磁盘中
AOF原理
因为快照记录数据不完全
作用:只记录写操作,就可以重放数据到内存中
缺点:AOF日志越来越多,需要缩身
解决方案:提供bgrewriteaof指令 开辟了一个子进程将写操作序列化到新的AOF中,增量AOF会追加到新的AOF文件中
fsync:
问题:如果掉电导致宕机AOF没有来得及写入怎么办?
答案:提供fsync保证aof数据不丢失,但是很慢!
两种策略:1.永不fsync,不安全
2.fsync,生产环境一般不会使用
混合持久化
重启时,先加载rdb,再加载aof,提高效率
问题:为什么Redis只适合缓存
1.无法大量存储,不能保证数据安全
2.复杂数据不易存,不能存表关联关系
3.sfsnc使redis比mysql效率慢
8.管道
pipeline将多个指令操作押入管道中,客户端只请求一次到服务器中,提高效率,节省IO时间。指令越多效果越好。
压力测试 redis-benchmark
指令:redis-benchmark -t set -q
9.事务
为确保原子性(事务全部成功或全部失败)
指令:multi/exec/discard,watch/unwatch
Redis没有原子性,只能保证隔离性
经验:和pipeline一起使用压缩io操作
watch监控一个变量
经验:若监控变量一旦被修改那么事务就会失败
问题:为什么redis不支持回滚?
1.只检查语法错误
2.不需要支持回滚,目的是简单高效
10.小对象压缩
Redis有两种bit编译,一种是32bit,第二种是64bit。使用32bit可以少一半内存,Redis的内存使用不超过4G。
小对象压缩存储ziplist
ziplist是一个紧凑的字节数据结构,为防止对象过大,conf里的限制条件
hash-max…
list-max…
zset-max…
set-max…
内存回收机制
Redis中数据是保存在内存中的,内存是由多个页组成
1.Redis存储key是分布到各个页中
2.简单删除key是将删除,但其他key还在页中,所以内存小不了
3.执行flushdb和flushall所有key被干掉,内存释放。
内存分配算法
两种
- jemalloc(Redis使用,性能稍好)
- tcmalloc
指令:info memory
11.主从同步
CAP
C-Consistent 一致性
A-Availability 可用性
P-Partition tolerance 分区容忍性
特点:一致性和可用性两难全
通常Redis集群满足可用性,单机主从满足一致性(指令AOF)
Redis支持主从同步和从从同步
增量同步(没懂):
Redis同步的是指令流
快照同步(besave)
增加从节点
无盘复制
Wait指令
12.哨兵模型Sentinel
哨兵模型单机使用保证一致性
不能保证消息不会丢失,可以限制延迟过大
- min-slaves-to-write 1 至少一个从节点
- min-slaves-max-lag 10 10s没有收到就断开
13.Codis
是Go语言开发,使用Redis协议对外提供服务。
Codis是无状态的转发代理中间件,可动态增加Redis实例实现扩容需求
分片原理(类似hashmap)
将所有的key划分为1024个槽,先对客户端传过来的key做crc32计算hash值,再对1024取模,余数就是key对应的槽位。
当Redis扩容时,对槽位进行调整,是通过SLOTSSCAN指令遍历slot所有key,然后挨个迁移到新节点上
特点:
自动均衡:每个Redis的slots数量平衡
缺点:
- 不支持事务
- key值不宜过大
- 增加proxy层,性能有所下降
- 增加zk的代价
优点:
- 简单
- 易调试
数据同步
是通过ZooKeeper实现同步持久化槽位关系,这里可以体现ZooKeeper的数据同步功能
14.Cluster
特点:
- 去中心化
- 精细,slots为16364
- 定位快
- 官方提供
槽位定位算法
key值使用crc32算法得到hash值,再对16384取模获取具体槽位
跳转
如何找到槽位
若发到一个错误节点,这个节点将返回一个正确节点信息,再从定向到正确节点
迁移
提供redis-trib手动调整
状态为migrating(源节点)–>importing(目标节点)–>删除内容(源节点)
过程为同步,会阻塞到key被成功删除
若key数据小,migrating快,很大容易卡顿,影响集群效率
访问流程
- 访问若两个槽都存在部分key数据,客户端先访问旧节点,若存在旧节点里正常处理。若不存在就有两种可能,不存在或者新节点里。
- 旧节点不知道,会返回一个-ASK targetNodeAddr的重定向指令。
- 客户端收到后,先去目标节点执行一个asking指令,然后执行原先操作。(asking指令是告诉目标指令下条指令需要处理)
结论是:影响效率,一个指令在迁移过程中需要3个ttl,正常需要一个ttl
容错
主从替换
cluster-require-full-coverage 允许部分节点故障
cluster-node-timeout 超时时进行主从切换
cluster-slave-validity-factor 超时宽松容错松弛系数,0为不抗拒网络抖动
投票机制
经过其他节点一起投票判断该节点是否下线,超过一半时就下线并主从替换
槽位感知和集群变更感知
15.Stream
5.0才出的数据结构,借鉴于kafka的设计
弥补Pub/Sub的缺点。
数据结构类似持久化得消息链表
分别是:
- 链表
- last_delivered_id(游标)
- 消费组
- 消息ID
- 消息内容
消费流程
Stream 提供了 xreadgroup 指令可以进行消费组的组内消费,需要提供消费组名称、消
费者名称和起始消息 ID。它同 xread 一样,也可以阻塞等待新消息。读到新消息后,对应
的消息 ID 就会进入消费者的 PEL(正在处理的消息) 结构里,客户端处理完毕后使用 xack
指令通知服务器,本条消息已经处理完毕,该消息 ID 就会从 PEL 中移除。
特点
- 可持久化阻塞消费。
- 通过ack保证消息的顺序性。
- 可以控制消息的长度 PEL:保证消息至少消费一次,不会丢失
16.Info
通过强大的 Info 指令,你可以清晰地知道 Redis 内部一系列运行参数。
九大参数
1、Server 服务器运行的环境参数
2、Clients 客户端相关信息
3、Memory 服务器运行内存统计数据
4、Persistence 持久化信息
5、Stats 通用统计数据
6、Replication 主从复制相关信息
7、CPU CPU 使用情况
8、Cluster 集群信息
9、KeySpace 键值对统计数量信息
# eg
# 获取所有信息
> info
# 获取内存相关信息
> info memory
# 获取复制相关信息
> info replication
17. 过期策略
过期集合
redis 会将每个设置了过期时间的 key 放入到一个独立的字典中,以后会定时遍历这个
字典来删除到期的 key。
定时扫描策略
1、从过期字典中随机 20 个 key;
2、删除这 20 个 key 中已经过期的 key;
3、如果过期的 key 比率超过 1/4,那就重复步骤 1;
丛库过期策略
从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF
文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的
key。
LRU
-
noeviction 不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样
可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。 - volatile-lru 尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过 期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
- volatile-ttl 跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。
- volatile-random 跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。
-
allkeys-lru 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不 只是过期的
key 集合。这意味着没有设置过期时间的 key 也会被淘汰。 - allkeys-random 跟上面一样,不过淘汰的策略是随机的 key。
-
volatile-xxx 策略只会针对带过期时间的 key 进行淘汰,allkeys-xxx 策略会对所有的 key
进行淘汰。如果你只是拿 Redis 做缓存,那应该使用 allkeys-xxx,客户端写缓存时 不必携带过期时间。如果你还想同时使用Redis 的持久化功能,那就使用 volatile-xxx 策略,这样可以保留没有设置过期时间的 key,它们是永久的 key 不会被LRU 算法淘 汰。
懒惰删除
在 4.0 版本引入了 unlink 指令,它能对删除操作进行懒
处理,丢给后台线程来异步回收内存。flushdb 和 flushall 指令,用来清空数据库,这也是极其缓慢的操作。
1、slave-lazy-flush 从库接受完 rdb 文件后的 flush 操作
2、lazyfree-lazy-eviction 内存达到 maxmemory 时进行淘汰
3、lazyfree-lazy-expire key 过期删除
4、lazyfree-lazy-server-del rename 指令删除 destKey
18. 安全
- 指令安全
- 端口安全
- Lua安全
- SSL 代理
本文地址:https://blog.csdn.net/qq_43168682/article/details/110353124
下一篇: Kerberos的简单实现