常用序列化方案比較
阿新 • • 發佈:2020-05-07
當你感到悲哀痛苦時,最好是去學些什麼東西。學習會使你永遠立於不敗之地。
##使用場景
在rdd的每一個分割槽上,執行迭代操作,在每一次的迭代操作中,需要先訪問redis快取,並獲取key對應的value,若value存在則對value進行反序列化操作,否則從db裡查詢並序列化存放到redis快取中。
虛擬碼如下:
```java
rdd.mapPartitions {
iter.map{
val value:Option[Array[Byte]] = getValueByKey(key)
value match {
case Some(bs) => {
deserilialize(bs);
other operations...
}
case None => {
val newVals = fetchFromDbByKey(key);
other operations ....;
val newBs = serialize(key);
storeRedis(newBs)
}
}
}
}
```
從這段位程式碼可以看出,影響效率的有序列化和反序列化的效率以及序列化後byte陣列的位元組大小(可以影響網路IO)。
##測試指標
主要從四方面來考慮,序列化總時間,反序列化總時間,序列化後平均位元組大小,cpu使用率峰值。
其中,使用jconsole監控其cpu使用率峰值。
注意,cpu使用率的峰值只是一個參考,因為在資料量增大時,在序列化和反序列化過程中,伴隨著gc,也會消耗cpu資源。
##測試資料
https://github.com/Devskiller/jfairy.git 是用來生成測試資料的,可以支援多國語言,由於其本身不是用來測試序列化的資料集,其生成的物件也不是完全可序列化的,欄位也多,也包含了一下二級欄位,故簡化之。
簡化之後的資料結構如下:
![](https://img2020.cnblogs.com/blog/798311/202005/798311-20200507105606990-1325960560.png)
生成測試資料程式碼如下:
```java
package com.wisers;
import com.devskiller.jfairy.Fairy;
import com.devskiller.jfairy.producer.person.Person;
import java.util.ArrayList;
import java.util.Locale;
public class DataGenerator {
public static ArrayList generatePeople(int sampleNum) {
Fairy chineseFairy = Fairy.create(Locale.CHINESE);
Fairy englishFairy = Fairy.create(Locale.ENGLISH);
ArrayList people = new ArrayList(sampleNum);
for (int i = 0; i < sampleNum; i++) {
Person person = Math.random() >= 0.5 ? chineseFairy.person() : englishFairy.person();
people.add(People.createBy(person));
}
return people;
}
}
```
## 測試環境
cpu|memory|disk|
:-:|:-:|:-:|
1顆cpu,8核|32g|可用 4.9g|
## 測試方案
儘可能地重用流物件,避免新建立物件對結果的影響
儘可能地避免gc對序列化和反序列化的影響,每次序列化反序列化之後都手動gc,並且測試資料集不宜過大,目前設定最大為1kw,儘可能避免gc對結果的影響
在測試操作過程中,避免列印以及磁碟讀取存放等io操作,序列化後的資料直接放在記憶體,供反序列化使用。
## 測試結果
下面開始對比業內的比較認可的幾種序列化方案。
### 序列化方案對比結果如下:
#### 不同資料集下各個序列化方案對比
對比結果如下:
times|type|serialize time(ms)|de-serialize time(ms)|avg size|cpu佔用率(峰值)|remark
:-:|:-:|:-:|:-:|:-:|:-:|:-:|
10000|jdk|73|211|531|2.1|
10000|kryo|71|48|144|1.6|
10000|msgpack|39|66|119|1.1|
10000|fst|53|49|151|2.4|
10000|hession|53|113|349|2.7|
10000|protoStuff|24|21|131|0.6|
100000|jdk|367|1387|531|5.9|
100000|kryo|116|88|144|1|
100000|msgpack|129|350|119|2|
100000|fst|101|104|151|0.9|
100000|hession|211|374|349|2.4|
100000|protoStuff|63|70|131|0.5|
500000|jdk|1746|7412|531|27.2|
500000|kryo|437|423|144|2.4|
msgpack|414|1510|119|6.6|
500000|fst|412|538|151|1.7|
500000|hession|890|1768|349|6.4|
500000|protoStuff|263|333|131|1|
1000000|jdk|3479|14130|531|37.2|
1000000|kryo|878|844|144|1.3|
1000000|msgpack|864|3036|119|13.6|
1000000|fst|827|993|151|3.6|
1000000|hession|1688|3522|349|12.8|
1000000|protoStuff|513|666|131|2.5|
2500000|jdk|15558|35460|531|70.2|
2500000|kryo|2151|2281|144|11.7|
2500000|msgpack|2185|8170|119|21.7|
2500000|fst|2014|2607|151|12.6|
2500000|hession|4169|9047|349|15.1|
2500000|protoStuff|1238|1777|131|8.1|
5000000|jdk|41637|332540|531|100|均值大概在80%
5000000|kryo|4255|4774|144|19.8|
5000000|msgpack|4603|16362|119|41.8|
5000000|fst|3985|5399|151|18.2|
5000000|hession|8716|18436|349|27|
5000000|protoStuff|2563|3770|131|20.1|
###結果分析
####時間角度分析
1. 由於jdk本身在序列化和反序列化時,ObjectOutputStream、ByteArrayInputStream以及ObjectInputStream不能複用,序列化時間會包含部分物件建立的時間,這會增加gc時間
2. msgpack在序列化過程中,MessageUnpacker不能複用,並且需要手動建立類,反序列化時間會比序列化時間長
3. hession在反序列化過程中,流不能複用,反序列化時間會比較長
4. kryo、fst、protoStuff 在序列化和反序列化的過程中,很好的使用了流複用,序列化效果比較好
5. 整體來看,同一種序列化方案,反序列化消耗時間會比序列化消耗時間長,多了物件的建立以及欄位對映時間
6. 數量級達到百萬級後,使用protoStuff、fst以及kryo效果比較好,整體時間消耗依次為 protoStuff < kryo < fst
####序列化後位元組大小分析
1. 整體大小如下:msgpack < protoStuff < kryo < fst < hession < jdk
2. msgpack需要手動序列化欄位,並不包含類資訊,故序列化後的結果比較小
###最大堆記憶體對序列化時間的影響
預設最大堆記憶體約為7.7g,由於序列化後的資料被存放在記憶體,不能被gc回收,資料量達到1kw 時,出現記憶體溢位異常,故調大堆記憶體,對比在1kw 資料量時最大堆記憶體對序列化和反序列化的影響。
Xmx|type|serialize time(ms)|de-serialize time(ms)|avg size|cpu佔用率(峰值)
:-:|:-:|:-:|:-:|:-:|:-:
20g|jdk|36283|130879|531|28.1
20g|kryo|8759|9288|144|20.2
20g|msgpack|9151|29653|119|38.2
20g|fst|8274|10719|151|19.5
20g|hession|17853|38109|349|21.2
20g|protoStuff|5218|7767|131|22.5
30g|jdk|37496|128481|531|22.5
30g|kryo|8994|9200|144|14.3
30g|msgpack|9035|29973|119|22.5
30g|fst|8544|10088|151|15.5
30g|hession|17366|36742|349|22.7
30g|protoStuff|5217|7289|131|20.1
#### 結果分析
當資料量在 1kw時,增大最大堆記憶體,對不能使用流複用技術的 jdk、hession影響比較大,影響為幾秒,但整體時間影響並不大,對於其他序列化方案影響在毫秒級,幾乎不影響。
## 分析總結
綜合考慮序列化和反序列化時間以及序列化後的大小來看,優先使用 protoStuff 、 kryo 以及 fst 。