You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala/Spark中读取无精度损失的Decimal(38,16)数据?

解决Spark读取Parquet时Decimal(38,16)转double丢失精度的问题

这个问题的核心原因是Parquet默认将Decimal类型映射为double,而double类型的有效精度仅为15-17位,无法完整保留Decimal(38,16)的16位小数精度,最终导致数值被四舍五入。要恢复原始数据精度,我们需要强制Spark读取时使用DecimalType而非默认的double类型。

解决方案一:读取时指定自定义Schema

直接在读取Parquet文件时显式定义包含DecimalType(38,16)的Schema,让Spark按照指定类型解析数据:

import org.apache.spark.sql.types.{StructType, StructField, IntegerType, DecimalType}

// 定义与原始RDBMS数据匹配的Schema
val customSchema = StructType(Array(
  StructField("id", IntegerType, nullable = true),
  StructField("cnt", DecimalType(38, 16), nullable = true)
))

// 使用自定义Schema读取Parquet文件
val df = spark.read.schema(customSchema).parquet("<your-parquet-path>")

验证效果:

// 检查Schema是否正确映射
df.printSchema()
// 预期输出:|-- cnt: decimal(38,16) (nullable = true)

// 查看数据是否保留完整16位小数精度
df.select("cnt").show(false)

解决方案二:写入Parquet时确保Decimal类型正确存储

如果Parquet文件是你自己生成的,建议在写入阶段就配置Spark以正确格式存储Decimal类型,从根源避免后续读取的类型转换问题:

// 假设源DataFrame中cnt字段已为DecimalType(38,16)
df.write
  .option("spark.sql.parquet.decimalConversion", "true") // 确保Decimal以二进制格式存储
  .parquet("<output-parquet-path>")

关键说明

  • DecimalType(38,16)完全匹配你原始RDBMS中的数据类型,它通过固定长度字节存储数值,不会出现double类型的精度损失。
  • Spark 2.0及以上版本对Decimal类型的Parquet读写支持已非常成熟,确保使用的Spark版本不低于2.0。
  • double的精度限制是硬件层面的特性,无法通过调整配置让它保留16位小数的完整精度,必须切换到DecimalType才能解决问题。

内容的提问来源于stack exchange,提问作者Vijay_Shinde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 03:53:07