目 录
摘 要 I
Abstract II
1 绪论 1
1.1选题背景及意义 1
1.1.1选题背景 1
1.1.2选题意义 2
1.2研究现状及趋势 2
1.3研究主要内容 5
2 重复数据删除存储系统架构 6
2.1 重复数据删除技术的基本概念 6
2.2 重复数据删除系统结构和基本原理 7
2.3 重复数据删除关键技术 10
2.3.1 数据划分方法 11
2.3.2 I/O优化技术 11
2.3.3 高可靠数据配置策略 12
2.3.4 系统可扩展性 12
2.4 hadoop系统架构 13
2.4.1 hadoop生态架构和概况 13
2.4.2 HDFS(Hadoop分布式文件系统) 14
2.4.3 Mapreduce(分布式计算框架) 17
3 重复数据删除系统实现 19
3.1 MapReduce计算框架实现 19
3.2指纹计算模块实现 20
3.2.1 指纹索引表的建立与指纹检索 20
3.2.2 BLOOM FILTER过滤算法的实现 21
3.3处理流程分析 22
3.3.1读流程分析 22
3.3.2 写流程分析 24
4系统测试与分析 26
4.1 测试环境介绍 26
4.2 测试结果及分析 26
4.2.1 系统性能测试与比较分析 26
4.2.2 重复数据删除压缩比测试 29
4.2.3 检索过滤性能优化的效果测试 30
5 总结与展望 31
5.1 总结 31
5.2 展望 31
致 谢 33
参考文献 34
本论文研究的主要内容有以下几个方面:
(1)重复数据删除技术的设计与实现。通过分析重复数据删除的一般流程,实现了重复数据删除模块的基本功能,包括MapReduce计算框架的管理、计算以及索引表的建立与管理。
(2)重复数据删除中检索优化设计与实现。检索过程是重复数据删除技术中的一大瓶颈,本系统通过基于MapReduce算法的检索过滤技术的实现,极大的提高了检索的性能。
本论文内容组织如下:
第一章对重复数据删除技术的相关背景知识做了简单的介绍,对课题研究的目的、意义以及国内外研究发展状况做了简要的描述。
第二章详细介绍了重复数据删除系统的总体设计。首先阐述了重复数据删除技术的基本原理和系统的总体设计框架,然后对各个功能模块分别进行介绍,包括MapReduce计算框架、计算模块和检索模块。
第三章描述了重复数据删除系统的具体实现过程。首先分模块详述了各个模块的实现方案,然后重点对检索优化算法部分的设计和实现进行了说明,最后分析了系统的处理流程。
第四章对重复数据删除系统各方面的性能进行测试。
第五章总结目前所做的工作并展望未来的研究工作。
3 重复数据删除系统实现
在系统总体设计的基础上,本章主要详述了基于Hadoop的重复数据删除系统的实现,包括MapReduce计算框架的实现、指纹计算和指纹检索模块的实现以及Bloom Filter过滤算法的实现,最后分析了整个系统的读写流程。
3.1 MapReduce计算框架实现
MapReduce计算框架实现的功能就是将重复数据删除之前的请求地址MapReduce_u转换成重复数据删除之后的MapReduce_l,多个去重前的MapReduce_u有可能被转换为同一个MapReduce_l。其计算关系如图4.1所示,MapReduce计算框架主要有两个内容:一是去重前的请求地址,二是指纹索引节点指针,它指向相应数据块的指纹索引节点,通过该节点可以获取数据块真实的存储地址,本文转载自http://www.biyezuopin.vip/onews.asp?id=13957即MapReduce_l。
图4.1 MapReduce计算关系图
MapReduce计算框架节点的数据结构如下:
typedef struct _L_MAP_ITEM
{
U64 MapReduce_u; //去重前的请求地址
HMAP_ITEM *hash_item; //指向对应的指纹索引节点
}L_MAP_ITEM;
重复数据删除之前用户下发的请求地址用MapReduce_u表示,hash_item表示的是一个指向对应数据块指纹索引节点的指针,通过该指针可以读取数据块真实的存储地址MapReduce_l。MapReduce计算框架是一个全局计算结构,针对全局的数据进行记录。每到来一个新的用户请求,先查找MapReduce计算框架,看该请求是否记录过,如果没有命中,那么就新建一个MapReduce计算节点插入到计算框架中;如果命中,就直接在该节点上更新记录。MapReduce计算框架中的所有计算节点利用Linux内核提供的radix_tree来组织,并对外提供查询、插入、删除等接口。
3.2指纹计算模块实现
本系统中使用SHA-1作为指纹计算算法,经过该算法将4KB的数据页转化为160bit的指纹值。系统中直接使用了SHA-1算法的标准C源代码,具体实现过程在此不再详述。
3.2.1 指纹索引表的建立与指纹检索
为了实现对指纹索引表的管理,系统实现中定义了一个三级索引表节点的结构体:
typedef struct hindex_node
{
U32 count; //本索引结点下属索引结点个数
union
{
struct hindex_node *next[INDEX_SANOUT]; //多级索引子数组
HMAP_SUB *sub[INDEX_SANOUT]; //指纹索引指数组
};
}HMAP_INDEX;
在此结构体中,count用来统计本索引结点下属索引结点的个数。通过union定义了一个联合结构,联合结构中的成员都是指针数据,如果该节点为三级索引结构中的第一级或第二级索引节点,那么选择struct hindex_node *next[INDEX_SANOUT]数组,此时下面挂接的仍然是多级索引节点;如果该节点为第三极索引节点,那么选择HMAP_SUB *sub[INDEX_SANOUT]数组,此时下面挂接的是指纹索引节点。其中INDEX_SANOUT为常量,值为256。
在指纹检索过程中,为了记录数据页的真实MapReduce,还定义了一个指纹索引节点结构体:
typedef struct _HMAP_ITEM
{
unsigned char hash[HASH_SIZE]; //保存160bit的指纹值
U64 MapReduce_l; //去重后的真实地址
int ref_cnt; //重复度
}HMAP_ITEM;
结构体中hash[HASH_SIZE]用来保存数据块的指纹值,MapReduce_l是重复数据删除之后数据块的真实存储地址,ref_cnt表示的是该数据页的重复度,即有多少个MapReduce_u计算到这个MapReduce_l上。MapReduce计算框架结构体中的hash_item指针指向的节点就是此结构体声明的节点,通过这种指向关系,我们可以清楚的找到MapReduce的对应关系。
package no.priv.garshol.duke.server;
import java.util.Date;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import java.text.SimpleDateFormat;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import no.priv.garshol.duke.Duke;
import no.priv.garshol.duke.DukeException;
import no.priv.garshol.duke.utils.ObjectUtils;
// we use this to make it easier to deal with properties
import static no.priv.garshol.duke.utils.PropertyUtils.get;
/**
* Starts up Duke processing, and provides a web interface containing
* some minimal information about the status of the service.
*/
public class StatusServlet extends HttpServlet {
private SimpleDateFormat format;
private static DukeController controller;
private static DukeTimer timer;
private int check_interval; // in seconds
private static String DEFAULT_TIMER =
"no.priv.garshol.duke.server.BasicTimer";
public StatusServlet() {
this.format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
// load properties
Properties props = loadPropertiesFromClassPath("duke.properties");
if (props == null)
throw new DukeException("Cannot find 'duke.properties' on classpath");
check_interval = Integer.parseInt(get(props, "duke.check-interval"));
// instantiate main objects
this.controller = new DukeController(props);
String val = get(props, "duke.timer-implementation", DEFAULT_TIMER);
this.timer = (DukeTimer) ObjectUtils.instantiate(val);
timer.init(props);
// start thread automatically if configured to do so
String autostart = get(props, "duke.autostart", "false");
if (autostart.trim().equalsIgnoreCase("true"))
timer.spawnThread(controller, check_interval);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (req.getParameter("nagios") != null) {
doNagios(req, resp);
return;
}
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.write("\n");
out.write("\n");
out.write("");
out.write("DukeThread status ");
out.write("");
out.write("");
out.write("DukeThread status
");
out.write("");
out.write("Status: " + controller.getStatus() +
" ");
out.write("Last check at: " +
format(controller.getLastCheck()) +
" ");
out.write("Last new record at: " +
format(controller.getLastRecord()) + " ");
out.write("Records processed: " +
controller.getRecordCount() +
" ");
out.write("
");
out.write("");
out.write("Duke version " + Duke.getVersionString() + "
");
out.write("");
}
private void doNagios(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (controller == null) {
resp.sendError(500, "No controller; Duke not running");
return;
}
if (controller.isErrorBlocked()) {
resp.sendError(500, controller.getStatus());
return;
}
PrintWriter out = resp.getWriter();
out.write(controller.getStatus() + ", last check: " +
format(controller.getLastCheck()) + ", last record: " +
format(controller.getLastRecord()) + ", records: " +
controller.getRecordCount());
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (req.getParameter("start") != null)
timer.spawnThread(controller, check_interval);
else
timer.stop();
resp.sendRedirect("");
}
private String format(long time) {
return format.format(new Date(time));
}
public void destroy() {
try {
if (controller != null)
controller.close();
if (timer != null)
timer.stop();
} catch (Exception e) {
throw new DukeException(e);
}
}
private static Properties loadPropertiesFromClassPath(String name) {
ClassLoader cloader = Thread.currentThread().getContextClassLoader();
Properties properties = new Properties();
InputStream istream = cloader.getResourceAsStream(name);
if (istream == null)
return null;
try {
properties.load(istream);
} catch (IOException e) {
throw new RuntimeException(e);
}
return properties;
}
}