您现在的位置是:首页 >其他 >Redis学习总结(三)网站首页其他
Redis学习总结(三)
Redis 内存淘汰机制
当 Redis 内存空间超限时,为了避免数据丢失,Redis 会通过内存淘汰机制从内存中删除一些数据。Redis 内存淘汰机制包括以下几种:
-
noeviction:当 Redis 内存空间已满,如果没有设置淘汰策略或者设置了
noeviction
策略,则 Redis 将停止执行写操作,并向客户端返回错误信息。 -
allkeys-lru:LRU(最近最少使用)淘汰策略会删除最近最少使用的 Key。当内存空间已满时,Redis 将从所有的 Key 中,选取最近最少使用的 Key 进行淘汰。
-
allkeys-random:随机淘汰策略会随机删除一个 Key。当内存空间已满时,Redis 将随机选取一个 Key 进行淘汰。
-
volatile-lru:与 allkeys-lru 策略类似,它会删除已设置过期时间的 Key 中,最近最少使用的 Key。
-
volatile-random:与 allkeys-random 策略类似,它会删除已设置过期时间的 Key 中,随机一个 Key 进行淘汰。
-
volatile-ttl:它会删除已设置过期时间的 Key 中,最近即将过期的 Key。
4.0 版本后增加以下两种:
-
volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰。
-
allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。
可以通过 CONFIG GET maxmemory-policy
命令获取当前设置的淘汰策略。
需要注意的是,对于 Master-Slave 模式的 Redis 集群,只有 Master 执行内存淘汰操作。Slave 只是接收来自 Master 的数据。
我们可以通过以下命令设置 Redis 的最大内存限制:
$ redis-cli config set maxmemory 1G // 设置最大内存限制为 1GB
当 Redis 的内存使用量超过这个限制时,Redis 将会执行内存淘汰,以保证 Redis 不会因内存不足而宕机。
总结来说,Redis 内存淘汰机制是用于保护 Redis 内存不会超限,避免数据丢失,并保证 Redis 的可用性。Redis 内存淘汰机制包括 noeviction、allkeys-lru、allkeys-random、volatile-lru、volatile-random 和 volatile-ttl 六种策略,可以根据具体应用场景进行配置。
Reactor 模式
Reactor 模式是一种高效的网络编程模式,常用于构建高性能的服务器应用程序。它是基于事件驱动机制来实现的,可以有效地处理大量的并发请求,同时避免了多线程编程中的一些问题。下面详细介绍 Reactor 模式的概念、作用、结构以及代码实现。
概念
Reactor 模式是指,在一个单独的线程中,通过异步 I/O 的方式来监视多个连接的状态,如果有连接有数据可读或可写,就触发相应的事件处理器进行处理。它的核心是将 I/O 操作异步化,避免了阻塞式 I/O 的等待时间,提高了服务器的并发能力和响应速度。
作用
Reactor 模式的主要作用是提高服务器的并发能力和响应速度。通过将 I/O 操作异步化,避免了阻塞式 I/O 的等待时间,使得服务器能够同时处理多个连接,大大提高了并发能力。同时,由于 Reactor 模式采用了事件驱动的方式,可以有效地减少线程的使用,避免了多线程编程中的一些问题,如线程间竞争、上下文切换等,提高了应用程序的性能和可靠性。
结构
Reactor 模式包含以下几个组件:
-
Handle:表示一个 I/O 事件,如可读、可写等。
-
Synchronous Event Demultiplexer:同步事件多路复用器,用于等待事件的发生,如 select、poll、epoll 等。
-
Event Handler:事件处理器,用于处理特定类型的 I/O 事件。
-
Concrete Event Handler:具体的事件处理器,实现了 Event Handler 接口,用于处理具体的业务逻辑。
Reactor 模式的基本流程
-
应用程序向 Reactor 注册一个或多个事件处理器,每个事件处理器都与一个 Handle 相关联。
-
Reactor 启动一个单独的线程,通过 Synchronous Event Demultiplexer 等待事件的发生。
-
当某个事件发生时,Synchronous Event Demultiplexer 将对应的 Handle 交给 Reactor 处理。
-
Reactor 根据 Handle 上注册的事件类型,选择相应的事件处理器来处理该事件。
-
事件处理器根据具体的业务逻辑,完成对该事件的处理。
-
处理完成后,事件处理器将控制权交还给 Reactor。
代码实现
Reactor模式是一种设计模式,用于处理高并发的I/O操作。它的核心思想是将I/O操作分为两个部分:读取请求和处理请求。Reactor模式通过使用一个单独的线程来处理所有的I/O请求,并将这些请求分派到不同的处理器中去。
在Java中,可以使用NIO(New I/O)来实现Reactor模式。下面是一个简单的示例代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Reactor implements Runnable {
private final Selector selector;
private final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor());
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey sk = it.next();
dispatch(sk);
}
selectedKeys.clear();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void dispatch(SelectionKey sk) {
Runnable runnable = (Runnable) sk.attachment();
if (runnable != null) {
runnable.run();
}
}
class Acceptor implements Runnable {
@Override
public void run() {
try {
SocketChannel socketChannel = serverSocket.accept();
if (socketChannel != null) {
new Handler(selector, socketChannel);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
class Handler implements Runnable {
private final SocketChannel socketChannel;
private final SelectionKey selectionKey;
private ByteBuffer input = ByteBuffer.allocate(1024);
private ByteBuffer output = ByteBuffer.allocate(1024);
public Handler(Selector selector, SocketChannel socketChannel) throws IOException {
this.socketChannel = socketChannel;
socketChannel.configureBlocking(false);
selectionKey = socketChannel.register(selector, 0);
selectionKey.attach(this);
selectionKey.interestOps(SelectionKey.OP_READ);
selector.wakeup();
}
@Override
public void run() {
try {
if (selectionKey.isReadable()) {
read();
} else if (selectionKey.isWritable()) {
write();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void read() throws IOException {
socketChannel.read(input);
input.flip();
// do something with input
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
private void write() throws IOException {
output.clear();
socketChannel.write(output);
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws IOException {
new Thread(new Reactor(8080)).start();
}
}
在这个示例代码中,Reactor类实现了Runnable接口,表示它是一个线程。在构造函数中,它创建了一个Selector对象,并将ServerSocketChannel对象注册到Selector中,用于监听客户端的连接请求。
Acceptor类是一个内部类,它实现了Runnable接口。当有新的客户端连接时,Acceptor将会被调用,用于创建一个新的Handler对象来处理客户端请求。
Handler类也是一个内部类,它实现了Runnable接口。它包含了两个ByteBuffer对象,一个用于读取数据,一个用于写入数据。当客户端发送数据时,Handler将读取数据并进行处理,然后切换到写模式,将结果写回给客户端。
在Reactor类的run方法中,它使用Selector对象的select方法来阻塞等待事件的发生。当有事件发生时,它将调用dispatch方法来处理这些事件。dispatch方法将会找到对应的Runnable对象并执行它的run方法。
这个示例代码只是一个简单的实现,实际上Reactor模式还有很多细节需要考虑,例如如何处理异步操作、如何处理异常等等。但是通过这个示例代码,你可以了解到Reactor模式的基本思想和实现方式。