原来大数据 Hadoop 是这样存储数据的
HDFS概述
产生背景
随着数据量越来越大,在一个操作系统中存不下所有的数据。需要将这些数据分配到更多的操作系统中,带来的问题是多操作系统不方便管理和维护。需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS是分布式文件管理系统中的一种
定义
HDFS(Hadoop Distributed File System)它是一个文件系统,用于存储文件,通过目录树来定位文件。其次,他是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色
HDFS 的使用场景:适合一次写,多次读的场景,且不支持文件的修改。适合用来做数据分析
优缺点
优点
- 高容错
- 数据自动保存多个副本,通过增加副本的形式,提高容错性
- 某一个副本丢失以后,可以自动恢复
- 适合处理大数据
- 数据规模:达到 GB、TB、甚至 PB 级别的数据
- 文件规模:能够处理百万规模以上的文件数量
- 可以构建在廉价机器上
缺点
- 不适合低延时数据访问。比如毫秒级的存储数据,做不到
- 无法高效的对大量小文件进行存储
- namenode 对每个文件至少需要消耗 150 字节去存储目录和块信息,小文件相比大文件更消耗 namenode 服务器内存
- 小文件寻址时间会超过读取时间,违背 HDFS 设计初衷
- 不支持并发写入、文件随机修改
- 一个文件只能有一个写,不允许多线程同时写
- 仅支持数据追加,不支持修改
组成架构
HDFS 文件块大小
HDFS 中的文件在物理上是分块存储(Block),块的大小可以手动配置参数 dfs.blocksize
来修改(Hadoop 2.x 是 128m,之前是 64m)
为什么取 128m 呢?
普遍认为,寻址时间(即查找目标 block 的时间)为 10ms,而寻址时间为传输时间的 1% 时,为 HDFS 运行的理想最佳状态。此时传输时间为 10ms / 1% = 1000ms = 1s,而目前硬盘的传输速度普遍为 100m/s ,因此 block 的大小取 1s*100m/s = 100m。离它最近的 2 的次幂就是 128 了。这块可以看出,影响 block 大小的主要因素就是硬盘的读取速度。因此当采用固态硬盘的时候完全可以把数值调整到 256 m 甚至更多。
块太小的时候,会增加寻址时间
但当块变得很大时,就要想办法避免热点数据的频繁读取了。这一点在 Google 的论文中有提到,论文中给到的解决思路是客户端缓存,但是并没有提具体实现 https://www.cnblogs.com/zzjhn/p/3834729.html
HDFS 的 Shell 操作
基本语法
bin/hadoop fs 具体命令 OR bin/hdfs dfs 具体命令
dfs是fs的实现类
常用命令
命令 | 解释 | 示例 | 备注 |
---|---|---|---|
-ls | 显示目录信息 | ||
-mkdir | 在HDFS上创建目录 | hadoop fs -mkdir -p /user/keats/love | -p 创建多级目录 |
-moveFromLocal | 从本地剪切粘贴到HDFS | hadoop fs -moveFromLocal ./yaya.txt /user/keats/love/ | 前面是来源路径 后面是目标路径,下同 |
-appendToFile | 追加一个文件到已经存在的文件末尾 | ||
-cat | 显示文件内容 | ||
-chgrp 、-chmod、-chown | Linux文件系统中的用法一样,修改文件所属权限 | ||
-copyFromLocal | 从本地文件系统中拷贝文件到HDFS路径去 | 同 -put | |
-copyToLocal | 从HDFS拷贝到本地 | 同 -get | |
-getmerge | 合并下载多个文件 | hadoop fs -getmerge /user/keats/love/* ./yaya.txt | |
-tail | 显示一个文件的末尾 | ||
-rm | 删除文件或文件夹 | ||
-rmdir | 删除空目录 | ||
-du | 统计文件夹的大小信息 | 可以理解为 disk use | |
-setrep | 设置HDFS中文件的副本数量 | 里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10 |
HDFS 客户端操作
环境准备
- 把之前下载的 hadoop 安装包解压,复制到一个不含中文路径的目录下(建议所有开发相关东西放在一个目录下,方便管理)
- 配置环境变量 HADOOP_HOME 和 Path
项目准备
项目地址 https://github.com/keatsCoder/HdfsClientDemo
创建 maven 项目,引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
</dependencies>
创建 Java 类,HdfsClient 主要进行了三步操作
- 创建 FileSystem 对象
- 通过 FileSystem 对象执行命令
- 关闭 FileSystem
public class HdfsClient {
static FileSystem fs;
static {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://linux102:9000");
try {
fs = FileSystem.get(conf);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
// 执行操作
mkDir();
// 释放资源
fs.close();
}
private static void mkDir() throws IOException {
fs.mkdirs(new Path("/john/keats"));
}
}
尝试运行,会得到第一个错误,大意是权限被拒绝,这个时候就需要配置 JVM 参数 -DHADOOP_USER_NAME=root
来告诉集群,使用 root 用户进行操作
配置好之后再运行,会遇到第二个错误
Could not locate Hadoop executable: D:\develop\hadoop\bin\winutils.exe -see https://wiki.apache.org/hadoop/WindowsProblems
这是因为我们之前配置环境的时候,解压的 hadoop 文件 bin 目录下没有 winutils.exe 这个文件,根据后面地址 wiki 百科的指示,可以下载该文件放在 bin 目录下。但是目前那个文件的最新版本是 2.8.1,也许会存在某些方面不兼容的问题,目前还暂时没有发现。因此可以直接下载该版本使用 https://github.com/steveloughran/winutils/releases
简化配置用户名和访问路径
FileSystem.get() 有一个重载方法,三个参数,第一个是 hadoop namenode 地址,第二个是 conf 对象,第三个是用户名。可以一次配好
测试方法详见示例代码 HdfsClient2.java 类
fs = FileSystem.get(new URI("hdfs://linux102:9000"), conf, "root");
上传文件
在项目 resource 目录下创建文件 zhangsan.txt
调用 copyFromLocalFile 方法上传文件
private static void uploadFile() throws IOException {
URL systemResource = ClassLoader.getSystemResource("zhangsan.txt");
String path = systemResource.getPath();
fs.copyFromLocalFile(new Path(path), new Path("/john/keats/love"));
}
copyFromLocalFile 还有三个重载方法,分别提供以下功能
- 是否删除源文件
- 当目标文件存在时,是否覆盖目标文件
- 多文件批量上传,但目标路径必须是文件夹路径
配置文件优先级
之前我们在 hadoop 集群配置的副本数量是 3 ,而 hadoop client 也支持两种方式配置参数
- conf 对象通过 key-value 形式配置
- resources 目录下放置 xml 配置文件配置
加上默认的 default-xxxx.xml 一共四种配置的方式。他们的优先级是
conf > resources 下的配置文件 > hadoop 集群配置文件 > default
ConfigFileTest.java 类对此处的配置进行的说明与测试,读者可以运行体验
下载文件
fs.copyToLocalFile(new Path("/three.txt"), new Path("D://zhangsan.txt"));
copyToLocalFile 还有两个重载方法,分别添加了。具体代码可参考 DownLoadFileTest.java
// 是否删除源文件
boolean delSrc
// 是否使用RawLocalFileSystem作为本地文件系统
// 默认是 false,目前比较直观的就是 false 状态下下载文件会同时生成 .crc 格式的校验文件,设置为 true 时不会生成
boolean useRawLocalFileSystem
删除文件
删除文件的API,第二个参数表示是否递归删除。如果要删除的 Path 对应的是文件夹,recursive 需要设置为 true ,否则会抛异常。其实就相当于 rm -r
中的参数 -r
public abstract boolean delete(Path f, boolean recursive) throws IOException;
private static void deleteFile() throws IOException {
// /john/keats 是文件夹目录,递归设置为 false 会报错 PathIsNotEmptyDirectoryException: ``/john/keats is non empty': Directory is not empty
// fs.delete(new Path("/john/keats"), false);
// 先上传,再删除
HdfsClient2.uploadFile(true);
fs.delete(new Path("/john/keats/love/zhangsan.txt"), true);
}
这块我在删除之前添加了上传操作,目的是为了防止文件不存在。而上传又存在一种可能就是目标文件已存在。这是个薛定谔的文件- -,因此我查看了 FileSystem 的上传 API,他是提供 overwrite 开关的,默认是 true 即覆盖目标文件
文件重命名
private static void renameFile() throws IOException {
String dstFileName = "wangwu.txt";
HdfsClient2.uploadFile();
deleteFile(dstFileName);
// 目标文件不存在,则更名成功
boolean rename = fs.rename(new Path("/john/keats/love/zhangsan.txt"), new Path("/john/keats/love/", dstFileName));
Assert.assertTrue(rename);
// 目标文件存在,则更名失败
boolean renameButDstIsExist = fs.rename(new Path("/john/keats/love/zhangsan.txt"), new Path("/john/keats/love/", dstFileName));
Assert.assertFalse(renameButDstIsExist);
}
读取文件详细信息
public static void listFiles() 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();
for (BlockLocation blockLocation : blockLocations) {
// 获取块存储的主机节点
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println(host);
}
}
System.out.println("-----------分割线----------");
}
}
判断某路径下的内容是文件还是文件夹
public static void isFile() throws IOException {
// FileStatus[] listStatus = fs.listStatus(new Path("/"));
FileStatus[] listStatus = fs.listStatus(new Path("/three.txt"));
for (FileStatus fileStatus : listStatus) {
// 如果是文件
if (fileStatus.isFile()) {
System.out.println("f:"+fileStatus.getPath().getName());
}else {
System.out.println("d:"+fileStatus.getPath().getName());
}
}
}
HDFS的I/O流操作
// 从本地上传到HDFS
public static void copyFileFromDiskByIO() throws IOException {
// 2 创建输入流
FileInputStream fis = new FileInputStream(new File("D:/zhangsan.txt"));
// 3 获取输出流
FSDataOutputStream fos = fs.create(new Path("/zhangsan.txt"));
// 4 流对拷
IOUtils.copyBytes(fis, fos, conf);
}
// 从HDFS拷贝到本地
public static void copyFileFromHDFSByIO() throws IOException {
FSDataInputStream fis = fs.open(new Path("/zhangsan.txt"));
// 3 获取输出流
FileOutputStream fos = new FileOutputStream(new File("D:/zhangsan1.txt"));
// 4 流的对拷
IOUtils.copyBytes(fis, fos, conf);
}
文件的定位读取
/**
* 从某个位置开始拷贝文件,用于读取某个完整文件的部分内容
*/
public static void copyFileSeek() throws Exception{
// 2 打开输入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.10.1.tar.gz"));
// 3 定位输入数据位置
fis.seek(1024*1024*128);
// 4 创建输出流
FileOutputStream fos = new FileOutputStream(new File("D:/hadoop-2.7.2.tar.gz.part2"));
// 5 流的对拷
IOUtils.copyBytes(fis, fos, conf);
}
HDFS 原理
HDFS的读写数据流程
写数据
1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在
2)NameNode返回是否可以上传
3)客户端请求第一个 Block上传到哪几个DataNode服务器上(根据服务器距离以及负载排序,取前副本数个服务器返回)
4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3
5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成
6)dn1、dn2、dn3逐级应答客户端
7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答
8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)
读数据
1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址
2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据
3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)
4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件
网络拓扑图,节点距离计算
在HDFS写数据的过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?
节点距离:两个节点到达最近的共同祖先的距离总和
机架感知,副本存储节点选择
机架感知说明
For the common case, when the replication factor is three, HDFS’s placement policy is to put one replica on one node in the local rack, another on a different node in the local rack, and the last on a different node in a different rack.
这样布置,第一考虑的是速度,也兼顾了容灾的要求
NameNode和SecondaryNameNode
NameNode中的元数据是存储在哪里的?
首先,我们做个假设,如果存储在NameNode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的FsImage
这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。
但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并
整体的操作机制和 Redis 差不多,FsImage 相当于 Redis 中的 RDB 快照,Edits 相当于 Redis 中的 AOF 日志,两者结合。而 Redis 合并两个文件是采用的 Fork 进程的方式
第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存
(2)客户端对元数据进行增删改的请求
(3)NameNode记录操作日志,更新滚动日志
(4)NameNode在内存中对数据进行增删改
第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果
(2)Secondary NameNode请求执行CheckPoint
(3)NameNode滚动正在写的Edits日志
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并
(6)生成新的镜像文件fsimage.chkpoint
(7)拷贝fsimage.chkpoint到NameNode
(8)NameNode将fsimage.chkpoint重新命名成fsimage
DataNode 工作机制
1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳
2)DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息
3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用
4)集群运行中可以安全加入和退出一些机器
DataNode 多目录配置
DataNode 也可以配置成多个目录,每个目录存储的数据不一样。不同于 NameNode 多目录配置,NameNode 多个目录直接的数据是一样的,仅做备份和容灾用。我想是因为 DataNode 已经使用副本来做备份了,如果还继续在本机复制多份,不是很有必要。而 NameNode 在未做高可用之前并没有足够的备份,因此产生了差异
hdfs-site.xml
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>
添加新数据节点
(1)在hadoop104主机上再克隆一台hadoop105主机
(2)修改IP地址和主机名称
(3)删除原来HDFS文件系统留存的文件(/opt/module/hadoop/data和log)
(4)source一下配置文件
(5)直接启动DataNode,即可关联到集群
如果数据不均衡,可以使用 ./start-balancer.sh
命令实现集群的再均衡
但是这样存在一个问题:如果某些恶意分子知道了 NameNode 的地址,便可以连接集群并克隆出集群的数据,这样是极不安全的
添加白名单
只允许白名单内的地址连接 NameNode
在 NameNode 的 /opt/module/hadoop/etc/hadoop目录下创建 dfs.hosts 文件,并添加如下主机名称
linux102
linux103
linux104
在 NameNode 的 hdfs-site.xml 配置文件中增加 dfs.hosts 属性
<property>
<name>dfs.hosts</name>
<value>/opt/module/hadoop/etc/hadoop/dfs.hosts</value>
</property>
文件分发
xsync hdfs-site.xml
刷新NameNode
hdfs dfsadmin -refreshNodes
打开 Web 页面,可以看到不在白名单的 DataNode 会被下线
黑名单退役
在黑名单上的节点会被强制退出
黑名单的配置 key 如下
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/module/hadoop/etc/hadoop/dfs.hosts.exclude</value>
</property>
需要注意
- 退役节点时,需要等待退役节点状态为 decommissioned(所有块已经复制完成),停止该节点及节点资源管理器
- 如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役
- 不允许黑白名单同时出现一个主机名
HDFS 2.X 新特性
集群间数据拷贝
bin/hadoop distcp
hdfs://linux102:9000/user/keats/hello.txt hdfs://linux103:9000/user/keats/hello.txt
小文件存档
回收站
开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、备份等作用
快照管理
快照相当于对目录做一个备份,并不会立刻复制所有文件。而是记录文件变化
参考内容
原来大数据 Hadoop 是这样存储数据的的更多相关文章
- 大数据Hadoop——初识Hadoop
Hadoop简介 官方网站: http://hadoop.apache.org/ 中文网站: http://hadoop.apache.org/docs/r1.0.4/cn/ Hadoop设计来源 ...
- Hive中的HiveServer2、Beeline及数据的压缩和存储
1.使用HiveServer2及Beeline HiveServer2的作用:将hive变成一种server服务对外开放,多个客户端可以连接. 启动namenode.datanode.resource ...
- android开发中的5种存储数据方式
数据存储在开发中是使用最频繁的,根据不同的情况选择不同的存储数据方式对于提高开发效率很有帮助.下面笔者在主要介绍Android平台中实现数据存储的5种方式. 1.使用SharedPreferences ...
- Android中数据存储(三)——SQLite数据库存储数据
当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...
- Pandas_数据读取与存储数据(全面但不精炼)
Pandas 读取和存储数据 目录 读取 csv数据 读取 txt数据 存储 csv 和 txt 文件 读取和存储 json数据 读取和存储 excel数据 一道练习题 参考 Numpy基础(全) P ...
- Android提供了5种方式存储数据:
--使用SharedPreferences存储数据: --文件存储数据: --SQLite数据库存储数据: --使用ContentProvider存储数据: --网络存储数据: 一:使用SharedP ...
- H5 存储数据sessionStorage
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Python JSON存储数据
前言: 很多程序都要求用户输入某种信息,如让用户存储游戏首选项或提供要可视化的数据.不管专注的是什么,程序都把用户提供的信息存储在列表和字典等数据结构中.用户关闭 程序时,你几乎总是要保存他们提供的信 ...
- [转]大数据hadoop集群硬件选择
问题导读 1.哪些情况会遇到io受限制? 2.哪些情况会遇到cpu受限制? 3.如何选择机器配置类型? 4.为数据节点/任务追踪器提供的推荐哪些规格? 随着Apache Hadoop的起步,云客户 ...
随机推荐
- 区块链知识博文1: 共识算法之争(PBFT,Raft,PoW,PoS,DPoS,Ripple)
注:这是本人读到的关于共识算法最全和最好的分享博文,系统的介绍了拜占庭容错技术以及共识算法的原理和常用共识算法,原文链接请见后. 目录 一.拜占庭容错技术(Byzantine Fault Tolera ...
- Python中sort、sorted的cmp参数废弃之后使用cmp_to_key实现类似功能
Python2.1以前的排序比较方法只提供一个cmp比较函数参数,没有__lt__等6个富比较方法, Python 2.1引入了富比较方法,Python3.4之后作废了cmp参数.相应地从Python ...
- uniapp vue 购车计算器,贷款计算器,保险计算器
基于vue开发的买车计算器,支持uniapp 概述 项目为工作中开发,感觉比较有意思,而且能够帮助其他人快速开发功能,我就发上来了,大佬勿喷吧,没什么技术含量! uniapp打包多端[小程序类]可能会 ...
- C# operator 关键字的用法
operator 只要是运算符都能重载 operator 关键字的主要作用是用来重载运算符的,还可以用于类或结构中类型的自定义转换. 下面看个例子 class Feige { //定义两个全局变量 i ...
- Day1-7【Scrum 冲刺博客集合】
Day1-Day7博客链接 Day1[Scrum 冲刺博客] Day2[Scrum 冲刺博客] Day3[Scrum 冲刺博客] Day4[Scrum 冲刺博客] Day5[Scrum 冲刺博客] D ...
- REHの个人主页
朝暮与年岁并往 然后与你一同行至天光. 简介 这是怎么做到的啊-- 把那些迷茫的浑浊的不可预知的,裁剪,变化,像个造物主一样,最终成为混沌而又分明的,除去一身的戾气和险恶,把那些复杂和晦涩都剖析成它精 ...
- CSP-S 2019 Solution
Day1-T1 格雷码(code) 格雷码是一种特殊的 \(n\) 位二进制串排列法,要求相邻的两个二进制串恰好有一位不同,环状相邻. 生成方法: \(1\) 位格雷码由两个 \(1\) 位的二进制串 ...
- nginx学习之——CentOS6.0下安装nginx
1.下载对应nginx版本 #注:下载地址:http://nginx.org/download/ wget -c http://nginx.org/download/nginx-1.10.3.tar. ...
- Linux文件被删除后恢复
当ext4中的文件被删除后,进行文件恢复:http://www.360doc.com/content/18/0320/08/51898798_738625260.shtml上面的博客是恢复删除的文件, ...
- 正交实验法之 Allpairs电商项目用例设计实战
一.正交实验法概述 正交实验法是研究多因素多水平的一种方法,它是通过正交表挑选部分有代表性的水平组合试验替代全面试验.这些有代表性的组合试验具备了"均匀分散,整齐可比"的特点.正交 ...