1. 程式人生 > >為什麼reids是單執行緒還那麼快?

為什麼reids是單執行緒還那麼快?

背景介紹:

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並沒有顯著的效能優勢