1. 程式人生 > >常用序列化方案比較

常用序列化方案比較

當你感到悲哀痛苦時,最好是去學些什麼東西。學習會使你永遠立於不敗之地。 ##使用場景 在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 。