一、 Redis有多快?

Redis不是一般地快!

Redis和Memcached同为内存数据库,且都支持分布式,近年来,Redis凭借着优秀的架构设计,不断蚕食Memcached领地,大有一统天下的趋势。

感受一下redis高端配置的吞吐量,横轴为连接数,纵轴为吞吐量,图片来源于官方文档

redis吞吐量 redis的吞吐量_单线程

基于epoll/kqueue,Redis事件循环具有很强的可扩展性。Redis已经在60000多个连接上进行了基准测试,并且在这些条件下仍能够维持50000q/s。根据经验,具有30000个连接的实例只能处理100个连接可实现的吞吐量的一半。

二、Redis为什么快?

2.1 采用单线程

单线程意味着,Redis不再考虑锁和进程的上下文切换及资源竞争问题。

Redis的单线程是指,网络请求处理模块使用了一个线程,Redis是线程安全的,不考虑并发安全性,也就是说,一个线程处理所有网络请求。

需要注意的是,Redis其他模块仍然使用了多个线程,如,文件的定时备份和恢复,执行数据清理策略等。

2.2 绝大部分请求是内存操作

  • 内存操作:
  • 内存数据读写
  • 磁盘持久化操作:
  • 文件备份与恢复

这里可以简单理解为,Redis处理网络请求的单个线程涉及到的操作都是内存操作,其他与磁盘相关的操作是其他线程来完成的。

2.3 采用IO多路复用策略epoll

IO多路复用的策略有select、pselect、poll和epoll,其中pselect与select大同小异,epoll是select和poll的改进版本。

epoll与select和poll相比,具有如下优点:

  1. 没有最大并发连接的限制。select和poll有系统FD_SETSIZE限制的问题,32位机器默认1024,64位机器默认是2048,epoll解决了select和poll只能监听有限数量FD(文件描述符)的问题,1G内存能监听10万左右的端口;
  2. 只遍历活跃的Socket,轮询效率高。select和poll不管Socket是否活跃,会遍历所有Socket,包括idle-connection和dead-connection,而epoll最大的优点就在于只管“活跃”的连接,与连接总数无关,因此,epoll效率更高;
  3. 内核空间和用户空间共享内存来减少FD复制的开销。利用mmap()文件映射内存加速与内核空间的消息传递来实现。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次,而select和poll需要将消息从内核传递到用户空间,都需要多次内核拷贝动作。

2.4 其他优化

2.4.1 压缩存储

Redis的数据类型都可能有2种或者多种内部编码实现,这使得Redis可以根据不同的场景选择最优的编码方式。

阅读源码会发现,当数据量较小时,Redis会使用ziplist实现list,使用zipmap实现HashTable,ziplist和zipmap是经过精心设计的数据编码方式,ziplist和zipmap在内存中的存储都是连续内存空间。ziplist列表两端push和pop操作都是O(1),zmap的key查询为O(n),为string-> string结构,节省了空间。

具体可参考源码:redis/src/ziplist.credis/src/zipmap.c

2.4.2 数据淘汰策略的优化

最新版本Redis v5.4具有8种Eviction策略,其中,LFU为最近版本加入。

值得一提的是,Redis实现的LRU是经典LRU的一种近似,简单说就是有一个专用的eviction pool,当执行evict时,采样N个样本,并非经典LRU算法的所有样本,加入到eviction pool,eviction pool的大小由EVPOOL_SIZE来定义。正是由于选取有限个值(一般是5)使用LRU算法,这大大提高了效率。

所有的策略如下:

Policy

Description

noeviction

如果在尝试插入更多数据时达到内存限制,则返回错误

allkeys-lru

从所有key中删除最近最少使用的key

allkeys-lfu

从所有key中删除最不常用的key

allkeys-random

随机删除所有key中的key

volatile-lru

使用“expire”字段从所有key中删除最近最少使用的key

volatile-lfu

使用“expire”字段从所有key中删除最不常用的key

volatile-random

使用“expire”字段随机删除key

volatile-ttl

使用“expire”字段,从所有key中删除最短的生存时间和最近最少使用的key

LRU和LFU具体算法可参考源码:redis/src/evict.c

2.4.3 数据结构及架构设计

这部分内容较为繁杂,专用的数据结构都有很多巧妙的设计,如,sds、redisobj等,有兴趣的可以读读源码。

总结

早期Memcached强调Cache,而如今Redis强调DB,大多时候Redis可以替代Memached。
单线程情况下,Redis往往比Memcached快,但Memcached本身基于多线程,这是它的优势所在,多线程情况下,Memcached往往比Redis快。具体的快慢总是相对的,需要根据自己的业务场景,适配合适的工具,除非性能是整个技术架构中的瓶颈,否则,应该长远考虑架构的稳定性和拓展性。

参考文献

  1. 聊聊IO多路复用之select、poll、epoll详解
  2. How fast is Redis?
  3. 为什么Redis是单线程?
  4. Redis Eviction Policies
  5. Redis源码

能力有限,欢迎指错交流~