Redis知识点树
2024-07-04 22:31:58 0 举报
AI智能生成
为你推荐
查看更多
Redis是一个开源的,高性能的Key-Value存储系统,使用ANSI C语言编写,支持多种数据类型,如字符串、哈希、列表、集合和有序集合。它是内存中的数据结构存储系统,通过磁盘或快照进行持久化。Redis支持主从复制、事务、Lua脚本等特性,确保数据的可靠性和一致性。它具有丰富的客户端库,支持多种编程语言,如Python、Java、C#等。Redis广泛应用于缓存、队列、排行榜、订阅/发布系统等场景,是构建现代Web应用程序的重要基础组件。
作者其他创作
大纲/内容
dump.rdb
Redis DataBase (redis 快照)
主线程去备份,备份期间不会处理其他指令,其他指令必须等待
save
子线程去备份,其他指令正常执行
bgsave
手动触发
save 900 1 900s检查一次,至少有1个key被修改就触发save 300 10 300s检查一次,至少有10个key被修改就触发save 60 10000 60s检查一次,至少有10000个key被修改
配置触发
shutdown
flushall指令
自动触发
触发机制
可靠性低
RDB 默认
Append Only File
appendonly no //默认关闭,可以进行开启
\"appendonly.aof\" //AOF文件名
表示每次写入都执行fsync(刷新)函数 性能会非常非常慢 但是非常安全
always
每秒执行一次fsync函数 可能丢失1s的数据
everysec 默认
由操作系统保证数据同步到磁盘,速度最快 你的数据只需要交给操作系统就行
no
fork子线程,来写新的压缩的AOF,最后替换
重写机制
# 重写触发机制auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb 就算达到了第一个百分比的大小,也必须大于 64M
大于64mb重写一次,。重写后的 aof 文件可能是40mb。上面配置了auto-aof-rewritepercentag为100,即 aof 文件到了80mb的时候,进行重写
什么时候重写
性能高,默认最多丢失1s数据
磁盘满了,追加失败可以用redischeck-aof 工具来修复
格式都是追加的日志,所以可读性更高
优势
. 数据集一般比RDB大
持久化跟数据加载比RDB更慢
在7.0之前,重写的时候,因为重写的时候,新的指令会缓存在内存区,所以会导致大量的内存使用
并且重写期间,会跟磁盘进行2次IO,一个是写入老的AOF文件,一个是写入新的AOF文件
不足
AOF会越来越大
同步机制
AOF
持久化
# Replicationrole:slave //角色master_host:192.168.8.129 //主节点IPmaster_port:6379 //主节点端口master_link_status:up //连接状态 up是正常同步连接状态 down表示复制端口master_last_io_seconds_ago:1 //主库多少秒没有发送数据到从库 0-10master_sync_in_progress:0 //是否正在跟主服务同步slave_repl_offset:163 //从节点偏移量slave_priority:100 //选举时成为主节点的优先级 越大优先级越高 0不会成为主节点
配置
主写从读
master_replid空全量同步
存在,增量
不存在,全量
master_replid有值,判断offset是否在缓存区存在
(master_replid,偏移量offset)
在slave的serverCron方法调用replicationCron方法,里面会发起跟master的数据同步
master开始执行bgsave,生成一个RDB文件,并且把RDB文件传输给我们的slave,同时把master的replid以及offerset(master的数据进度,处理完命令后,都会写入自身的offerset)
slave接收到rdb文件后,清空slave自己内存中的数据,然后用rdb来加载数据,这样保证了slave拿到的数据是master生成rdb时候的最新数据。
client-output-buffer-limit replica 256mb 64mb 60256mb 硬性限制,大于256M断开连接64mb 60 软限制 超过64M 并且超过了60s还没进行同步内存数据就会断开连接
. 在master生成rdb文件期间,会接收新的指令,这些新的指令会保存在一个内存区间,这个内存区间就是replication_buffer。我们可以通过以下方式设置replication_buffer的大小
这个空间不能太小,如果太小,为了数据安全,会关闭跟从库的网络连接。再次连接得重新全量同步,但是问题还在,会导致无限的在同步
由于master生成RDB文件是用的bgsave生成,所以,在生成文件的时候,是可以接收新的指令的。那么这些指令,我们需要找一个地方保存,等到slave加载完RDB文件以后要同步给slave。
全量
只有在至少连接了一个副本后,才会分配积压工作。
会一直被覆盖,变成最新的缓存数据
master中有个另外的积压缓存(replication_backlog_buffer)。
增量
slave数据同步
解决负载、数据备份
主从数据同步
master挂了,slave不会自动升级为master
能够监控我的Redis各实例是否正常工作
监控
如果Redis的实例出现问题,能够通知给其他实例以及sentinel
通知
当我的master宕机,slave可以自动升级为master
如果slave与主服务器断开的连接时间超过主服务器配置的超时时间(down-after-milliseconds)的十倍,被认为不适合成为master。直接去除资格
与master的断开连接时间
配置 replica-priority,replica-priority越小,优先级越高,但是配置为0的时候,永远没有资格升为
配置的优先级
比较slave的赋值的数据偏移量,数据最新的优先升级为master
已复制的偏移量
每个实例启动都会有个Run ID
Run ID
选举sentinel,来做故障转移
单个哨兵发现故障做标记SDOWN
询问其他sentinel,如果超过Quorum(法定人数)的sentinel都认为master不可用,那么就会将master标为ODOWN(Objectively Downcondition 客观下线)
发现master故障
Quorum如果小于等于一半,那么必须超过半数的sentinel授权,你才能去做故障迁移,
Quorum如果大于一半,那么必须Quorum的sentinel授权,故障迁移才能启动
转移条件
自动故障转移
sentinel可以提供Redis的master实例地址,那么客户端只需要跟sentinel进行连接,master挂了后会提供新的master
配置提供
功能
自动故障转移可能会导致脑裂问题
min-replicas-to-write 1 至少有1个从节点同步到我主节点的数据,但是由于是异步同步,所以是最终一致性 不会确保有数据写入min-replicas-max-lag 10 判断上面1个的延迟时间必须小于等于10s
解决方案
脑裂问题
sentinel哨兵机制
就是我希望把数据分布到不同的节点。这样如果某些节点异常,其他数据能正常提供服务,跟我们微服务的思想很相似。
分片
是根据
Redis cluster中有16384个虚拟槽。
会根据key 通过CRC16 取模 16383 得到一个0到16383的值 计算公式:slot = CRC16(key) & 16383,得到的值,就代表这个key在哪个虚拟槽。
key跟虚拟槽是根据key计算的,不会变
eg:key1{18} 跟 key2{18} 就会在一个虚拟槽
hash slot(虚拟槽)
每次节点的扩容与缩容,只需要改变节点跟虚拟槽的关系即可,不需要全部变动。
节点移动、宕机故障转移、扩容缩容
cluster
集群
高可用
抢占到,设置key
标记
设置过程中是不可见的,redis单线程完美避开
标记的可见性
setnx原子性,是单线程执行
保证原子性
redis 锁原理
不会先去执行指令,而是将指令放到队列queue
multi incr myidincr testidexec
一次性执行多个指令 中间结果不能使用
可能执行一部分,并且不支持回滚!!! 是个笑话
命令是原子的 在执行事务中的指令时 服务器阻塞 不能执行其他指令
redis事务
解决事务中需要使用中间结果的问题
执行lua脚本文件redis-cli --eval 脚本文件 key列表,参数列表
evalsha
命令行执行: eval 脚本内容 key个数 key列表 参数列表
eval
需要原子性执行多个命令
需要中间值来组合后面的命令
需要中间值来编排后边的命令
使用场景
lua脚本
标记是否有线程
互斥条件
用来判断是不是同一线程
线程信息
记录重入的次数,再释放的时候,减少相应的次数
重入次数
核心
:通过Lua保证原子性
key: 大keyfiled:UUID+线程IDvalue:1
利用hash结构保存锁数据
加锁关键源码
看门狗机制--采用时间轮定时 定时时间为10s
返回
订阅频道,释放时获取及时通知
通过Semaphroe进行阻塞,阻塞时间=剩余等待时间<锁释放的时间?剩余等待时间:锁释放时间
加入队列,用时间轮处理
返回还要多久时间过期
加锁失败
加锁方法
解决加锁后宕机问题
设置加锁机器数通过数量才获得锁 减少宕机导致锁丢失问题
至少3主三从,三主做sharding 3从来保证主宕机后的高可用
联锁(红锁)
Redission实现
ReentrantLock
可重入锁
redis多指令单原子性
subscribe key1 key2psubscribe key1* key2*
订阅
publish key message
发布
订阅与发布
分布式锁
hash-max-ziplist-value 64 // ziplist中最大能存放的值长度hash-max-ziplist-entries 512 // ziplist中最多能存放的entry节点数量
1)哈希对象保存的键值对数量<512个;2)所有的键值对的健和值的字符串长度都<64byte(一个英文字母一个字节)。
会根据存入的数据类型以及大小,分配不同大小的空间
缺点 一整块完整空间,当元素发生变化时,会产生连锁更新,严重影响性能。所以只适用于数据量比较小的场景
缺点
节省空间
优点
优缺点
ZipList
扩容=已使用大小的2倍且2N次幂
初始大小4
支持不同的数据类型对应dictType抽象方法
dictType *type
也和不同类型相关,不同类型特定函数的可选参数
void *prvidata
2个hash表 用来数据存储 2个dictht
dictht ht[2]
rehash 标记-1代表不在rehash
long rehashidx
当前dict中的数量
unsigned long iterator
结构 dict.h
dict
typedef struct dictEntry {void *key; //keyunion {void *val;uint64_t u64;int64_t s64;double d;} v; //valuestruct dictEntry *next; //指向下一个节点} dictEntry;
dictEntry
dictEntry 数组
dictEntry **table
数组大小 默认为4 # define DICT_HT_INITIAL_SIZE 4
unsigend long size
size-1 用来取模得到数据的下标值
unsigned long sizemask
表中已有的节点数据
unsigned long used
dictht结构
dict.h
数据结构源码
兼顾了ZipList的节省内存,并一定程度上解决了连锁更新问题
整数表示node的数量-1:max size 4kb-2: max size 8kb-3: max size 16kb-4 -5...
list-max-ziplist-size 设置每个node的ziplist大小
quicklistNode节点里是个ziplist
quicklistNode组成的链
QuicklistNodecount ziplist中的元素个数zl 指针
count
链式结构
quickList
不是整数类型,用dictht hash表 (数组+链表)
元素不超过512个,也用hashtable存储
set-max-intset-entries 512
inset
hashtable
Set
默认
Ziplist
如果元素数量大于等于128或着任一member长度等于64字节用
zset-max-ziplist-entries 128zset-max-ziplist-value 64
skiplist +dict
Zset
数据结构
K-V结构内存型数据服务
分布式锁
应用缓存
应用场景
String
List
Hash
Set
Zset
bitMap
Streams流
HyperLogLog
Geo(地理位置)
数据类型
意大利西西里岛小伙antirez,为解决访客信息网站LLOOGG.COM(类似百度统计)用mysql数据库的性能问题,所创建的C语言内存数据库。redis全称是REmote Dictionary Service
http://www.redis.cn/
中文官网
内存操作,不需要和磁盘交互
本身就是k-v结构,类似hashMap,所以查询速度接近O(1)
同时redsi自己底层数据结构支持,如跳表、SDS
命令执行是单线程,同时通讯采用多路复用
IO多路复用,单个线程中通过记录跟踪每个(I/O流)的状态来管理多个I/O流
快
介绍
单线程,头插更快
头插法
单个key/value都是 默认最大512M
k-v结构
数据越大越慢
zipList
查找更快
skipList
Simple Dynamic String
空间预分配
惰性释放
SDS减少修改字符串带来的内存重新分配次数
c字符串中的字符必须符合某种编码(ASCII),并除了字符串末尾外,字符串中不能包含空字符串,否则会误读空字符串为结尾。c是以空字符串来标记是否结束的。限制了c字符串的存储类型,只能文本,不能音视频等
SDS字符串是否结束是根据len来,所以也就不会有这样的问题。
二进制安全
len
alloc
flags
buffer
结构
SDS
批量设置
mset key1 v1 key v2
批量获取
mget key1 key2
获取长度
strlen key
字符串追加元素
append key value
获取指定范围的字符
getrange key 0 5
整数值递增
incr intkey / incrby inkey 50
浮点值递增
set f 2.6 /incrbyfloat f 7.3
EX 设置指定的到期时间 秒PX 设置置顶的到期时间 毫秒NX 仅在key不存在的时候设置XX 只在key存在的时候才设置
set lock1 1 EX 10 NX
set nx k1 1
基本指令
缓存 、token(过期)
线程安全的技术场景 软限流、分布式ID
String类型的无序集合,最大2ˇ32-1
添加删除时间O(1)
sadd mawh a b c d e f
smembers key 获取所有元素
scard key 获取所有元素个数
srandmember key 随机获取一个元素
spop key 随机弹出一个元素
srem key a 弹出指定
sdiff key key2 获取前一个集合有后一个集合没的元素
sinter key key2 获取交集
sunion 获取并集
抽奖
点赞、签到等 sadd集合存储
关注等场景 交集并集
Set 无序集合 不可重复
zadd z1 10 a 20 b 30 c 40 d
根据分数从低到高
zrange
根据分数从高到低
zrevrange z1 0 -1 withscores
根据分数获取值
zrangebyscore
移除元素
zrem z1 a
获取zset个数
zcard z1
给某个元素加分
zincrbt z1 20 b
获取范围内个数
zcount z1 50 60
获取指定元素的索引
zrank z1 d
获取元素的分数
zscore z1 d
基本命令
排行榜 销售、热搜、游戏评分等
Zset 有序集合 不可重复
最大2ˇ32-1个元素,40亿个元素
lpush queue alpush queue b cRpush queue d e
左右添加元素
lpop queue
rpop queue
左右弹出一条
blpop queue 10brpop queue 10
左右弹出一条,并设置超时时间,知道有数据弹出或者超时
lindex queue 0lrange queue 0 -1
取值
用户消息时间线 有序
消息队列 BRPOP|BLPOP
List 有序集合 可重复
位图不是实际的数据类型,而是String类型中定义的一种面向位的操作,所以这个位图默认最大长度是512
可以容纳2ˇ32不同的位,可以在不同的位设置0或者1
把位5设置为1
setbit premission 5 1
得到位5的值
getbit premission 5
获得1点总数
bitcount premission
获取premission位为1的第一个位置
bitpos premission 1
获取bitkey与permission的&运算 并且赋值给hbit
bitop and hbit bitkey permission
学生到课率
实际的统计数据
用户权限 不同的位代表不同的权限
存储与ID相关的节省空间并报性能的 是或者否的值
BitMap
hset h1 f 6hset h1 e 4hmset h1 u 8 r 9hget h1 ahmget h1 a b chkeys h1hvals h1hgetall h1hgetall h1hincrby h1 a 10 给某个字段添加值hexists h1 y 查key中file字段是否存在hdel h1 ahlen h1
String能做的hash都能做,因为hash就比String多了一层key而已!并且单个key可以单独更改
1存储对象累的数据
2统计类的数据 可以对单个数据进行进行单独修改
场景
ditch hash表
底层结构
hash
基本数据类型对应结构
Redis发生Hash冲突为什么要头插?而HashMap为什么1.8之后要尾插
为什么要扩容?
QA
没有fork子线程在进行RDB和持久化时,ht[0]的used大于等于size时,触发
有fork子线程在进行RDB和持久化时,ht[0]的used大于size*5时,触发
时机
按桶来扩容,每次转移15个元素,不够找新桶,最多100个
满足条件,触发扩容时,判断是否在扩容,如果在扩容,或者扩容大小和我一样,放弃本次
new一个新的dictht,切大小为ht[0].used*2(但必须向上2的幂),并且ht[1]=新创建的dictht
if (dictIsRehashing(d)) _dictRehashStep(d);
每次curd都会进行一个hash桶的迁移
serverCron
定时任务进行迁移
逐步有序迁移
把dict的rehashidx=-1
步骤
dictExpand具体方法
扩容
数据结构和扩容
客户端发一个请求给服务端--》客户端等待服务端响应--〉服务端收到请求并处理-返回客户端
Rounf Trip Time
客户端到服务端需要时间的,包括网络传输,连接等
pipelining = 多个请求一个RTT
性能提升5- 10倍
大数据并发写入且客户端不需要获取读的场景
对性能有要求
管道内是原子性,但客户端命令会交叉执行
不需要以上一个指令的返回结果来判断后续逻辑
redisTemplate.executepipeline()
指令
管道技术pipelining
每次访问key,触发
expireIfNeeded方法
惰性过期
定时方法serverCron
1.扫描所有有过期时间的key2.根据hash桶的维度扫描,扫到20个为止3.单个桶不足20,扫描下一个桶,扫到的桶就把桶内扫完,扫描最多不超过400个桶4.删除扫描到的过期的key5.如果400个桶没数据,或者扫描的删除跟扫描的key总数超过10%,继续执行2,3,46.循环16次后会去检测时间,超过指定时间会跳出
定时过期
过期
noeviction 不淘汰 能读不能写
所有key
有过期时间的所有key
LRU 最近最少使用least recently used
LFU 最不常使用least frequently used
random 随机
ttl 即将过期
策略
淘汰池,默认大小16
1.每次指令操作,自旋会判断当前内存是否满足指令所需内存2.如果不满足,会从淘汰池中的尾部取最适合淘汰的数据 2.1 会取样(配置 maxmemory-samples)从Redis中获取随机获取到取样的数据 解决一次性读取所有的数据慢的问题 2.2 在取样的数据中,根据淘汰算法 找到最适合淘汰的数据 2.3 将最合适的那个数据跟淘汰池中的数据比较,是否比淘汰池中的更适合淘汰,如果更适合,放入淘汰池 2.4 按照适合的程度进行排序,最适合淘汰的放入尾部3.将需要淘汰的数据从Redis删除,并且从淘汰池移除
淘汰流程
通过lruclock-redisObject.lru得到这个对象多久没访问
如果redisObject.lru<lruclock(当前时间)
通过lruclock+(24bit的最大值-redisObject.lru)
如果redisObject.lru>lruclock
段记录的是对象操作访问时候的秒单位时间的后24bit!
lruclock=当前时间的秒单位的最后24bit
相差越大越容易淘汰
lru
LRU算法
新的数据进不来,老的数据进不去
失效性问题
最不常使用
低8位用来记录访问频率
counter 8bit最大255
counter并不是一个简单的线性计数器,而是用基于概率的对数计数器来实现 (值越大增加的概率越低)
增加
根据前18位计算距离但前时间,并根据衰减因子计算衰减值
减少
counter
高16位用来记录访问时间(单位为分钟
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr;} robj;
24 bits = 18bit +6bit
LFU算法
淘汰
过期策略 淘汰策略
Redis CAP中的AP
收藏
0 条评论
回复 删除
下一页