【spark】使用kryo序列化和壓縮,減少數據緩存和傳輸的大小

 本文其實主要是想說說spark的kryo機制和壓縮!

首先spark官網對於kryo的描述:http://spark.apache.org/docs/latest/tuning.html#data-serialization

官網相關參數:http://spark.apache.org/docs/latest/configuration.html#compression-and-serialization

 

大概是說,kryo很強,建議使用,spark2.x的很多地方已經自動幫你用上了kryo!

1、kryo用在哪些地方

(1)、算子函數中使用到的外部變量(例如上一篇提到的隨機抽取數據的map)

(2)、持久化RDD時進行序列化,StorageLevel.MEMORY_ONLY_SER 

(3)、shuffle階段(還記得我上一篇文章講的shuffle中用的unsafe嗎?)

所以我會將數據緩存,並使用MEMORY_ONLY_SER緩存策略來實際測試下效果!

 

2、使用序列化,肯定會讓佔用的內存變小

(這裏我用的只是java的序列化方式)

persist(StorageLevel.MEMORY_ONLY());

persist(StorageLevel.MEMORY_ONLY_SER());

可以看到,使用序列化的緩存之後,佔用的內存明顯變小了!

 

3、使用java序列化和kryo序列化的區別

(代碼拉到最下面看,先看結論)

(1)、使用java序列化

 

 

(2)、使用kryo序列化,但不註冊自定義的類型

(3)、使用kryo序列化,並且註冊自定義的類型

可以看到對於數據源頭的RDD和Dataset都沒有影響,只對於中間類型的RDD有影響

kryo序列化如果不註冊自定義類型,會導致,反而比java序列化的效果還差!

(4)、RDD壓縮

spark.rdd.compress false Whether to compress serialized RDD partitions (e.g. forStorageLevel.MEMORY_ONLY_SER in Java and Scala or StorageLevel.MEMORY_ONLY in Python). Can save substantial space at the cost of some extra CPU time. Compression will use spark.io.compression.codec. 0.6.0

可以看到,開啓了壓縮,數據量進一步變小,而且是對rdd和dataset都有效果

(5)、提高壓縮率

spark.io.compression.lz4.blockSize 32k Block size used in LZ4 compression, in the case when LZ4 compression codec is used. Lowering this block size will also lower shuffle memory usage when LZ4 is used. Default unit is bytes, unless otherwise specified. 1.4.0

spark的壓縮,默認是lz4,一個32k的塊,會被壓縮一下,如果我把這個參數改成512k,那麼,可以進一步提高壓縮率,但是對於內存和cpu就是更多的壓力

(6)、kryo模式相關的調整

spark.kryo.unsafe false Whether to use unsafe based Kryo serializer. Can be substantially faster by using Unsafe Based IO. 2.1.0
spark.kryoserializer.buffer.max 64m Maximum allowable size of Kryo serialization buffer, in MiB unless otherwise specified. This must be larger than any object you attempt to serialize and must be less than 2048m. Increase this if you get a "buffer limit exceeded" exception inside Kryo. 1.4.0
spark.kryoserializer.buffer 64k Initial size of Kryo's serialization buffer, in KiB unless otherwise specified. Note that there will be one buffer per core on each worker. This buffer will grow up tospark.kryoserializer.buffer.max if needed.

1.4.0

 這些配置參數,請具體看spark的描述,和百度一些文章,但是請大家始終記住一點就好,此消彼長,減少了磁盤io,加大緩衝區,勢必會加大spark的內存壓力,就看真實情況中短板是什麼,然後做出取捨了!

 

4、相關代碼如下

public class RDDInfoTest extends SparkAnalyzer{

    public static void main(String[] args) throws InterruptedException {
        //構建sparksession
        SparkSession sparkSession = SparkSession
                .builder()
                .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
                //.config("spark.serializer","org.apache.spark.serializer.JavaSerializer")
                //.config("spark.kryo.registrator", MyRegistrator.class.getName())
                //.config("spark.rdd.compress", "true")
                //.config("spark.io.compression.lz4.blockSize","512k")
                .master("local[*]")
                .appName("RDDInfoTest")
                .getOrCreate();

        //2、讀取數據文件形成RDD
        String path = "datas/page_views.data";
        RDD<String> stringRDD = sparkSession.sparkContext().textFile(path, 2);
        stringRDD.persist(StorageLevel.MEMORY_ONLY_SER());
        stringRDD.count();

        //3、將每條文件添加了字符串1,並且把String類型,轉換成自己的SelfUrl類型
        JavaRDD<String> stringJavaRDD = stringRDD.toJavaRDD();
        JavaRDD<SelfUrl> mapRDD = stringJavaRDD.map(new Function<String, SelfUrl>() {
            @Override
            public SelfUrl call(String v1) throws Exception {

                return new SelfUrl(v1,"1");
            }
        });
        mapRDD.persist(StorageLevel.MEMORY_ONLY_SER());
        mapRDD.count();

        //4、把剛剛的SelfUrl類型的Rdd轉換爲Ds,並且類型轉爲Row
        Dataset<Row> dataFrame = sparkSession.createDataFrame(mapRDD, SelfUrl.class);
        dataFrame.persist(StorageLevel.MEMORY_ONLY_SER());
        dataFrame.count();


        //5、再把row類型的爲Ds,類型轉爲SelfUrl
        Dataset<SelfUrl> map = dataFrame.map(new MapFunction<Row, SelfUrl>() {
            @Override
            public SelfUrl call(Row value) throws Exception {

                return new SelfUrl(value.getString(0), value.getString(1));
            }
        }, Encoders.bean(SelfUrl.class));
        map.persist(StorageLevel.MEMORY_ONLY_SER());
        map.count();


        Thread.sleep(100000L);

    }

}

 

import java.io.Serializable;

/**
 * Created with IntelliJ IDEA
 * Description:
 * User: lsr
 * Date: 2020/6/29
 * Time: 15:47
 */
public class SelfUrl implements Serializable {
    private String url;
    private String count;

    public SelfUrl() {
    }

    public SelfUrl(String url, String count) {
        this.url = url;
        this.count = count;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getCount() {
        return count;
    }

    public void setCount(String count) {
        this.count = count;
    }
}
public class MyRegistrator implements KryoRegistrator {

    @Override
    public void registerClasses(Kryo kryo) {
        kryo.register(SelfUrl.class);
    }
}

 

5、總結

1、使用kryo肯定是有益的,但是其中用到的自定義的數據類型,一定要記得註冊下!

2、序列化和壓縮,肯定會帶來額外的cpu消耗,會導致時間增長,請務必確認,運行任務的時候,相應的執行機器的網絡io和cpu情況,如果網絡io壓力不大,cpu壓力大,其實就不需要做太多壓縮序列化等額外操作,反之。

 

菜雞一隻!如果有說錯的地方,請大家批評指正!

 

《Spark內核設計的藝術架構設計與實現》這本書看完了,emmm,要說全看懂嘛,肯定是瞎說的,spark裏面涉及的東西太多了,不是我這樣一個小菜雞就能全搞懂的,但是重點(我自認爲重點)的地方我還是認真看了看,並寫了一些筆記,也寫了博客,算是對使用了這麼長時間的spark一個交代吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章