Hadoop集群问题解决记录

下面是我最近一段时间修复公司Hadoop集群(CDH-5.5.1)的一些问题的记录。目前所遇到的挑战都还不算太大,主要是CDH对开源组件的侵入性不强,开源的bugfix代码都能正常合并进来。每次遇到具体的问题,只要善于使用google,就会发现,大部分我们所面对的问题,很早就有人遇到过了,开源社区一般都有相应的解决方案,我们要做的就是,精确的定位问题,找到它的官方bug记录,然后就是合并bug fix代码,自己打包,替换集群里面jar包,然后重启集群,问题基本都能够顺利解决。

1. Hue的mysql从5.5升级到5.7以后,Hue界面执行hql的时候不支持任何中文。解决方案是将Hue的数据库以及所有表的字符集改成utf8。

ALTER DATABASE hue CHARACTER SET utf8 COLLATE utf8_unicode_ci;
ALTER TABLE “${all tables in hue}” CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

2. 使用Sqoop1将Mysql数据抽取到Hive的时候,中文注释变成乱码的解决方案:下载cdh-hive源代码,合并https://issues.apache.org/jira/browse/HIVE-11837 的bugfix代码。参考:https://www.iteblog.com/archives/1687.html  代码下载:https://github.com/cloudera/hive/releases?after=cdh5.4.10-release

修改DDLTask.java:2084行

// outStream.writeBytes(createTab_stmt.render());
 outStream.write(createTab_stmt.render().getBytes("UTF-8"));

1899行

// outStream.writeBytes(createTab_stmt.toString());
 outStream.write(createTab_stmt.toString().getBytes("UTF-8"));

修改完以后,单独打包hive-exec.jar

mvn clean package -DskipTests -Dmaven.javadoc.skip=true -Phadoop-2

3.通过sqoop1将Hive数据导入Mysql的时候,如果启用“–direct”模式,数据中如果有中文会出现乱码。解决方案是合并https://issues.apache.org/jira/browse/SQOOP-2639的bugfix代码。代码下载:https://github.com/cloudera/sqoop/releases

另外,如果在安装Hive的元数据库Mysql的时候各种字符集不是utf8,需要将数据库、所有表的字符集改成utf8,collation也需要修改utf8系列。

代码修改位置:org.apache.sqoop.mapreduce.MySQLExportMapper.java 322行

// this.mysqlCharSet = MySQLUtils.MYSQL_DEFAULT_CHARSET;
 this.mysqlCharSet = "utf-8";

需要注意的是,sqoop1是一个Ant工程,根目录下由一个cloudera-pom.xml,在执行ant package的时候里面会引用该文件来打包。在mac上执行的时候需要设置JAVA_HOME, 必须是Java 1.7,不能用1.8, Ant必须是1.9系列,不能是1.10系列。Ant的安装命令是brew install ant@1.9.

“ant package”执行的时候应该会失败,只要不是在“jar”这个target步骤失败就行了,在build目录下会生成sqoop-1.4.6-cdh5.5.1.jar。将这个jar包替换整个集群上所有机器上的/opt/cloudera/parcels/CDH-5.5.1-1.cdh5.5.1.p0.11/jars/sqoop-1.4.6-cdh5.5.1.jar。

4. “FSImage may get corrupted after deleting snapshot”。HDFS 2.6的一个bug,Apache官方记录HDFS-9406。症状是,NameNode无法启动成功,报错信息如下:

2017-01-27 23:29:04,832 ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: Failed to start namenode.
java.lang.NullPointerException
at org.apache.hadoop.hdfs.server.namenode.INodeDirectory.addChild(INodeDirectory.java:531)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatPBINode$Loader.addToParent(FSImageFormatPBINode.java:252)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatPBINode$Loader.loadINodeDirectorySection(FSImageFormatPBINode.java:202)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf$Loader.loadInternal(FSImageFormatProtobuf.java:261)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf$Loader.load(FSImageFormatProtobuf.java:180)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormat$LoaderDelegator.load(FSImageFormat.java:226)

查看HDFS的源代码,发现是在加载FSImage文件的时候报空指针错误。修改“FSImageFormatPBINode.java:202”,增加空指针判断:

for (long id : e.getChildrenList()) {
  INode child = dir.getInode(id);
 addToParent(p, child); //line 202
}

改成如下:

for (long id : e.getChildrenList()) {
  INode child = dir.getInode(id);
 if (child != null) {
    addToParent(p, child);
 }
}

重新打包HDFS的jar包,替换以后重启NameNode,发现新的报错信息:

2017-01-28 19:51:43,441 ERROR org.apache.hadoop.hdfs.server.namenode.FSImage: Failed to load image from FSImageFile(file=/srv/cloudera/meta01/dfs/nn/current/fsimage_0000000000510693370, cpktTxId=0000000000510693370)
java.io.IOException: Cannot find an INode associated with the INode 000000_0 in created list while loading FSImage.
at org.apache.hadoop.hdfs.server.namenode.snapshot.SnapshotFSImageFormat.loadCreated(SnapshotFSImageFormat.java:158)
at org.apache.hadoop.hdfs.server.namenode.snapshot.FSImageFormatPBSnapshot$Loader.loadCreatedList(FSImageFormatPBSnapshot.java:241)
at org.apache.hadoop.hdfs.server.namenode.snapshot.FSImageFormatPBSnapshot$Loader.loadDirectoryDiffList(FSImageFormatPBSnapshot.java:333)
at org.apache.hadoop.hdfs.server.namenode.snapshot.FSImageFormatPBSnapshot$Loader.loadSnapshotDiffSection(FSImageFormatPBSnapshot.java:188)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf$Loader.loadInternal(FSImageFormatProtobuf.java:270)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormatProtobuf$Loader.load(FSImageFormatProtobuf.java:180)
at org.apache.hadoop.hdfs.server.namenode.FSImageFormat$LoaderDelegator.load(FSImageFormat.java:226)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:929)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:913)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImageFile(FSImage.java:732)
at org.apache.hadoop.hdfs.server.namenode.FSImage.loadFSImage(FSImage.java:668)
at org.apache.hadoop.hdfs.server.namenode.FSImage.recoverTransitionRead(FSImage.java:281)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.loadFSImage(FSNamesystem.java:1061)

从上面红色字体的地方,能分析出在加载fsimage文件的时候,有一个加载snapshot diff的过程,就是在这个过程中发现真实文件信息和元数据不一致才导致的空指针错误的。既然是由加载snapshot的diff的过程出错的,联想到官方的bug信息,可以断定的确是因为某次删除snapshot的时候导致元数据没有及时更新导致的问题。先注释掉FSImageFormatProtobuf.java:270这一行,跳过这一步:

case SNAPSHOT_DIFF:
  snapshotLoader.loadSnapshotDiffSection(in); //line 270
 break;

改成:

 case SNAPSHOT_DIFF:
// snapshotLoader.loadSnapshotDiffSection(in);
 break;

重新打包部署,重启namenode,发现其能正常启动。HDFS service back on line!

尝试过将HDFS-9406的官方bugfix的代码合并到我们的部署版本(cdh-5.5.1)中,发现非常有难度,因为官方的修复代码并不是基于hdfs 2.6.0更改的。与这个bug相关的HDFS-9696代码,是和保存snapshot相关的代码,可以很方便的合并。

后续操作步骤:

  1. 基于修改过的hdfs jar包,启动HDFS namenode的HA模式;
  2. 删除HDFS下的所有snapshot。这么做的目的是想确保fsimage文件中不再有snapshot-diff信息;
  3. 将其中的一个NameNode的hdfs jar包替换成官方发行的,重启,发现没有任何问题;
  4. 将另外一个NameNode的hdfs jar包还原,重启,没有发现问题;
  5. 多次测试创建快照、删除快照、重启NameNode的过程,均没有发现任何问题。
发表评论?

0 条评论。

发表评论


注意 - 你可以用以下 HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>