因为
Encoders
为了表现牺牲共性。这个想法并不新鲜。为什么Kryo比Java序列化更快?出于同样的原因。考虑一下这份成绩单:
scala> val spark = SparkSession.builder.config("spark.serializer", "org.apache.spark.serializer.JavaSerializer").getOrCreate()
spark: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@1ed28f57
scala> val map = Map[String, Int]("foo" -> 1).withDefaultValue(0)
map: scala.collection.immutable.Map[String,Int] = Map(foo -> 1)
scala> map("bar")
res1: Int = 0
scala> val mapSerDe = spark.sparkContext.parallelize(Seq(map)).first
mapSerDe: scala.collection.immutable.Map[String,Int] = Map(foo -> 1)
scala> mapSerDe("bar")
res2: Int = 0
与…相比
scala> val spark = SparkSession.builder.config("spark.serializer", "org.apache.spark.serializer.KryoSerializer").getOrCreate()
spark: org.apache.spark.sql.SparkSession = org.apache.spark.sql.SparkSession@5cef3456
scala> val map = Map[String, Int]("foo" -> 1).withDefaultValue(0)
map: scala.collection.immutable.Map[String,Int] = Map(foo -> 1)
scala> map("bar")
res7: Int = 0
scala> val mapSerDe = spark.sparkContext.parallelize(Seq(map)).first
mapSerDe: scala.collection.immutable.Map[String,Int] = Map(foo -> 1)
scala> mapSerDe("bar")
java.util.NoSuchElementException: key not found: bar
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:59)
at scala.collection.MapLike$class.apply(MapLike.scala:141)
at scala.collection.AbstractMap.apply(Map.scala:59)
... 48 elided
(我找不到确切的帖子,但这个例子的想法来自开发者列表)。
正如您所见,Kryo虽然速度更快,但并不能处理所有可能的情况。它关注最常见的问题,而且做得很好。
火花
编码器
做同样的事,但更不一般。如果您只支持16种左右的类型,并且不关心互操作性(必须与真正的序列化库一起),那么您有很多机会进行优化。
了解模式,如
himanshuIIITian
是另一个因素。
为什么这很重要?因为具有定义良好的形状,所以可以优化序列化和存储。如果您知道您的数据是结构化的,那么您可以切换维度,而不是使用存储和访问成本高昂的异构行,您可以使用列式存储。
一旦数据存储在列中,您将打开一组全新的优化机会:
-
固定大小字段上的数据访问速度非常快,因为您可以直接访问特定地址(还记得堆外/本机内存/钨丝带来的所有兴奋吗?)。
-
您可以使用多种压缩和编码技术来最小化数据的大小。
这种想法也不是什么新鲜事。柱状数据库、存储格式(如Parquet)或为分析设计的现代序列化格式(如Arrow)使用了相同的想法,并且往往将这些想法推得更远(零拷贝数据共享)。