前言
目前正在出一个查漏补缺专题
系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~
本专题主要以Java
语言为主, 好了, 废话不多说直接开整吧~
Q1 & 请介绍一下redis的架构
Redis
是一种开源的高性能键值存储系统,通常被用作内存数据库、缓存和消息中间件。它具有简单、灵活、高效的特点,以及丰富的数据结构和功能,使其成为许多应用程序的首选。
Redis的架构主要由以下几个关键组件组成:
- 客户端:应用程序通过
Redis
客户端与Redis
服务器进行通信。客户端可以使用多种编程语言和协议与Redis
进行交互,如Redis
的官方客户端库、REST API
或其他第三方客户端。 - 服务器:
Redis
服务器是Redis
的核心组件,负责处理客户端请求、执行命令和存储数据。服务器使用单线程事件循环模型,通过监听网络连接和处理事件来实现高并发和低延迟的性能。 - 数据存储:
Redis
支持多种数据结构,包括字符串、哈希表、列表、集合和有序集合等。这些数据结构都可以通过唯一的键来进行访问和操作。Redis的数据存储是基于内存的,但也可以通过持久化机制将数据写入磁盘,以便在服务器重启时进行恢复。 - 内存管理:
Redis
通过使用自定义的内存分配器来管理内存,以提高性能并减少内存碎片
。它使用了多种技术,如对象共享、对象池和内存压缩,来有效地管理内存使用。 - 高可用性:为了实现高可用性和数据冗余,
Redis
提供了主从复制和哨兵机制。主从复制通过将数据从主服务器复制到多个从服务器,实现数据的冗余备份和读写分离。哨兵机制用于监控和管理Redis
服务器集群,当主服务器发生故障时,自动选举新的主服务器。 - 集群模式:
Redis
还提供了集群模式,可以将数据分片存储在多个节点上,实现水平扩展和负载均衡。集群模式使用哈希槽来划分数据,每个节点负责管理一部分哈希槽的数据。
总体而言,Redis
的架构设计简单而灵活,适用于各种场景。它的高性能、丰富的数据结构和功能,使其成为处理大规模数据、高并发访问和实时应用的理想选择。
Q2 & 请详细讲一下redis的线程模型
Redis
的线程模型采用的是单线程的事件循环模型,也称为I/O多路复用
模型。
- 单线程模型:
Redis
使用单线程
来处理所有的客户端请求和数据库操作。这意味着Redis在任何给定的时间只能处理一个请求,但通过事件循环机制,它能够高效地处理大量并发请求。 - 事件循环:
Redis
使用事件循环来实现高并发和低延迟的性能。事件循环是通过I/O多路复用技术实现的,通常使用select、poll或epoll
等系统调用。Redis
通过监听套接字上的事件,如可读、可写或异常事件,来感知和处理客户端请求。 - 非阻塞
I/O
:Redis
使用非阻塞I/O
来实现事件循环。在接收到客户端请求时,Redis不会阻塞等待请求完成,而是立即返回到事件循环并继续处理其他请求。当请求的I/O
操作完成时,Redis会通过回调函数来处理响应数据。 - 文件事件:
Redis
使用文件事件
来表示与客户端或其他网络连接相关的事件。每当有新的客户端连接或数据到达时,Redis
会生成相应的文件事件并将其加入到事件队列中。事件循环会从队列中获取文件事件并处理它们。 - 时间事件:
Redis
还支持时间事件,用于执行定时任务。时间事件可以是一次性的,也可以是周期性的。通过时间事件,Redis
可以执行诸如过期键的清理、统计信息的更新等后台任务。 - 非阻塞操作:
Redis
的数据存储是基于内存的,因此读写操作通常是非阻塞的。这使得Redis
能够在快速的内存访问下提供高性能的读写操作。 - 多个数据库:
Redis
支持多个数据库,每个数据库都有自己的键值空间。在单线程模型下,Redis
使用字典来存储数据库,通过索引来快速访问不同的数据库。
通过以上的线程模型,Redis
能够高效地处理大量的并发请求,而无需为每个请求创建线程或进程。单线程模型可以避免线程切换和同步开销,提供较低的延迟和较高的吞吐量。同时,Redis
通过使用非阻塞I/O
和事件驱动
的方式,实现了高效的事件处理和资源利用。
下面我们使用Java
来简单的仿写一下单线程模型
public class Q2 { public static void main(String[] args) throws InterruptedException { RedisTaskQueue taskQueue = new RedisTaskQueue(); RedisEventLoop eventLoop = new RedisEventLoop(taskQueue); RedisClient client = new RedisClient(taskQueue); // 启动事件处理器线程 Thread eventLoopThread = new Thread(eventLoop); eventLoopThread.start(); // 模拟客户端发送命令 client.sendCommand("SET key1 value1"); client.sendCommand("GET key1"); client.sendCommand("DEL key1"); // 等待所有命令执行完成 eventLoopThread.join(); // Executing Redis command: SET key1 value1 // Executing Redis command: GET key1 // Executing Redis command: DEL key1 } } // 模拟Redis命令的执行任务 class RedisCommandTask { private String command; public RedisCommandTask(String command) { this.command = command; } public void execute() { // 模拟命令的执行 System.out.println("Executing Redis command: " + command); } } // 模拟Redis的任务队列 class RedisTaskQueue { private Queue<RedisCommandTask> queue = new LinkedList<>(); public synchronized void enqueue(RedisCommandTask task) { queue.add(task); notify(); // 通知等待中的线程有新任务 } public synchronized RedisCommandTask dequeue() throws InterruptedException { while (queue.isEmpty()) { wait(); // 如果队列为空,则等待新任务的到来 } return queue.poll(); } } // 模拟Redis的事件处理器 class RedisEventLoop implements Runnable { private RedisTaskQueue taskQueue; public RedisEventLoop(RedisTaskQueue taskQueue) { this.taskQueue = taskQueue; } @Override public void run() { while (true) { try { RedisCommandTask task = taskQueue.dequeue(); task.execute(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } // 模拟Redis的客户端 class RedisClient { private RedisTaskQueue taskQueue; public RedisClient(RedisTaskQueue taskQueue) { this.taskQueue = taskQueue; } public void sendCommand(String command) { RedisCommandTask task = new RedisCommandTask(command); taskQueue.enqueue(task); } }
使用synchronized
关键字实现了任务队列的线程安全性。enqueue()
方法用于将任务添加到队列中,并使用notify()
通知等待中的线程有新任务可执行。dequeue()
方法则在队列为空时调用wait()
进入等待状态,直到有新任务到来时被唤醒。
事件处理器线程在start()
方法中通过循环不断从任务队列中取出任务并执行。如果队列为空,线程会调用wait()
方法进入等待状态,直到有新任务到来。这样保证了事件处理器在没有任务时不会空转消耗CPU
资源。
主程序中创建了一个RedisClient
实例,并通过sendCommand()
方法模拟了客户端发送命令的过程。每个命令都被封装成RedisCommandTask
对象,并通过任务队列传递给事件处理器线程执行。这种方式模拟了Redis的非阻塞命令执行,即命令被添加到任务队列后,客户端可以继续发送其他命令而无需等待命令执行完成。
Q3 & 请详细讲一下redis的非阻塞式IO和多路复用
Redis
使用非阻塞式I/O
和多路复用
技术来实现高性能的事件驱动模型。
- 非阻塞式
I/O
: 非阻塞式I/O
是一种I/O
操作的模式,在进行读取或写入操作时,不会阻塞线程等待操作完成。Redis使用非阻塞I/O
来实现事件驱动模型,它的实现原理如下:
- 设置套接字为非阻塞模式:
Redis
在接受客户端连接或创建套接字时,将套接字设置为非阻塞模式。这样,在进行读取或写入操作时,不会阻塞线程,而是立即返回。 - 非阻塞的读取操作:当
Redis
执行读取操作时,它会检查套接字上是否有数据可读。如果没有数据可读,读取操作会立即返回,避免线程阻塞。Redis
可以使用非阻塞的方式读取数据,以便处理其他事件。 - 非阻塞的写入操作:当
Redis
执行写入操作时,它会检查套接字是否可写。如果套接字不可写,写入操作会立即返回,避免线程阻塞。Redis
可以使用非阻塞的方式写入数据,以便处理其他事件。
- 多路复用:
多路复用
是一种I/O
多路复用技术,用于同时监视多个套接字上的事件。Redis
使用多路复用技术来管理多个套接字上的事件,以实现高效的事件驱动模型。下面是多路复用的实现原理:
注册事件
和套接字
:Redis
将需要监听的套接字注册到多路复用器中,并指定事件类型,如可读、可写或异常。- 多路复用器监听事件:多路复用器负责监听所有已注册套接字上的事件。它使用系统调用(如
select、poll或epoll
)来监视套接字上的事件状态。 - 事件就绪通知:当套接字上的事件状态发生变化时,多路复用器会通知
Redis
。这样,Redis
可以及时处理事件。 - 事件处理:一旦事件就绪,
Redis
会调用相应的事件处理函数来处理事件。例如,如果有可读事件就绪,Redis
会调用读取数据的函数来处理请求。
通过非阻塞式I/O
和多路复用
的组合,Redis
实现了高效的事件驱动模型
。非阻塞式I/O
使得Redis
能够在进行读取或写入操作时不会阻塞线程,提高了并发处理能力。
多路复用技术
允许Redis
同时监听多个套接字上的事件,提供高效的事件管理和触发能力。这种组合使得Redis
能够处理大量并发请求,提供高性能和低延迟的服务。
下面通过Java
简单的演示一下非阻塞式I/O和多路复用
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; public class NonBlockingServer { public static void main(String[] args) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.bind(new InetSocketAddress(8888)); Selector selector = Selector.open(); serverChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select(); Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); if (key.isAcceptable()) { ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); SocketChannel clientChannel = serverSocketChannel.accept(); clientChannel.configureBlocking(false); clientChannel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = clientChannel.read(buffer); if (bytesRead > 0) { buffer.flip(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); String message = new String(data); System.out.println("Received message: " + message); } clientChannel.close(); } } } } }
这个示例中,我们创建了一个非阻塞的服务器,使用ServerSocketChannel
进行监听,并将其注册到Selector
上。然后,在循环中,调用selector.select()
等待事件发生。当有事件发生时,通过遍历selectedKeys
来处理事件。如果事件是OP_ACCEPT
,表示有新的客户端连接,我们接受连接并将客户端的SocketChannel
注册到Selector
上,监听可读事件OP_READ
。如果事件是OP_READ
,表示有数据可读,我们读取数据并处理。
结束语
大家可以针对自己薄弱的地方进行复习, 然后多总结,形成自己的理解,不要去背~
本着把自己知道的都告诉大家,如果本文对您有所帮助,点赞+关注
鼓励一下呗~
项目源码(源码已更新 欢迎star⭐️)
往期设计模式相关文章
- 一起来学设计模式之认识设计模式
- 一起来学设计模式之单例模式
- 一起来学设计模式之工厂模式
- 一起来学设计模式之建造者模式
- 一起来学设计模式之原型模式
- 一起来学设计模式之适配器模式
- 一起来学设计模式之桥接模式
- 一起来学设计模式之组合模式
- 一起来学设计模式之装饰器模式
- 一起来学设计模式之外观模式
- 一起来学设计模式之享元模式
- 一起来学设计模式之代理模式
- 一起来学设计模式之责任链模式
- 一起来学设计模式之命令模式
- 一起来学设计模式之解释器模式
- 一起来学设计模式之迭代器模式
- 一起来学设计模式之中介者模式
- 一起来学设计模式之备忘录模式
- 一起来学设计模式之观察者模式
- 一起来学设计模式之状态模式
- 一起来学设计模式之策略模式
- 一起来学设计模式之模板方法模式
- 一起来学设计模式之访问者模式
- 一起来学设计模式之依赖注入模式
设计模式项目源码(源码已更新 欢迎star⭐️)
Kafka 专题学习
- 一起来学kafka之Kafka集群搭建
- 一起来学kafka之整合SpringBoot基本使用
- 一起来学kafka之整合SpringBoot深入使用(一)
- 一起来学kafka之整合SpringBoot深入使用(二)
- 一起来学kafka之整合SpringBoot深入使用(三)
项目源码(源码已更新 欢迎star⭐️)
ElasticSearch 专题学习
- 利用docker搭建es集群
- 一起来学ElasticSearch(一)
- 一起来学ElasticSearch(二)
- 一起来学ElasticSearch(三)
- 一起来学ElasticSearch(四)
- 一起来学ElasticSearch(五)
- 一起来学ElasticSearch(六)
- 一起来学ElasticSearch(七)
- 一起来学ElasticSearch(八)
- 一起来学ElasticSearch(九)
- 一起来学ElasticSearch(十)
- 一起来学ElasticSearch之整合SpringBoot(一)
- 一起来学ElasticSearch之整合SpringBoot(二)
- 一起来学ElasticSearch之整合SpringBoot(三)
项目源码(源码已更新 欢迎star⭐️)
往期并发编程内容推荐
- Java多线程专题之线程与进程概述
- Java多线程专题之线程类和接口入门
- Java多线程专题之进阶学习Thread(含源码分析)
- Java多线程专题之Callable、Future与FutureTask(含源码分析)
- 面试官: 有了解过线程组和线程优先级吗
- 面试官: 说一下线程的生命周期过程
- 面试官: 说一下线程间的通信
- 面试官: 说一下Java的共享内存模型
- 面试官: 有了解过指令重排吗,什么是happens-before
- 面试官: 有了解过volatile关键字吗 说说看
- 面试官: 有了解过Synchronized吗 说说看
- Java多线程专题之Lock锁的使用
- 面试官: 有了解过ReentrantLock的底层实现吗?说说看
- 面试官: 有了解过CAS和原子操作吗?说说看
- Java多线程专题之线程池的基本使用
- 面试官: 有了解过线程池的工作原理吗?说说看
- 面试官: 线程池是如何做到线程复用的?有了解过吗,说说看
- 面试官: 阻塞队列有了解过吗?说说看
- 面试官: 阻塞队列的底层实现有了解过吗? 说说看
- 面试官: 同步容器和并发容器有用过吗? 说说看
- 面试官: CopyOnWrite容器有了解过吗? 说说看
- 面试官: Semaphore在项目中有使用过吗?说说看(源码剖析)
- 面试官: Exchanger在项目中有使用过吗?说说看(源码剖析)
- 面试官: CountDownLatch有了解过吗?说说看(源码剖析)
- 面试官: CyclicBarrier有了解过吗?说说看(源码剖析)
- 面试官: Phaser有了解过吗?说说看
- 面试官: Fork/Join 有了解过吗?说说看(含源码分析)
- 面试官: Stream并行流有了解过吗?说说看
推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)
- springboot-all
地址
: github.com/qiuChenglei…- SpringBoot系列教程合集
- 一起来学SpringCloud合集
- SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证(一)
- SpringCloud整合 Oauth2+Gateway+Jwt+Nacos 实现授权码模式的服务认证(二)