redis是什么
redis是有C语言编写的高性能非关系型键值对数据库;
官方的说法是读可达到100000次/s,写可达810000次/s,
采用IO多路复用模型处理处理网络请求,
单进程执行命令保证了原子性。
优点
- 读写性能优秀,开源nosql数据库中性能最好
- 支持丰富的数据类型:string、hash、list、set、zset
- 好用的增强功能:bitmap作为布隆过滤器,HyperLogLog估算集合的基数
- 提供数据持久化:RDB、AOF
- 自带cluster和主从复制
缺点
- 数据库容量受物理内容限制,无法作为海量数据的高性能读写
- 不具备自动容错和恢复的功能,主从机宕机都会导致请求失败
- 哨兵的监控和数据同步过程中可能发生(脑裂)问题,主机假死,从升主的过程中,有数据继续写入原主机,导致数据不一致
- 难以在线扩容
- 持久化策略仍然可能有数据丢失
- 内存溢出的部分淘汰策略也可能导致数据丢失
redis为什么快
- 完全基于内存
- 精巧的底层数据结构简单动态字符串、hash表、双向链表、数组、跳表、压缩链表,基本操作都是O(1),集合的全量查询和集合操作也只是O(n)的时间复杂度
- 采用单线程执行command,避免了不必要的上线文切换和竞争问题
- 使用多路IO复用模型,提高吞吐量
- RESP协议,简化回复数据解析过程
- 6.0以后采用多进程处理网络IO进一步提高吞吐量(请求获取后寄存到队列中,执行命令还是单线程)
有哪些数据类型
数据类型 | 底层数据结构 | 应用场景 |
---|---|---|
string | 简单动态字符串 | 简单的热点数据 |
hash | hash-map,压缩链表 | 对象 |
list | 双向链表,压缩链表 | 队列 |
set | 数组,压缩链表 | 集合 |
zset | 跳表,hash-map | 有序集合,排行榜,热门数据分页 |
持久化
提供两种持久化方式RDB和AOF
RDB:默认的持久化方式,快照保存,间隔m秒内有n个数据发生变化,触发一次bgsave
优点:
- 容灾性好,全量备份,只有一个dump.rdb文件,rdb文件紧凑
- 性能好,bgsave命令fork子进程进行持久化,主进程不需要进行任何磁盘IO
- 相对数据集大时,比AOF的恢复效率高
缺点:
- 数据安全性低,间隔一段时间的持久化策略,间隔时间中的数据可能会丢失,子进程备份数据期间主进程发生的修改不会反应给子进程,数据会丢失
- fork子进程需要增加内容开销,如果内存不足,可能会使用虚拟内存,导致redis阻塞
AOF:Append Only File,redis执行的命令记录到单独的日志文件中;
三种触发机制:
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢数据 | fork子进程进行备份,性能好 | 不备份 |
缺点 | 同步进行,主进程参与磁盘IO | 可能会丢失1s的数据 | -- |
优点:
- 更好的保护数据,一般采用everysec策略,只会丢失1s的数据
- 没有磁盘寻址的的开销,写入性能高
- AOF文件可读性强,适合做灾难性误删除的紧急修复(抢在bgrewriteaof之前)。
缺点:
- 相对RDB,AOF文件更大
过期删除策略
- 定时删除,一般不采用,对cpu不友好
- 惰性删除,get这个key时检查是否过期,如果过期则删除,对cpu友好,浪费内存
- 定期删除,隔一段时间随机检测一些key,如果过期则删除,对内存好友,检查频率不宜过高,否则会对CPU造成压力
一般采用 惰性删除+定期删除
内存满后的淘汰策略
基于lru算法的
- allkeys-lru
- volatitle-lru
基于lfu算法的
- allkeys-lfu
- volatitle-lfu
随机删除
- allkeys-random
- volatitle-random
即将过期的
- volatitle-ttl
不淘汰,直接报错的
- noeviction
redis线程模型
redis基于reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的由4个部分组成:多个server socket、IO多路复用程序,文件事件分派器、事件处理器。因为文件事件分派队列的消费是单线程、同步、阻塞的,redis才被称为单线程模型。
- IO多路复用程序同时监听多个socket,并根据套接字目前执行的任务为套接字关联不同的时间处理器。实现了高性能的网络通信模型
- 当被监听的套接字准备好执行应答(accept)、读取(read)、写入(write)、关闭(close)等操作时,与操作相应的文件事件就会产生,这是文件事件处理器,就会调用套接字之前关联好的事件处理器来处理这些时间。
事务
配合piple使用,既可以保证原子性,也可以减少开销;
==redis的事务只保证一致性和隔离性==。
watch 监控key
multi 开启事务
exec 执行事务
discard 放弃事务
//如果监控的key发生改变,事务执行失败
redis集群的方案
- 推特开源的twemproxy,是一个代理服务,使用方式简单与使用普通的redis无任何区别,设置好它下属的多个redis实例后,使用时在本需要连接redis的地方改为连接twemproxy,它会以代理的方式接收请求并使用一致性hash算法,将请求转发给具体的redis实例,再讲结果返回给twemproxy,是对就项目扩展的首选。
问题:twemproxy自身单端口实例的的压力,使用一致性hash后,redis节点数改变计算值改变,数据无法自动转移到新的节点。
- 豌豆荚开源的codis,目前用的最多的集群方案,基本上跟twemproxy的效果一致,弥补了twemproxy对redis节点数改变的情况下,旧节点数据无法自动转移到新节点的问题。
- redis3.0以后自带的cluster。利用hash slot的概念,以及自身支持主从。请求发送给任意节点,接收到请求的节点会查询节点,客户端再redirect到正确的节点上执行。
- 业务代码实现,多个无关的redis实例,对数据进行hash分配到对应的redis实例。
优点:简单灵活,易于线性扩展
缺点: 不支持动态增删节点,服务端redis的实例群拓扑结构变化时,每个客户端都需要更新调整。连接不能共享,当应用规模增大时,资源浪费制约优化。
分布式寻址算法:
- hash算法,一般采用取模的方式进行分片
- 一致性hash
- hash slot 哈希槽
主从复制
再从节点配置 slaveof masterip masterport
过程:
- 从节点发主节点发送sync请求
- 主节点快照生成rdb文件,在次期间缓存新的命令
- 快照完成后,主节点将rdb文件和所有缓存的写命令发送给从节点
- 从节点保存rdb文件,载入数据,之后执行缓存中的命令
- 之后,主节点没当接收到写命令都会讲命令发送给从节点
缺点:复制和同步都需要主节点处理,会造成master节点压力太大,使用主从从架构解决
redis集群中的主从是怎样的?
集群中有N个节点,至少一个从节点
分布式锁
官方推荐了redlock
- 高可用,超过半数的节点可用,就能保证加锁成功
- 可重入,避免死锁
- 排他性
- 避免误删,自由持锁者可以解锁
缓存异常
雪崩
统一时间缓存大面积失效
解决方案:缓存的过期时间附加一个随机值,避免同时失效
穿透
请求缓存中不存在的数据
解决方案:
- 不存在的数据也进行缓存(出现穿透很可能是遭到攻击,该方案会导致出现大量无意义缓存)
- bimap,布隆过滤,快速高效的校验数据是否存在。占空间很少
- 接口层校验,比如id<0的请求直接拦截
击穿
某个并发量极高热点数据过期
解决方案:
- 这种数据不设置过期时间,(注意淘汰策略不要设置成allkeys-random)
- 加互斥锁,只允许一个请求进入数据库查询
预热
系统上后,直接将数据加载到缓存内,避免用户请求时再去刷新缓存。
- 写一个脚本,上线后跑一下
- 数据量不大,自己把页面点一下
缓存降级
访问量剧增,非核心服务出现问题影响大核心服务,系统可以自动降级,也可以配置开关实现人工降级。
比如雪崩导致数据库压力骤增,打爆了数据库。此时可以让非核心服务的请求直接返回404,而不去请求数据库。