HDFS簡介
HDFS(全稱:Hadoop Distribute File System)分佈式文件系統,是Hadoop核心組成。
HDFS中的重要概念
分塊存儲
HDFS中的文件在物理上是分塊存儲的,塊的大小可以通過配置參數來規定;Hadoop2.x版本默認的block大小是128M
命名空間
HDFS支持傳統的層次性文件組織結構。用戶或者應用程序可以創建目錄,然後將文件保存在這些目錄裏。文件系統名字空間的層次結構和大多數現有的文件系統類似:用戶可以創建、刪除、移動或重命名這些文件。
NameNode負責維護文件系統的名字空間,任何對文件系統名字空間或屬性的修改都被NameNode記錄下來。
NameNode元數據
我們把目錄結構及文件分塊位置信息叫做元數據。NameNode的元數據記錄每一個文件對應的block信息。
DataNode數據存儲
文件的各個block的具體存儲管理由DataNode負責。一個block會有多個DataNode來存儲,DataNode會定時向NameNode來彙報自己持有的block信息。
副本機制
爲了容錯,文件的所有block都會有副本。每隔文件的block大小和副本數都是可配置的。副本數默認是3個。
一次寫入,多次讀出
HDFS是設計成適應一次寫入,多次讀出的場景,且不支持文件的隨機修改。正因爲如此,HDFS適合用來做大數據分析的底層存儲服務,而不適合做網盤等應用。(修改不方便,延遲大)
HDFS架構
-
NameNode:Hdfs集羣的管理者
- 維護管理Hdfs的名稱空間
- 維護副本策略
- 記錄文件塊的映射關係
- 負責處理客戶端讀寫請求
-
DataNode:NameNode下達命令,DataNode執行實際操作
- 保存實際的數據塊
- 負責數據塊的讀寫
-
Client:客戶端
- 上傳文件到HDFS的時候,Client負責將文件切分成Block,然後進行上傳
- 請求NameNode交互,獲取文件的位置信息
- 讀取或寫入文件,與DataNode交互
- Client可以使用一些命令來管理HDFS或者訪問HDFS
寫數據流程:
HDFS客戶端操作
Shell客戶端
- 查看所有命令
hadoop fs
- 查看命令幫助
#查看rm的幫助信息
hadoop fs -help rm
- 顯示目錄信息
hadoop fs -ls /
- 創建目錄
hadoop fs -mkdir -p /test/data
- 從本地剪切到HDFS
hadoop fs -moveFromLocal ./word.txt /test/data
- 追加文件內容到指定文件
hadoop fs -appendToFile test.txt /test/data/word.txt
- 顯示文件內容
hadoop fs -cat /test/data/word.txt
- 修改文件所屬權限
hadoop fs -chmod 666 /test/data/word.txt
hadoop fs -chown root:root /test/data/word.txt
- 從本地文件系統拷貝文件到HDFS路徑去
hadoop fs -copyFromLocal test.txt /test
- 從HDFS拷貝到本地
hadoop fs -copyToLocal /test/data/word.txt /opt
- 從HDFS的一個路徑拷貝到HDFS的另一個路徑
hadoop fs -cp /test/data/word.txt /test/input/t.txt
- 在HDFS目錄中移動文件
hadoop fs -mv /test/input/t.txt /
- 從HDFS中下載文件,等同於copyToLocal
hadoop fs -get /t.txt ./
- 從本地上傳文件到HDFS,等同於copyFromLocal
hadoop fs -put ./yarn.txt /user/root/test/
- 顯示一個文件的末尾
hadoop fs -tail /t.txt
- 刪除文件或文件夾
hadoop fs -rm /t.txt
- 刪除空目錄
hadoop fs -rmdir /test
- 統計文件夾的大小信息
hadoop fs -du -s -h /test
hadoop fs -du -h /test
- 設置HDFS的副本數量
hadoop fs -setrep 10 /lagou/bigdata/hadoop.txt
注意:這裏設置的副本數只是記錄在NameNode的元數據中,是否真的會有這麼多副本,還得看DataNode的數量。因爲目前只有3臺設備,最多也就3個副本,只有節點數的增加到10臺時,副本數才能達到10.
Java客戶端
環境準備
- 將Hadoop安裝包解壓到非中文路徑
- 配置環境變量
- 依賴導入
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop-version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop-version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop-version}</version>
</dependency>
- 配置文件(可選步驟)
將hdfs-site.xml(內容如下)拷貝到項目的resources下
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration>
參數優先級排序:(1)代碼中設置的值 >(2)用戶自定義配置文件 >(3)服務器的默認配置
HDFS Java API
- 上傳文件
@Test
public void testCopyFromLocalFile() throws Exception{
Configuration conf = new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.56.103:9000"),conf,"root");
fs.copyFromLocalFile(new Path("e://aa.txt"),new Path("/cc.txt"));
fs.close();
}
爲了方便,下面的示例就隱藏構建FileSystem的過程
- 下載文件
@Test
public void testCopyToLocalFile() throws Exception{
fs.copyToLocalFile(false,new Path("/aa.txt"),new Path("e://cd.txt"),true);
}
- 刪除文件
@Test
public void testDelete() throws Exception{
fs.delete(new Path("/aa.txt"),true);
}
- 查看文件名稱、權限、長度、塊信息等
public void testList() throws IOException {
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
while (listFiles.hasNext()){
LocatedFileStatus status = listFiles.next();
//文件名稱
System.out.println(status.getPath().getName());
System.out.println(status.getLen());
System.out.println(status.getPermission());
System.out.println(status.getGroup());
BlockLocation[] blockLocations = status.getBlockLocations();
Stream.of(blockLocations).forEach(
block->{
String[] hosts = new String[0];
try {
hosts = block.getHosts();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(Arrays.asList(hosts));
}
);
System.out.println("-----------華麗的分割線----------");
}
}
- 文件夾判斷
@Test
public void testListStatus() throws IOException {
FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
Stream.of(fileStatuses).forEach(fileStatus -> {
String name = fileStatus.isFile() ? "文件:" + fileStatus.getPath().getName() : "文件夾:"+fileStatus.getPath().getName();
System.out.println(name);
});
}
- 通過I/O流操作HDFS
- IO流上傳文件
@Test
public void testIOUpload() throws IOException {
FileInputStream fis = new FileInputStream(new File("e://11.txt"));
FSDataOutputStream fos = fs.create(new Path("/io_upload.txt"));
IOUtils.copyBytes(fis,fos,new Configuration());
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
}
- IO流下載文件
@Test
public void testDownload() throws IOException{
FSDataInputStream fis = fs.open(new Path("/io_upload.txt"));
FileOutputStream fos = new FileOutputStream(new File("e://11_copy.txt"));
IOUtils.copyBytes(fis,fos,new Configuration());
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
}
- seek定位讀取
@Test
public void readFileSeek() throws IOException{
FSDataInputStream fis = fs.open(new Path("/io_upload.txt"));
IOUtils.copyBytes(fis,System.out,1024,false);
//從頭再次讀取
fis.seek(0);
IOUtils.copyBytes(fis,System.out,1024,false);
IOUtils.closeStream(fis);
}
HDFS文件權限問題
HDFS的文件權限和linux系統的文件權限機制類似。如果在linux系統中root用戶使用hadoop命令創建了一個文件,那麼該文件的owner就是root。
當出現權限問題時,解決方法有如下幾種:
- 獲取FileSystem對象時指定有權限的用戶
- 關閉HDFS權限校驗,修改hdfs-site.xml
#添加如下屬性
<property>
<name>dfs.permissions</name>
<value>true</value>
</property>
- 直接修改HDFS的文件或目錄權限爲777,允許所有用戶操作
hadoop fs -chmod -R 777 /