Redis 为什么使用单进程单线程方式也这么快

多路 I/O 复用模型

不用重的Libevent,自己轻量级封装 select、epoll、evport、kqueue,不同OS选择适合的接口

单线程好处

代码更清晰,处理逻辑更简单

避免锁性能消耗

不存在多进程或者多线程导致的切换而消耗 CPU

单线程弊端

无法发挥多核 CPU 性能,通常单机多个 Redis 实例来完善

算法

一是内存爆了要用 LRU、LFU、FIFO 清理一些

二是超时键过期要删除,用主动或惰性的方法

LRU

redis

2.6 随机找三条记录出来,比较哪条空闲时间最长就删哪条,再随机三条出来,一直删到内存足够

3.0 改进:每次随机五条记录出来,插入到一个长度为十六的按空闲时间排序的队列里,排头的那条删掉,再找五条出来,继续尝试插入队列,不只在一次随机的五条里面找最久那条,会连同之前的一起做比较……

Memcached

中规中矩的,用了教科书式的双向链表存储 slab 内的 LRU 关系

元素插入时把自己放到列头,删除时把自己的前后两个元素对接起来,更新时先做删除再做插入

分配内存超限时,从 LRU 的队尾开始清理

Guava Cache

同 Memcached

Ehcache

随机 8 条记录,找出最旧那条,刷到磁盘里

redis 默认设置都是 noeviction,内存不够直接报错,懒得建个双向链表,而且每次访问时都要更新它

过期键删除

每一个设置了过期的元素启动一个 Timer,能最快删除过期键最省空间

Java 可用 DeplayQueue 存着,开条线程不断读取就能做到,但该线程消耗 CPU 较多,内存不紧张时浪费

各家都用惰性检查,每次元素访问时,才检查否已经超时

元素后来都没再被访问,各家都再提供了一个定期主动删除的方式

redis

主线程 100 毫秒执行一次,随机抽 20 条 Key ,1/4 过期,证明此时过期的键可能比较多,就不等 100 毫秒,立刻开始下一轮的检查

限定每轮的总执行时间不超过 1 毫秒优化CPU占用

Memcached

LRU 爬虫线程,利用之前LRU 队列,可设置多久跑一次 (默认也是 100 毫秒),沿着列尾一直检查过去,每次检查 N 条数据。但没有 Redis 过期的多了立马展开下一轮检查的功能,需要自己自己权衡 N 的设置

Guava Cache

检查调用时机并不是 100 毫秒什么的,而是每次各种写入数据时的 preWriteCleanup()

Ehcache

只有惰性检查,没有主动检查过期的,只会在内存超限时不断用近似 LRU 算法把内存中的元素刷到磁盘中

文件存储中才有超时检查的线程

redis有6种

  • no-enviction:禁止删除数据
  • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集中任意选择数据淘汰
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰