基於Hive的大資料分析系統

哥不是小萝莉發表於2024-07-28

1.概述

在構建大資料分析系統的過程中,我們面對著海量、多源的資料挑戰,如何有效地解決這些零散資料的分析問題一直是大資料領域研究的核心關注點。大資料分析處理平臺作為應對這一挑戰的利器,致力於整合當前主流的各種大資料處理分析框架和工具,以實現對資料的全面挖掘和深入分析。本篇部落格筆者將為大家介紹如何構建一個大資料分析平臺,來實現對複雜資料環境中的有價值資訊的精準提取和深度分析。

2.內容

構建一個完善的大資料分析平臺涉及眾多元件,這些元件往往具有不同的側重點和功能特性。從資料的採集、儲存,到處理和分析的各個環節,每個元件都扮演著關鍵的角色。如何在這種複雜的元件體系中實現協同,將它們有機地結合起來,成為了一項非常複雜而關鍵的工作。
這個過程需要考慮到資料的規模、種類以及處理的實時性等方面的要求。同時,為了達到挖掘海量資料的目標,還需要考慮分散式計算、儲存最佳化、以及高效的演算法設計等方面的技術挑戰。只有透過精心設計和整合這些元件,才能夠完成對海量資料的深度挖掘,從而獲得對業務和決策有價值的資訊。

2.1 瞭解大資料分析系統

在構建大資料分析平臺之前,我們必須首先站在業務需求的前沿,深刻理解使用者的期望與場景。大資料分析平臺不僅僅是一個技術堆疊的堆砌,更是一個服務業務的智慧引擎。明確業務需求場景和使用者期望,瞭解在這個資料的海洋中,我們追求哪些有價值的資訊,是構建大資料分析系統的關鍵起點。

2.1.1 瞭解大資料分析系統的價值

建設一個完善的大資料分析系統,不僅為企業構建了基礎資料中心,還為企業提供了一個統一的資料儲存體系,透過資料建模奠定了資料的價值呈現的堅實基礎。

1.構建基礎資料中心

大資料分析系統的第一項價值體現在建設企業的基礎資料中心上。透過統一的資料儲存體系,企業能夠有效地管理、儲存和檢索海量的資料,包括來自不同業務部門和多源的資料。這種集中化的資料管理不僅提高了資料的可靠性和一致性,還降低了資料管理的複雜性和成本。

2.統一資料建模

透過對資料的統一建模,大資料分析系統為企業提供了一種標準化的資料表示方式,使得不同部門和業務能夠使用相同的資料模型進行工作。這種一致的資料模型有助於消除資料孤島,促進跨部門和跨系統的資料共享和協同工作,從而提高了企業整體的工作效率和決策水平。

3.資料處理能力下沉

大資料分析系統將資料處理能力下沉,建設了集中的資料處理中心,為企業提供了強大的資料處理能力。這意味著企業能夠更加高效地進行資料的清洗、轉換和分析,從而更好地挖掘資料潛在的價值。同時,這種集中化的處理模式有助於提高處理效率和降低處理成本。

4.統一資料管理監控體系

為了保障大資料分析系統的穩定執行,建設了統一的資料管理監控體系。這包括對資料質量、安全性和可用性的全面監控,以及對系統效能和故障的實時監測。透過這種全面的監控體系,企業能夠及時發現和解決潛在的問題,確保系統的穩定和可靠執行。

5.構建統一的應用中心

最終,大資料分析系統透過構建統一的應用中心,滿足企業業務需求,真正體現了資料的價值。透過應用中心,企業能夠基於大資料分析系統提供的資料和分析結果,開發各種智慧應用,為業務提供更有力的支援。這使得資料不再是一種被動的資源,而是能夠主動為業務創造價值的動力源。
綜合而言,大資料分析系統的價值不僅僅在於處理和分析海量的資料,更在於為企業建設了一個統一、高效的資料基礎設施,為業務創新提供了強大的支援。

2.1.2 瞭解大資料分析系統的目的

在當今數字化浪潮中,大資料不再只是龐大的資訊堆積,而是成為驅動企業智慧決策和業務創新的核心資源。瞭解大資料分析系統的目的,遠不僅僅是追逐技術的潮流,更是深刻洞察資料對業務行動的引導作用。

1. 資料度量:洞察業務趨勢

大資料分析系統的首要目的之一是幫助企業洞察業務趨勢。透過分析海量資料,系統能夠識別並理解市場的動向、消費者的行為以及競爭對手的策略。這種深度洞察有助於企業預測未來趨勢,制定戰略計劃,並做出敏銳的業務決策。

2. 資料理解:改進決策制定過程

大資料分析系統的另一個關鍵目標是改進決策制定過程。透過提供實時、準確的資料分析,系統可以幫助管理層更好地理解當前業務狀況,減少決策的盲目性。這種資料驅動的決策制定能夠降低風險,提高成功的機率,並在競爭激烈的市場中保持靈活性。

3.資料驅動:最佳化運營效率

大資料分析系統的目的還在於最佳化企業的運營效率。透過對業務流程的深入分析,系統可以識別出潛在的最佳化點,提高生產效率,減少資源浪費。這種最佳化不僅帶來成本的降低,還可以加速業務運營,提高客戶滿意度。

4.資料預測:實現個性化營銷

大資料分析系統有助於企業實現更個性化的營銷策略。透過深入瞭解客戶行為和偏好,系統能夠生成精準的使用者畫像,為企業提供更具針對性的市場營銷方案。這種個性化營銷不僅提高了市場推廣的效果,還加強了客戶關係,提升了品牌忠誠度。

5.資料安全:增強安全性和合規性

大資料分析系統的另一個重要目標是增強企業的安全性和合規性。透過對資料進行監控和分析,系統能夠及時發現異常活動和潛在的安全威脅。同時,它也有助於確保企業遵循法規和行業標準,降低法律風險。

2.1.3 瞭解大資料分析系統的應用場景

在資訊時代的今天,大資料正成為推動科技和商業發展的關鍵力量。隨著技術的不斷進步,大資料分析系統的應用場景也越來越廣泛。這些系統不僅僅是企業決策的得力助手,還在醫療、城市規劃、金融等多個領域展現出強大的應用潛力。

1.企業決策最佳化

大資料分析系統在企業營銷與銷售中的應用早已不再是新鮮事物。透過分析大規模的市場資料,企業可以更好地瞭解消費者行為、趨勢和喜好。基於這些分析結果,企業可以最佳化廣告策略、制定個性化的市場推廣計劃,並更精準地鎖定潛在客戶。同時,在銷售過程中,大資料分析系統也能夠幫助企業實時監控庫存、調整價格策略,提高銷售效益。

2.金融風控與反欺詐

在金融領域,大資料分析系統為風險管理和反欺詐提供了有力的支援。透過分析使用者的交易歷史、行為模式和其他多維資料,金融機構可以更準確地評估信用風險,及時發現異常交易行為,從而提高風險控制的水平。大資料分析系統還能夠構建複雜的欺詐檢測模型,識別潛在的欺詐活動,保護使用者的資產安全。

3.醫療健康管理

在醫療領域,大資料分析系統為健康管理和醫療決策提供了前所未有的支援。透過分析患者的病歷資料、醫療記錄和生命體徵等資訊,醫療機構可以更好地瞭解患者的健康狀況,預測慢性病的風險,制定個性化的治療方案。大資料分析系統還能夠協助醫學研究,加速新藥研發和臨床試驗的程序。

4.智慧城市建設

在城市管理中,大資料分析系統為智慧城市的建設提供了有力的支援。透過收集和分析城市各個方面的資料,包括交通流量、環境汙染、能源消耗等,城市管理者可以更好地規劃城市發展、最佳化交通流動,提高城市的整體執行效率。

5.製造業智慧生產

在製造業中,大資料分析系統為智慧生產提供了關鍵支援。透過監控生產線上的大量感測器資料,企業可以實時瞭解生產狀態、預測裝置故障,從而進行及時維護,提高生產效率。大資料分析系統還能夠最佳化供應鏈管理,降低庫存成本,提高生產計劃的精準度。
總的來說,大資料分析系統的應用場景越來越廣泛,其在不同領域的作用不可忽視。透過深度挖掘和分析資料,我們能夠更全面、更準確地理解複雜的系統和現象,從而為決策、創新和發展提供有力支援。

2.2 從架構上了解大資料分析系統

大資料分析系統扮演著整合、整理和分析龐大資料集的角色,它不僅僅是一個簡單的資料倉儲,更是一個複雜的系統,涵蓋了系統資料、業務資料等多個維度的資訊。
大資料分析系統的核心任務是在統一的資料框架下實現對資料的挖掘和分析。這意味著涉及到眾多元件和複雜的功能,因此在系統的建設過程中,如何巧妙地將這些元件有機地結合起來成為至關重要的環節。本小節將探討大資料分析系統的組成結構,分析各個元件之間的協同作用,以及在這個多層次、多功能的系統中如何實現高效的資料處理和視覺化展示。

2.2.1 瞭解大資料分析系統的體系架構

隨著資料規模的急劇膨脹和多樣性的增加,構建一個高效、可擴充套件的大資料分析系統變得至關重要。為了深入理解這一龐雜系統的運作,本節將引領讀者一同探索其體系架構,從資料採集到最終的洞察展示,揭示大資料分析系統如何在龐大而多元的資料海洋中發現有價值的資訊。如圖所示。

1. 資料採集層:連線多樣化的資料來源

資料採集層是大資料分析平臺的基礎,它直接涉及到資料的獲取和整合。底層是各類資料來源,包括各種業務資料、使用者資料、日誌資料等。為了確保全面性,常常採用傳統的ETL離線採集和實時採集兩種方式。這一層的目標是將零散的資料從各個角落整合起來,形成一個全面而連貫的資料集。

2. 資料儲存和處理層:為資料提供強有力的支援

有了底層資料後,下一步是將資料儲存到合適的持久化儲存層中(比如Hive資料倉儲),並根據不同的需求和場景進行資料預處理。這包括OLAP、機器學習等多種形式。在這一層次,資料得到了進一步的加工,確保了資料的質量、可用性和安全性,為後續的深層次分析提供了堅實的基礎。

3. 資料分析層:挖掘資料的深層次價值

在資料分析層,報表系統和BI分析系統扮演著關鍵角色。資料在這個階段經過簡單加工後,進行深層次的分析和挖掘。這一層的任務是從龐大的資料中提取有價值的資訊,為企業決策提供有力支援。在這個階段,資料變得更加智慧化和易於理解。

4. 資料應用層:將資料轉化為業務洞察

最終,根據業務需求,資料被分為不同的類別應用。這包括了資料包表、儀表板、數字大屏、及時查詢等形式。資料應用層是整個資料分析過程的輸出,也是對外展示資料價值的關鍵。透過視覺化手段,將分析結果生動地呈現給終端使用者,助力業務決策。
深入瞭解系統的體系架構不僅僅是技術的問題,更需要對業務需求和使用者期望的深刻理解。只有透過合理設計的體系架構,從資料的採集到最終的應用,才能實現對資料的全面挖掘和深度分析。

2.2.2 設計大資料分析系統的核心模組

設計大資料分析系統的核心模組涵蓋了資料採集、資料儲存、資料分析以及資料服務等,這些關鍵模組協同工作,構建了一個完整而高效的大資料分析系統。如圖所示。

1.資料採集

作為系統的第一步,資料採集模組承擔了從各業務自系統中彙集資訊資料的任務。系統選擇支撐Kafka、Flume及傳統的ETL採集工具,以確保對多樣化資料來源的高效處理和整合。

2.資料儲存

資料儲存模組採用了一體化的儲存方案,結合了Hive、HBase、Redis及MySQL,形成了支援海量資料的分散式儲存體系。這種綜合性的儲存模式保證了對大規模資料的高效管理和檢索。

3.資料分析

資料分析模組是系統的核心引擎,支援傳統的OLAP分析和基於Spark的常規機器學習演算法。這使得系統能夠對龐大的資料集進行深入挖掘,發現潛在的價值和趨勢,為決策提供強有力的支援。

4.資料服務

資料服務模組是系統的樞紐,提供對資料資源的統一管理和排程。透過資料服務,系統實現了對資料的整體治理,使得資料的流動、儲存和分析能夠有序而高效地進行。同時,它向外提供資料服務,為其他系統和應用提供了規範的介面和訪問方式。
這些核心模組的協同作用,使得大資料分析系統能夠從資料的採集到儲存、再到分析,最終向外提供服務,形成了一個有機而完善的體系結構。透過整合各個模組的功能,系統能夠應對多變的資料環境,為使用者提供高效、可靠、靈活的大資料分析解決方案。

2.3 實現大資料分析系統

大資料分析系統的實現流程主要涵蓋以下關鍵步驟,包括資料採集、資料整合、資料加工以及資料視覺化等環節。這一系列步驟構成了通常所稱的一站式大資料分析平臺。
在這個平臺上,資料採集負責從多源獲取原始資料,而後的資料整合將這些資料匯聚並確保格式一致性。接下來,資料加工階段進行資料清理、轉換和處理,以使資料達到可分析的標準。
最終,透過資料視覺化,使用者能夠以直觀的方式理解和探索資料,為決策提供有力支援。這個標準流程為設計和實施大資料分析系統提供了基本框架,使其能夠高效處理龐大的資料集,滿足多樣化的分析需求。

2.3.1 資料採集

資料採集是大資料分析系統中至關重要的第一步,它扮演著系統獲取資訊源頭的關鍵角色。在這個階段,系統透過各種渠道和技術,廣泛而高效地蒐集原始資料,為後續的分析和處理奠定基礎。資料採集的過程涵蓋了從感測器、日誌、外部資料庫到線上平臺等多樣化的資料來源,確保系統能夠獲得全面而多維度的資訊。
在本小節中,我們為了模擬資料採集場景,特別設計了一個應用程式,其主要功能是生成模擬資料作為原始資料,並將這些資料傳送到Kafka訊息中介軟體。
下面是一個使用Java編寫的簡單應用程式,用於生成模擬電影資料併傳送到Kafka。在這個示例中,我們使用了Apache Kafka的Java客戶端庫。具體依賴見程式碼所示。

<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka_2.13</artifactId>
    <version>3.4.0</version>
</dependency>

實現將模擬資料傳送到Kafka的詳細步驟在程式碼中展示。以下是一些關鍵實現細節:

  • Kafka配置:在程式碼中,你需要配置Kafka的伺服器地址(bootstrap.servers)、key和value的序列化器等引數,以便建立與Kafka叢集的連線。
  • 建立KafkaProducer:使用配置資訊建立KafkaProducer物件,該物件負責將資料傳送到Kafka叢集。
  • 生成模擬資料:在一個迴圈中,使用你的資料生成邏輯生成模擬資料。這可能包括建立JSON格式的資料、設定資料欄位、模擬日期等。
  • 構建ProducerRecord:使用生成的模擬資料構建ProducerRecord物件,其中包括目標主題、key(如果有的話)、以及待傳送的資料。
  • 傳送資料: 使用KafkaProducer的send方法將ProducerRecord傳送到Kafka主題。
  • 控制傳送速率(可選):在迴圈中,你可以透過Thread.sleep等方法控制資料生成和傳送的速率,以避免傳送過於頻繁。

具體實現見程式碼所示。

@Slf4j
public class MovieDataProducer {
    public static void main(String[] args) {
        sendRawData();
    }

    private static void sendRawData() {
        // Kafka 伺服器地址
        String kafkaBootstrapServers = "localhost:9092";

        // Kafka 主題
        String kafkaTopic = "ods_movie_data";

        // 建立 Kafka 生產者配置
        Properties properties = new Properties();
        properties.put("bootstrap.servers", kafkaBootstrapServers);
        properties.put("key.serializer", 
        "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", 
        "org.apache.kafka.common.serialization.StringSerializer");

        // 建立 Kafka 生產者
        try {
            Producer<String, String> producer 
            = new KafkaProducer<>(properties)
            // 生成併傳送模擬電影資料
            for (int i = 1; i <= 1000; i++) {
                String movieData = generateMovieData(i);
                producer.send(new ProducerRecord<>(kafkaTopic, 
                Integer.toString(i), movieData));

                // 列印傳送的資料資訊(可選)
                System.out.println("傳送資料到 Kafka: " + movieData);

                // 控制資料生成的速率,例如每秒傳送一次
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            log.error("傳送資料到 Kafka 出現異常:{}", e);
        }
    }

    // 生成模擬電影資料
    private static String generateMovieData(int rank) {
        String[] countries = {"美國", "中國", "印度", "英國", "日本"};
        String[] genres = {"動作", "劇情", "喜劇", "科幻", "冒險"};

        LocalDate releaseDate = LocalDate.now()
        .minusDays(new Random().nextInt(180));
        DateTimeFormatter formatter = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd");

        MovieData movieData = new MovieData(
                rank,
                "Movie" + rank,
                releaseDate.format(formatter),
                countries[new Random().nextInt(countries.length)],
                genres[new Random().nextInt(genres.length)],
                5 + 5 * Math.random(),
                new Random().nextInt(1000000)
        );

        // 返回字串結果
        String result = "";

        // 使用 Jackson 庫將物件轉為 JSON 字串
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            result = objectMapper.writeValueAsString(movieData);
        } catch (Exception e) {
            log.error("轉換 JSON 字串出現異常:{}", e);
        }
        return result;

    }

    // 電影資料類
    @Data
    private static class MovieData {
        private int rank;
        private String name;
        private String releaseDate;
        private String country;
        private String genre;
        private double rating;
        private int playCount;

        public MovieData(int rank, String name, String releaseDate
        , String country, String genre
        , double rating, int playCount) {
            this.rank = rank;
            this.name = name;
            this.releaseDate = releaseDate;
            this.country = country;
            this.genre = genre;
            this.rating = rating;
            this.playCount = playCount;
        }
    }
}

請確保替換localhost:9092和ods_movie_data為你實際使用的Kafka伺服器地址和主題名稱。這個簡單的Java應用程式會生成包含電影排名、電影名稱、上映日期、製作國家、型別、評分、播放次數等欄位的模擬電影資料,並將其傳送到指定的Kafka主題。

2.3.2 資料儲存

資料儲存在大資料分析系統中扮演著關鍵的角色,它不僅需要提供高度可靠性的儲存機制,還需要根據業務需求進行智慧分割槽,以便後續的離線分析和查詢。在當前場景中,我們面對的是一批不斷湧入的實時流資料,這些資料經過實時處理後需要被有效地儲存到Hive中,以滿足後續的離線分析需求。
為了保證資料的時效性,我們計劃將每隔5分鐘的資料作為一個時間視窗進行儲存,這不僅有助於提高查詢效率,還能夠更好地支援基於時間的分析。為了實現這一目標,我們將使用Apache Flink作為我們的流處理引擎,透過其與Kafka的整合,實時地消費並處理Kafka Topic中的資料。具體實現流程如圖所示。

1.環境依賴

Flink在消費Kafka叢集中的資料時,需要引入一系列依賴項以確保系統的順利執行。
為了實現對Kafka叢集中資料的高效消費,我們需要引入Flink相關的依賴項。這些依賴項不僅包括Flink核心庫,還涉及到與Kafka連線和互動的庫。具體依賴見程式碼所示。

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-filesystem_2.12</artifactId>
    <version>${flink.connector.version}</version>
 </dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.11_2.12</artifactId>
    <version>${flink.kafka.version}</version>
 </dependency>
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-streaming-java_2.12</artifactId>
    <version>${flink.streaming.version}</version>
 </dependency>

2.讀取資料

編寫Flink程式碼來消費Kafka Topic並將資料直接儲存到HDFS,無需進行額外的邏輯處理,以備後續使用MapReduce進行資料預處理。具體實現見程式碼所示。

@Slf4j
public class FlinkTemplateTask {

    public static void main(String[] args) {
        // 檢查輸入引數是否滿足要求
        if (args.length != 3) {
            log.error("kafka(server01:9092), 
            hdfs(hdfs://cluster01/data/), 
            flink(parallelism=2) must be exist.");
            return;
        }
        String bootStrapServer = args[0];
        String hdfsPath = args[1];
        int parallelism = Integer.parseInt(args[2]);

        // 建立 Flink 流處理環境
        StreamExecutionEnvironment env = 
        StreamExecutionEnvironment.getExecutionEnvironment();
        env.enableCheckpointing(5000);
        env.setParallelism(parallelism);
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        // 從 Kafka 中讀取資料
        DataStream<String> transction = 
        env.addSource(new FlinkKafkaConsumer010<>("ods_movie_data"
        , new SimpleStringSchema(), configByKafkaServer(bootStrapServer)));

        // 儲存到 HDFS
        BucketingSink<String> sink = new BucketingSink<>(hdfsPath);

        // 自定義儲存到HDFS上的檔名,用小時和分鐘來命名,方便後面計算策略
        sink.setBucketer(new JDateTimeBucketer<String>("HH-mm"));

        sink.setBatchSize(1024 * 1024 * 4); // 大小為 5MB
        sink.setBatchRolloverInterval(1000 * 30); // 時間 30s
        transction.addSink(sink);
        
        // 執行 Flink 任務
        env.execute("Kafka2Hdfs");
    }

    // 設定Kafka消費者的配置
    private static Object configByKafkaServer(String bootStrapServer) {
        Properties props = new Properties();
        props.setProperty("bootstrap.servers", bootStrapServer);
        props.setProperty("group.id", "test_bll_group");
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", 
        "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", 
        "org.apache.kafka.common.serialization.StringDeserializer");
        return props;
    }

}

在這裡需要特別注意,我們將時間視窗設定得較短,每隔30秒進行一次檢查。如果在該批次的時間視窗內沒有資料到達,我們將生成一個檔案並儲存到HDFS上。
此外,我們對DateTimeBucketer進行了重寫,建立了JDateTimeBucketer。這一調整的邏輯並不複雜,只是在原有的方法基礎上增加了一個年-月-日/時-分的檔案生成路徑。舉例來說,在HDFS上生成的路徑可能是:xxxx/2023-10-10/00-00。這個調整有助於更好地組織和管理生成的檔案,使其更符合時間和日期的結構。

3.檔案命名策略

在這個步驟中,我們需要對已經儲存到HDFS上的檔案進行預處理。處理邏輯如下:例如,當前時間是2023-10-10 14:00,我們需要將當天的13:55、13:56、13:57、13:58、13:59這最近5分鐘的資料處理到一起,並載入到Hive的最近5分鐘的一個分割槽中。為了實現這一目標,我們需要生成一個邏輯策略集合,其中以HH-mm作為key,與之最近的5個檔案作為value。這個集合將被用於資料預處理和合並。具體實現見程式碼所示。

public class DateRangeStrategy {
    public static void main(String[] args) {
        getFileNameStrategy();
    }

    // 生成策略
    private static void getFileNameStrategy() {
        for (int i = 0; i < 24; i++) {
            for (int j = 0; j < 60; j++) {
                if (j % 5 == 0) {
                    if (j < 10) {
                        if (i < 10) {
                            if (i == 0 && j == 0) {
                                System.out.println
                                ("0" + i + "-0" + j 
                                + "=>23-59,23-58,23-57,23-56,23-55");
                            } else {
                                if (j == 0) {
                                    String tmp = "";
                                    for (int k = 1; k <= 5; k++) {
                                        tmp += "0" + (i - 1) + "-" 
                                        + (60 - k) + ",";
                                    }
                                    System.out.println
                                    ("0" + i + "-0" + j 
                                    + "=>" + tmp.substring(0, 
                                    tmp.length() - 1));
                                } else {
                                    String tmp = "";
                                    for (int k = 1; k <= 5; k++) {
                                        if (j - k < 10) {
                                            tmp += "0" + i + "-0" 
                                            + (j - k) + ",";
                                        } else {
                                            tmp += "0" + i + "-" 
                                            + (j - k) + ",";
                                        }
                                    }
                                    System.out.println("0" + i + "-0" + j 
                                    + "=>" + tmp.substring(0, tmp.length() - 1));
                                }
                            }
                        } else {
                            if (j == 0) {
                                String tmp = "";
                                for (int k = 1; k <= 5; k++) {
                                    if (i - 1 < 10) {
                                        tmp += "0" + (i - 1) + "-" + (60 - k) + ",";
                                    } else {
                                        tmp += (i - 1) + "-" + (60 - k) + ",";
                                    }
                                }
                                System.out.println(i + "-0" + j + "=>" 
                                + tmp.substring(0, tmp.length() - 1));
                            } else {
                                String tmp = "";
                                for (int k = 1; k <= 5; k++) {
                                    if (j - k < 10) {
                                        tmp += i + "-0" + (j - k) + ",";
                                    } else {
                                        tmp += i + "-" + (j - k) + ",";
                                    }
                                }
                                System.out.println(i + "-0" + j 
                                + "=>" + tmp.substring(0, tmp.length() - 1));
                            }
                        }
                    } else {
                        if (i < 10) {
                            String tmp = "";
                            for (int k = 1; k <= 5; k++) {
                                if (j - k < 10) {
                                    tmp += "0" + i + "-0" 
                                    + (j - k) + ",";
                                } else {
                                    tmp += "0" + i + "-" 
                                    + (j - k) + ",";
                                }
                            }
                            System.out.println("0" + i + "-" 
                            + j + "=>" + tmp.substring(0, tmp.length() - 1));
                        } else {
                            String tmp = "";
                            for (int k = 1; k <= 5; k++) {
                                if (j - 1 < 10) {
                                    tmp += i + "-0" + (j - k) + ",";
                                } else {
                                    tmp += i + "-" + (j - k) + ",";
                                }
                            }
                            System.out.println(i + "-" + j 
                            + "=>" + tmp.substring(0, tmp.length() - 1));
                        }
                    }
                }
            }
        }
    }
}

4.資料載入

當資料準備完畢後,我們可以藉助Hive的LOAD命令直接將HDFS上預處理的檔案載入到相應的表中。具體實現見程式碼所示。

LOAD DATA INPATH '/data/hive/hfile/data/min/2023-10-10/14-05/' 
OVERWRITE INTO TABLE 
game_user_db.ods_movie_data PARTITION(day='2023-10-10',hour='14',min='05');

在執行命令時,如果檔案不存在可能導致載入出錯。因此,在載入 HDFS 路徑之前,我們可以先判斷一下路徑是否存在。具體實現見程式碼所示。

#!/bin/bash

# HDFS 資料路徑
hdfs_path='/data/hive/hfile/data/min/2023-10-10/14-05/'

# 檢查 HDFS 路徑是否存在
if hdfs dfs -test -e "$hdfs_path"; then
    # 如果存在,則執行載入操作
    echo "執行 Hive 資料載入操作"
    hive -e "LOAD DATA INPATH 
        '$hdfs_path' 
        OVERWRITE INTO TABLE 
        game_user_db.ods_movie_data 
        PARTITION(day='2023-10-10',hour='14',min='05');"
else
    echo "HDFS路徑: ['$hdfs_path'] 不存在"
fi

這裡需要注意的是,這個指令碼先檢查 HDFS 路徑是否存在,如果存在則執行載入操作,否則輸出錯誤資訊。這樣可以避免因檔案不存在而導致的載入錯誤。

2.3.3 資料分析

資料分析是一門科學,透過對資料的整理、清洗和分析,我們能夠從中挖掘出隱藏在龐大資料背後的規律和趨勢。資料分析工具的不斷髮展使得這一過程變得更加高效。統計學、機器學習、人工智慧等技術的應用,使得我們能夠更深入地理解資料中的資訊。透過資料視覺化技術,我們能夠將抽象的資料變成直觀的圖表和影像,更容易理解和傳達資料所蘊含的含義。

1.分析電影年份柱狀圖

為了生成電影年份的柱狀圖,我們可以透過對電影年份資料進行聚合。具體實現見程式碼所示。

-- 電影年份
SELECT release_date, COUNT(1) AS pv
FROM ods_movie_data
WHERE day = '2023-10-10'
GROUP BY release_date;

執行上述程式碼,分析結果如圖所示。

2.分析電影型別扇形圖

為了生成電影型別的扇形圖,我們可以透過對電影型別資料進行聚合。具體實現見程式碼所示。

-- 電影型別
SELECT genre, COUNT(1) AS pv
FROM ods_movie_data
WHERE day = '2023-10-10'
GROUP BY genre;

執行上述程式碼,分析結果如圖所示。

3.分析電影評分散點圖

為了生成電影評分的散點圖,我們可以透過對電影評分資料進行聚合。具體實現見程式碼所示。

-- 電影評分
SELECT rating, COUNT(1) AS pv
FROM ods_movie_data
WHERE day = '2023-10-10'
GROUP BY rating;

執行上述程式碼,分析結果如圖所示。

3.總結

本篇聚焦於構建大資料分析系統,全面介紹了該系統的架構設計,並深入探討了各個模組的實現細節。透過循序漸進的方式,讀者能夠逐步瞭解大資料分析系統的構建過程,從而更好地理解其運作機制。
總體而言,本篇內容旨在為讀者提供一個全面而深入的大資料分析系統實現指南,透過理論和實踐相結合的方式,幫助讀者更好地掌握大資料分析的核心概念和技術,為實際專案應用打下堅實基礎。

4.結束語

這篇部落格就和大家分享到這裡,如果大家在研究學習的過程當中有什麼問題,可以加群進行討論或傳送郵件給我,我會盡我所能為您解答,與君共勉!

另外,博主出新書了《深入理解Hive》、同時已出版的《Kafka並不難學》和《Hadoop大資料探勘從入門到進階實戰》也可以和新書配套使用,喜歡的朋友或同學, 可以在公告欄那裡點選購買連結購買博主的書進行學習,在此感謝大家的支援。關注下面公眾號,根據提示,可免費獲取書籍的教學影片。

相關文章