分析Redis性能主要考虑两个问题:
- 1、Redis为什么这么快( 优 化 点 \color{red}{优化点} 优化点)
- 2、Redis怎么用能更快( 注 意 点 \color{red}{注意点} 注意点)
- 3、Redis如何预防问题和修复问题( 监 控 点 \color{red}{监控点} 监控点)
1、分析Redis为什么这么快,我们从以下几方面去分析:
2、分析Redis怎么用能更快,我们从以下几方面去分析:
3、关于Redis使用时如何预防、排查和修复问题,从以下几方面去分析:
1.1、网络层和操作系统层
-
网络层
- Redis选取了一种比较优秀的IO模型:epoll,其相对于之前的select模型、poll模型,多出来三个优点,具体见博客:IO多路复用的三种机制Select,Poll,Epoll:
- 优化点1.1.1:使用红黑树结构,一来句柄的最大限制没有了,二来方便了句柄新增、查找和删除。
- 优化点1.1.2:操作方式由遍历转换成回调,极大的提升了IO效率。
- 优化点1.1.3:回调方式新增边缘触发,可以理解为优先级高的触发方式,
- 优化点1.1.4:fd拷贝问题由每次询问就全量复制,修改为增量拷贝。
- Redis选取了一种比较优秀的IO模型:epoll,其相对于之前的select模型、poll模型,多出来三个优点,具体见博客:IO多路复用的三种机制Select,Poll,Epoll:
-
操作系统层
- 优化点1.1.5:Redis使用单线程,避免了多线程切换导致的资源和时间消耗,但是这一点不完全属于优化点,针对整个消息流而言,瓶颈并不在CPU,就好像自来水流量小(网络IO),增加接水的人(线程)并不能增大水量,另外要注意:
- Redis 的单线程指的是 Redis 的网络 IO 以及键值对指令读写是由一个线程来执行的。
- 整个Redis运行过程中不止一个线程,仅仅是用于IO的是单线程,后续6.0页增加了多线程。
- 即使是单线程的Redis,也可以通过多起Redis服务,搭建主从、读写分离等方式充分利用多核CPU。
- 多线程的问题:
- 多线程的创建和销毁,会带来一定的系统开销
- 多线程的切换,会带来系统开销(CPU保存上下文)
- 多线程的竞争,会带来加解锁的性能问题和死锁问题。
- 优化点1.1.5:Redis使用单线程,避免了多线程切换导致的资源和时间消耗,但是这一点不完全属于优化点,针对整个消息流而言,瓶颈并不在CPU,就好像自来水流量小(网络IO),增加接水的人(线程)并不能增大水量,另外要注意:
1.2、内存及数据结构
-
内存:
- 设置内存
- 在配置文件redis.conf 中配置一行 maxmemory xxx 即可
- 或者通过 config set 命令在运行时动态配置 Redis 的内存大小
- Redis内存回收策略
- 定时删除
- 对每个key新建一个监视器,过期就删除(相当耗费CPU,不采用)
- 定期删除+随机
- 每隔一段时间,随机抽取若干个key,若过期,则删除
- 惰性删除
- 当访问的时候判断是否过期,过期就删除,重新去数据库取值
- 定时删除
- Redis的Key过期策略
- 1.volatile-lru: 在所有带有过期时间的 key 中使用 LRU 算法淘汰数据;
- 2.alkeys-lru: 在所有的 key 中使用最近最少被使用 LRU 算法淘汰数据,保证新加入的数据正常;
- 3.volatile-random: 在所有带有过期时间的 key 中随机淘汰数据;
- 4.allkeys-random: 在所有的 key 中随机淘汰数据;
- 5.volatile-ttl: 在所有带有过期时间的 key 中,淘汰最早会过期的数据;
- 6.noeviction: 不回收,当达到最大内存的时候,在增加新数据的时候会返回 error,不会清除旧数据,这是 Redis 的默认策略;
- Key过期策略如何被触发:新增数据时,Redis 会检查内存的使用情况,如果已经超过的最大限制,就是根据配置的内存淘汰策略去淘汰相应的 key,从而保证新数据正常添加
- 参考博客:Redis的内存回收策略和Key过期策略
- 设置内存
-
数据结构
- 数据结构上的优化主要是考虑数据存取的时间复杂度,是一种用空间换时间的做法
- 优化点1.2.1:以跳表为代表的数据结构:数组方便查找,不利于新增,链表利于新增,不利于查找,于是Redis底层实现了跳表,一种可以折半查找的双向链表,增删改查都快。
- 注意到:在使用单向链表实现跳表的实践中发现,当数据从小到大排序时,按照从大到小插入,效率极慢,但是如果是双向链表,就可以很好解决这个问题。
- 跳表的实现:Redis底层数据结构:跳表的JAVA实现
- 优化点1.2.2:以SDS为代表的简单动态字符串结构:
- O(1)时间内获取字符串长度。
- 字符串修改时,首先检查字符数组的长度,不够进行动态扩充,防止字符串溢出(缓冲区溢出)
- 减少字符串修改带来的内存分配次数:空间预分配和惰性释放
- 二进制安全:允许识别空字符
- 参考博客:Redis中的简单动态字符串
1.3、Redis版本更新做了那些优化
- 多线程
- 实际上redis一直都是多线程的,除了处理请求流程是单线程,其他的比如AOF等都是多线程异步在做,
- Redis4.0,引入lazyfree机制,异步释放大内存。
- Redis6.0,引入多线程完成请求数据的协议解析。
1.4、阿里及其他云公司又做了那些优化
2.1、Redis用于缓存
- 多级缓存
- 通过使用【数据库->Redis->本地缓存】构建起来的三级缓存方式,极大的减小了数据的IO压力,提升了并发数,具体实现可见:缓存系列:构建多级缓存(本地、Redis、数据库)。
2.2、防止缓存穿透、缓存击穿、缓存雪崩
- 缓存雪崩
- 说明:当大量的缓存数据短期内集中过期或者失效,原本可以从redis中查询到的数据,都需要去数据库中查询,数据库IOPS急速增高。
- 解决办法:放置key值的时候,使用随机时间。
- 缓存击穿
- 说明:某些热点数据扛住了大并发的请求,当这些数据一旦失效,瞬间请求就会压到数据库,类似一种击穿redis的效果。
- 解决办法:将请求进行排队
- 缓存穿透
- 说明:数据库中没有的值,缓存也没有,那么每次请求就会直接发送到数据库,类似于对数据库发起DDos攻击。
- 解决办法1:使用布隆过滤器,大概率的拦截下无效请求。
- 解决办法2:缓存下无效值,且设置过期时间,在过期时间内可以限制住这些无效请求
- 解决办法3:对无效值的发起IP进行记录,对于多次发起相同无效值的IP进行黑名单。
2.3、冷热数据分离
3.1、Redis监控
- redis监控/使用工具
3.2、Redis故障排查
3.3、Redis故障恢复
- 通过AOF/RDB进行恢复