使用输入拆分 (HADOOP)
Working with input splits(HADOOP)
我有一个 .txt
文件如下:
这里是 xyz
这是我家
这是我的电脑
这是我的房间
这是ubuntuPC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxxxxxxxxxxxxxxxxxx
(忽略每条记录后的空行)
我已将块大小设置为 64 字节。我要检查的是,是否存在将单个记录分成两个块的情况。
现在逻辑上,由于块大小为 64 字节 ,将文件上传到 HDFS 后,它应该创建 3 个大小为 64、64、27 的块bytes respectively,它确实如此。此外,由于第一个块的大小为 64 字节,因此它应该只包含以下数据:
这里是 xyz
这是我家
这是我的电脑
这是我的房间
日
现在我想看看第一个块是不是这样,如果我通过浏览器浏览HDFS并下载文件,它下载整个文件而不是单个块.
所以我决定 运行 一个只显示记录值的 map-reduce 作业。(设置 reducers=0
,映射器输出为 context.write(null,record_value)
,同时更改默认值分隔符为 ""
)
现在 运行 作业计数器显示 3 拆分 ,这很明显,但完成后当我检查输出目录时,它显示 3 个映射器输出文件,其中 2 个是空的,第一个映射器输出文件包含文件的所有内容。
谁能帮我解决这个问题?新版本的 hadoop 是否有可能自动处理不完整的记录?
使用以下命令获取 HDFS 上文件的阻止列表
hdfs fsck PATH -files -blocks -locations
其中 PATH 是您的文件所在的完整 HDFS 路径。
输出(下面部分显示)将是这样的(行号 2、3...忽略)
通过http://ec2-54-235-1-193.compute-1.amazonaws.com:50070/fsck?ugi=student6&files=1&blocks=1&locations=1&path=%2Fstudent6%2Ftest.txt连接到namenode
FSCK 由 student6(auth:SIMPLE)从 /172.31.11.124 开始,路径为 /student6/test.txt,时间为 3 月 22 日星期三 15:33:17 UTC 2017
/student6/test.txt 22 字节,1 个块:OK 0.BP-944036569-172.31.11.124-1467635392176:blk_1073755254_14433 len=22 repl= 1 [DatanodeInfoWithStorage[172.31.11.124:50010,DS-4a530a72-0495-4b75-a6f9-75bdb8ce7533,DISK]]
复制输出命令的粗体部分(不包括_14433)如上例输出
转到Linux数据节点上的文件系统到存储块的目录(这将由hdfs-dfs.datanode.data.dir参数指向-site.xml 并从该位置在整个子树中搜索具有您刚刚复制的粗体字符串的文件名。这将告诉您 dfs.datanode.data.dir 下的哪个子目录包含名称中包含该字符串的文件(排除任何带有 .meta 的文件名后缀)。找到这样的文件名后,您可以 运行 在该文件名上执行 Linux cat 命令以查看文件内容。
请记住,尽管该文件是一个 HDFS 文件,但在幕后,该文件实际上存储在 Linux 文件系统中,并且 HDFS 文件的每个块都是唯一的 Linux 文件。该块由 Linux 文件系统标识,名称如步骤 2 的粗体字符串所示
重现场景的步骤
1) 创建了一个文件 sample.txt
,其内容总大小为 ~153B
cat sample.txt
This is xyz
This is my home
This is my PC
This is my room
This is ubuntu PC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxxxxxxxxxxxxxxxxxx
2) 添加 属性 到 hdfs-site.xml
<property>
<name>dfs.namenode.fs-limits.min-block-size</name>
<value>10</value>
</property>
并加载到 HDFS 中,块大小为 64B
.
hdfs dfs -Ddfs.bytes-per-checksum=16 -Ddfs.blocksize=64 -put sample.txt /
这创建了大小为 64B
、64B
和 [= 的三个块27=].
Block0
中的内容:
This is xyz
This is my home
This is my PC
This is my room
This i
Block1
中的内容:
s ubuntu PC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx
Block2
中的内容:
xx xxxxxxxxxxxxxxxxxxxxx
3)一个简单的mapper.py
#!/usr/bin/env python
import sys
for line in sys.stdin:
print line
4) Hadoop Streaming with 0
reducers:
yarn jar hadoop-streaming-2.7.1.jar -Dmapreduce.job.reduces=0 -file mapper.py -mapper mapper.py -input /sample.txt -output /splittest
具有 3 个输入拆分的作业 运行 调用 3 个映射器并生成 3 个输出文件,其中一个文件包含 sample.txt
的全部内容,其余 0B
个文件。
hdfs dfs -ls /splittest
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/_SUCCESS
-rw-r--r-- 3 user supergroup 168 2017-03-22 11:13 /splittest/part-00000
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/part-00001
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/part-00002
文件 sample.txt
被分成 3 个部分,这些部分被分配给每个映射器,
mapper1: start=0, length=64B
mapper2: start=64, length=64B
mapper3: start=128, length=25B
这只决定映射器必须读取文件的哪一部分,不一定是准确的。映射器读取的实际内容由 FileInputFormat 及其边界决定,此处为 TextFileInputFormat
.
这使用 LineRecordReader
从每个拆分中读取内容,并使用 \n
作为分隔符(行边界)。对于未压缩的文件,每个映射器都会读取这些行,如下所述。
对于起始索引为0的mapper,行读取从split的开始处开始。如果拆分以 \n
结束,则读取在拆分边界处结束,否则它会查找第一个 \n
post 分配的拆分长度(此处为 64B
)。这样它就不会结束处理部分行。
对于所有其他映射器(起始索引!= 0),它检查其起始索引(start - 1
)的前一个字符是否为\n
,如果是,则从中读取内容拆分的开始否则它会跳过其起始索引和该拆分中遇到的第一个 \n
字符之间的内容(因为此内容由其他映射器处理)并从第一个 [=38= 开始读取].
此处,mapper1
(起始索引为0)以Block0
开头,其拆分在一行的中间结束。因此,它继续读取消耗整个 Block1
的行,并且由于 Block1
没有 \n
字符,mapper1
继续读取直到找到 \n
也以消耗整个 Block2
结束。这就是 sample.txt
的全部内容如何在单个映射器输出中结束。
mapper2
(起始索引!= 0),其起始索引前的一个字符不是 \n
,因此跳过该行并以无内容结束。空映射器输出。 mapper3
与 mapper2
具有相同的场景。
尝试像这样更改 sample.txt
的内容以查看不同的结果
This is xyz
This is my home
This is my PC
This is my room
This is ubuntu PC xxxx xxxx xxxx xxxx
xxxx xxxx xxxx xxxx xxxx xxxx xxxx
xxxxxxxxxxxxxxxxxxxxx
我有一个 .txt
文件如下:
这里是 xyz
这是我家
这是我的电脑
这是我的房间
这是ubuntuPC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxxxxxxxxxxxxxxxxxx
(忽略每条记录后的空行)
我已将块大小设置为 64 字节。我要检查的是,是否存在将单个记录分成两个块的情况。
现在逻辑上,由于块大小为 64 字节 ,将文件上传到 HDFS 后,它应该创建 3 个大小为 64、64、27 的块bytes respectively,它确实如此。此外,由于第一个块的大小为 64 字节,因此它应该只包含以下数据:
这里是 xyz
这是我家
这是我的电脑
这是我的房间
日
现在我想看看第一个块是不是这样,如果我通过浏览器浏览HDFS并下载文件,它下载整个文件而不是单个块.
所以我决定 运行 一个只显示记录值的 map-reduce 作业。(设置 reducers=0
,映射器输出为 context.write(null,record_value)
,同时更改默认值分隔符为 ""
)
现在 运行 作业计数器显示 3 拆分 ,这很明显,但完成后当我检查输出目录时,它显示 3 个映射器输出文件,其中 2 个是空的,第一个映射器输出文件包含文件的所有内容。
谁能帮我解决这个问题?新版本的 hadoop 是否有可能自动处理不完整的记录?
使用以下命令获取 HDFS 上文件的阻止列表
hdfs fsck PATH -files -blocks -locations
其中 PATH 是您的文件所在的完整 HDFS 路径。
输出(下面部分显示)将是这样的(行号 2、3...忽略)
通过http://ec2-54-235-1-193.compute-1.amazonaws.com:50070/fsck?ugi=student6&files=1&blocks=1&locations=1&path=%2Fstudent6%2Ftest.txt连接到namenode FSCK 由 student6(auth:SIMPLE)从 /172.31.11.124 开始,路径为 /student6/test.txt,时间为 3 月 22 日星期三 15:33:17 UTC 2017 /student6/test.txt 22 字节,1 个块:OK 0.BP-944036569-172.31.11.124-1467635392176:blk_1073755254_14433 len=22 repl= 1 [DatanodeInfoWithStorage[172.31.11.124:50010,DS-4a530a72-0495-4b75-a6f9-75bdb8ce7533,DISK]]
复制输出命令的粗体部分(不包括_14433)如上例输出
转到Linux数据节点上的文件系统到存储块的目录(这将由hdfs-dfs.datanode.data.dir参数指向-site.xml 并从该位置在整个子树中搜索具有您刚刚复制的粗体字符串的文件名。这将告诉您 dfs.datanode.data.dir 下的哪个子目录包含名称中包含该字符串的文件(排除任何带有 .meta 的文件名后缀)。找到这样的文件名后,您可以 运行 在该文件名上执行 Linux cat 命令以查看文件内容。
请记住,尽管该文件是一个 HDFS 文件,但在幕后,该文件实际上存储在 Linux 文件系统中,并且 HDFS 文件的每个块都是唯一的 Linux 文件。该块由 Linux 文件系统标识,名称如步骤 2 的粗体字符串所示
重现场景的步骤
1) 创建了一个文件 sample.txt
,其内容总大小为 ~153B
cat sample.txt
This is xyz
This is my home
This is my PC
This is my room
This is ubuntu PC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxxxxxxxxxxxxxxxxxx
2) 添加 属性 到 hdfs-site.xml
<property>
<name>dfs.namenode.fs-limits.min-block-size</name>
<value>10</value>
</property>
并加载到 HDFS 中,块大小为 64B
.
hdfs dfs -Ddfs.bytes-per-checksum=16 -Ddfs.blocksize=64 -put sample.txt /
这创建了大小为 64B
、64B
和 [= 的三个块27=].
Block0
中的内容:
This is xyz
This is my home
This is my PC
This is my room
This i
Block1
中的内容:
s ubuntu PC xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xx
Block2
中的内容:
xx xxxxxxxxxxxxxxxxxxxxx
3)一个简单的mapper.py
#!/usr/bin/env python
import sys
for line in sys.stdin:
print line
4) Hadoop Streaming with 0
reducers:
yarn jar hadoop-streaming-2.7.1.jar -Dmapreduce.job.reduces=0 -file mapper.py -mapper mapper.py -input /sample.txt -output /splittest
具有 3 个输入拆分的作业 运行 调用 3 个映射器并生成 3 个输出文件,其中一个文件包含 sample.txt
的全部内容,其余 0B
个文件。
hdfs dfs -ls /splittest
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/_SUCCESS
-rw-r--r-- 3 user supergroup 168 2017-03-22 11:13 /splittest/part-00000
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/part-00001
-rw-r--r-- 3 user supergroup 0 2017-03-22 11:13 /splittest/part-00002
文件 sample.txt
被分成 3 个部分,这些部分被分配给每个映射器,
mapper1: start=0, length=64B
mapper2: start=64, length=64B
mapper3: start=128, length=25B
这只决定映射器必须读取文件的哪一部分,不一定是准确的。映射器读取的实际内容由 FileInputFormat 及其边界决定,此处为 TextFileInputFormat
.
这使用 LineRecordReader
从每个拆分中读取内容,并使用 \n
作为分隔符(行边界)。对于未压缩的文件,每个映射器都会读取这些行,如下所述。
对于起始索引为0的mapper,行读取从split的开始处开始。如果拆分以 \n
结束,则读取在拆分边界处结束,否则它会查找第一个 \n
post 分配的拆分长度(此处为 64B
)。这样它就不会结束处理部分行。
对于所有其他映射器(起始索引!= 0),它检查其起始索引(start - 1
)的前一个字符是否为\n
,如果是,则从中读取内容拆分的开始否则它会跳过其起始索引和该拆分中遇到的第一个 \n
字符之间的内容(因为此内容由其他映射器处理)并从第一个 [=38= 开始读取].
此处,mapper1
(起始索引为0)以Block0
开头,其拆分在一行的中间结束。因此,它继续读取消耗整个 Block1
的行,并且由于 Block1
没有 \n
字符,mapper1
继续读取直到找到 \n
也以消耗整个 Block2
结束。这就是 sample.txt
的全部内容如何在单个映射器输出中结束。
mapper2
(起始索引!= 0),其起始索引前的一个字符不是 \n
,因此跳过该行并以无内容结束。空映射器输出。 mapper3
与 mapper2
具有相同的场景。
尝试像这样更改
sample.txt
的内容以查看不同的结果
This is xyz
This is my home
This is my PC
This is my room
This is ubuntu PC xxxx xxxx xxxx xxxx
xxxx xxxx xxxx xxxx xxxx xxxx xxxx
xxxxxxxxxxxxxxxxxxxxx