天天看點

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

有個小夥伴面試回來說面試官問了他一些 Redis 問題,但是他好像沒有回答上來。

我說,你 Redis 不是用的很溜嗎,什麼問題難住你了。

他說,事情是這樣的,剛開始,問了一些基礎的問題,比如 Redis 的幾種基本資料類型和使用場景,以及主從複制和叢集的一些問題,這些都還好。

然後問 Redis 的兩種持久化方式,我說與 RDB 和 AOF 兩種方式,RDB 資料檔案小,恢複速度快,但是對性能有影響,而且不适合實時存儲。而 AOF 是現在最常用的持久化方式,它的一大優點就是實時性,并且對 Redis 半身性能影響最小。

那面試又問了,你知道 AOF 持久化之後的檔案是什麼格式嗎?

答:好像就是文本檔案吧?

好,文本檔案,那你知道它有什麼規則嗎?或者說,它和 Redis 的協定有什麼關系嗎?

答:啊,這個,恩,不太清楚呢。

現在就來看一下 AOF 和 RESP 協定的關系

  1. 從兩種持久化方式說起。
  2. RESP 協定是什麼
  3. 動手實作一個簡單的協定解析指令行工具
socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

先從持久化說起,雖然一提到 Redis,首先想到的就是緩存,但是 Redis 不僅僅是緩存這麼簡單,它的定位是記憶體型資料庫,可以存儲多種類型的資料結構,還可以當做簡單消息隊列使用。既然是資料庫,持久化功能是必不可少的。

Redis 的兩種持久化方式

Redis 提供了兩種持久化方式,一種是 RDB 方式,另外一種是 AOF 方式,AOF 是目前比較流行的持久化方案。

RDB 方式

RDB持久化是通過快照的方式,在指定的時間間隔内将記憶體中的資料集快照寫入磁盤。它以一種緊湊壓縮的二進制檔案的形式出現。可以将快照複制到其他伺服器以建立相同資料的伺服器副本,或者在重新開機伺服器後恢複資料。RDB是Redis預設的持久化方式,也是早期版本的必須方案。

RDB 由下面幾個參數控制。

# 設定 dump 的檔案名dbfilename dump.rdb# 持久化檔案的存儲目錄dir ./# 900秒内,如果至少有1個key發生變化,就會自動觸發bgsave指令建立快照save 900 1# 300秒内,如果至少有10個key發生變化,就會自動觸發bgsave指令建立快照save 300 10# 60秒内,如果至少有10000個key發生變化,就會自動觸發bgsave指令建立快照save 60 10000複制代碼
           

持久化流程

上面說到了配置檔案中的幾個觸發持久化的機制,比如 900 秒、300秒、60秒,當然也可以手動執行指令 save或bgsave進行觸發。bgsave是非阻塞版本,通過 fork 出子程序的方式來進行快照生成,而 save會阻塞主程序,不建議使用。

1、首先 bgsave指令觸發;

2、父程序 fork 出一個子程序,這一步是比較重量級的操作,也是 RDB 方式性能不及 AOF 的一個重要原因;

3、父程序 fork 出子程序後就可以正常的相應用戶端發來的其他指令了;

4、子程序開始進行持久化工作,對現有資料進行完整的快照存儲;

5、子程序完成操作後,通知父程序;

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

RDB的優點:

  • RDB是一個緊湊壓縮的二進制檔案,代表Redis在某個時間點上的資料 快照。非常适用于備份,全量複制等場景。比如每6小時執行bgsave備份, 并把RDB檔案拷貝到遠端機器或者檔案系統中(如hdfs),用于災難恢複。
  • Redis加載RDB恢複資料遠遠快于AOF的方式。

RDB的缺點:

  • RDB方式資料沒辦法做到實時持久化/秒級持久化。因為bgsave每次運 行都要執行fork操作建立子程序,屬于重量級操作,頻繁執行成本過高。
  • RDB檔案使用特定二進制格式儲存,Redis版本演進過程中有多個格式 的RDB版本,存在老版本Redis服務無法相容新版RDB格式的問題。

AOF 方式

AOF 由下面幾個參數控制。

# appendonly參數開啟AOF持久化appendonly yes# AOF持久化的檔案名,預設是appendonly.aofappendfilename "appendonly.aof"# AOF檔案的儲存位置和RDB檔案的位置相同,都是通過dir參數設定的dir ./# 同步政策# appendfsync alwaysappendfsync everysec# appendfsync no# aof重寫期間是否同步no-appendfsync-on-rewrite no# 重寫觸發配置auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb# 加載aof出錯如何處理aof-load-truncated yes# 檔案重寫政策aof-rewrite-incremental-fsync yes複制代碼
           

針對RDB不适合實時持久化的問題,Redis提供了AOF 持久化方式來解決,AOF 也是目前最流程的持久化方式。

AOF(append only file),以獨立日志的方式記錄每次寫指令, 重新開機時再重新執行AOF檔案中的指令達到恢複資料的目的。

1、所有的寫入指令會追加到aof_buf(緩沖區)中;

2、AOF緩沖區根據對應的政策向硬碟做同步操作;

3、随着AOF檔案越來越大,需要定期對AOF檔案進行重寫,達到壓縮的目的;

4、當Redis伺服器重新開機時,可以加載AOF檔案進行資料恢複;

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

AOF 檔案裡存的是什麼

我在本地的測試 redis 環境中随便刷了幾條指令,然後打開 appendonly.aof 檔案檢視,發現裡面的内容像下面這樣子。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

RESP 協定

Redis用戶端與服務端通信,使用 RESP 協定通信,該協定是專門為 Redis 設計的通信協定,但也可以用于其它用戶端-伺服器通信的場景。

RESP 協定有如下幾個特點:

  • 實作簡單;
  • 快速解析;
  • 可閱讀;

用戶端發送指令給服務端,服務端拿到指令後進行解析,然後執行對應的邏輯,之後傳回給用戶端,當然了,這一發一回複都是用的 RESP 協定特點的格式。

一般情況下我們會使用 redis-cli或者一些用戶端工具連接配接 Redis 服務端。

./redis-cli複制代碼
           

然後整個互動過程的指令發送和傳回結果像下面這樣,綠色部分為發送的指令,紅色部分為傳回的結果。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

這就是我們再熟悉不過的部分了。但是,這并不能看出 RESP 協定的真實面貌。

用 telnet 試試

RESP 是基于 TCP 協定實作的,是以除了用各種用戶端工具以及 Redis 提供的 redis-cli工具,還可以用 telnet 檢視,用 telnet 就可以看出 RESP 傳回的原始資料格式了。

我本地的 Redis 是用的預設 6379 端口,并且沒有設定 requirepass ,我們來試一下用 telnet 連接配接。

telnet 127.0.0.1 6379複制代碼
           

然後執行與前面相同的幾條指令,發送和傳回的結果如下,綠色部分為發送的指令,紅色為傳回的結果。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

怎麼樣,有些指令的傳回還好,但是像get str:hello這條,傳回的結果除了 world值本身,上面還多了一行 $5,是不是有點迷糊了。

協定規則

請求指令

一條用戶端發往伺服器的指令的規則如下:

* CR LF$ CR LF CR LF...$ CR LF CR LF複制代碼
           

RESP 用作為分隔符,會表明此條指令的具體參數個數,在指令上看來,空格分隔的都表示一個參數,例如 set str:hello world 這條指令就是3個參數,會表明每個參數的字元數和具體内容。

用這條指令舉例,對應到 RESP 協定規則上就會變成下面這個樣子:

*3$3set$9str:hello$5world複制代碼
           
socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

服務端回複

Redis 指令會傳回多種不同類型的回複。

通過檢查伺服器發回資料的第一個位元組, 可以确定這個回複是什麼類型:

1、狀态回複(status reply)的第一個位元組是 "+"

比如 ping指令的回複,+PONG

2、錯誤回複(error reply)的第一個位元組是 "-"

比如輸入一個 redis 中不存在的指令,或者給某些指令設定錯誤的參數,例如輸入 auth,auth 指令後面需要有一個密碼參數的,如果不輸入就會傳回錯誤回複類型。

-ERR wrong number of arguments for 'auth' command

3、整數回複(integer reply)的第一個位元組是 ":"

例如 INCR、DECR 自增自減指令,傳回的結果是這樣的 :2

4、批量回複(bulk reply)的第一個位元組是 "$"

例如對 string 類型執行 get 操作,$5world,$後面的數字 5 表示傳回的結果有 5 個字元,後面是傳回結果的實際内容。

5、多條批量回複(multi bulk reply)的第一個位元組是 "*"

例如 LRANGE key start stop或者 hgetall等傳回多條結果的指令,比如 lrange指令傳回的結果:

*2$6news-2$6news-1複制代碼
           

多條批量回複和前面說的用戶端發送指令的格式是一緻的。

實作一個簡單的 Redis 互動工具

了解了 Redis 的協定規則,我們就可以自己寫一個簡單的用戶端了。當然,通過官網我們可以看到已經有各種語言,而且每種語言有不止一個用戶端工具了。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

比如 Java 語言的用戶端就有這麼多種,其中 Jedis 應該是用的最多了,既然已經有這麼好用的輪子了,當然沒必要重複造輪子,主要還是為了加深印象。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

RESP 協定基于 TCP 協定,可以使用 socket 方式進行連接配接。

public Socket createSocket() throws IOException {  Socket socket = null;  try {    socket = new Socket();    socket.setReuseAddress(true);    socket.setKeepAlive(true);    socket.setTcpNoDelay(true);    socket.setSoLinger(true, 0);    socket.connect(new InetSocketAddress(host, port), DEFAULT_TIMEOUT);    socket.setSoTimeout(DEFAULT_TIMEOUT);    outputStream = socket.getOutputStream();    inputStream = socket.getInputStream();    return socket;  } catch (Exception ex) {    if (socket != null) {      socket.close();    }    throw ex;  }}複制代碼
           

然後剩下的就是對傳回的結果進行字元串的解析了,我做的工具就到簡陋的到這一步了,下面是一些簡單指令的傳回輸出。

socket timeout是什麼引起的_Redis RESP 協定與 AOF 持久化有什麼關系?Redis 的兩種持久化方式RDB 方式持久化流程RDB的優點:RDB的缺點:AOF 方式AOF 檔案裡存的是什麼RESP 協定用 telnet 試試協定規則請求指令服務端回複實作一個簡單的 Redis 互動工具

代碼已放到 github 上,有興趣的可以 clone 下來看一下。

https://github.com/huzhicheng/medis

作者:古時的風筝

連結:https://juejin.cn/post/6898492095271534606