數據遷移心得

  前幾天出差,去客戶現場幫忙遷移數據,經過幾天的奮戰,終於將遷移數據自動化起來,並且可以日跑批操作,這裏小編就跟大家分享下,這其中踩過的坑(也可能是實戰經驗不豐富導致)。
  首先,榮小編我抱怨一下,不是自己熟悉的開發環境真的有些難過,給一臺電腦,咱不說沒有IDE,就連java都沒有安裝,連接數據庫的工具也沒有,唯一值得慶幸的是有xshell,但是完全不符合個人快捷鍵的喜好,沒辦法,想要開發高效,自己動手配置吧。單單是配置這些開發環境就整整犧牲了小編一上午的時間,還好後期開發有明顯的提速。中午吃個飯,下午進入正題。
  下午拿到遷移任務,發現一個庫中有很多表,其中表有大有小,當時我沉默了幾秒鐘,感覺這個星期是交代在這裏了。這裏小編在那幾秒鐘的沉默中也把這個遷移流程想了一下,首先數據是存在關係型數據庫中的,然後我們要通過sqoop將數據上傳到HDFS中,然後用hive的外表去映射這些數據,最終建立有索引的內表來存儲一份完整的數據。內表一般都是分區,分桶,且有索引的orc表,查詢速度明顯比外表快很多。那麼接下來小編就將這其中的步驟,一點點的分析下。

前期準備:

   拿到一個庫的數據時,我們首先分析下這裏有哪些表比較大,哪些表比較小,將大表和小表分開,使用不同的遷移方法,一般都是客戶提供每張表的數據條數,如果沒有的話,只能selecct count(*) from table; 將這些表的數據查出來,不僅便於區分大小表,而且對後期數據覈對有較大的幫助。

1. 從關係型數據庫將數據遷移到HDFS中

  這裏遷移數據小編是用的sqoop,雖然sqoop比較慢,但是學習成本相對較低,而且便於批量的生成語句,對開發要求沒那麼高。首先先測試一個sqoop是否可以成功的遷移數據,然後編寫腳本批量的生成sqoop語句,最後調用這些語句,後臺並行的遷移數據。這裏小編先說說使用sqoop的幾個小竅門:

  • 如果集羣的資源比較充分,一般新集羣是沒有什麼生產任務的,我們的sqoop語句中可以加入:
    -m 這個參數可以設置爲>1 ,表示並行多個map去抽取數據。
    --split-by 當然-m 參數設置大於後,要同時設置這個參數,表示以表中的哪一個字段去分map並行。

    這裏選取--split-by 儘量使用表中比較分散的字段,保證每一個map任務抽取的數據量都大致相同。

  • 如果表的數據量比較大,比如超過億條,我們這裏就需要將這個表分成多個sqoop去抽取:

    --query : 指定where後的條件,抽取部分數據
       這樣的好處是:如果只有一個sqoop任務,抽取了90%的數據後,發現sqoop任務掛了,那麼本次抽取失敗,不僅耗時,而且數據沒有抽取到。分多個sqoop任務,不僅可並行,而且每個任務的數據量也不大,如果有任務掛了,只需要抽象抽取那個where條件下的數據即,並且對於找錯也有極大的幫助。
       分區字段的選取也同樣重要,這裏一般都是使用日期作爲where的後的條件,保證每個sqoop任務分的的數據量相差無幾。

    • sqoop抽取數據的目錄規劃
      #小表目錄規劃
      /tmp/庫名/表名
      #大表的目錄規劃
      /tmp/庫名/表名/分區名
  • query語句:在sqoop命令中,我們編寫查詢語句去抽取數據時,切記不要:

    -- ×
    select * from table;
    -- √
    select 字段1,字段2.... from table;
    不然可能會導致sqoop抽取速度變慢,甚至可能導致沒有抽取到數據。
       當我們注意了以上的內容後,就可以編寫腳本批量的生成每張表的sqoop語句了,根據庫名.表名,獲取關係型數據庫中表的元數據,最後將sqoop組裝起來。最後在編寫任務腳本,定時執行這些sqoop語句。
    實際數據分享:
       這裏小編測試過,數據量比較大時,多sqoop和單sqoop的耗時:
    以600G數據爲例:
       - 多sqoop 並行抽取數據耗時:3~4小時。
       - 單sqoop 抽取數據耗時:12小時以上。
       - 單sqoop && (-m 1)抽取5千萬條數據,大概是27分鐘。

2. 建立外表映射

   說白了就是將抽取到的數據,在hive中通過外表的方式映射出來,其實這裏沒什麼難的,主要是看客戶如果要求,可能是外表單獨一個庫,或者外表的名稱統一是:表名_ext。但是切記,不要手動的去編寫建表語句,如果表有百張以上,心態容易炸,這裏可以使用關係型數據庫的元數據 ,生成hive的建表語句的,這裏我們與MySQL爲例:

SELECT CONCAT('create table ', TABLE_NAME, '(', substring(column_info, 1, length(column_info) - 1), ')', ' comment ', '"', TABLE_COMMENT, '"', ';')
FROM (SELECT TABLE_NAME, TABLE_COMMENT, group_concat(CONCAT(COLUMN_NAME, ' ', DATA_TYPE, ' comment ', '"', COLUMN_COMMENT, '"')) AS column_info
FROM (SELECT t1.TABLE_NAME, CASE WHEN t2.TABLE_COMMENT = NULL THEN t1.TABLE_NAME ELSE t2.TABLE_COMMENT END AS TABLE_COMMENT, COLUMN_NAME, CASE WHEN DATA_TYPE = 'varchar' THEN 'string' WHEN DATA_TYPE = 'int' THEN 'int' WHEN DATA_TYPE = 'tinyint' THEN 'tinyint' WHEN DATA_TYPE = 'decimal' THEN 'double' WHEN DATA_TYPE = 'datetime' THEN 'string' WHEN DATA_TYPE = 'timestamp' THEN 'string' WHEN DATA_TYPE = 'float' THEN 'double' WHEN DATA_TYPE = 'double' THEN 'double' WHEN DATA_TYPE = 'bigint' THEN 'bigint' END AS DATA_TYPE, CASE WHEN COLUMN_COMMENT = NULL THEN COLUMN_NAME ELSE COLUMN_COMMENT END AS COLUMN_COMMENT
FROM COLUMNS t1 JOIN TABLES t2 ON t1.TABLE_NAME = t2.TABLE_NAME
WHERE t1.TABLE_NAME = 't_app_equipment_status'
) t3
GROUP BY TABLE_NAME, TABLE_COMMENT
) t4;

網上這樣的例子很多,這裏小編就不在介紹。當外表建立好之後,最好覈對一下數據量的大小,對比下關係型數據庫中表的數據和hive中的數據是否相同,這樣驗證了sqoop這一環節是否有數據丟失的情況。
遇到的坑:
  當大表我們在分區抽取時,是無法直接映射成爲外表的,我們需要建立範圍分區表,將表的分區目錄映射到各個分區上。

3. 建立高效的內表

   其實這一步就是將,外表的數據,insert到一張和外表字段相同的經過優化的內表中,這張內表一般都是分區分桶,建立索引,或者基於閃存的表,反正就是查詢的速度大大提高的一張表,也叫做業務表。
   小編這裏用的是一種基於閃存的高效查詢的,企業內部開發的一種表結構。小編這裏介紹一下如何確定分桶字段:分桶的好處是
   (1)獲得更高的查詢處理效率。桶爲表加上了額外的結構,Hive 在處理有些查詢時能利用這個結構。具體而言,連接兩個在(包含連接列的)相同列上劃分了桶的表,可以使用 Map 端連接 (Map-side join)高效的實現。比如JOIN操作。對於JOIN操作兩個表有一個相同的列,如果對這兩個表都進行了桶操作。那麼將保存相同列值的桶進行JOIN操作就可以,可以大大較少JOIN的數據量。
   (2)使取樣(sampling)更高效。在處理大規模數據集時,在開發和修改查詢的階段,如果能在數據集的一小部分數據上試運行查詢,會帶來很多方便。
   那麼如果確定分桶字段呢,一般的如果有主鍵的表就使用主鍵作爲分桶字段,如果沒有主鍵的表,找幾個比較分散的字段使用:

 select count(distinct feild) from table;

找出數據最大的那個字段作爲分桶字段。具體的分桶數,這裏建議是一個質數,因爲如果是一個非質數,那麼可能導致分桶不均勻,因爲如果分桶數是9的話,那麼字段值如果爲18、27都會分到一個桶中,可能會導致桶“熱點”。

4. 外表insert 到 內表中

   這個過程是耗時僅次於sqoop的過程,由於我們的內表建立的分區,那麼在這個步驟中我們需要使用hive的動態分區插入,插入語句一般都是:

    insert into table_1 partition(par_field) select field1.field2...from table_2 ;

這裏需要注意的是select 最後一個字段一定要是分區字段。
   當我們insert 後,需要覈對下是否所有的數據全部insert成功,此時:外表數據量=內表數據量=關係型數據庫數據量

5. 日增量數據的處理

   當我們完成數據遷移後,其實外表相當於一箇中轉站,僅僅是將數據中轉到內表中,如果我們確保了內表中有一份完整的數據,此時可以將外表的數據清空,這也是爲什麼我們將外表的location 設置爲/tmp下 的原因,清空外表數據後,將日增數據抽取到外表的location地址上,然後全量的將外表數據insert到內表中,就保證了日增量數據的成功導入到hive。

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