春招面試複習:訊息佇列(七)-資料壓縮
1 資料壓縮意義
Kafka使用資料壓縮,最高可提升約幾十倍吞吐量。資料壓縮不僅可節省儲存空間,還可用於提升網路傳輸效能。這種使用壓縮提升系統性能的方法,不僅在MQ使用,日常開發也可。比如傳輸大量資料或要在磁碟、資料庫中儲存較大資料,這些情況下,都可考慮使用資料壓縮提升效能,還能節省網路頻寬和儲存空間。
2 資料壓縮適用場景
程序間通過網路傳輸資料是不是需要壓縮?
- 不壓縮直接傳輸耗時: 傳輸未壓縮資料的耗時
- 使用資料壓縮耗時: 壓縮耗時 + 傳輸壓縮資料耗時 + 解壓耗時
壓縮快還是不壓縮快呢?
不好說。影響因素多,比如資料壓縮率、網路頻寬、收發兩端伺服器的繁忙度。
壓縮和解壓的操作都是計算密集型操作。如果你的應用處理業務邏輯就需耗費大量CPU資源,就不太適合再壓縮解壓。
若系統瓶頸是磁碟IO效能,CPU資源又閒,這就非常適合在把資料寫入磁碟前先壓縮。
但若系統讀寫比嚴重失調,要考慮每讀次資料就解壓次是不是划算。
壓縮的本質是資源置換,即時間換空間或CPU資源換儲存資源。
就像木桶理論,每個系統都有效能瓶頸資源,可能磁碟IO、網路頻寬、CPU。
若使用壓縮,能用長板來換些短板,那總體上就能提升效能,這就划算。
若用壓縮後,短板更短,就不划算。
只有通過效能測試,確認資料壓縮可提升系統性能,就需選擇合適壓縮演算法了。
3 壓縮演算法抉擇
壓縮演算法可以分為
- 有失真壓縮
主要用來壓縮音視訊,它壓縮之後會丟失資訊 - 無失真壓縮
這討論都是無失真壓縮,即資料經過壓縮和解壓過程後,與壓縮前相比100%相同。
資料為什麼可被壓縮呢?各種各樣壓縮演算法又怎麼壓縮資料的?
舉個極端例子:
00000000000000000000
人肉壓縮下:
20個0
20個字元就被壓縮成4字元,且可無損還原。
常用壓縮演算法:ZIP,GZIP,SNAPPY,LZ4,XZ。
選擇時考慮資料的壓縮率和壓縮耗時。一般壓縮率越高,壓縮耗時越高。
- 若要高效能,可選擇壓縮速度快的LZ4
- 要更高壓縮比,考慮GZIP或壓縮率更高的XZ
壓縮樣本對壓縮速度和壓縮比的影響也較大,同樣大小的一段數字和一段新聞的文字,即使用相同壓縮演算法,壓縮率和壓縮時間差異也較大。
如果感興趣,可學習最經典的壓縮演算法:哈夫曼編碼。
4 壓縮分段選型
大部分壓縮演算法區別主要是,對資料進行編碼的演算法,壓縮的流程和壓縮包的結構大致一樣。
而在壓縮過程中,你最需要了解的就是如何選擇合適的壓縮分段。
壓縮時,給定的被壓縮資料它必須有確定長度,或是有頭有尾的,不能是個無限資料流,若要對流資料壓縮,必須把流資料劃分成多幀,一幀幀分段壓縮。
主要因為壓縮演算法在壓縮前,一般都需對被壓縮資料從頭到尾掃描:確定如何對資料劃分和編碼。
- 一般原則:
重複次數多、佔用空間大的內容,使用盡量短的編碼,這樣壓縮率會更高。
被壓縮資料長度越大,重位元速率更高,壓縮比也越高。
比如這篇文章,可能出現幾十次“壓縮”,將整篇文章壓縮,這詞重複率幾十次,但按照每個自然段來壓縮,每段中這詞重複率只有二三次。顯然全文壓縮壓縮率高於分段壓縮。
分段並非越大越好,超過一定長度後,再增加長度對壓縮率貢獻不大了。
過大的分段長度在解壓時,還有更多解壓浪費。
比如,一個1MB大小的壓縮檔案,即使你只是需要讀其中很短的幾個位元組,也不得不把整個檔案全部解壓縮,造成很大的解壓浪費。
所以要根據業務,選擇合適壓縮分段,在壓縮率、壓縮速度、解壓浪費間找到平衡點。
確定資料劃分和壓縮演算法後,就可壓縮了,壓縮過程就是用編碼替換原始資料。
壓縮後的壓縮包是由這編碼字典和用編碼替換後的資料組成。
這就是資料壓縮過程。解壓時,先讀取編碼字典,然後按字典把壓縮編碼還原成原始資料即可。
5 Kafka 訊息壓縮流程
首先可以配置Kafka是否開啟壓縮,支援配置使用哪種壓縮演算法。
因為不同場景是否需要開啟壓縮,選擇哪種壓縮演算法都不能一概而論。
所以Kafka將選擇權交給使用者。
在開啟壓縮時,Kafka選擇一批訊息一起壓縮,每一個批訊息就是一個壓縮分段。使用者也可以通過引數來控制每批訊息的大小。
在Kafka中,生產者生成一個批訊息發給服務端,在服務端中是不會拆分批訊息的。那按批壓縮,意味在服務端也不用對這批訊息進行解壓,可整批直接儲存,然後整批發給消費者。最後,批訊息由消費者解壓。
在服務端不用解壓,就不會耗費服務端CPU,同時還能獲得壓縮後,佔用傳輸頻寬小,佔用儲存空間小。
使用Kafka時,如果生產者和消費者的CPU不是特別吃緊,開啟壓縮後,可節省網路頻寬和服務端的儲存空間,提升總體的吞吐量,一般都是個不錯的選擇。
Kafka在生產者上,對每批訊息進行壓縮,批訊息在服務端不解壓,消費者在收到訊息之後再進行解壓。Kafka的壓縮和解壓都是在客戶端完成的。
6 RocketMQ 訊息壓縮原始碼
- 預設壓縮大於4K的訊息(可配置)
壓縮演算法是zip,預設級別5(可配置)
也是客戶端做解壓縮工作,服務端只儲存。
private boolean tryToCompressMessage(final Message msg) {
if (msg instanceof MessageBatch) {
// 當前不支援批量訊息的壓縮
return false;
}
byte[] body = msg.getBody();
if (body != null) {
if (body.length >= this.defaultMQProducer.getCompressMsgBodyOverHowmuch()) {
try {
byte[] data = UtilAll.compress(body, zipCompressLevel);
if (data != null) {
msg.setBody(data);
return true;
}
} catch (IOException e) {
log.error("tryToCompressMessage exception", e);
log.warn(msg.toString());
}
}
}
return false;
}
DefaultLitePullConsumerImpl#pullSyncImpl會呼叫PullAPIWrapper#processPullResult,會為壓縮訊息進行解壓縮。
RocketMQ的壓縮機制也是Producer壓縮,Broker傳輸,Consumer解壓縮,
不同的是kaffka的壓縮是基於一批批訊息,對於CPU空閒較多的場景下會有更大的吞吐提升。
7 總結
一直以來,常見演算法都是空間換時間,但在MQ和一些IO密集型應用中還有CPU計資源換網路頻寬/磁碟IO。
資料壓縮本質是CPU資源換儲存資源,或用壓縮解壓的時間換取儲存的空間。
在選擇壓縮演算法的時候,需綜合考慮壓縮時間和壓縮率兩個因素,被壓縮資料的內容也是影響壓縮時間和壓縮率的重要因素。
必要時可先用業務資料做一個壓縮測試,這樣有助於選擇最合適的壓縮演算法。
另外影響壓縮率的重要因素是壓縮分段,需根據業務情況選擇一個合適分段,在保證壓縮率前提下,儘量減少解壓浪費。