本文档主要介绍存储类型与 SparkSQL 实例类型映射,以及 SequoiaDB 存储引擎向 SparkSQL 实例类型转换的兼容性。
存储引擎类型 | SparkSQL 实例类型 | SQL 实例类型 |
---|---|---|
int32 | IntegerType | int |
int64 | LongType | bigint |
double | DoubleType | double |
decimal | DecimalType | decimal |
string | StringType | string |
ObjectId | StringType | string |
boolean | BooleanType | boolean |
date | DateType | date |
timestamp | TimestampType | timestamp |
binary | BinaryType | binary |
null | NullType | null |
BSON(嵌套对象) | StructType | struct<field:type,…> |
array | ArrayType | array<type> |
Note:
关于 SparkSQL 日期、Timestamp 类型取值范围:
- DATE 类型取值范围为:0000-01-01 至 9999-12-31
- TIMESTAMP 类型取值范围为:0000-01-01 00:00:00.000000 至 9999-12-31 12:59:59.999999
注意:SparkSQL 中 TIMESTAMP 类型取值范围大于 SequoiaDB 中 timestamp 类型取值范围(1902-01-01 00:00:00.000000 至 2037-12-31 23:59:59.999999),如果写入数据超出 SequoiaDB timestamp 类型取值范围则会发生溢出导致数据不准确。
Y 表示兼容,N 表示不兼容
ByteType | ShortType | IntegerType | LongType | FloatType | DoubleType | DecimalType | BooleanType |
---|---|---|---|---|---|---|---|
int32 | Y | Y | Y | Y | N | N | Y |
int64 | Y | Y | Y | Y | N | N | Y |
double | Y | N | N | Y | N | N | Y |
decimal | Y | Y | Y | Y | N | N | Y |
string | Y | Y | Y | Y | N | N | Y |
ObjectId | Y | N | N | Y | N | N | Y |
boolean | Y | N | N | Y | N | N | Y |
date | Y | Y | Y | Y | N | N | Y |
timestamp | Y | Y | Y | Y | N | N | Y |
binary | Y | N | N | Y | N | N | Y |
null | Y | Y | Y | Y | Y | Y | Y |
BSON | Y | N | N | N | N | Y | Y |
array | Y | N | N | N | Y | N | Y |
Note:
- 不支持 SparkSQL 的 CalendarIntervalType 类型;
- null 转换为任意类型仍为 null;
- 不兼容类型转换时变为目标类型的零值;
- date 和 timestamp 与数值类型转换时取其毫秒值;
- string 如果是数值的字符串类型,则可转为对应的数值时,否则转换为 null;
- boolean 值转为数值类型时,true 为 1,false 为 0;
- 数值类型之间转换可能会溢出或损失精度。
Spark 2.x 使用 java.sql.Date/java.sql.Timestamp 类型处理时间数据,但不幸的是这两种类型与其父类 java.util.Date 一样,在国际化的处理上不够完善。这会导致部分时间数据在 Spark 与 SequoiaDB 中不一致,如上海时区(东八区) 1900 年之前的时间数据。
Spark 3.x 引入了 Java8 的新日期类型 java.time.LocalDate/java.time.Instant 处理时间数据,以解决上述时间类型不准确问题。
Note
Spark 3.x 新增参数 spark.sql.datetime.java8API.enabled 用于控制处理时间数据的日期类型,开启该参数时,Spark 才会使用 java.time.LocalDate/java.time.Instant 类型处理时间数据。
为了保证时间数据在 Spark 与 SequoiaDB 中的一致性,建议:
升级 Spark 框架至 3.x 版本,并搭配使用 Spark 3.0 的连接器
开启参数 spark.sql.datetime.java8API.enabled,以 spark-sql 为例:
spark-sql> set spark.sql.datetime.java8API.enabled=true;
当开启参数 spark.sql.datetime.java8API.enabled 后,Timestamp 类型对应的 java 类型为 java.time.Instant,该类型的处理不再依赖于 JVM 的默认时区,而是依赖于 Spark 参数 spark.sql.session.timeZone 设置的 session 时区。
以 SequoiaDB 中存储的 Timestamp 数据为例:
> db.sample.employee.find() { "_id": { "$oid": "6278924d9da46d7dba91a618" }, "ts": { "$timestamp": "1970-01-01-00.00.00.123456" } }
创建 spark-sql 映射表映射到 SequoiaDB 集合 sample.employee
spark-sql> create table test(ts timestamp) using com.sequoiadb.spark options (host 'sdbserver:11810', collectionspace 'sample', collection 'employee', username 'sdbadmin', password 'sdbadmin');
数据查询会根据具体的 session 时区将 SequoiaDB 中存储的 UTC 时间转换为对应时区的时间
# 上海时区 spark-sql> set spark.sql.session.timeZone=Asia/Shanghai; spark-sql> select * from test; 1970-01-01 00:00:00.123456 # UTC spark-sql> set spark.sql.session.timeZone=UTC; spark-sql> select * from test; 1969-12-31 16:00:00.123456 # 洛杉矶时区 spark-sql> set spark.sql.session.timeZone=America/Los_Angeles; spark-sql> select * from test; 1969-12-31 08:00:00.123456
数据写入会根据具体的 session 时区将时间转换为不带时区的 UTC 时间
# 上海市区 spark-sql> set spark.sql.session.timeZone=Asia/Shanghai; spark-sql> insert into test values (TIMESTAMP '1970-01-01 00:00:00.123456'); # 洛杉矶时区 spark-sql> set spark.sql.session.timeZone=America/Los_Angeles; spark-sql> insert into test values (TIMESTAMP '1970-01-01 00:00:00.123456');
写入结果分别为:
# 上海时区 > db.sample.employee.find() { "_id": { "$oid": "627899b0c2dc5f404a5faac4" }, "ts": { "$timestamp": "1970-01-01-00.00.00.123456" } } # 洛杉矶时区 > db.sample.employee.find() { "_id": { "$oid": "62789a55c2dc5f404a5faac5" }, "ts": { "$timestamp": "1970-01-01-16.00.00.123456" } }