1

我注意到 sqoop 导入有一个奇怪的问题。我尝试导入的数据在 MySQL DB 中采用以下形式:

<a1, a2, a3, d1, a4, a5, a6, a7, a8>

其中 a1, a2,..., a8 是 varchar 类型,d1 是时间戳类型。由于数据的规范化,我必须执行 JOIN 多个表才能获取这些列 - 如下所示:

SELECT t1.a1, t2.a2....... from table t1 INNER JOIN table t2 ON t1.t2_id = t2.id ........... WHERE <some condition>

最初,我从这样的视图开始:

CREATE OR REPLACE VIEW my_view AS
SELECT t1.a1, t2.a2....... from table t1 INNER JOIN table t2 ON t1.t2_id = t2.id ........... WHERE <some condition>

使用该视图导入 sqoop 命令如下:

sqoop import --connect [jdbc url] --username [user] --password [password] --table my_view --target-dir my_dir --split-by a5 --mysql-delimiters --verbose --boundary-query 'SELECT min(a5), max(a5) from t5'

这工作正常。由于 MySQL 视图没有像人们希望的那样优化,我想使用原始 SQL 来查看它是否会提高性能。为了测试这一点,我使用了自由格式查询:

sqoop import --connect [jdbc url] --username [user] --password [password] --query "SELECT t1.a1, t2.a2....... from table t1 INNER JOIN table t2 ON t1.t2_id = t2.id ............ WHERE <some condition> AND \$CONDITIONS" --target-dir my_dir --split-by a5 --mysql-delimiters --verbose --boundary-query 'SELECT min(a5), max(a5) from t5'

因此,在这种情况下,--query 参数有效地具有视图定义 SELECT 语句以及 sqoop 所需的 $CONDITIONS。但是,这不起作用。sqoop 导入了一半的记录并失败并出现以下奇怪的错误:

13/09/27 20:28:10 INFO mapred.JobClient: Task Id : attempt_201309130032_0122_m_000000_2,  Status : FAILED
java.io.IOException: SQLException in nextKeyValue
    at org.apache.sqoop.mapreduce.db.DBRecordReader.nextKeyValue(DBRecordReader.java:265)
    at org.apache.hadoop.mapred.MapTask$NewTrackingRecordReader.nextKeyValue(MapTask.java:531)
    at org.apache.hadoop.mapreduce.MapContext.nextKeyValue(MapContext.java:67)
    at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:144)
    at org.apache.sqoop.mapreduce.AutoProgressMapper.run(AutoProgressMapper.java:64)
    at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:764)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:364)
    at org.apache.hadoop.mapred.Child$4.run(Child.java:255)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:396)
    at    org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1190)
    at org.apache.hadoop.mapred.Child.main(Child.java:249)
Caused by: java.sql.SQLException: Value 'xxxxxx' can not be represented as     java.sql.Timestamp
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1078)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920)
    at com.mysql.jdbc.ResultSetRow.getTimestampFast(ResultSetRow.java:1102)
    at com.mysql.jdbc.BufferRow.getTimestampFast(BufferRow.java:576)
    at com.mysql.jdbc.ResultSetImpl.getTimestampInternal(ResultSetImpl.java:6592)
    at com.mysql.jdbc.ResultSetImpl.getTimestamp(ResultSetImpl.java:6192)
    at org.apache.sqoop.lib.JdbcWritableBridge.readTimestamp(JdbcWritableBridge.java:111)
    at com.cloudera.sqoop.lib.JdbcWritableBridge.readTimestamp(JdbcWritableBridge.java:83)
    at QueryResult.readFields(QueryResult.java:156)
    at org.apache.sqoop.mapreduce.db.DBRecordReader.nextKeyValue(DBRecordReader.java:245)
    ... 11 more

据我所知,sqoop 正在尝试将其他一些列 (a3) 值解释为时间戳,并且转换失败,因为它只是一个字符串而不是日期类型。我还应该提到我们的一些数据很糟糕——我们在一些不应该存在的字段中有换行符和制表符,但日期字段确实有有效值——我什至尝试在 MySQL 中使用 REPLACE 函数来摆脱这些但是这是没有用的。

鉴于数据是相同的,并且在任何一种情况下都使用相同的 SELECT 语句,我希望结果是相同的(即,SELECT 返回的相同数量的记录被导入到 HDFS)。

有没有人见过这种行为?关于如何解决这个问题的任何想法?

4

2 回答 2

5

我尝试使用不同的 MySQL 驱动程序版本执行相同的命令 - 虽然在所有情况下都发生了相同的错误,但这次消息更加清晰:

13/10/21 22:19:18 INFO mapred.JobClient: Task Id : attempt_201309130032_0308_m_000000_0,   Status : FAILED
java.io.IOException: SQLException in nextKeyValue
    at   org.apache.sqoop.mapreduce.db.DBRecordReader.nextKeyValue(DBRecordReader.java:265)
    at  org.apache.hadoop.mapred.MapTask$NewTrackingRecordReader.nextKeyValue(MapTask.java:531)
    at org.apache.hadoop.mapreduce.MapContext.nextKeyValue(MapContext.java:67)
    at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:144)
    at org.apache.sqoop.mapreduce.AutoProgressMapper.run(AutoProgressMapper.java:64)
    at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:764)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:364)
    at org.apache.hadoop.mapred.Child$4.run(Child.java:255)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:396)
    at  org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1190)
    at org.apache.hadoop.mapred.Child.main(Child.java:249)
Caused by: java.sql.SQLException: Cannot convert value '0000-00-00 00:00:00' from column 7 to TIMESTAMP.
    at com.mysql.jdbc.ResultSet.getTimestampFromBytes(ResultSet.java:6886)
    at com.mysql.jdbc.ResultSet.getTimestampInternal(ResultSet.java:6921)
    at com.mysql.jdbc.ResultSet.getTimestamp(ResultSet.java:6245)
    at org.apache.sqoop.lib.JdbcWritableBridge.readTimestamp(JdbcWritableBridge.java:111)
    at  com.cloudera.sqoop.lib.JdbcWritableBridge.readTimestamp(JdbcWritableBridge.java:83)
    at QueryResult.readFields(QueryResult.java:156)
    at org.apache.sqoop.mapreduce.db

所以基本问题是'0000-00-00 00:00:00'日期的值存储在我们的数据库中,但驱动程序无法处理(我尝试了几个版本,但都没有工作)。在 sqoop 中使用带有自由格式查询选项的原始 sql 时,驱动程序尝试将此日期转换为日期对象,但失败导致上述错误。请注意,如果使用视图提取相同的日期值,则不会发生这种情况 - 在这种情况下,驱动程序似乎不会尝试将此值转换为日期对象。无论出于何种原因,MySQL 驱动程序和服务器似乎在处理无效日期方面不同步。

来自MySQL 文档

As of 5.0.2, the server requires that month and day values be legal, and not merely in   the range 1 to 12 and 1 to 31, respectively. With strict mode disabled, invalid dates such   as '2004-04-31' are converted to '0000-00-00' and a warning is generated. With strict mode   enabled, invalid dates generate an error.

我们的旧版数据库服务器禁用了严格模式,每当旧版应用程序尝试插入无效日期(例如“2004-04-31”)时,它都会转换为“0000-00-00”,这无法由上面提到的原始 sql 案例的驱动程序。一旦使用 where 子句中的过滤器删除这些记录,sqoop 导入就会按预期工作。

于 2013-10-24T00:45:41.940 回答
0

您可以在 sqoop 命令中使用此 JDBC URL

jdbc:mysql://yourserver:3306/yourdatabase?zeroDateTimeBehavior=convertToNull

这对我有用

于 2016-10-31T08:46:38.250 回答