为何redis是单线程还那么快?

背景介绍:

Redis一个很是大的特色就是快,官方给出说明:web

1. 单线程,减小线程切换时间。
2. 纯内存操做
3. I/O多路复用机制
redis

可能前二者比较好理解,可是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并无显著的性能优点socket