posts - 200, comments - 8, trackbacks - 0, articles - 0

     摘要: 1.malloc怎么分配空间    malloc与new的关系    看完下面的2再回答这个问题。2. linux对内存的结构描述    a)         /proc/${pid}/         存放进程运行时候所有的信息。程序一结...  阅读全文

posted @ 2012-12-26 19:55 鑫龙 阅读(581) | 评论 (0)编辑 收藏

倒腾了一天,终于在CentOS上装上了hadoop-0.21.0,特此记录,以备后用。

操作系统:CentOS 5.5

Hadoop:hadoop-0.21.0
JDK:1.6.0_17
namenode主机名:master,namenode的IP:192.168.90.91
datanode主机名:slave,datanode的IP:192.168.90.94

第一步:安装并启动ssh服务

CentOS 5.5安装完毕之后以及默认启动了sshd服务,可以在“系统”->“管理”->“服务”中查看sshd服务是否启动。当然了,如果机器上没有安装ssh服务,则执行命令sudo yum install ssh来安装。安装rsync,它是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机间的文件,执行命令sudo yum install rsync。修改每个节点的/etc/hosts文件,将 namenode和datanode的IP信息加入到该文件的尾部:

192.168.90.91 master
192.168.90.94 slave

第二步,配置SSH服务

(1),(2)是针对每一台机器

(1)创建hadoop用户名与用户组

     运行命令su - root,注意,不是命令su root,后者不能携带root用户的参数信息,是不能执行创建用户组和用户命令的。执行命令:groupadd hadoop和命令useradd -g hadoop hadoop。注意不能在/home目录下创建hadoop目录,否则创建hadoop用户会失败。创建好用户以后最好是重新启动计算机,以hadoop用户登录系统。这样在之后的操作中就不需要su到hadoop用户下,而且也不会纠缠于文件的owner问题。

(2)生成ssh密钥

     如果是其他用户登录的则切换到hadoop用户下,执行命令su - hadoop,在/home/hadoop目录下执行命令:ssh-keygen -t rsa(一路回车,选择默认的保存路径),密钥生成成功之后,进入.ssh目录,执行cd .ssh,执行命令:cp id_rsa.pub authorized_keys。这个时候运行ssh localhost,让系统记住用户,之后ssh localhost就不需要再输入密码了。

(3)交换公钥

     将namenode上的公钥拷贝到datanode,在hadoop用户的用户目录下(/home/hadoop)下执行命令ssh-copy-id -i $HOME/.ssh/id_rsa.pub hadoop@slave。同理,也可以将datanode上的公钥拷贝到namenode,但这不是必须的。这样两台机器在hadoop用户下互相ssh就不需要密码了。

 

第三步,安装JDK1.6或以上(每台机器)

(1)执行命令yum install jdk

(2)如果第一步没有找到源码包,那么就需要到官网上下载了,https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u22-oth-JPR@CDS-CDS_Developer。

(3)新建目录/usr/java,将源码包jdk-6u22-linux-i586.bin复制到该目录下,执行命令chmod a+x jdk-6u22-linux-i586.bin
      使当前用户拥有对jdk-6u22-linux-i586.bin的执行权限。执行命令sudo ./jdk-6u22-linux-i586.bin进行安装

(4)修改/etc/profile来添加环境变量,/etc/profile中设置的环境变量就像Windows下环境变量中的系统变量一样,所有用户都可以使用。
      用文本编辑器打开/etc/profile
      # vi /etc/profile
      在最后加入以下几行:
      export JAVA_HOME=/usr/java/jdk1.6.0_22
      export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
      export PATH=$PATH:$JAVA_HOME/bin
      这样我们就设置好了JDK,在centos下 source /etc/profile 就可以生效了.

运行命令java -version可以判断是否安装成功

 

第四步,安装hadoop

原来现在才开始安装hadoop,准备工作也作得太多了,废话少说。

(1)新建目录/usr/local/hadoop,将hadoop-0.21.0.tar.gz解压缩到该目录下,执行命令sudo tar -xvzf hadoop-0.21.0.tar.gz,修改/etc/profile文件,将hadoop的安装目录append到文件最后:

export HADOOP_HOME=/usr/local/hadoop/hadoop-0.21.0
export PATH=$HADOOP_HOME/bin:$PATH
(2)配置/conf/hadoop-env.sh文件,修改java_home环境变量
export JAVA_HOME=/usr/java/jdk1.6.0_22/
export HADOOP_CLASSPATH=.
(3)配置 core-site.xml 文件
<configuration>
      <property>
            <name>hadoop.tmp.dir</name>
            <value>/usr/local/hadoop/hadoop-0.21.0/tmp</value>
           (注意,请先在 hadoopinstall 目录下建立 tmp 文件夹)
            <description>A base for other temporary directories.</description>
      </property>
<!-- file system properties -->
      <property>
            <name>fs.default.name</name>
      <value>hdfs://master:54310</value>
      </property>
</configuration>
(4)配置 hdfs-site.xml 文件
<configuration>
      <property>
            <name>dfs.replication</name>
            <value>1</value>(这里共两台机器,如果将主节点也配置为datanode,则这里可以写2)
      </property>
<configuration>
(5)配置 mapred-site.xml 文件
<configuration>
      <property>
            <name>mapred.job.tracker</name>
            <value>master:54311</value>
      </property>
</configuration>
(6)配置 conf/masters 文件,加入 namenode 的 ip 地址
master
(7)配置 slaves 文件, 加入所有 datanode 的 ip 地址

slave

 

(如果之前的hdfs-site.xml文件中的拷贝数设置为2,则需要将master也加入到slaves文件中)

(8)将 namenode 上 配 置 好 的 hadoop 所 在 文 件 夹 hadoop-0.21.0 复 制 到
datanode 的/usr/lcoal/hadoop/目录下(实际上 masters,slavers 文件时不必要的, 复制了也
没问题)。
(9)配置datanode的/etc/profile 文件,在文件尾append下列内容:
export HADOOP_HOME=/usr/local/hadoop/hadoop-0.21.0
export PATH=$HADOOP_HOME/bin:$PATH

第五步,启动hadoop
首先记得关闭系统的防火墙,root用户下执行命令/etc/init.d/iptables stop,运行命令/etc/init.d/iptables status检查防火墙状态。hadoop用户下,在namenode的/usr/local/hadoop/hadoop-0.21.0/bin目录下打开终端,执行命令hadoop namenode -format,格式化目录节点。注意,/usr/local/hadoop/hadoop-0.21.0/tmp目录是可以写的,否则在格式化时会出现异常。执行命令start-all.sh启动hadoop集群,执行命令jps查看进程,执行命令hadoop dfsadmin -report查看状态。在浏览器中输入http://master:50070以web方式查看集群状态。查看jobtraker的运行状态:http://www.ibm.com/developerworks/cn/linux/l-hadoop-2/index.html
PS:格式化namenode的时候最好将节点的tmp目录清空、删除logs目录中的文件。

到这里,基于CentOS5.5的hadoop集群搭建完毕!

 

参考资料:http://www.ibm.com/developerworks/cn/linux/l-hadoop-2/index.html

posted @ 2012-12-25 20:54 鑫龙 阅读(287) | 评论 (0)编辑 收藏

第一步:查看Linux自带的JDK是否已安装 (卸载centOS已安装的1.4)

安装好的CentOS会自带OpenJdk,用命令 java -version ,会有下面的信息:

java version "1.6.0"
OpenJDK Runtime Environment (build 1.6.0-b09)
OpenJDK 64-Bit Server VM (build 1.6.0-b09, mixed mode)

最好还是先卸载掉openjdk,在安装sun公司的jdk.

先查看 rpm -qa | grep java

显示如下信息:

java-1.4.2-gcj-compat-1.4.2.0-40jpp.115
java-1.6.0-openjdk-1.6.0.0-1.7.b09.el5

卸载:

rpm -e --nodeps java-1.4.2-gcj-compat-1.4.2.0-40jpp.115
rpm -e --nodeps java-1.6.0-openjdk-1.6.0.0-1.7.b09.el5

还有一些其他的命令

rpm -qa | grep gcj

rpm -qa | grep jdk

如果出现找不到openjdk source的话,那么还可以这样卸载

yum -y remove java java-1.4.2-gcj-compat-1.4.2.0-40jpp.115
yum -y remove java java-1.6.0-openjdk-1.6.0.0-1.7.b09.el5

 

 

<1># rpm -qa|grep jdk ← 查看jdk的信息或直接执行 
或 
# rpm -q jdk 
或 
# java -version 
<2># rpm -qa | grep gcj ← 确认gcj的版本号 
<3># yum -y remove java-1.4.2-gcj-compat ← 卸载gcj 

第二步:安装JDK 
<1>从SUN下载jdk-1_5_0_14-linux-i586-rpm.bin或jdk-1_5_0_14-linux-i586.bin 
在/usr下新建java文件夹,将安装包放在/usr/java目录下 
# mkdir /usr/java 
<2>安装JDK 
# cd /usr/java 
①jdk-1_5_0_14-linux-i586-rpm.bin文件安装 
# chmod 777 jdk-1_5_0_14-linux-i586-rpm.bin ← 修改为可执行 
# ./jdk-1_5_0_14-linux-i586-rpm.bin ← 选择yes同意上面的协议 
# rpm -ivh jdk-1_5_0_14-linux-i586.rpm ← 选择yes直到安装完毕 
②jdk-1_5_0_14-linux-i586.bin文件安装 
# chmod a+x jdk-1_5_0_14-linux-i586.bin ← 使当前用户拥有执行权限 
# ./jdk-1_5_0_14-linux-i586.bin ← 选择yes直到安装完毕 

第三步:配置环境变量 
<1># vi /etc/profile 
<2>在最后加入以下几行: 
export JAVA_HOME=/usr/java/jdk1.6.0_10 
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 
export PATH=$PATH:$JAVA_HOME/bin 
<3># reboot ← 重启机器配置生效

posted @ 2012-12-25 19:17 鑫龙 阅读(320) | 评论 (0)编辑 收藏

引子:

怎么对容器中的所有对象都进行同一个操作?我们可能首先想到的是用循环来实现。
比如有如下的一个类:

class ClxECS{
public:
    int DoSomething() { 
    cout << "Output from method DoSomething!" << endl; // 这里以输出一句话来代替具体的操作
    return 0;
    };
};


现在定义如下一个vector:

vector<ClxECS*> vECS;

for(int i = 0; i < 13; i++){
    ClxECS *pECS = new ClxECS;
    vECS.push_back(pECS);
}


 如果要对容器vECS中的所有对象都进行DoSomething()的操作,那么下面的循环可能是首先想到的方案:

for(int i = 0; i < vECS.size(); i++)
    vECS.at(i)->DoSomething();
   

 当然,我们也可以用iterator:

for(vector<ClxECS*>::iterator it = vECS.begin(); it != vECS.end(); ++it)
    (*it)->DoSomething();


 但是,有很多C++的高手和牛人们都会给我们一个忠告,那就是:在处理STL里面的容器的时候,尽量不要自己写循环。
那么,我们就只好用STL算法里面的for_each了。
首先,添加如下一个函数:

int DoSomething(ClxECS *pECS)
{
    return pECS->DoSomething();
}
然后就可以用for_each来实现我们想要的功能:

for_each(vECS.begin(), vECS.end(), &DoSomething);
说了半天,似乎跟mem_fun和mem_fun_ref没有什么关系。其实,说那么多都是为了引出mem_fun和mem_fun_ref。在用for_each的时候,如果我们不添加上面的那个函数,该怎么办呢?


这个时候就该mem_fun和mem_fun_ref隆重登场了。用如下这一行代码就行了:

for_each(vECS.begin(), vECS.end(), mem_fun(&ClxECS::DoSomething));
实际上就是由迭代器去调用成员函数.

 

例子:

list<Widget *> lpw;
for_each(lpw.begin(), lpw.end(),mem_fun(&Widget::test)); // pw->test();

vector<Widget> vw;
for_each(vw.begin(), vw.end(),mem_fun_ref(&Widget::test)); // w.test();

成员函数有参数的情况:将值传入,再bind1st为this

std::for_each(m_erased.begin(), m_erased.end(),std::bind1st(std::mem_fun(&SocketSet::_replace_with_last), this));
//相当于this->_replace_with_last(iter)  //iter

两者区别:
mem_fun_ref的作用和用法跟mem_fun一样,唯一的不同就是:

当容器中存放的是对象实体的时候用mem_fun_ref,

当容器中存放的是对象的指针的时候用mem_fun。

posted @ 2012-12-25 17:06 鑫龙 阅读(2633) | 评论 (0)编辑 收藏

     摘要:        本文的技术可以参考本博客:  Traits 技术 --- 模板元编程 (转)          迭代器可以区分为不同的类型,每个类型都有特定的迭代器功能。        根据迭代器类型,将算法根据类型的不同实现出更加有效率的版本,将会很...  阅读全文

posted @ 2012-12-24 21:38 鑫龙 阅读(507) | 评论 (0)编辑 收藏

     问题的由来:
vector<int> coll;
.....
sort(++coll.begin(),coll.end());
    对于sort那句,如果vecotor的底层是用指针实现(通常就是用指针实现),那么编译无法通过。
   
    其实此次问题该系列上一次文章类似<<c++标准程序库----p55----为什么需要auto_ptr_ref? (转)>>,都是临时对象惹的祸。
   c++不允许任何修改基本类型(包括指针)临时值的行为,但对于类类型则允许。怎么理解呢?举个例子:
#include <iostream>
using namespace std;
 
 
class node
{
 
public:
    node(int j = 0):i(j){}
    int i;
    void operator++()//这里重定义一个赋值操作符=,为下面的赋值做准备。
    {
        i++;
    }
};
 
node fun()                          
{
    node temp(1);
    return temp;//这里返回一个类的临时变量
}
 
int* fun2()
{
    int a =1;
    int* b = &a;  //这里返回存在栈中局部变量的地址是不对的,但只是做实验! 
    return b;//这里返回一个普通int临时变量
 
}
int main()
{
    ++fun();   // ok!
    ++fun2(); //这句无法通过编译 
    system("pause");
 
}
    上面的例子fun()返回类类型的临时变量可以被修改,但fun2()返回的指针的临时变量不能被修改。
    回到开始的例子,因为vector的迭代器底层是用指针实现,所以begin()函数返回的是一个指针类型的临时变量,如果用++操作去修改,那就违反了c++的设计原则,所以无法编译通过。做到底还是左值不能被修改!!!

posted @ 2012-12-23 22:18 鑫龙 阅读(628) | 评论 (0)编辑 收藏

            从hadoop框架与MapReduce模式中谈海量数据处理

前言

    几周前,当我最初听到,以致后来初次接触Hadoop与MapReduce这两个东西,我便稍显兴奋,觉得它们很是神秘,而神秘的东西常能勾起我的兴趣,在看过介绍它们的文章或论文之后,觉得Hadoop是一项富有趣味和挑战性的技术,且它还牵扯到了一个我更加感兴趣的话题:海量数据处理。

    由此,最近凡是空闲时,便在看“Hadoop”,“MapReduce”“海量数据处理”这方面的论文。但在看论文的过程中,总觉得那些论文都是浅尝辄止,常常看的很不过瘾,总是一个东西刚要讲到紧要处,它便结束了,让我好生“愤懑”。

    尽管我对这个Hadoop与MapReduce知之甚浅,但我还是想记录自己的学习过程,说不定,关于这个东西的学习能督促我最终写成和“经典算法研究系列”一般的一系列文章。

    Ok,闲话少说。本文从最基本的mapreduce模式,Hadoop框架开始谈起,然后由各自的架构引申开来,谈到海量数据处理,最后谈谈淘宝的海量数据产品技术架构,以为了兼备浅出与深入之效,最终,希望得到读者的喜欢与支持。谢谢。

    由于本人是初次接触这两项技术,文章有任何问题,欢迎不吝指正。再谢一次。Ok,咱们开始吧。

第一部分、mapreduce模式与hadoop框架深入浅出

架构扼要

         想读懂此文,读者必须先要明确以下几点,以作为阅读后续内容的基础知识储备:

  1. Mapreduce是一种模式。
  2. Hadoop是一种框架。
  3. Hadoop是一个实现了mapreduce模式的开源的分布式并行编程框架。

    所以,你现在,知道了什么是mapreduce,什么是hadoop,以及这两者之间最简单的联系,而本文的主旨即是,一句话概括:在hadoop的框架上采取mapreduce的模式处理海量数据。下面,咱们可以依次深入学习和了解mapreduce和hadoop这两个东西了。

Mapreduce模式

    前面说了,mapreduce是一种模式,一种什么模式呢?一种云计算的核心计算模式,一种分布式运算技术,也是简化的分布式编程模式,它主要用于解决问题的程序开发模型,也是开发人员拆解问题的方法。

    Ok,光说不上图,没用。如下图所示,mapreduce模式的主要思想是将自动分割要执行的问题(例如程序)拆解成map(映射)和reduce(化简)的方式,流程图如下图1所示:

    在数据被分割后通过Map 函数的程序将数据映射成不同的区块,分配给计算机机群处理达到分布式运算的效果,在通过Reduce 函数的程序将结果汇整,从而输出开发者需要的结果。

    MapReduce 借鉴了函数式程序设计语言的设计思想,其软件实现是指定一个Map 函数,把键值对(key/value)映射成新的键值对(key/value),形成一系列中间结果形式的key/value 对,然后把它们传给Reduce(规约)函数,把具有相同中间形式key 的value 合并在一起。Map 和Reduce 函数具有一定的关联性。函数描述如表1 所示:

    MapReduce致力于解决大规模数据处理的问题,因此在设计之初就考虑了数据的局部性原理,利用局部性原理将整个问题分而治之。MapReduce集群由普通PC机构成,为无共享式架构。在处理之前,将数据集分布至各个节点。处理时,每个节点就近读取本地存储的数据处理(map),将处理后的数据进行合并(combine)、排序(shuffle and sort)后再分发(至reduce节点),避免了大量数据的传输,提高了处理效率。无共享式架构的另一个好处是配合复制(replication)策略,集群可以具有良好的容错性,一部分节点的down机对集群的正常工作不会造成影响。

    ok,你可以再简单看看下副图,整幅图是有关hadoop的作业调优参数及原理,图的左边是MapTask运行示意图,右边是ReduceTask运行示意图:

    如上图所示,其中map阶段,当map task开始运算,并产生中间数据后并非直接而简单的写入磁盘,它首先利用内存buffer来对已经产生的buffer进行缓存,并在内存buffer中进行一些预排序来优化整个map的性能。而上图右边的reduce阶段则经历了三个阶段,分别Copy->Sort->reduce。我们能明显的看出,其中的Sort是采用的归并排序,即merge sort。

    了解了什么是mapreduce,接下来,咱们可以来了解实现了mapreduce模式的开源框架—hadoop。

Hadoop框架

    前面说了,hadoop是一个框架,一个什么样的框架呢?Hadoop 是一个实现了MapReduce 计算模型的开源分布式并行编程框架,程序员可以借助Hadoop 编写程序,将所编写的程序运行于计算机机群上,从而实现对海量数据的处理。

    此外,Hadoop 还提供一个分布式文件系统(HDFS)及分布式数据库(HBase)用来将数据存储或部署到各个计算节点上。所以,你可以大致认为:Hadoop=HDFS(文件系统,数据存储技术相关)+HBase(数据库)+MapReduce(数据处理)。Hadoop 框架如图2 所示:

    借助Hadoop 框架及云计算核心技术MapReduce 来实现数据的计算和存储,并且将HDFS 分布式文件系统和HBase 分布式数据库很好的融入到云计算框架中,从而实现云计算的分布式、并行计算和存储,并且得以实现很好的处理大规模数据的能力。

Hadoop的组成部分

    我们已经知道,Hadoop是Google的MapReduce一个Java实现。MapReduce是一种简化的分布式编程模式,让程序自动分布到一个由普通机器组成的超大集群上并发执行。Hadoop主要由HDFS、MapReduce和HBase等组成。具体的hadoop的组成如下图:

    由上图,我们可以看到:

    1、             Hadoop HDFS是Google GFS存储系统的开源实现,主要应用场景是作为并行计算环境(MapReduce)的基础组件,同时也是BigTable(如HBase、HyperTable)的底层分布式文件系统。HDFS采用master/slave架构。一个HDFS集群是有由一个Namenode和一定数目的Datanode组成。Namenode是一个中心服务器,负责管理文件系统的namespace和客户端对文件的访问。Datanode在集群中一般是一个节点一个,负责管理节点上它们附带的存储。在内部,一个文件其实分成一个或多个block,这些block存储在Datanode集合里。如下图所示(HDFS体系结构图):

    2、             Hadoop MapReduce是一个使用简易的软件框架,基于它写出来的应用程序能够运行在由上千个商用机器组成的大型集群上,并以一种可靠容错的方式并行处理上TB级别的数据集。

    一个MapReduce作业(job)通常会把输入的数据集切分为若干独立的数据块,由 Map任务(task)以完全并行的方式处理它们。框架会对Map的输出先进行排序,然后把结果输入给Reduce任务。通常作业的输入和输出都会被存储在文件系统中。整个框架负责任务的调度和监控,以及重新执行已经失败的任务。如下图所示(Hadoop MapReduce处理流程图):

    3、             Hive是基于Hadoop的一个数据仓库工具,处理能力强而且成本低廉。

主要特点

存储方式是将结构化的数据文件映射为一张数据库表。提供类SQL语言,实现完整的SQL查询功能。可以将SQL语句转换为MapReduce任务运行,十分适合数据仓库的统计分析。

不足之处:

采用行存储的方式(SequenceFile)来存储和读取数据。效率低:当要读取数据表某一列数据时需要先取出所有数据然后再提取出某一列的数据,效率很低。同时,它还占用较多的磁盘空间。

由于以上的不足,有人(查礼博士)介绍了一种将分布式数据处理系统中以记录为单位的存储结构变为以列为单位的存储结构,进而减少磁盘访问数量,提高查询处理性能。这样,由于相同属性值具有相同数据类型和相近的数据特性,以属性值为单位进行压缩存储的压缩比更高,能节省更多的存储空间。如下图所示(行列存储的比较图):

4、             HBase

    HBase是一个分布式的、面向列的开源数据库,它不同于一般的关系数据库,是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。HBase使用和 BigTable非常相同的数据模型。用户存储数据行在一个表里。一个数据行拥有一个可选择的键和任意数量的列,一个或多个列组成一个ColumnFamily,一个Fmaily下的列位于一个HFile中,易于缓存数据。表是疏松的存储的,因此用户可以给行定义各种不同的列。在HBase中数据按主键排序,同时表按主键划分为多个HRegion,如下图所示(HBase数据表结构图):

    Ok,行文至此,看似洋洋洒洒近千里,但若给读者造成阅读上的负担,则不是我本意。接下来的内容,我不会再引用诸多繁杂的专业术语,以给读者心里上造成不良影响。

    我再给出一副图,算是对上文所说的hadoop框架及其组成部分做个总结,如下图所示,便是hadoop的内部结构,我们可以看到,海量的数据交给hadoop处理后,在hadoop的内部中,正如上文所述:hadoop提供一个分布式文件系统(HDFS)及分布式数据库(Hbase)用来存储或部署到各个计算点上,最终在内部采取mapreduce的模式对其数据进行处理,然后输出处理结果:


第二部分、淘宝海量数据产品技术架构解读—学习海量数据处理经验

    在上面的本文的第一部分中,我们已经对mapreduce模式及hadoop框架有了一个深入而全面的了解。不过,如果一个东西,或者一个概念不放到实际应用中去,那么你对这个理念永远只是停留在理论之内,无法向实践迈进。

    Ok,接下来,本文的第二部分,咱们以淘宝的数据魔方技术架构为依托,通过介绍淘宝的海量数据产品技术架构,来进一步学习和了解海量数据处理的经验。

淘宝海量数据产品技术架构

    如下图2-1所示,即是淘宝的海量数据产品技术架构,咱们下面要针对这个架构来一一剖析与解读。

    相信,看过本博客内其它文章的细心读者,定会发现,图2-1最初见于本博客内的此篇文章:从几幅架构图中偷得半点海量数据处理经验之上,同时,此图2-1最初发表于《程序员》8月刊,作者:朋春。

    在此之前,有一点必须说明的是:本文下面的内容大都是参考自朋春先生的这篇文章:淘宝数据魔方技术架构解析所写,我个人所作的工作是对这篇文章的一种解读与关键技术和内容的抽取,以为读者更好的理解淘宝的海量数据产品技术架构。与此同时,还能展示我自己读此篇的思路与感悟,顺带学习,何乐而不为呢?。

    Ok,不过,与本博客内之前的那篇文章(几幅架构图中偷得半点海量数据处理经验)不同,本文接下来,要详细阐述这个架构。我也做了不少准备工作(如把这图2-1打印了下来,经常琢磨):

 

                                              图2-1 淘宝海量数据产品技术架构

    好的,如上图所示,我们可以看到,淘宝的海量数据产品技术架构,分为以下五个层次,从上至下来看,它们分别是:数据源,计算层,存储层,查询层和产品层。我们来一一了解这五层:

  1. 数据来源层。存放着淘宝各店的交易数据。在数据源层产生的数据,通过DataX,DbSync和Timetunel准实时的传输到下面第2点所述的“云梯”。
  2. 计算层。在这个计算层内,淘宝采用的是hadoop集群,这个集群,我们暂且称之为云梯,是计算层的主要组成部分。在云梯上,系统每天会对数据产品进行不同的mapreduce计算。
  3. 存储层。在这一层,淘宝采用了两个东西,一个使MyFox,一个是Prom。MyFox是基于MySQL的分布式关系型数据库的集群,Prom是基于hadoop Hbase技术 的(读者可别忘了,在上文第一部分中,咱们介绍到了这个hadoop的组成部分之一,Hbase—在hadoop之内的一个分布式的开源数据库)的一个NoSQL的存储集群。
  4. 查询层。在这一层中,有一个叫做glider的东西,这个glider是以HTTP协议对外提供restful方式的接口。数据产品通过一个唯一的URL来获取到它想要的数据。同时,数据查询即是通过MyFox来查询的。下文将具体介绍MyFox的数据查询过程。
  5.  产品层。简单理解,不作过多介绍。

    接下来,咱们重点来了解第三层-存储层中的MyFox与Prom,然后会稍带分析下glide的技术架构,最后,再了解下缓存。文章即宣告结束。

    我们知道,关系型数据库在我们现在的工业生产中有着广泛的引用,它包括Oracle,MySQL、DB2、Sybase和SQL Server等等。

MyFOX

    淘宝选择了MySQL的MyISAM引擎作为底层的数据存储引擎。且为了应对海量数据,他们设计了分布式MySQL集群的查询代理层-MyFOX。

如下图所示,是MySQL的数据查询过程:

                                                            图2-2 MyFOX的数据查询过程

    在MyFOX的每一个节点中,存放着热节点和冷节点两种节点数据。顾名思义,热节点存放着最新的,被访问频率较高的数据;冷节点,存放着相对而来比较旧的,访问频率比较低的数据。而为了存储这两种节点数据,出于硬件条件和存储成本的考虑,你当然会考虑选择两种不同的硬盘,来存储这两种访问频率不同的节点数据。如下图所示:

                                                           图2-3 MyFOX节点结构

     “热节点”,选择每分钟15000转的SAS硬盘,按照一个节点两台机器来计算,单位数据的存储成本约为4.5W/TB。相对应地,“冷数据”我们选择了每分钟7500转的SATA硬盘,单碟上能够存放更多的数据,存储成本约为1.6W/TB。

Prom

出于文章篇幅的考虑,本文接下来不再过多阐述这个Prom了。如下面两幅图所示,他们分别表示的是Prom的存储结构以及Prom查询过程:

                                              图2-4 Prom的存储结构

 

                                                          图2-5 Prom查询过程

glide的技术架构

    

                                               图2-6 glider的技术架构

    在这一层-查询层中,淘宝主要是基于用中间层隔离前后端的理念而考虑。Glider这个中间层负责各个异构表之间的数据JOIN和UNION等计算,并且负责隔离前端产品和后端存储,提供统一的数据查询服务。

缓存

    除了起到隔离前后端以及异构“表”之间的数据整合的作用之外,glider的另外一个不容忽视的作用便是缓存管理。我们有一点须了解,在特定的时间段内,我们认为数据产品中的数据是只读的,这是利用缓存来提高性能的理论基础。

在上文图2-6中我们看到,glider中存在两层缓存,分别是基于各个异构“表”(datasource)的二级缓存和整合之后基于独立请求的一级缓存。除此之外,各个异构“表”内部可能还存在自己的缓存机制。

                                                           图2-7 缓存控制体系

    图2-7向我们展示了数据魔方在缓存控制方面的设计思路。用户的请求中一定是带了缓存控制的“命令”的,这包括URL中的query string,和HTTP头中的“If-None-Match”信息。并且,这个缓存控制“命令”一定会经过层层传递,最终传递到底层存储的异构“表”模块。

    缓存系统往往有两个问题需要面对和考虑:缓存穿透与失效时的雪崩效应。

  1. 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。至于如何有效地解决缓存穿透问题,最常见的则是采用布隆过滤器(这个东西,在我的此篇文章中有介绍:),将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

    而在数据魔方里,淘宝采用了一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

      2、缓存失效时的雪崩效应尽管对底层系统的冲击非常可怕。但遗憾的是,这个问题目前并没有很完美的解决方案。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

    在数据魔方中,淘宝设计的缓存过期机制理论上能够将各个客户端的数据失效时间均匀地分布在时间轴上,一定程度上能够避免缓存同时失效带来的雪崩效应。

本文参考:

  1. 基于云计算的海量数据存储模型,侯建等。
  2. 基于hadoop的海量日志数据处理,王小森
  3. 基于hadoop的大规模数据处理系统,王丽兵。
  4. 淘宝数据魔方技术架构解析,朋春。
  5. Hadoop作业调优参数整理及原理,guili。

读者点评@xdylxdyl:

  1. We want to count all the books in the library. You count up shelf #1, I count up shelf #2. That's map. The more people we get, the faster it goes. Now we get together and add our individual counts. That's reduce。
  2. 数据魔方里的缓存穿透,架构,空数据缓存这些和Hadoop一点关系都么有,如果是想讲一个Hadoop的具体应用的话,数据魔方这部分其实没讲清楚的。
  3. 感觉你是把两个东西混在一起了。不过这两个都是挺有价值的东西,或者说数据魔方的架构比Hadoop可能更重要一些,基本上大的互联网公司都会选择这么做。Null对象的缓存保留五分钟未必会有好的结果吧,如果Null对象不是特别大,数据的更新和插入不多也可以考虑实时维护。
  4. Hadoop本身很笨重,不知道在数据魔方里是否是在扮演着实时数据处理的角色?还是只是在做线下的数据分析的?

结语:写文章是一种学习的过程。尊重他人劳动成果,转载请注明出处。谢谢。July、2011/8/20。完。

转自:
http://blog.csdn.net/v_july_v/article/details/6704077

posted @ 2012-12-23 19:55 鑫龙 阅读(334) | 评论 (0)编辑 收藏

1.模块子系统(Module Subsystem)、统一设备模型(Unified Device Model)和 PnP支持模块子系统发生了重大变化。
    
2.稳定性有所提高 
    为了彻底避免内核加载或者导出正在被使用的内核模块,或者至少为了减少加载或者卸载模块的同时使用该模块的可能性(这有时会导致系统崩溃),内核加载和导出内核模块的过程都得到了改进。
    我一直以为Linux不会死机,直到我的基于2.4内核Linux突然死机,造成重大损失。所以,强烈建议所有基于2.4内核的Linux应用系统马上升级内核,一定注意,升级前备份系统,防止升级出错造成重大损失,也要防止硬件不兼容、应用系统不兼容问题,最好先测试一下,再上线运行!

3.统一设备模型 
    统一设备模型的创建是 2.6 内核最重要的变化之一。它促进了模块接口的标准化,其目的是更好地控制和管理设备,例如:更准确地确定系统设备。 电源管理和设备电源状态。 改进的系统总线结构管理。

4.即插即用(PnP)支持 
    运行 2.6 内核的 Linux 成为一个真正即插即用的 OS。例如,对 ISA PnP 扩展、遗留 MCA 和 EISA 总线以及热插拔设备的 PnP 支持。

5.内核基础设施的变化 
    为了区别以 .o 为扩展名的常规对象文件,内核模块现在使用的扩展名是 .ko。 创建了新的 sysfs 文件系统,当内核发现设备树时就会描述它。 内存支持,NUMA 支持 ,支持更大数量的 RAM。2.6 内核支持更大数量的 RAM,在分页模式下最高可达 64GB。

6.NUMA 
    对非一致内核访问(Non-Uniform Memory Access - NUMA)系统的支持是 2.6 内核中新出现的。

7.线程模型,NPTL 
    相对于 v2.4 的 LinuxThreads,在版本 2.6 中新出现的是 NPTL(Native POSIX Threading Library)。 NPTL 为 Linux 带来了企业级线程支持,提供的性能远远超过了 LinuxThreads。它所基于的用户与内核线程的比率是 1:1。 
    在 2003 年 10 月,GNU C 程序库 glibc 中融入了 NPTL 支持,Red Hat 率先在 Red Hat Linux 9 和 Red Hat Enterprise Linux 中使用定制的 v2.4 内核实现了 NPTL。

8.性能改进 
    新的调度器算法 ,2.6 Linux 内核引入了新的 O(1) 算法。在高负载情况下它运行得特别好。新的调度器基于每个 CPU 来分布时间片, 这样就消除了全局同步和重新分配循环,从而提高了性能。 
    内核抢占(Kernel Preemption) ,新的 2.6 内核是抢占式的。这将显著地提高交互式和多媒体应用程序的性能。 
    I/O 性能改进,Linux 的 I/O 子系统也发生了重大的变化,通过修改 I/O 调度器来确保不会有进程驻留在队列中过长时间等待进行输入/输出操作, 这样就使得 I/O 操作的响应更为迅速。
    快速用户空间互斥(Fast User-Space Mutexes) ,“futexes”(快速用户空间互斥)可以使线程串行化以避免竞态条件,引入它也提高了响应速度。 通过在内核空间中部分实现“futexes”以允许基于竞争设置等待任务的优先级而实现改进。

9.扩展性改进 
    处理器数目更多,Linux 内核 2.6 最多可以支持 64 个 CPU。支持更大的内存,归功于 PAE(物理地址扩展,Physical Address Extensions),在 32-位系统上分页模式下所支持的内存增加到了 64GB。 
    用户和组,惟一用户和组的数量从 65,000 增至 40 多亿,也就是从 16-位增加到了 32-位。 
    PID 的数量,PID 的最大数量从 32,000 增至 10 亿。 
    打开文件描述符的数量,打开文件描述符的数量没有增加,但是不再需要事先设置该参数,它将自行调节。

10.支持更多的设备
    在 Linux 内核 2.6 之前,内核中有可以约束大型系统的限制,比如每条链 256 个设备。v2.6 内核彻底地打破了这些限制, 不但可以支持更多类型的设备,而且支持更多同类型的设备。在 Linux 2.6 系统中,可以支持 4095 种主要的设备类型, 每一个单独的类型可以有超过一百万个子设备。 文件系统大小, Linux 内核 2.6 所允许的可寻址文件系统大小最大为 16 TB。

11.文件系统 
    ext2、ext3 和 ReiserFS 等传统 Linux 文件系统得到了显著的改进。最值得注意的改进是扩展属性(或文件元数据)的引入。 最重要的是 POSIX ACL 的实现,这是对普通 UNIX 权限的扩展,可以支持更细化的用户访问控制。

12.除了对传统 Linux 文件系统的改进支持以外,新的内核完全支持在 Linux 中相对较新的 XFS 文件系统。 Linux 2.6 内核现在还引入了对 NTFS 文件系统的改进的支持,现在允许以读/写模式安装 NTFS 文件系统。

posted @ 2012-12-19 16:14 鑫龙 阅读(238) | 评论 (0)编辑 收藏

     摘要: 转自https://www.ibm.com/developerworks/cn/linux/l-ipc/part6/, 作者:郑彦兴一个套接口可以看作是进程间通信的端点(endpoint),每个套接口的名字都是唯一的(唯一的含义是不言而喻的),其他进程可以发现、连接并且 与之通信。通信域用来说明套接口通信的协议,不同的通信域有不同的通信协议以及套接口的地址结构等等,因此,创建一个套接口时,要指明它...  阅读全文

posted @ 2012-12-18 14:04 鑫龙 阅读(268) | 评论 (0)编辑 收藏

     摘要: 转自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html, 作者:郑彦兴系统调用mmap()通过映射一个普通文件实现共享内存。系统V则是通过映射特殊文件系统shm中的文件实现进程间的共享内存通信。也就是说,每个共享内存区域对应特殊文件系统shm中的一个文件(这是通过shmid_kernel结构联系起来的),后面还将阐述...  阅读全文

posted @ 2012-12-18 14:03 鑫龙 阅读(219) | 评论 (0)编辑 收藏

仅列出标题
共20页: First 7 8 9 10 11 12 13 14 15 Last