Redis复习
2021-04-05 23:38:35 0 举报
AI智能生成
参考数据: Redis深度历险核心原理与应用实践 Redis设计与实现
作者其他创作
大纲/内容
Redis:6379
常用指令
设置过期时间:expire key time(秒),如果设置了过期时间后被修改,过期时间将失效
带过期时间的set:setex key time(秒) value,等价set+expire
如果key不存在设值成功返回1,存在则失败返回0:setnx key value
是否存在某一个key :exists key
删除key:del key
expire
setex
setnx
exists
del
常见应用
通过list列表实现,阻塞读使用blpop命令
byte数组,就是普通的字符串,最大512M
提供不精确的去重计数方案
可能会误判,但是绝不会漏判
对坐标进行编码,然后存储到zset中,通过score找到附近的值
分布式锁
set key true ex 5 nx
超时问题
RedisLockRegistry
先本地加锁,再远程加锁(redis事务+watch)
双重加锁
获取不到锁就等待100毫秒继续重试
60s过期时间
Redlock
加锁时向过半的节点发送nx和ex的原子命令
删除时向所有节点发送del命令
setnx和expire的原子性组合
设置超时以免和redis服务器断开连接导致无法释放锁
队列
位图
hyperLogLog
添加ID去重
获取统计数
pfadd
pfcount
布隆过滤器
限流
zset的score存时间戳,每个请求到来根据时间戳范围统计次数
简单限流
漏斗限流
初始化参数
漏斗容量
漏嘴流水速率
剩余容量
上一次漏水时间
算法流程
1、根据当前时间和上一次漏水时间算出从上次漏水时间到现在漏斗空闲出多少空间
2、如果时间差间隔太短,生成一个单位的空间则跳到第4步,否则跳到第3步
3、更新剩余容量,不能大于漏斗容量
4、剩余容量>0,放行请求,剩余容量减1
令牌桶限流
令牌桶容量
令牌桶剩余容量
下一个令牌发放的时间next
发放令牌的间隔
1、当前时间小于下一令牌发放的时间,说明有其他线程预定了未来一段时间的令牌,当前线程需要等到此时next时间点才能执行,并且需要更新next,在next基础上再加一个单位间隔表示下一个线程可以获取到令牌的时间点
2、当前时间大于下一令牌发放时间,说明有一段时间没有线程取令牌了,需要更新令牌剩余数,在原令牌剩余数基础上加上(时间间隔/发放间隔),再减去一枚令牌,并更新下一令牌发放时间为当前时间,如果减去一枚令牌后剩余数量为0,需再将下一令牌发送时间增加一个隔间单位
GeoHash
scan
hashmap
避免扩容rehash重复遍历:遍历到槽位是4的数组时发送扩容(8-16),槽位3的数据有一部分会跑到11,普通变量会重复遍历槽位11的数据,高位进位扩容时不会发送槽位跳跃避免缩容遗漏遍历:遍历到槽位是4的数组时发送缩容(16-8),槽位11的数据会跑到槽位3,普通遍历会遗漏,高位进位缩容不会遗漏,但是会重复
查找大key,属于redis-cli指令
keys的缺点
rediskey的存储结构
scan的特点
游标分步执行,不会阻塞线程
提供limit参数,控制每次返回结果的最大条数,实际情况可能多可能少,limit控制的是扫描hashmap中数组的槽位个数
返回结果可能有重复,需要客户端去重
遍历过程中有修改数据,改动后的数据不一定能找到
返回空不一定代表结束,要看返回的游标是否为0
高位进位加法
遍历其他基础数据结构
遍历zset
遍历hashmap
遍历set
zscan
hscan
sscan
bigkeys
集群
redis是AP,保证最终一致性
将操作指令写入环形的内存buff中,从节点读buff同步,buff循环覆盖写入
将主节点数据快照写入磁盘,传给从节点同步,再同步增量buff
先快照同步,后续再增量同步
直接遍历内存数据通过套接字发送给从节点
Sentinel系统监控所有节点,当主节点下线时选择新的从节点变为主节点
CAP原理
C:一致性
A:可用性
P:分区容忍性
增量同步
快照同步
增加从节点
无盘复制
Sentinel哨兵
Sentinel以每1秒一次的频率向被监视的服务器发送PING消息确认服务是否存在
多个Sentinel之前的选举:raft协议
Sentinel无法保证消息不丢失,只能尽可能保证消息不丢失
设置正常同步的从节点个数
设置同步延迟,超过延迟就说明从节点不正常
Cluster
去中心化
16384个槽位,每个节点一份信息
槽定位
槽跳转
迁移
流程
1、获取源节点内容
2、存到目标节点
3、从源节点内容删除
迁移过程中的访问
1、访问的数据还在旧节点则直接访问
2、访问的数据不在旧节点,返回客户重定向指定
3、客户端收到重定向指令后先发送一个asking指定告诉新节点要强制访问,否则新节点会校验改数据不属于他的槽位
4、最终发送原指令的访问
容错
一个节点的失联判断需要通过大多数节点进行确认后才能确定是否失联
通过Gossip协议广播自己的状态,大多数节点确认后再广播其他节点接受结果
槽位迁移感知
告诉客户端重定向到新的槽位,客户端更新本地槽位信息
临时让下一个请求转到新的槽位,忽略槽位校验
moved指令
asking指令
子主题
Raft和Gossip
减少心跳消息头的大小,消息头中,最占空间的是 myslots[CLUSTER_SLOTS/8],16384/8=2K
对key使用crc16进行hash,然后对16384取模,可强制指定槽位
校验key是否属于指定槽位,不属于则触发跳转,返回客户端-moved指令有客户端跳转
常见问题
恶意攻击者使用不存在的用户id频繁请求接口,导致查询缓存不命中,大量请求穿透缓存访问到 DB
某个热key失效时,大量针对这个数据的请求会穿透到DB
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机
缓存穿透
查DB不存在缓存一个空值,不建议
BloomFilter 过滤器拦截不存在的请求
缓存击穿
访问DB时加锁
不设置过期时间
缓存雪崩
缓存是添加随机时间
常用场景
记录帖子点赞数,评论数,点击数
记录用户的帖子ID列表(排序)
记录帖子的相关文章ID
帖子ID自增
hash:key为帖子id,hash中的key为对应操作名,value为数值
zset:key为用户名,zset的value为帖子id,score为帖子对应分数,根据分数排序,例如置顶帖子分数最高
list
计数器
基础数据结构
String
SDS对象,记录字符串长度,剩余空间
字符串小于1M时,扩容是加倍,字符串大于1M时,每次扩容只增加1M
存储结构
embstr格式
raw格式
对象头(19字节)和SDS连在一起
对象头和SDS分开,内存分配超过64字节(实际对象44字节,64-19-1(字符串以null结尾))使用raw
扩容
操作指令
一次set多组key-value
一次get多个key
自增1操作,incr key
自增指定数:incrby key 5
获取字符串长度
set key value
get key
mset
mget
incr
incrby
strlen
双向指针,支持双向遍历
rpush
lpop
rpop
llen
右边新增:rpush key value1 value2
左右取值,和rpush组合就是队列:lpop key
右边取值,和rpus组合就是栈:rpop key
获取链表长度
慢操作
lindex
ltrim
lrange
取指定索引的值,O(n)
获取指定开始索引到结束索引的值
获取指定范围的值:lrange key index1 index2
优化点
元素较少时会使用一块连续的内存,类似数组
元素较多时,会将多个压缩列表通过指针连起来,默认单个压缩列表8KB
快速列表
压缩列表
hash
渐进式rehash
塞值:rediskey hashkey value
获取值:hget rediskey hashkey
获取元素个数
获取所有元素:hgetall rediskey
hset
hget
hlen
hgetall
数组+链表:类似java里面的hashmap,value只能是字符串
保留新旧两个hash结构,查询时同时查两个hash,新hash没有就将旧hash中的值移到新hash中,后台定时任务也会进行迁移,扩容条件:hash中元素个数等于第一维数组长度,缩容条件:元素个数小于第一维数组10%
元素较少时使用压缩列表
set
数组+链表:类似java里面的hashset,是一个特殊的hashmap,所有的value都为空
set集合元素是整数并且较少时使用intset
sadd
spop
smember
scard
添加元素:sadd key value
弹出一个元素:spop key
获取所有元素(无序):smember key
zset
跳表
存储value和score的映射
根据score排序所有元素
zadd
zscore
zrange
zrevrange
zrangebyscore
zrem
添加元素:zadd score value
获取指定value的score:zscore value
按score排序输出:zrange key 0 -1(从第1个元素到倒数第一个元素)
按score排序逆序输出:zrevrange key 0 -1
输出指定score区间的元素:zrangebyscore key score1 score2
移除元素:zrem key value
原理篇
客户端技术,合并多条操作请求一起发给服务端,客户端改变读写顺序带来的性能提升
redis事务不具备原子性,只具备隔离性(串行化)
事务开启前watch一个或多个变量,如果在开始执行缓存的事务队列时发现watch的变量被修改了则执行事务失败
redis的线程IO模型:IO多路复用
select
poll
epoll/kqueue
通信协议:RESP序列化协议
单行字符以+开头
多行字符以$开头,后面跟字符串长度
整数以:开头,后面跟整数的字符串形式
错误消息以-开头
数组以*开头,后面跟数组长度
持久化
二进制序列化形式,紧凑
内存数据修改的指令记录文本,长期运行会导致AOF日志巨大,重启回放时间长,需定期重写瘦身
不要在主节点持久化
先RDB持久化,记录持久化期间新增的操作指令到增量AOF文件
RDB快照:一次全量备份
fork子进程,共享代码段和数据段
Copy On Write
子进程只做数据持久化,不会修改数据
父进程进行数据修改时,从数据段复制对应内存页进行修改
AOF日志:连续增量备份
先执行指令在存AOF日志
AOF瘦身
fsync
开启一个子进程对内存进行遍历,转换成一系列的redis操作指令然后序列化成一个新的AOF文件,最后追加增量AOF到新的AOF中
写AOF时会先写到内核缓存,内核再异步刷盘,redis每隔1s执行一次fsync操作进行刷盘(交给异步线程处理)
耗时操作
fork子进程遍历内存加重系统负载
fsync增加系统IO负担
混合持久化
管道
事务
watch
扩展篇
Info指令
获取所有信息
获取内存相关信息
获取主从复制相关信息
查看服务端每秒收到的指令数,redis可以每秒执行10万次指令
info
info memory
info replication
info stats|grep ops
monitor
过期策略
将设置了过期时间的key放入一个独立的字典中,定时遍历删除过期的key避免同时过期,建议在过期时间上加一个随机时间
访问key时进行过期判断,如果过期了就立即删除
带过期时间key的集合
避免过度循环,默认循环扫描上线是25ms
每秒扫描10次过期集合
贪心策略
1、从字典中选出20个key
2、删除这20个key中已经过期的key
3、如果过期key的比例超过1/4则重复步骤1
惰性策略
淘汰策略
停止写请求
淘汰设置了过期时间,最少使用的key
淘汰设置了过期时间,剩余寿命小的key
淘汰设置了过期时间,随机选取的key
全部key使用LRU算法淘汰
全部key随机淘汰
默认淘汰策略
LRU算法
淘汰算法
双向链表+hashmap
近似LRU算法
随机采样
从所有key中随机选择5个淘汰最旧的那个
从过期列表中随机选择5个淘汰最旧的那个
淘汰池
维护一个候选池(大小为16),将随机选择的key的访问时间和淘汰池中的比较,淘汰最旧的一个
需要消耗大量内存,不适合直接拿来使用
24bit存储时间戳
懒惰删除
最高64层,容纳2的64次方个元素
随机分层
遍历
更新
排序规则
如何保存排名
每一层的晋升率为25%
由于晋升率低,所以层数不会太高,跳表会记录当前最高层数,不然从最高层遍历下来比较浪费
先删除,再插入
避免所有score都一样导致查找效率退化,实际上是根据score+value来排序的
节点指向下一个元素的指针上面记录了跳过元素的个数
字典树(前缀树)
redis-cli monitor:命令用于实时打印出 Redis 服务器接收到的命令
超过最大内存时需要淘汰掉一部分key
删除大key时不直接清空内存,扔到删除队列中交给异步线程处理
记录了槽位和key的映射关系,早起版本使用的是跳表记录某一个槽位对应哪些key
0 条评论
回复 删除
下一页