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