首先将官方的介绍翻译出来:
Redis 是一种开源(BSD 许可)的内存数据结构存储,用作数据库、缓存、消息代理和流式处理引擎。Redis 提供数据结构,例如字符串、哈希、列表、集、带有范围查询的排序集、位图、超日志、地理空间索引和流。Redis 具有内置复制、Lua 脚本、LRU 逐出、事务和不同级别的磁盘持久性,并通过 Redis Sentinel 和 Redis 集群的自动分区提供高可用性。
您可以对这些类型运行原子操作,例如附加到字符串;递增哈希值;将元素推送到列表中;计算集合交集、并集和差分集;或获取排序集中排名最高的成员。
为了实现最佳性能,Redis 使用内存中数据集。根据您的使用案例,Redis 可以通过定期将数据集转储到磁盘或将每个命令附加到基于磁盘的日志来保留数据。如果您只需要一个功能丰富的网络内存中缓存,也可以禁用持久性。
Redis 支持异步复制,具有快速无阻塞同步和自动重连,并在网络拆分时进行部分重新同步。
Redis 还包括:
- 交易
- 发布/订阅
- Lua 脚本
- 生存时间有限的密钥
- LRU 逐出密钥
- 自动故障转移
我先从几个宏观的角度来介绍下redis:
基于内存
计算机存储体系分四层,也就是呈金字塔型,塔尖的速度最慢、容量最小、造假最贵;塔底的速度最慢、容量最大、造假最便宜:
- 外存:容量最大、速度最慢、稳定性最高。
- 内存:容量远小于外存,速度远高于外存,不稳定(断电内容消失)。
- 高速缓存:虽然内存是由电路组成的,其速度远高于外存,但是比CPU速度而言,内存速度依然很慢,若CPU所执行的指令数据每每都需要从内存中取得,或者CPU计算结果每每都需要存储到内存,则CPU再快都没有用,因为最后的速度取决于内存速度。
- CPU缓存:分为一级缓存、二级缓存、三级缓存。
虽然基于内存极大的加快了数据的读取,但是数据存储在内存也就是容易断电消失,因此redis提供了数据持久化到磁盘。
单线程
进程是os分配资源的最小单位;线程是os执行的最小单位。单线程的情况不涉及到线程切换、竞争、加锁等资源消耗。而redis的单线程指的是 【接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端】 这个过程是由一个线程(主线程)来完成的。这也是说他是单线程的原因。但是,由于网络i/o、数据持久化、大key的处理等需求。作者不得不在后面的版本开始引入多线程。比如:unlink 大key;aof刷盘等任务。
I/O多路复用(epoll)
Redis的I/O多路复用程序的所有功能是通过包装select、epoll、evport和kqueue这些I/O多路复用函数库来实现的,每个I/O多路复用函数库在Redis源码中都对应一个单独的文件,比如ae_select.c、ae_epoll.c、ae_kqueue.c等。
epoll相对于select、poll有几个主要的性能提高:
- select使用数组存fd O(n);poll使用链表O(n);epoll使用红黑树O(logn)。在查找发生事件的fd时候,红黑树显然查找效率更高
- 数据拷贝时select、poll会将整个fd队列数据拷贝到内核态、用户态。epoll会维持一个事件队列,只把有事件响应的fd进行拷贝。
同时epoll支持俩种事件触发模式:边缘触发(edge-triggered,ET)和水平触发(level-triggered,LT)。而 select/poll 只支持水平触发,一般而言,边缘触发的方式会比水平触发的效率高。
- 使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完;
- 使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取;