欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

为什么reids是单线程还那么快?

程序员文章站 2022-05-18 10:25:38
...

背景介绍:

redis一个非常大的特点就是快,官方给出说明:
1.单线程,减少线程切换时间。
2.纯内存操作
3.I/O多路复用机制
理解起来单线程操作减少了线程切换的时间,以及减少了多线程的复杂度这个很好理解;纯内存操作,减少了访问硬盘的操作,但是I/O多路复用是什么意思?

内容

1.什么是I/O多路复用机制?
多路 I/O 复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快

2.redis操作的瓶颈在哪里?
操作的瓶颈在于网络的I/O,I/O操作的步骤分为:

  • 数据通过网关到达内核,内核准备好数据
  • 数据从内核缓存缓存写入到用户程序数据

3.内核和用户数据分别代表什么区域?
为什么reids是单线程还那么快?
4.阻塞I/O,非阻塞I/O,I/O多路复用之间的区别
为什么reids是单线程还那么快?

类比于jdk中NIO操作,NIO提供了selector器,是selectableChannel的多路服务器,用于监控SelectableChannel的io状态。通道注册到selector器上,而且可以选择注册那种事件类型,由selector对注册事件进行轮询。
以nio的SocketChannel为例,写示例代码;

public class TestNonBlockNIO {
    @Test
    public  void testClient() throws IOException {
        //1.获取通道
        SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
        //2.切换到非阻塞的状态
        sChannel.configureBlocking(false);
        //3.分配指定大小的缓冲区
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //4.发送数据给服务端
        Scanner scan=new Scanner(System.in);
        while(scan.hasNext()){
            String str=scan.next();
            buffer.put((new Date().toString()+"\n"+str).getBytes());
            buffer.flip();
            sChannel.write(buffer);
            buffer.clear();
        }
//        buffer.put(LocalDate.now().toString().getBytes());
        //5.关闭通道
        sChannel.close();

    }
    @Test
 public  void testServer() throws IOException {
        //1.获取server通道
     ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();

      //2.切换成非阻塞的模式
      serverSocketChannel.configureBlocking(false);
       //3.绑定连接
        serverSocketChannel.bind(new InetSocketAddress(9898));
        //4.获取选择器
        Selector selector = Selector.open();
        //5.通道注册到选择器上,并且选择监听的事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //6.轮询式获取选择器上已经“准备就绪”的事件
        while (selector.select()>0){
            //7.获取当前选择器中所有注册的“已经就绪的监听事件”
           Iterator<SelectionKey> iterable= selector.selectedKeys().iterator();
           while (iterable.hasNext()){
               //8.获取准备就绪的事件
               SelectionKey sk=iterable.next();
               //9.判断具体是什么事件就绪了
               if(sk.isAcceptable()){   //接收就绪
                   //10.若接收就绪,获取客户端的连接
                   SocketChannel socketChannel=serverSocketChannel.accept();
                   //11.切换到非阻塞的模式
                   socketChannel.configureBlocking(false);
                   //12.将该通道注册到选择器上
                   socketChannel.register(selector,SelectionKey.OP_READ);
               }

               if(sk.isReadable()){
                   //13.获取读状态就绪的通道
                 SocketChannel socketChannel= (SocketChannel) sk.channel();
                   ByteBuffer buffer=ByteBuffer.allocate(1024);
                   int len=0;
                   while((len=socketChannel.read(buffer))>0){
                       buffer.flip();
                       System.out.println(new String(buffer.array(),0,len));
                       buffer.clear();
                   }
               }
           }

           //14.取消选择键
       iterable.remove();
        }


 }

}

总结

其实reids之所以单线程还如此之快的原因就是因为内部采用了I/O多路复用机制模型,但是这种机制不是什么情况下都是使用的,应为用与大量的链接,处理时间又不是很长的业务,连接数最好是大于1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势