Redis
2020-05-07 22:45:59 432 举报
AI智能生成
redis总结
作者其他创作
大纲/内容
IO
redis使用多路复用的epoll
redis6.0读写使用多线程,执行线程依旧是单线程
https://blog.csdn.net/SDDDLLL/article/details/105597205
通信协议
使用RESP协议
纯文本协议,虽然换行符较多,但是其足够简单,解析也极其容易
事务
multi
开启一个事务
exec
执行从multi到当前位置所有命令
discard
取消执行multi到当前位置所有命令
watch
必须在multi之前执行,如果watch到exec之间数据有变动,exec会执行不成功
弱事务
只能保证事务的隔离性,串行化,中间有命令执行错误依然会向下执行,不回滚
管道pipeline
执行多条命令,只需一次网络连接
实际是客户端实现的功能
由open->write->read->close open->write->read->close
改为open->write->write->read->read->close
改为open->write->write->read->read->close
集群
主从同步
全量同步
刚建立主从关系后,会触发全量同步
主节点bgsave,将内存中数据生成rdb文件,传输给从节点
在bgsave的过程中,新生成的数据会放到环形buffer里,
rdb传输完成后,传输buffer中的数据
rdb传输完成后,传输buffer中的数据
如果在bgsave过程中,buffer超出限制并覆盖,
则会再次触发全量同步,所以buffer大小需要合理设置
则会再次触发全量同步,所以buffer大小需要合理设置
增量同步
传输buffer中的数据
落后超过限制后,触发全量同步
无磁盘复制
2.8之后支持,纯内存复制
哨兵sentinel
主节点挂了后,从节点升级为主节点
哨兵需要保证高可用
哨兵之间采用raft协议保证分布式一致性
在平常,哨兵之间是平等的,只有在检测到redis节点超过一定时间没有回复,才会使用raft协议进行选主
一个哨兵认为server节点下线标记该server为主观下线
超过配置的哨兵数认为server节点下线,则该server节点为客观下线,需要进行主从切换
主从切换对配置文件也会进行修改
raft动图
http://thesecretlivesofdata.com/raft/
分布式
client模式/proxy模式
hash
redis服务不知道其他节点的存在,完全由客户端对key进行hash取模后决定放到哪个节点
缺点
一个节点挂掉后,或者增减节点,都会导致原先的key不能hash到对应节点,从而引起缓存雪崩
一致性hash
hash环结构
为了改进hash带来的问题,提出了一致性hash环的概念
hash取模后落在hash环的某个位置,取顺时针方向第一个节点读写
当增减节点的时候,去掉节点的所有key都落到了下一个节点上
虽然可以继续使用,但下一个节点承受了2倍的流量,容易过载导致节点挂掉引起雪崩
虚拟节点
由一个实际节点,虚拟出多个节点分散在hash环上
进一步优化了增减节点时导致的下一个节点过载问题
redis cluster
分为16384个hash槽
采用gossip协议实现无中心化通信
https://www.jianshu.com/p/54eab117e6ae
最少3个master节点(投票需要超过半数,防止脑裂)
集群不可用时机
挂掉的master没有对应slave,即有任一slot不可用,集群不可用
超过半数master都挂掉了,不管有没有slave,都将不可用
常见问题
缓存一致性问题
更新数据的时候,先更新db,再删除缓存,可以很大程度避免缓存不一致
先更新db,后更新缓存,可能造成旧的更新覆盖新的更新导致不一致
缓存穿透
缓存中不存在,查询直接打到了db上,即为缓存穿透,一般情况下,问题不大,读完db写会缓存即可
黑客攻击:故意请求db中也不存在的数据
(我们这种情况一般不会写到缓存)
(我们这种情况一般不会写到缓存)
解决方案
即使db中不存在,也将空值写回缓存,但是时间需要短一些
使用布隆过滤器(不存在肯定不存在,存在可能不存在)
缓存击穿
某个key过期的时候,瞬间有大量请求过来,全部打到了db上,为缓存击穿
解决方案
使用分布式锁,同一时刻只能有一个请求去db拿该数据,其他请求返回错误,或者等待写回缓存
缓存雪崩
同一时间大量key失效,导致大量请求穿透到db,造成雪崩
常见情况
大量key在同一时间失效
解决方案
在设置过期时间的时候加入随机数
redis某个或某几个服务down机
解决方案
需要提前做好数据持久化,重新启动可以快速恢复
提前做好高可用方案,主从,哨兵或者redis cluster等
常见应用
分布式锁
单节点
获取锁
set key value nx ex 5
使用nx确保锁没人用
使用ex是为了防止获取到锁的节点down,导致死锁
value使用随机串,确保唯一性,防止释放了别人的锁
释放锁
先判断当前redis中的value是否与本线程的随机value相等,相等则删除
需要使用lua脚本保证原子性
问题
锁获取成功,还没同步到从节点的时候,主节点挂掉了,哨兵将从升级为主节点后,
其他线程又可以争夺锁,此时出现了多人拥有锁
其他线程又可以争夺锁,此时出现了多人拥有锁
当获取锁的线程执行时间超过了过期时间,此时又出现了多人拥有锁的情况
redlock
多个redis的时候,需要在超过半数的节点上获取锁,没有超过半数,需要主动释放已获取的
缺点
复杂度上升
应用不广泛,可能有遗留bug
限流
滑动窗口
可以使用zset实现,score设置为当前时间,每次请求的时候,删除掉过期的窗口,判断数量是否超出了限额
漏斗
redis-cell模块提供了方便的漏斗限流
消息队列
简单队列
pub/sub
stream
延时队列
可使用zset实现
score存执行时间戳,每次取任务获取0-当前时间戳的一个任务
优化
延迟删除
unlink key
异步回收,防止大key回收内存时卡顿
巨大代价为不再使用共用结构
避免大key造成卡顿
扩容时需要申请巨大空间,删除时内存一次性回收都会造成卡顿
可以使用 redis-cli --bigkeys扫描大key
redis分库
当业务庞大的时候,可以按照业务使用不同的redis集群
其他
scan
防止使用keys导致redis卡顿
每次扫出一部分,需要多次扫描
可能出现重复,需要业务上自己去重
底层为对redis整个字典结构的数组挨个遍历
内存回收机制
并不是删除完一个key之后,内存会立即给操作系统
内存以块为单位,只有一整块内存key都删除了,才会回收这块内存
虽然没有立即给操作系统,但是删除后的空间可以被再次利用
info
info stats
info memory
info replication
info clients
...
安全性
rename-command
将危险指令重命名,防止误操作,比如flushall、keys
绑定指定ip访问
设置访问密码
ssl代理
spiped
数据结构
string
比较小时使用embstr,超出后使用raw
embstr:redis通用对象头和数据存储采用顺序存储,只需要分配一次内存即可
raw为redis对象头有指针指向数据内存块,需要分配两次内存
为何不都使用embstr?
和内存分配方式jemelloc实现有关
底层为字节数组,对编码安全
扩容
当size小于1m时,采用翻倍策略,大于1m时,扩容只会多分配1m
应用
存开关、序列化之后的对象等
存数字,并支持原子增加操作
存二进制数,支持一系列二进制操作(额外有计算1的个数)
list
3.2之前比较小时ziplist,增多时升级为linkedlist
ziplist
由于linkedlist为双向链表,每个节点之间都有两个4字节指针(32位),比较费空间
ziplist采用数组结构,省去了指针空间,并对内部数据使用一定规范进行压缩
由于ziplist没有多余空间,每次增加元素都需要扩容,remelloc(如果旁边有连续空间则不需要copy)
比较适合元素较少时存储
3.2之后,统一使用quicklist
quicklist
结合ziplist和linkedlist
一段使用ziplist,每一段之间用指针连接
应用
简易消息队列
hash
数量较少使用ziplist,达到一定大小后升级为类似Java HashMap结构
数量较少时,ziplist反而会快
扩容
当元素数量等于数组长度时,会触发扩容
遇到bgsave延迟扩容,防止过多页面分离(COW),当元素数量为数组长度5倍时,强制扩容
当元素数量不足数组长度10%,会触发缩容
渐进式rehash
扩容比较耗费资源,为了较少操作卡顿,采用部分迁移,查的时候先查旧后查新
再次触发部分扩容时机
用户每次查询该hash会触发部分迁移
redis会起定时任务去触发扩容
应用
分布式session
缓存整个页面的数据
set
底层使用hash,value为空
当元素都为int时,并且数量较少,结构优化为intset,采用紧凑存储
应用
粉丝列表
zset
底层由skiplist和hash实现
skiplist
skiplist vs 红黑树
skiplist实现更简单,方便调试
skiplist修改结构的时候,添加指针即可,不需要大范围改动,而红黑树可能会触发reblance
ZRANGE和ZREVRANGE使用频率较高,skiplist只需要找到第一个元素,即可根据指针方便查到RANGE
作者发话
结构图
分层结构,每上一层,数据越少,第二层25%概率,往上12.25%,以此类推,一共64层
查询元素从上往下比对,找到每一层score小于查询值且最大的节点,进入下层继续同理查找
修改score时,先剔除节点,再插入
ZREVRANK原理
每个节点存一个span,记录该层据后一个节点相对底层跨越了几个节点,算排名只需要将查询路径的span加起来即可
hash存value和score对应关系
应用
排行榜
滑动窗口
延时队列
geo
可对地理位置进行处理,添加,获取坐标,求hash,算距离,获取坐标附近的元素等
底层使用zset实现
pub/sub
发布订阅,消息队列
支持多个消费者同时消费一组数据
比较鸡肋,消费者必须先在线,如果消费者离线,会丢失消息
stream
5.0新增的持久化发布订阅
狠狠的借鉴了kafka
hyperloglog
海量数据去重计数
精准性换取空间
应用
统计UV
布隆过滤器
海量数据判断是否不存在
不存在一定不存在,存在有可能不存在
精准性换取空间
通过位数组和hash实现,对数据hash后取模存入位数组
可以通过扩大位数组和增加hash函数个数提高准确性
需要在创建时预估数据量
数据量超出很多后,准确度急速下降,这时需要重建过滤器,对数据进行重放
配置数据?
redis4.0之后提供插件功能后可以集成,也可以使用github开源
应用
过滤已经爬取过的网址
防止黑客攻击式的缓存穿透
持久化
rdb
存内存快照,生成慢,还原快
每次持久化需要fork子进程异步进行,相对消耗较大
使用cow方式节省内存,
即主进程在修改的时候,
直接copy一份内存页修改
即主进程在修改的时候,
直接copy一份内存页修改
aof
存指令,生成快,还原慢
重写
文件大小达到一定阈值,就对当前内存有效key重新生成一份指令
刷盘策略
不主动刷盘,由内核决定何时fsync
丢失数据较多
每次fsync
性能损耗较大
每秒fsync
推荐使用,最多丢失1s数据
4.0新增混合模式
aof文件前面是rdb快照,后面是追加的指令,结合两者优点
优化
为提高性能,可以不在主节点开启持久化,而在从节点开启,但出现网络分区,容易导致数据丢失,需要做好监控
内存淘汰策略
volatile-random
allkeys-random
volatile-lru
allkeys-lru
当用redis存热点数据时,建议用该方案
Java中LinkedHashMap可以方便实现
或者用LinkedList和HashMap组合实现
volatile-ttl
拒绝写入
4.0新增
volatile-lfu
allkeys-lfu
过期策略
定时删除
每隔一段时间抓取一部分key,判断过期时间,
过期则删除,过期超过一定比例则重复执行
过期则删除,过期超过一定比例则重复执行
惰性删除
每次get的时候判断是否过期,过期则删除
IO
IO演进
阻塞IO
传统IO模型,在等待连接和read会阻塞线程
只能通过开启多个子线程的方式实现同时响应多个请求
缺点
每个请求都需要开启线程,并且阻塞不能干其他事情,比较耗费资源
非阻塞IO
不管有没有,在等待连接和read都会立即返回
相比阻塞IO,进化为非阻塞,一个线程可以处理多个客户端请求,
在没有客户端请求时,可以处理其他任务
在没有客户端请求时,可以处理其他任务
缺点
当有大量客户端连接时,每次判断fd状态都需要从用户态切换到内核态,
即10k个连接需要循环10k次系统调用,费时费力
即10k个连接需要循环10k次系统调用,费时费力
多路复用
select
在非阻塞IO基础上,改进10k次系统调用为1次系统调用
缺点
每次系统调用都需要传10k个fd
实际上是由用户循环10k次改为了内核循环10k次,同样是O(n)复杂度
32位机器上,源码中写死了最多接受的fd为1024个,容易成为瓶颈
poll
在select基础上,改进了1024个的限制
缺点
除1024缺陷外,其余都在
epoll
在poll基础上,改进系统调用只需要传1个fd即可监听多个fd状态,
实现是在内核开辟一个缓存区,将监听的fd注册一次即可
实现是在内核开辟一个缓存区,将监听的fd注册一次即可
10k个fd不再循环10k次,而实现了事件机制,当fd可
建立连接或者可读可写,会触发回调通知用户态线程
建立连接或者可读可写,会触发回调通知用户态线程
底层为每个硬件在有操作的时候,都会给CPU一个中断,
即网卡给CPU中断的时候,会知道是哪个fd的什么事件
即网卡给CPU中断的时候,会知道是哪个fd的什么事件
缺点
每次读和写的时候,都是由用户线程自己从内核buffer读
异步IO
在epoll基础上,改进读写的时候由内核完成,用户线程只需要提供读写完成后的回调方法即可
目前linux系统还没有成熟的商用异步IO
C10K问题
http://www.kegel.com/c10k.html
0 条评论
下一页