Hawk原理:通過IEnumerable實現通用的ETL管道

來源:互聯網
上載者:User

標籤:protect   難度   targe   轉換   ase   限制   alc   匹配   設定   

  針對IEnumerable已經有多篇文章,本篇介紹如何使用IEnumerable實現ETL. ETL,是英文 Extract-Transform-Load 的縮寫,用來描述將資料從來源端經過萃取(extract)、轉置(transform)、載入(load)至目的端的過程。通常來說,從原始端採集的資料有很多問題,同時可能業務需求與採集的資料格式不相匹配,所以就必須實現ETL過程。

  ETL可以理解為一條清洗管線,資料從一端流入,從另一端流出。資料量可能很大,所以管線不大可能也沒有必要載入全部內容。同時,一般情況下,從管線流出來的資料會進入新的資料池,很少直接修改到原表。

  從管線的概念可以看出,ETL需要構造可組合的鏈條,首先實現一組組件,然後實現可將這些組件組裝為一條ETL管線的架構。IEnumerable一大堆的LINQ擴充,正好幫我們實現了這一思想。

1. 資料的表達

  我們先討論清楚如何表達資料,因為資料處理涉及到動態增減屬性的問題,因此一般的實體類是做不到的,我們採用字典來實現。為此我封裝了一個實現IDictionary<string, object>的類。叫做FreeDocument。它可以簡單表示如下:

  /// <summary>    /// 自由格式文檔    /// </summary>    public interface IFreeDocument : IDictionarySerializable, IDictionary<string, object>, IComparable    {        #region Properties        IDictionary<string, object> DataItems { get; set; }        IEnumerable<string> PropertyNames { get; }        #endregion    }

 

  因此資料的處理,本質就是對每一個字典對象中的索引值對進行增刪改查。

2 .基本組件

  資料清洗組件的基底介面是ICollumProcess. 定義如下:

 public interface ICollumProcess : IDictionarySerializable    {        string CollumName { get; set; }  //針對的列名        bool ShouldCalculated { get; set; }  //是否需要重新計算         double Priority { get; set; }  //優先順序        void Finish();  //處理完成時的回收函數        void Init(IList<IFreeDocument> datas);  //對資料進行初始化的探測行為    }

 

  更清晰的說,其實派生出四部分:

  (1) 產生器

  產生器即提供/產生資料的組件。這可能包括產生一個從0-1000的數,擷取某個資料表中的資料,或從網頁檢索的結果。它的介面可以表示如下:

[Interface("ICollumGenerator", "資料產生器", SearchStrategy.FolderSearch)]    public interface ICollumGenerator : ICollumProcess      {          /// <summary>          /// 當前迭代的位置          /// </summary>          int Position { get; set; }          IEnumerable<FreeDocument> Generate();/// <summary>          /// 產生器能產生的文檔數量          /// </summary>          /// <returns></returns>          int? GenerateCount();      }

 

  最主要的方法是Generate,它能夠枚舉出一組資料出來,同時還有可能(有時做不到)得到能夠產生文檔的總數量。

  (2)過濾器

  過濾器即能夠分析一個文檔是否滿足條件,不滿足則剔除的組件。介面也很簡單:

  

  [Interface("ICollumDataFilter", "資料列過濾器", SearchStrategy.FolderSearch)]     public interface ICollumDataFilter :  ICollumProcess    {        bool FilteData(IFreeDocument data);         }

   (3)排序器

  顧名思義,對資料實現排序的介面,定義如下:

  [Interface("ICollumDataSorter", "資料排序器", SearchStrategy.FolderSearch)]    public interface ICollumDataSorter : IDictionarySerializable, ICollumProcess,IComparer<object>    {                       SortType SortType { get; set; }         IEnumerable<IFreeDocument> Sort(IEnumerable<IFreeDocument> data);    }

 

    排序一般需要升序和降序,但排序最大的問題是破壞了管線的單向流動性和虛擬性。最少LINQ的標準實現上,排序是記憶體排序,因此必須把資料全部載入進來才能排序,這嚴重影響了效能。因此目前的排序最好在小資料的情況下進行。

  (4)列轉換器

  它最重要的組件。整個ETL過程,實質上就是不同的列進行變換,組成另外一些列的過程(列就是索引值對)。 定義實現如下:

[Interface("ICollumDataTransformer", "資料轉換器", SearchStrategy.FolderSearch)]    public interface ICollumDataTransformer : ICollumProcess    {        string NewCollumName { get; set; }        SimpleDataType TargetDataType { get; set; }        ObservableCollection<ICollumDataFilter> FilterLogics { get; set; }        object TransformData(IFreeDocument datas);        IEnumerable<string> AffectedCollums { get; }    }

   看著很複雜,但其實就是將文檔中的一些列轉換為另外一些列。比如對一個字串的列進行正則替換,或轉換其資料類型(如從string變成int)。舉個最簡單的HTML編解碼的例子:

   public override object TransformData(IFreeDocument document)        {            object item = document[CollumName];            if (item == null)                return "";            switch (ConvertType)            {                case ConvertType.Decode:                    return HttpUtility.HtmlDecode(item.ToString());                    break;                case ConvertType.Encode:                    return HttpUtility.HtmlEncode(item.ToString());                    break;            }            return "";        }

 

  

3. ETL管線的設計

   相信你已經想到,ETL管線的核心就是動態組裝的LINQ了。

   一個最基本的ETL管理類,應當具有以下的屬性:

    public ObservableCollection<ICollumProcess> CurrentETLTools { get; set; }  //當前已經載入的ETL工具

    protected List<Type> AllETLTools { get; set; }  //所有能夠使用的ETL工具。當然Type只是此處為了方便理解而設定的,更合適的應該是記錄了組件中繼資料,名字和介紹的擴充類。

    以及一個方法:

    public IEnumerable<IFreeDocument> RefreshDatas(IEnumerable<IFreeDocument> docuts)   //從未經處理資料轉換為新的資料

    那麼,這個函數的實現可以如下定義:

  public IEnumerable<IFreeDocument> RefreshDatas(IEnumerable<IDictionarySerializable> docuts)        {            if (SampleMount <= 0)            {                SampleMount = 10;            }            IEnumerable<IFreeDocument> ienumable = docuts.Where(d=>d!=null).Select(d => d.DictSerialize());            Errorlogs = new List<ErrorLog>();                     List<IFreeDocument> samples = docuts.Take((int) SampleMount).Select(d => d as IFreeDocument).ToList();            foreach (ICollumProcess tool in                CurrentETLTools.Where(d => d.ShouldCalculated).OrderByDescending(d => d.Priority))            {                tool.SourceCollection = CurrentCollection;                tool.Init(samples);                if (tool is ICollumDataTransformer)                {                    var ge = tool as ICollumDataTransformer;                    ienumable = Transform(ge, ienumable);                }                if (tool is ICollumGenerator)                {                    var ge = tool as ICollumGenerator;                    if (!ge.CanAppend) //直接拼接                        ienumable = ienumable.Concat(ge.Generate());                    else                    {                        ienumable = ienumable.MergeAll(ge.Generate());                    }                }                else if (tool is ICollumDataFilter)                {                    var t = tool as ICollumDataFilter;                    ienumable = ienumable.Where(t.FilteData);                }                else if (tool is ICollumDataSorter)                {                    var s = tool as ICollumDataSorter;                    switch (s.SortType)                    {                        case SortType.AscendSort:                            ienumable = ienumable.OrderBy(d => d, s);                            break;                        case SortType.DescendSort:                            ienumable = ienumable.OrderByDescending(d => d, s);                            break;                    }                }                tool.Finish();            }            return ienumable;        }

 

    基本實現思路如上。即通過優先順序排序所有載入的ETL組件,並提取一部分範例資料,為組件進行一次初始化。然後通過組裝不同的轉換器,產生器,排序器和過濾器,最後即可組裝為一個新的ienumable對象。注意整個過程都是延遲計算的,只有在真正需要ETL結果時才會進行實質性的操作。

 4. 最佳化ETL管線和實現虛擬視圖

  以上就是ETL的基本思路。但是僅僅做到這些是很不夠的。以下才是這篇文章的核心。

  ETL管線破壞了原有集合的特性,原有集合可能是能夠支援索引查詢甚至能夠執行高效能尋找的。但ETL將其退化為僅能夠枚舉。枚舉意味著只能從頭訪問到尾,不能回退和索引。要想使用新集合,就只能訪問其前n個元素,或者全部訪問。這顯然對一些操作是很不利的。

  先考慮索引器。如果能滿足以下條件:

  (1) 管線中不包括排序器和過濾器,因為它們使得得集合產生了亂序。

  (2) 原創組合能夠支援索引器

  (3) 使用的產生器能夠提供產生的大小,同時產生器也能夠實現索引器

  (4) 轉換器應當只實現1到1轉換,沒有額外的副作用。

   那麼原創組合和新集合元素的對應關係是可計算的。此時索引器就能發揮作用。在實際使用中,轉換器是用的最多的。條件不可謂不苛刻。

 

  關於高效能尋找,我們先不考慮針對複雜的SQL查詢,先考慮那種最簡單的find(item[key]==value)的查詢。但這個條件更加苛刻:

  (1) key在原創組合中必須支援高效能尋找

  (2) 滿足上述索引器的四個條件

  (3) 針對key這一列的操作,轉換器必須是可逆的。而且最好能實現1-1映射。

    所謂可逆的意思就是說,轉換器能從A轉換為B,同時也能通過結果B反推出結果A。 但這種條件何其苛刻!a*5=b,這樣的操作是可逆的,然而正則轉換,替換以及絕大多數的運算都是無法復原的。

  怎麼辦呢?可能的做法,就是轉換器在轉換過程中,就動態地將key的轉換結果儲存下來。於是,對新集合的尋找操作,最後就能一步步回退到原創組合的尋找操作。還有更好的辦法嗎?

 

  如何讓新集合應對複雜的SQL查詢?首先需要解析SQL, 這可能涉及到大量的數學推導和轉換。以至於在實現當中因為限制太多,基本上不可能實現。以篩選key為一定範圍的資料為例,每次都需要逆向推導,這種推導難度非常大。

 

5. 智能ETL和使用者體驗最佳化

  整個ETL過程,是人為觀察資料的特性,組合和配置不同的ETL組件,這一過程能夠實現自動化嗎?

  人是很智能的,它能夠觀察不同資料的格式和類型,發現其中的特徵,比如以下資料:

高樓層/21層,南垡頭翠成馨園,2004年建,塔樓中樓層/5層,南北豆各莊5號院,2003年建,板樓

 

  人通過觀察這麼兩行的資料,就可以大概的判斷出這些資訊分別代表的是什麼意思,以及如何去分割和轉換。可以用正則,提取第一個出現的數字,即樓層,再使用\d{4}提取年份,而用逗號分割,即可得到小區名稱。

  但是,這個操作依舊需要最少懂得一定程式基礎的人來參與,如果用機器來做的話,又該如何做呢?自動化步驟可以分為兩個層次:

  (1) 自動分割和對齊。

  資料尤其是來自web的資料,由於本身是由程式產生的,因此在格式上有高度的統一性,同時分隔字元也是類似的,包括逗號,分號,空格,斜杠等。因此,可以統計不同分割符出現的次數,以及對應的位置,通過機率模型,產生最可能的分割方案,使得每一條資料分割出來的長度和子項數量儘可能一致。

  (2) 自動識別內容

  自動識別內容可以依賴於規則或者辨識器。一種比較可靠的方法是通過基於正則的文本規則,構造一組規則群組。通常200x這樣的數值,很容易被理解為年份,而12:32這樣的結構,則很容易被識別為時間。通過基於結構的識別引擎,不僅能夠識別”這是什麼內容“,更能提出其中繼資料,比如日期中的日月年等資訊,為之後的工作做準備。

  Web表格最大的好處,在於它的格式一致性。只要分析很少的具有代表性的範例資料,就能夠掌握整個資料集的特徵。因此完全可以用比較大的代價獲得一個儘可能高的識別模組,而在執行過程中盡量提升效能。

 

  

       

Hawk原理:通過IEnumerable實現通用的ETL管道

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.