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

背景介绍:

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值