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

版权声明:欢迎访问,本文为小编原创文章 https://blog.csdn.net/changyinling520/article/details/82262410

背景介绍:

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.内核和用户数据分别代表什么区域?
这里写图片描述
4.阻塞I/O,非阻塞I/O,I/O多路复用之间的区别
这里写图片描述

类比于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并没有显著的性能优势

猜你喜欢

转载自blog.csdn.net/changyinling520/article/details/82262410