侧边栏壁纸
  • 累计撰写 244 篇文章
  • 累计创建 16 个标签
  • 累计收到 0 条评论
隐藏侧边栏

通过 Redis 指令执行的生命周期看 Redis 的底层架构和基本实现

kaixindeken
2021-04-28 / 0 评论 / 0 点赞 / 141 阅读 / 1,879 字

通信协议

当 Redis 客户端发送了一条新增(或更新)字符串键值对的指令后,该指令会被封装到网络包中发送给 Redis 服务端:

127.0.0.1:6379> set name 'kaixindeken'
OK

这个通信是基于 TCP 连接的 Socket 通信,采用的通信协议是 RESP(REdis Serialization Protocol,即 Redis 序列化协议,关于这个通信协议的细节我们后面会详细介绍,这里先了解即可)。

Redis 没有使用动态链接库实现客户端与服务端进程之间的通信,而是使用这种基于网络协议的通信,好处是简单、易于实现、解析方便、可读性好(RESP 是一个基于纯文本的协议),但是坏处也是显而易见的,即网络 IO 普遍存在的性能问题,下面我们来介绍 Redis 是如何处理这个问题保证键值对数据库的高性能的。

单线程 IO 模型

Redis 服务端接收到客户端发送过来的指令后,会基于 RESP 协议解析指令信息,然后进行相应的处理。此时需要面临一个系统设计问题:客户端网络连接的处理、指令请求的解析、以及解析后请求数据的存取,是用一个线程还是多个线程来交互处理?通常我们将这个问题称之为 IO 模型设计。

并发执行的程序可以更好地榨取 CPU 的性能,从而提升程序的性能,而并发编程需要借助多进程、多线程、协程等方式来实现。但是,并发也不是万精油,如果涉及到共享资源的操作,需要引入锁机制来保证操作的原子性时,并发程序的性能会大幅降低,因为锁会导致线程竞争和阻塞。

而 Redis 服务端在进行键值对存取的时候,同一个键名对应的正是同一个资源,如果使用多线程来处理多个客户端对同一个键名的并发存取操作,势必导致一个线程在处理的时候,其他线程处于阻塞状态,所以在这里多线程并不是最优解。

因此 Redis 在进行网络 IO 模型设计时,选择了单线程 IO 模型,可不要小瞧单线程 IO 模型哦,Nginx 也是通过这种 IO 模型实现高性能 Web 服务器的。

Redis 的单线程 IO 模型是能够处理 10 万级并发请求的,这个性能非常强悍,那单线程 IO 模型是如何做到的如此高性能的呢,这就不得不提及大名鼎鼎的 IO 多路复用机制,关于这个技术在 Socket 编程(下):服务器如何提高并发量 简单介绍过,后面我们还会详细介绍 Redis 底层如何使用它实现高性能的网络 IO。

通过索引快速定位

Redis 服务端通过单线程接收并解析客户端指令后,如果是存储操作,就要将键值对数据存储到内存,在此之前需要先判断键值对是否已经存在;如果是读取操作,就要从内存获取键名对应的键值并返回,无论是存储还是读取,都会涉及到键名对应键值的定位问题。

MySQL 通常会通过 B+ 树索引来优化查询效率,如果使用的存储引擎是内存的话,默认使用的是哈希索引技术。

对于 Redis 这种基于内存的键值对数据库,使用的也是哈希表来快速定位键名对应的键值,毕竟,对于键值对这种数据结构,天然适合哈希索引,性能也达到了非常高的 O(1)(不考虑哈希冲突的情况下)。

当然,除了基本的字符串键值对外,Redis 还支持其他数据结构,比如列表、集合、字典等,这些数据结构内存还存在获取元素性能优化的问题,后面我们还会系统介绍 Redis 底层如何基于哈希索引为高性能保驾护航。

另外,对于存储/删除操作而言,还涉及到分配新的内存空间以及释放内存空间的问题,Redis 底层会有分配器专门完成这些事情。

数据持久化概述

日常使用的时候,Redis 服务端基于内存操作保证了数据存取的高性能和高效率,不过和 Memcached 相比,除了支持更丰富的数据结构外,Redis 还支持将数据持久化到磁盘,这就意味着 Redis 不仅可以用作高性能的缓存系统,还可以用作 NoSQL 数据库。

Redis 支持通过全量备份的 RDB 快照和增量备份的 AOF 日志两种机制进行数据持久化,当系统重启后,内存数据丢失,Redis 会通过 AOF 日志对数据进行重放来恢复内存数据。当然了,对于这种持久化到磁盘涉及到磁盘 IO 的操作,Redis 会通过其他线程后台异步进行处理,并且为了保证 Redis 的高性能,对持久化时机进行了优化。

0

评论区