輕輕鬆鬆談談大數據

大數據概述

什麼是大數據

大數據定義:

大數據(big data),IT行業術語,是指無法在一定時間範圍內用常規軟件工具進行捕捉、管理和處理的數據集合,是需要新處理模式才能具有更強的決策力、洞察發現力和流程優化能力的海量、高增長率和多樣化的信息資產。

麥肯錫全球研究所:一種規模大到在獲取、存儲、管理、分析方面大大超出了傳統數據庫軟件工具能力範圍的數據集合,具有海量的數據規模、快速的數據流轉、多樣的數據類型和價值密度低四大特徵

我:大數據顧名思義:數據大(至少TB以上)

數據單位:

bit、Byte、KB、MB、GB、TB、PB、EB、ZB、YB、BB、NB、DB

1024

產生的背景:
在這裏插入圖片描述

大數據的意義:

(1)對大量消費者提供產品或服務的企業可以利用大數據進行精準營銷;

(2)做小而美模式的中小微企業可以利用大數據做服務轉型;

(3)面臨互聯網壓力之下必須轉型的傳統企業需要與時俱進充分利用大數據的價值。

大數據的作用

(1)及時解析故障、問題和缺陷的根源,每年可能爲企業節省數十億美元。(網站日誌)

(2)爲成千上萬的快遞車輛規劃實時交通路線,躲避擁堵。

(3)分析所有SKU(庫存進出容量),以利潤最大化爲目標來定價和清理庫存。

(4)根據客戶的購買習慣,爲其推送他可能感興趣的優惠信息。

(5)從大量客戶中快速識別出金牌客戶。

(6)使用點擊流分析和數據挖掘來規避欺詐行爲。(刷點擊)) [13]

(4)信用評價

(5)犯罪預測(洗黑錢、離岸金融數據監控、國內非法資金轉移 )(網聯清算有限公司)

大數據涉及的一般流程

數據採集:數據採集,又稱數據獲取,是利用一種裝置,從系統外部採集數據並輸入到系統內部的一個接口。數據採集技術目前廣泛應用於各個領域。針對製造業企業的龐大生產數據,數據採集工具尤爲重要。()

數據處理/分析、挖掘:從大量的原始數據抽取出有價值的信息,即數據轉換成信息的過程

數據存儲:將處理後的數據存入到數據庫中,一般爲大型的分佈式存儲

數據可視化:將數據化的展示出來

數據收集(獲取):爬蟲爬數據或者其他

flume採集工具

大數據處理目前比較流行的是兩種方法,一種是離線處理,一種是在線處理,基本處理架構如下:

img

離線處理計算:(對時間要求不是非常嚴格)

hadoop 生態體系(Hadoop集羣、)

img

後面我們的工作正是要基於Flume採集到HDFS中的數據做離線處理與分析。

MapReduce來對數據進行預處理,預處理之後的結果,我們也是保存到HDFS中,即採用如下的架構:

img

實時處理計算:(對時間要求非常嚴格)

spark生態體系(相對於Hadoop快一百倍,理論上)

Apache Spark是專爲大規模數據處理而設計的快速通用的計算引擎 。現在形成一個高速發展應用廣泛的生態系統

首先,高級 API 剝離了對集羣本身的關注,Spark 應用開發者可以專注於應用所要做的計算本身。

其次,Spark 很快,支持交互式計算和複雜算法。

最後,Spark 是一個通用引擎,可用它來完成各種各樣的運算,包括 SQL 查詢、文本處理、機器學習等,而在 Spark 出現之前,我們一般需要學習各種各樣的引擎來分別處理這些需求。

Hadoop詳解

Hadoop架構

img

  • HDFS: 分佈式文件存儲
  • YARN: 分佈式資源管理
  • MapReduce: 分佈式計算
  • Others: 利用YARN的資源管理功能實現其他的數據處理方式

內部各個節點基本都是採用Master-Woker架構

Hadoop Distributed File System,分佈式文件系統

架構

img

  • Block數據&##x5757;
  1. 基本存儲單位,一般大小爲64M(配置大的塊主要是因爲:1)減少搜尋時間,一般硬盤傳輸速率比尋道時間要快,大的塊可以減少尋道時間;2)減少管理塊的數據開銷,每個塊都需要在NameNode上有對應的記錄;3)對數據塊進行讀寫,減少建立網絡的連接成本)
  2. 一個大文件會被拆分成一個個的塊,然後存儲於不同的機器。如果一個文件少於Block大小,那麼實際佔用的空間爲其文件的大小
  3. 基本的讀寫S#x5355;位,類似於磁盤的頁,每次都是讀寫一個塊
  4. 每個塊都會被複制到多臺機器,默認複製3份
  • NameNode
  1. 存儲文件的metadata,運行時所有數據都保存到內存,整個HDFS可存儲的文件數受限於NameNode的內存大小
  2. 一個Block在NameNode中對應一條記錄(一般一個block佔用150字節),如果是大量的小文件,會消耗大量內存。同時map task的數量是由splits來決定的,所以用MapReduce處理大量的小文件時,就會產生過多的map task,線程管理開銷將會增加作業時間。處理大量小文件的速度遠遠小於處理同等大小的大文件的速度。因此Hadoop建議存儲大文件
  3. 數據會定時保存到本地磁盤,但不保存block的位置信息,而是由DataNode註冊時上報和運行時維護(NameNode中與DataNode相關的信息並不保存到NameNode的文件系統中,而是NameNode每次重啓後,動態重建)
  4. NameNode失效則整個HDFS都失效了,所以要保證NameNode的可用性
  • Secondary NameNode
  1. 定時與NameNode進行同步(定期合併文件系統鏡像和編輯日&#x#x5FD7;,然後把合併後的傳給NameNode,替換其鏡像,並清空編輯日誌,類似於CheckPoint機制),但NameNode失效後仍需要手工將其設置成主機
  • DataNode
  1. 保存具體的block數據
  2. 負責數據的讀寫操作和複製操作
  3. DataNode啓動時會向NameNode報告當前存儲的數據塊信息,後續也會定時報告修改信息
  4. DataNode之間會進行通信,複製數據塊,保證數據的冗餘性

replication:複製

Hadoop MapReduce

一種分佈式的計算方式指定一個Map(映#x5C04;)函數,用來把一組鍵值對映射成一組新的鍵值對,指定併發的Reduce(歸約)函數,用來保證所有映射的鍵值對中的每一個共享相同的鍵組

img

map: (K1, V1) → list(K2, V2) combine: (K2, list(V2)) → list(K2, V2) reduce: (K2, list(V2)) → list(K3, V3)

Map輸出格式和Reduce輸入格式一定是相同的

package WebLogCleanBigdata;

import com.EducationWebLog.IpHelper;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebLogClean extends Configured implements Tool {
/*主函數*/
    public static void main(String[] args) {
        /*創建配置實例*/
        Configuration conf = new Configuration();
        /*檢測異常處理*/
        try {
            int res = ToolRunner.run(conf, new WebLogClean(), args);
            System.exit(res);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int run(String[] args) throws Exception {
        final Job job = new Job(new Configuration(),
                WebLogClean.class.getSimpleName());
// 設置爲可以打包運行的處理類
        job.setJarByClass(WebLogClean.class);
        /*設置輸入文件的路徑*/
        FileInputFormat.setInputPaths(job, args[0]);

        job.setMapperClass(MyMapper.class);
        /*設置mapper的相關參數*/
        job.setMapOutputKeyClass(LongWritable.class);
        job.setMapOutputValueClass(Text.class);
        /*設置reduce的相關係數*/
        job.setReducerClass(MyReduce.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);
        /*設置輸出的文件的路徑*/
        FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 清理已存在的輸出文件
        FileSystem fs = FileSystem.get(new URI(args[0]), getConf());
        Path outPath = new Path(args[1]);
        if (fs.exists(outPath)) {
            fs.delete(outPath, true);
        }

        boolean success = job.waitForCompletion(true);
        if(success){
            System.out.println("Clean process success!");
        }
        else{
            System.out.println("Clean process failed!");
        }
        return 0;
    }



    //------------------------------------------------------------------------------------------------
    /*
     * 日誌解析類
     */
    /*對數據的切分與合併*/
    static class LogParser {
        public static void main(String[] args) throws ParseException {
            //final String S1 = "111.161.98.60 - - [10/Nov/2016:00:01:50 +0800] \"GET /search/article?page=4&words=%E6%97%B6%E6%97%B6%E5%BD%A9%E5%90%8E%E4%B8%80%E5%80%8D%E6%8A%95%E6%96%B9%E6%A1%88%E3%80%90%E5%AE%A2%E6%9C%8Dqq7 HTTP/1.1\" 200 14154 \"www.example.com\" \"http://www.example.com/search/article?page=4&words=%E6%97%B6%E6%97%B6%E5%BD%A9%E5%90%8E%E4%B8%80%E5%80%8D%E6%8A%95%E6%96%B9%E6%A1%88%E3%80%90%E5%AE%A2%E6%9C%8Dqq7\" - \"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)\" \"-\" 10.100.136.65:80 200 0.086 0.086";
            final String S1="117.35.88.11 - - [10/Nov/2016:00:01:02 +0800] \"GET /article/ajaxcourserecommends?id=124 HTTP/1.1\" 200 2345 \"www.example.com\" \"http://blog.jobbole.com/wfsdf/107417\" - \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\" \"-\" 10.100.136.65:80 200 0.616 0.616";
            LogParser parser = new LogParser();
            final String[] array = parse(S1);
            if (array[0].equals("-")){
                return;
            }else if (array[2].matches(".*[a-zA-Z].*")||array[2].equals("-")||array[2].equals(null)){
                return;
            }else {
                System.out.println(array[0]+"\t"+array[1]+"\t"+array[2]+"\t"+array[3]+"\t"
                        +array[4]+"\t"+array[5]+"\t"+array[6]+"\t"+array[7]);
            }
    
            String classno="";
    
        }
    
    }
    /*時間格式的轉換*/
    public static final SimpleDateFormat FORMAT = new SimpleDateFormat(
            "d/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);
    public static final SimpleDateFormat dateformat1 = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");
    //解析英文時間字符串
    private static Date parseDateFormat(String string) {
        Date parse = null;
        try {
            parse = FORMAT.parse(string);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return parse;
    }
    /*對Time數據的"[]"處理,獲取時間*/
    private static String parseTime(String line) {
        final int first = line.indexOf("[");
        final int last = line.indexOf("+0800]");
        String time = line.substring(first + 1, last).trim();
        Date date = parseDateFormat(time);
        return dateformat1.format(date);
    }
    /**
     * 解析日誌的行記錄
     *
     * @param line
     * @return 數組含有8個元素
     */
    public static String[] parse(String line){
        /*以空格切分,取第一個,獲取時間*/
        String ip = line.split(" ")[0];
        String tmp = line.substring(line.indexOf("[")+1,
                line.indexOf("]")).replace("+0800","").trim();
        String dataTmp[] = tmp.split(":")[0].split("/");
    
        String datatime = parseTime(line);
        String data = datatime.substring(0,getCharacterPosion(datatime," ",1));


        String time = tmp.replace(tmp.split(":")[0]+":","");
        //String url = line.split(" ")[11].replace("\"","");
        String url = line.substring(getCharacterPosion(line,"\"",5)+1,
                getCharacterPosion(line,"\"",6));
        String cmsType;
        String cmsNo;
        if (url.equals("-")){
            cmsNo="-1";
            cmsType= "-";
        }else {
            String tmp1[] = url.split("/");
            cmsType = tmp1[tmp1.length-2];
            //cmsNo = url.substring(tmp1[4].length()+1);
            if (tmp1.length>5){
                cmsNo = "-";
            }else {
                cmsNo = url.substring(getCharacterPosion(url,"/",4)+1);
            }
    
        }
        String stream = line.split(" ")[9];
    
        String city = IpHelper.findRegionByIp(ip);
        if (city.contains("中國")||city.contains("全球")||city.contains("未知")){
            return new String[] {""};
        }
        //return ip +"\t"+ data +"\t"+dataTime +"\t"+url +"\t"+cmsType +"\t"+cmsNo +"\t"+city +"\t"+stream;
        return new String[]{url,cmsType,cmsNo,stream,ip,city,time,data};
    }
    private static int getCharacterPosion(String value,String operator,int index){
        Matcher slashMatcher = Pattern.compile(operator).matcher(value);
        int mIdx = 0;
        while (slashMatcher.find()){
            mIdx++;
            if (mIdx == index){
                break;
            }
        }
        return slashMatcher.start();
    }
    //------------------------------------------------------------------------------------
    public static class MyMapper extends Mapper<LongWritable, Text, LongWritable, Text> {
        @Override
        protected void map(LongWritable key, Text value, Context context) {
            try {
                String[] string = parse(value.toString());
                if (string[0].equals("-")){
                    return;
                }else if (string[2].matches(".*[a-zA-Z].*")||string[2].equals("-")){
                    return;
                }else {
                    context.write(key, new Text((string[0]+"\t"+string[1]+"\t"+string[2]+"\t"+string[3]+
                            "\t"+string[4]+"\t"+string[5]+"\t"+string[6]+"\t"+string[7])));
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public static class MyReduce extends Reducer<LongWritable, Text, NullWritable, Text> {
        @Override
        protected void reduce(LongWritable key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
            for (Text value : values) {
                context.write(NullWritable.get(), value);
            }
        }
    }
}

基本流程

MapReduce主要是先讀取文件數據,然後進行Map處理,接着Reduce處理,最後把處理結果寫到文件中//img.mukewang.com/wiki/5bf260b309b7114706030175.jpg

img

Map Side

Record reader

記錄閱讀器會翻譯由輸入格式生成的記錄,記錄閱讀器用於將數據解析給記錄,並不分析記錄自身。記錄讀取器的目的是將數據解析成記錄,但不分析記錄本身。它將數據以鍵值對的形式傳輸給mapper。通常鍵是位置信息,值是構成記錄的數據存儲塊.自定義記錄不在本文討論範圍之內.

Map

在映射器中用戶提供的代碼稱爲中間對。對於鍵值的具體定義是慎重的,因爲定義對於分佈式任務的完成具有重要意義.鍵決定了數據分類的依據,而值決定了處理器中的分析信息.本書的設計模式將會展示大量細節來解釋特定鍵值如何選擇.

Shuffle and Sort

ruduce任務以隨機和排序步驟開始。此步驟寫入輸出文件並下載到本地計算機。這些數據採用鍵進行排序以把等價密鑰組合到一起。

Reduce

reducer採用分組數據作爲輸入。該功能傳遞鍵和此鍵相關值的迭代器。可以採用多種方式來彙總、過濾或者合併數據。當ruduce功能完成,就會發送0個或多個鍵值對。

輸出格式

輸出格式會轉換最終的鍵值對並寫入文件。默認情況下鍵和值以tab分割,各記錄以換行符分割。因此可以自定義更多輸出格式,最終數據會寫入HDFS

構成記錄的數據存儲塊.自定義記錄不在本文討論範圍之內.

Map

在映射器中用戶提供的代碼稱爲中間對。對於鍵值的具體定義是慎重的,因爲定義對於分佈式任務的完成具有重要意義.鍵決定了數據分類的依據,而值決定了處理器中的分析信息.本書的設計模式將會展示大量細節來解釋特定鍵值如何選擇.

Shuffle and Sort

ruduce任務以隨機和排序步驟開始。此步驟寫入輸出文件並下載到本地計算機。這些數據採用鍵進行排序以把等價密鑰組合到一起。

Reduce

reducer採用分組數據作爲輸入。該功能傳遞鍵和此鍵相關值的迭代器。可以採用多種方式來彙總、過濾或者合併數據。當ruduce功能完成,就會發送0個或多個鍵值對。

輸出格式

輸出格式會轉換最終的鍵值對並寫入文件。默認情況下鍵和值以tab分割,各記錄以換行符分割。因此可以自定義更多輸出格式,最終數據會寫入HDFS

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章