【数据库学习】Redis 解析器 && 单线程 && 模型
Redis 协议的高性能解析器
虽然 redis 协议很容易阅读和实现,但它可以以类似于二进制协议的性能实现。Resp 使用前缀长度传输大容量数据,因此它不需要扫描负载以查找 JSON 之类的特殊字符,也不需要引用需要发送到服务器的负载。批次和多批次长度可以使用代码进行处理,这些代码对每个字符执行单个操作,并同时扫描 CR 字符,例如以下 C 代码:Resp 使用前缀长度传输多行数据,因此它不需要扫描负载以查找 JSON 之类的特殊字符,也不需要引用需要发送到服务器的负载。可以用代码处理多行和多行长度。该代码对每个字符执行单个操作,并同时扫描 CR 字符。
识别第一个 CR 后,您可以跳过它和下面的 LF,而无需任何处理。然后,可以使用不以任何方式检查有效负载的单个读取操作来读取大容量数据。最后,剩余的 Cr 和 LF 字符将在不进行任何处理的情况下丢弃。Redis 协议的性能与二进制协议相当。更重要的是,它很容易在大多数高级语言中实现,从而减少了客户端软件中的错误数量。
C 语言实现,虽然 C 有助于 redis 的性能,但语言不是核心因素。纯内存输入/输出。与其他基于磁盘的数据库相比,redis 的纯内存操作具有天然的性能优势。输入/输出多路复用,基于 epoll/select/kqueue 等输入/输出多路复用技术,实现高吞吐量的网络输入/输出。在单线程模型中,一个线程不能使用多个内核,但另一方面,它避免了多线程频繁切换上下文和锁等同步机制的开销。
单线程
对于 DB,CPU 通常不是瓶颈,因为大多数请求不是 CPU 密集型的,而是 i/o 密集型的。对于 redis,如果不考虑 rdb/aof 等持久化方案,redis 是一个完整的纯内存操作,执行速度非常快。因此,这部分操作通常不是性能瓶颈。redis 真正的性能瓶颈在于网络 i/o,即客户端和服务端之间的网络传输延迟。因此,redis 选择单线程 i/o 多路复用来实现其核心网络模型。
避免过多的上下文切换开销
在多线程调度过程中,需要在 CPU 之间切换线程上下文,上下文切换涉及一系列寄存器替换,如程序计数器、堆栈指针和程序状态字、程序堆栈重置,甚至 CPU 缓存和 TLB 快速表的替换。如果在进程内进行多线程切换,则效果更好,因为单个进程内的多线程共享进程地址空间,因此,线程上下文比进程上下文小得多。如果是跨进程调度,则需要切换整个进程地址空间。如果是单线程,则可以避免进程中频繁切换线程的开销,因为程序始终在进程中的单线程中运行,并且不存在多线程切换的场景。
期望的多线程编程与实际的多线程编程相比:
网络模型
Redis 协议通过 TCP,客户端和 Redis 实例保持双工连接,如下图所示:
序列化实现
序列化的实现相对简单。在执行前一个命令后,同一个连接发送第二个请求。如下图所示:
单连接吞吐量 = 1 / (2*网络延迟 + 服务器处理时间 + 客户端处理时间)
redis 对单个请求的处理时间(10 微秒)通常比 LAN 的延迟小 1 个数量级。因此,在串行模式下,单个连接在网络上等待的时间最多,服务器的处理能力没有得到充分利用。
版权声明: 本文为 InfoQ 作者【黎燃】的原创文章。
原文链接:【http://xie.infoq.cn/article/04f93a32782fa290fc84396a9】。文章转载请联系作者。
评论