Redis是如何用单线程来实现每秒10w+的QPS的呢?
- 使用IO多路复用(区分Blocking /IO--阻塞I/O,NIO--非阻塞I/O),redis 多路复用通过宏定义,支持不同操作系统的“select“”函数,Solaries 10 中的
evport
、Linux 中的epoll
和 macOS/FreeBSD 中的kqueue,保底用select,这个只支持1024个文件描述符。
- 非CPU密集型任务
- 纯内存操作
一、效率提升点一,过期处理:
redis使用时间长了之后,会有很多过期的key,如何做过期key的清理,做好容量管理,是比较重要的,过期处理策略有以下几个:
- 定时删除,在设置键的过期时间的同时,创建一个定时器。当键的过期时间来临时,立即执行对键的删除操作
- 惰性删除,每次获取键的时候,判断键是否过期,如果过期的话,就删除该键,如果没有过期,则返回该键
- 定期删除,每隔一段时间,对键进行一次检查,删除里面的过期键
定时删除策略对CPU不友好,当过期键比较多的时候,Redis线程用来删除过期键,会影响正常请求的响应。
- 每秒进行10次过期扫描,每次从过期字典中随机选出20个key
- 删除20个key中已经过期的key
- 如果过期key的比例超过1/4,则进行步骤一
- 每次扫描时间的上限默认不超过25ms,避免线程卡死
- 给同时过期的key增加一个随机数,打散过期时间,降低清除key的压力
- 如果你使用的是redis4.0版本以上的redis,可以开启lazy-free机制(lazyfree-lazy-expire yes),当删除过期key时,把释放内存的操作放到后台线程中执行
二、可用性提升
持久化:Redis的持久化机制有RDB快照和AOF日志,每次写命令之后后,Redis提供了如下三种刷盘机制
always:同步写回,写命令执行完就同步到磁盘
everysec:每秒写回,每个写命令执行完,只是先把日志写到aof文件的内存缓冲区,每隔1秒将缓冲区的内容写入磁盘
no:操作系统控制写回,每个写命令执行完,只是先把日志写到aof文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回到磁盘
提升持久化效率方式:Redis生成rdb文件和aof日志重写,都是通过主线程fork子进程的方式,让子进程来执行的,主线程的内存越大,阻塞时间越长,策略有以下几个:
- 控制Redis实例的内存大小,尽量控制到10g以内,因为内存越大,阻塞时间越长
- 配置合理的持久化策略,如在slave节点生成rdb快照
三、大key的处理
如果一个key对应的value非常大,那么这个key就被称为bigkey。写入bigkey在分配内存时需要消耗更长的时间。同样,删除bigkey释放内存也需要消耗更长的时间
1、如何定位bigkey?
Redis提供了扫描bigkey的命令:redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01
2、如何解决bigkey带来的性能问题?
- 尽量避免写入bigkey
- 如果使用的是redis4.0以上版本,可以用unlink命令代替del,此命令可以把释放key内存的操作,放到后台线程中去执行
- 如果使用的是redis6.0以上版本,可以开启lazy-free机制(lazyfree-lazy-user-del yes),执行del命令的时候,也会放到后台线程中去执行