漫談千億級數據優化實踐:一次數據優化實錄

0x00 前言

即便沒有數據傾斜,千億級的數據查詢對於系統也是一種巨大負擔,對於數據開發來講,如何來優化它,既是挑戰,也是機遇!git

在上一篇文章 《漫談千億級數據優化實踐:數據傾斜(純乾貨)》中,咱們分享了一些在千億級數據優化實踐中和數據傾斜相關的內容。本文將分享千億級數據優化的另個一點:如何使用使用數據!github

注意:sql

  1. 本文會限定一些業務場景和技術架構,所以解決方法會侷限於此。不少問題能夠經過換架構或者引入新的組件來解決,可是成本可能會很高,所以暫不考慮。apache

  2. 本文不是一篇Hive使用和優化文檔,更側重於梳理筆者的思路,讓你們少走些坑。數組

文章主題

在流行的大數據領域中,Hive絕對佔據了很大的一片天地,不論是數據倉庫和數據分析,仍是數據挖掘和機器學習,凡是須要和大數據量打交道的童鞋們,基本上都要接觸Hive。所以,本文將側重於千億級數據在Hive中的使用,並經過一個典型的數據使用難題來總結一些在大規模數據場景下的優化方式。數據結構

本文主要以一個具體的使用場景爲切入點,爲了解決該場景下的使用難題,筆者經理了一次次的嘗試+失敗,最終找到了一種相對比較合適的方式。架構

文章結構

本文能夠看過是一種記錄和思考,徹底還原筆者在遇到問題時的解決方式。所以全文會以事情的發展爲主線,每次嘗試一種解決方法,失敗後繼續查找新的方法,中間會穿插一些技術細節。機器學習

文章主線以下:函數

  1. 明確使用場景和困難。學習

  2. 如何解決,這是一個不斷推翻重來的過程。

  3. 回顧總結

0x01 問題來了!

本章做用主要有二:

  1. 明確業務背景和使用場景

  2. 明確困難所在

1. 業務背景和使用場景

按照慣例,我又來到了一家電商網站來工做,咱們有一張十分重要的表:用戶和購買過的商品表。

以下圖,該表只有三個字段,分別是用戶ID、商品ID。咱們能夠簡單地理解這是一張事實表。對事實表沒印象的能夠參考這篇《漫談數據倉庫之維度建模》

咱們暫且無論當初爲什麼這樣設計的,如今的狀況就是

| 列名 | 字段類型 |
| ------------- |:-------------:|
| user_id | string |
| product_id | string |

這張表有哪些使用場景呢:

  1. 輸入一批用戶,找到和他們有類似購買行爲的用戶

  2. 統計用戶購買商品的數據區間

  3. ......

總之這張表能用到的地方是極多的,相信數據分析和數據挖掘的童鞋們確定能想到不少場景,這裏就不展開講了。

問題來了: 數據量太大,隨便一個查詢就是五六個小時,有沒有辦法優化?

2. 困難

先說明一下問題在哪。

數據量大

這張表裏面保存了我站來自全球的50億用戶和他們購買過的商品,粗略估計一下,人均會購買60件商品,也就是說這張表有 3000億 的數據。

3千億條記錄是什麼概念呢,若是存成沒有壓縮的txt文件的話,大體有30T以上。若是作一個壓縮,咱們保守一點估計,要有接近10T的數據。

查詢速度慢

這麼大數據量,查詢起來的確比較慢。可能隨便跑一個數據,就要3到5個小時。

咱們能夠大體地分析一下慢的緣由:

  1. 掃描數據量大

  2. join的時候時間長

  3. 由於咱們的reduce數量右限制,每一個reduce須要處理的數據量太多

  4. shuffle的時候效率過低。

0x02 解決過程

咱們在解決這個難題的時候是圍繞一些出發點的:

  1. 減小掃描的數據量

  2. 加快關聯查詢的速度

1. 分區

第一個思路就是分區,咱們能夠根據用戶的帳號分佈來進行分區,而後在掃描的時候,只掃描部分分區就行。 好,咱們作一個設計。

咱們美好的願望是:假設有一個需求須要查詢必定的用戶的購買記錄,咱們不用掃描全量的數據,只掃描其中一部分便可。

下面咱們基於幾個設定來設計咱們的分區規則:

  • 假設咱們的用戶id都是數字類型的,以下圖。

  • 咱們按照帳號的id來設計分區函數,好比說前四位相同的放在一個分區中。

  • 寫入數據,和查詢數據使用相同的分區函數

這樣咱們就有了1萬個分區,每一個分區中有30萬用戶的購買記錄,也就是說每一個分區中會有1800萬的記錄數,總計約1G的文件大小。

下面就是咱們設計出來的分區。

咱們的想法是好的,下面舉幾個場景:

  1. 好比如今須要查100個用戶的數據,不分區的話,咱們須要掃描全量的數據,如今咱們可能只要掃描10個分區,最多100個分區,也就是咱們的速度回提高100倍以上。

  2. 須要查1萬個用戶的數據,咱們假設會命中1000個分區。

  3. 須要查10萬個用戶的數據,咱們假設會命中5000個分區。

例子我都舉不下去了,實際狀況是,若是用戶分佈比較分散的話,超過20萬個用戶的話,基本上就命中了全部了分區了。 這個感興趣的能夠測一下。

增長分區數?

這個方案是能夠的,好比咱們變成10萬個分區,這樣固然能夠,可是讓須要查詢的用戶多的話,效果照樣變弱,並且更多的分區意味着每一個分區的數據會變少,這樣小文件就會多不少。

結論

分區的方式不靠譜!

2. 索引

注意: Hive的索引也是個坑,怪不得沒人用,可是咱們仍是要設計一下。

基於「減小掃描的數據量」這點來說,索引是一種極妙的方式,有了索引,咱們就沒必要全量掃描全部的數據,速度確定就快了呀。 可是, Hive的索引是個坑。

下面講一下Hive索引的機制就明白了。

Hive索引機制

在指定列上創建索引,會產生一張索引表(Hive的一張物理表),裏面的字段包括,索引列的值、該值對應的HDFS文件路徑、該值在文件中的偏移量。

以下,是Hive的索引表。其中,索引表中key字段,就是原表中key字段的值,_bucketname 字段,表明數據文件對應的HDFS文件路徑,_offsets 表明該key值在文件中的偏移量,有可能有多個偏移量,所以,該字段類型爲數組。

| 列名 | 字段類型 |
| ------------- |:-------------:|
| user_id | string |
| _bucketname | string |
| _offset | array < bigint > |

在執行索引字段查詢時候,首先額外生成一個MR job,根據對索引列的過濾條件,從索引表中過濾出索引列的值對應的hdfs文件路徑及偏移量,輸出到hdfs上的一個文件中,而後根據這些文件中的hdfs路徑和偏移量,篩選原始input文件,生成新的split,做爲整個job的split,這樣就達到不用全表掃描的目的。

注意: 按照上面的說明,咱們的索引其實就是另外一張Hive的表,並且數據量仍是很大。 下面從兩個點說明Hive的索引方案不能用。

  1. 通過測試,索引表就有四、5T,咱們在查詢的時候,要先和這張索引表作關聯,而後再和原表作關聯,損失太大了。

  2. HDFS文件系統的設計問題。會致使最終咱們掃描的仍是全表。爲何?下面講解。

HDFS的設計

咱們默認你們對HDFS原理有所認知。這裏只說一下此次咱們優化的內容。

假設咱們10T的數據,按照128M一個文件塊,那就是咱們有七八萬個文件塊。和前面的分區的狀況相似,當須要查詢的用戶數量到必定程度,基本上仍是要掃描全部的文件塊。

結論

索引的方式不靠譜,至少Hive中不可用。

索引的使用方式,就再也不描述了,看官網仍是挺簡單的:Hive官網:Index

3. 分桶

分桶就再也不說了,和前面說的問題相似,也不可用。

到這裏我就絕望了,有打算不解決了。準備用最初的一招:按活躍度區分。

4. 區分活躍用戶

這也是一種很值得考慮的方式,由於咱們大部分對數據的使用都會考慮活躍用戶,這裏咱們把30億用戶中活躍的10億的用戶抽出來放一個分區,這樣的話,咱們的查詢效率能提升3倍左右。

問題

  1. 活躍用戶很差定義,每一個業務方的定義不同。

  2. 運行成本太大,跑這個數據挺耗時間。

結論

這是一種方法,若是沒有更好的方法就用這個了。

5. 數據結構

受大神的指點,咱們更換了一種在Hive中的存儲方式,如今更新表以下

| 列名 | 字段類型 |
| ------------- |:-------------:|
| user_id | string |
| product_list | array< string > |

這是一個很簡單的轉表,咱們使用了Hive中的數據結構Array,把一個用戶的全部購買過的商品放入到一個字段中,這樣的話,咱們的總數據量就只有30億了,在作關聯查詢的時候速度必然很快。

實踐

通過實踐,這樣的存儲,只需佔用以前存儲量的1/2左右,也就是隻須要不到5T的大小,查詢速度從平均一個任務4個小時縮減到半個小時。

問題

這裏有兩個問題:

  1. 數據更新,數據結構的改變致使在更新數據的時候有一些障礙,這點再也不展開,方法老是有的,筆者是保留了兩份數據,這一份專門供查詢用。

  2. 使用成本增長,由於數據結構變了,相應的查詢sql也要調整。 這裏就須要用到lateral view explode,詳情可看官網

總結

總的來接,這種方式仍是可行的,目前的反饋還都是正向的,額外的sql複雜度不是很大,大部分童鞋都能接收。

0xFF 總結

本文主要是描述了一次千億級數據量查詢優化的過程,回頭來看,其實也聽簡單的,可是身在其中未必能想清楚,這也和經驗有關,所以走了不少的彎路。

總的來說,筆者嘗試了5種方式:分區、索引、分桶、活躍度區分、新的數據結構。最後一種方式基本解決了遇到的問題。

本文對一些技術細節沒有過多描述,好比建索引,建表,這些在官網很容易找到,所以再也不過多涉及。思想到位了,其它的問題都不大。

參考


做者:dantezhao |簡書 | CSDN | GITHUB

我的主頁:http://dantezhao.com
文章能夠轉載, 但必須以超連接形式標明文章原始出處和做者信息

相關文章
相關標籤/搜索