hive/impala使用列式存儲(chǔ)追加一列數(shù)據(jù)的可行性

我們知道parquet文件格式是不能進(jìn)行update操作的。但是是否可以對(duì)其進(jìn)行添加一列數(shù)據(jù)呢?

先看看parquet文件長(zhǎng)什么樣

Parquet文件是以二進(jìn)制方式存儲(chǔ)的忧设,是不可以直接讀取和修改的古沥,Parquet文件是自解析的彩扔,文件中包括該文件的數(shù)據(jù)和元數(shù)據(jù)。在HDFS文件系統(tǒng)和Parquet文件中存在如下幾個(gè)概念:

  1. HDFS塊(Block):它是HDFS上的最小的副本單位蒸痹,HDFS會(huì)把一個(gè)Block存儲(chǔ)在本地的一個(gè)文件并且維護(hù)分散在不同的機(jī)器上的多個(gè)副本春弥,通常情況下一個(gè)Block的大小為256M、512M等叠荠。
  2. HDFS文件(File):一個(gè)HDFS的文件匿沛,包括數(shù)據(jù)和元數(shù)據(jù),數(shù)據(jù)分散存儲(chǔ)在多個(gè)Block中榛鼎。
  3. 行組(Row Group):按照行將數(shù)據(jù)物理上劃分為多個(gè)單元逃呼,每一個(gè)行組包含一定的行數(shù)鳖孤,在一個(gè)HDFS文件中至少存儲(chǔ)一個(gè)行組,Parquet讀寫的時(shí)候會(huì)將整個(gè)行組緩存在內(nèi)存中抡笼。
  4. 列塊(Column Chunk):在一個(gè)行組中每一列保存在一個(gè)列塊中苏揣,行組中的所有列連續(xù)的存儲(chǔ)在這個(gè)行組文件中。不同的列塊可能使用不同的算法進(jìn)行壓縮推姻。
  5. 頁(yè)(Page):每一個(gè)列塊劃分為多個(gè)頁(yè)平匈,一個(gè)頁(yè)是最小的編碼的單位,在同一個(gè)列塊的不同頁(yè)可能使用不同的編碼方式藏古。

通常情況下增炭,在存儲(chǔ)Parquet數(shù)據(jù)的時(shí)候會(huì)按照HDFS的Block大小設(shè)置行組的大小,由于一般情況下每一個(gè)Mapper任務(wù)處理數(shù)據(jù)的最小單位是一個(gè)Block拧晕,這樣可以把每一個(gè)行組由一個(gè)Mapper任務(wù)處理隙姿,增大任務(wù)執(zhí)行并行度。


Parquet文件結(jié)構(gòu)

上圖展示了一個(gè)Parquet文件的結(jié)構(gòu)防症,一個(gè)文件中可以存儲(chǔ)多個(gè)行組孟辑,文件的首位都是該文件的Magic Code,用于校驗(yàn)它是否是一個(gè)Parquet文件蔫敲,F(xiàn)ooter length存儲(chǔ)了文件元數(shù)據(jù)的大小饲嗽,通過(guò)該值和文件長(zhǎng)度可以計(jì)算出元數(shù)據(jù)的偏移量,文件的元數(shù)據(jù)中包括每一個(gè)行組的元數(shù)據(jù)信息和當(dāng)前文件的Schema信息奈嘿。除了文件中每一個(gè)行組的元數(shù)據(jù)貌虾,每一頁(yè)的開始都會(huì)存儲(chǔ)該頁(yè)的元數(shù)據(jù),在Parquet中裙犹,有三種類型的頁(yè):數(shù)據(jù)頁(yè)尽狠、字典頁(yè)和索引頁(yè)。數(shù)據(jù)頁(yè)用于存儲(chǔ)當(dāng)前行組中該列的值叶圃,字典頁(yè)存儲(chǔ)該列值的編碼字典袄膏,每一個(gè)列塊中最多包含一個(gè)字典頁(yè),索引頁(yè)用來(lái)存儲(chǔ)當(dāng)前行組下該列的索引掺冠,目前Parquet中還不支持索引頁(yè)沉馆,但是在后面的版本中增加。

從parquet文件格式中可以看出德崭,如果我們需要為文件添加一列數(shù)據(jù)的話斥黑,需要對(duì)每個(gè)row group 添加列,這樣勢(shì)必會(huì)打破原有的數(shù)據(jù)偏移量眉厨,也有可能因?yàn)槎嗔艘涣性斐珊罄m(xù)的row group全部變化锌奴,字典頁(yè)和索引頁(yè)也可能造成變化;另外在Footer中也需要根據(jù)新的數(shù)據(jù)列重新對(duì)每個(gè)偏移量進(jìn)行計(jì)算憾股。

所以基于此鹿蜀,并未看到parquet在加列操作中箕慧,是在原文件中添加(甚至在使用impala對(duì)table加列后,原parquet文件schema都未曾變化)茴恰,而往往是新生成一個(gè)parquet文件销钝。

示例

原表 entry, uuid, age, name 三個(gè)字段,建表指定parquet存儲(chǔ)琐簇,并插入兩條數(shù)據(jù),在hdfs上生成一個(gè)parquet文件座享。
然后在impala執(zhí)行add column操作婉商,添加tel字段,并向其插入一些數(shù)據(jù)渣叛,生成一個(gè)新的parquet文件丈秩。通過(guò)parquet-Hadoop API直接讀取parquet文件信息,獲得

uuid->age->name->
row count: 2
{"uuid":"1",age":"20",name":"bob"}
{"uuid":"2",age":"10",name":"tom"}
uuid->age->name->tel->
row count: 6
{"uuid":"1",age":"20",name":"bob",tel":"186152372"}
{"uuid":"2",age":"30",name":"tom",tel":"186152372"}
{"uuid":"3",age":"40",name":"laiwb2",tel":"186152372"}
{"uuid":"4",age":"22",name":"bingo1",tel":"186152372"}
{"uuid":"6",age":"23",name":"feng",tel":"186152372"}
{"uuid":"7",age":"24",name":"bixians",tel":"186152372"}

從打印的信息來(lái)看淳衙,parquet文件1 schema只有三個(gè)字段uuid,age,name蘑秽,這個(gè)文件就是建表時(shí)插入的,后面在add column后又插入了6條數(shù)據(jù)箫攀,這時(shí)肠牲,新生成的parquet文件的metadata多了一個(gè)tel,數(shù)據(jù)頁(yè)中數(shù)據(jù)也多了tel列。
讀取parquet文件主要的代碼片段:

ParquetMetadata readFooter = ParquetFileReader.readFooter(conf, path, ParquetMetadataConverter.NO_FILTER);
// parquet 文件的 schema信息
MessageType schema = readFooter.getFileMetaData().getSchema();
List<Type> columnInfos = schema.getFields();
for (Type type : columnInfos) {
    System.out.print(type.getName() + "->");
}
// 每個(gè) row group 的數(shù)量
List<BlockMetaData> blockMeta = readFooter.getBlocks();
for (BlockMetaData bl : blockMeta) {
    System.out.println("row count: " + bl.getRowCount());
}

ParquetReader<Group> reader = ParquetReader.builder(new GroupReadSupport(), path).withConf(conf).build();
int count = 0;
Group recordData = reader.read();

while (count < 10 && recordData != null) {
    StringBuilder builder = new StringBuilder();
    builder.append("{\"");
    for (int j = 0; j < columnInfos.size(); j++) {
        if (j < columnInfos.size() - 1) {
            String columnName = columnInfos.get(j).getName();
            String value = recordData.getValueToString(j, 0);
            builder.append(columnName + "\":\"" + value + "\",");
        } else {
            String columnName = columnInfos.get(j).getName();
            String value = recordData.getValueToString(j, 0);
            builder.append(columnName + "\":\"" + value + "\"}");
        }
    }

    System.out.println(builder.toString());
    count++;
    recordData = reader.read();
}

然后drop name字段(parquet文件不會(huì)有什么變動(dòng)靴跛,只是元數(shù)據(jù)信息變化)缀雳,在hive元數(shù)據(jù)中只有uuid,age,tel三個(gè)字段,使用impala查詢和hive查詢出現(xiàn)了不同數(shù)據(jù)梢睛。hive能夠查詢匹配出這三個(gè)字段的數(shù)據(jù)來(lái)肥印,但impala卻查詢出的是uuid, age, name數(shù)據(jù),看樣子impala是根據(jù)元數(shù)據(jù)信息逐個(gè)取值的绝葡,并不是根據(jù)hive元數(shù)據(jù)和parquet元數(shù)據(jù)對(duì)應(yīng)取值深碱。

綜上,parquet 理論上可以追加一列藏畅,但是代價(jià)比較高敷硅,需要對(duì)row group 進(jìn)行修改,并且需要同步parquet metadata信息墓赴,修改數(shù)據(jù)的offerset和schema等竞膳,并且parquet-Hadoop也未提供相應(yīng)的方法。


后續(xù)~~~

列式存儲(chǔ)的另外一種存儲(chǔ)格式ORC诫硕,與parquet的對(duì)比圖坦辟。


parquet vs orc

從parquet與orc對(duì)比圖中可以知道,orc支持ACID和update操作章办,接下來(lái)說(shuō)說(shuō)ORC锉走。
和Parquet類似滨彻,它并不是一個(gè)單純的列式存儲(chǔ)格式,仍然是首先根據(jù)行組分割整個(gè)表挪蹭,在每一個(gè)行組內(nèi)進(jìn)行按列存儲(chǔ)亭饵。ORC文件是自描述的,它的元數(shù)據(jù)使用Protocol Buffers序列化梁厉,并且文件中的數(shù)據(jù)盡可能的壓縮以降低存儲(chǔ)空間的消耗辜羊,目前也被Spark SQL、Presto等查詢引擎支持词顾,但是Impala對(duì)于ORC目前沒(méi)有支持八秃,仍然使用Parquet作為主要的列式存儲(chǔ)格式(可能parquet是cloudera自家開發(fā)的吧,競(jìng)品ORC架構(gòu)和parquet又相似度很高)肉盹。

ORC文件結(jié)構(gòu)

和Parquet類似昔驱,ORC文件也是以二進(jìn)制方式存儲(chǔ)的,所以是不可以直接讀取上忍,ORC文件也是自解析的骤肛,它包含許多的元數(shù)據(jù),這些元數(shù)據(jù)都是同構(gòu)ProtoBuffer進(jìn)行序列化的窍蓝。ORC的文件結(jié)構(gòu)見下圖腋颠,其中涉及到如下的概念:

  1. ORC文件:保存在文件系統(tǒng)上的普通二進(jìn)制文件,一個(gè)ORC文件中可以包含多個(gè)stripe它抱,每一個(gè)stripe包含多條記錄秕豫,這些記錄按照列進(jìn)行獨(dú)立存儲(chǔ),對(duì)應(yīng)到Parquet中的row group的概念观蓄。
  2. 文件級(jí)元數(shù)據(jù):包括文件的描述信息PostScript混移、文件meta信息(包括整個(gè)文件的統(tǒng)計(jì)信息)、所有stripe的信息和文件schema信息侮穿。
  3. stripe:一組行形成一個(gè)stripe歌径,每次讀取文件是以行組為單位的,一般為HDFS的塊大小亲茅,保存了每一列的索引和數(shù)據(jù)回铛。
  4. stripe元數(shù)據(jù):保存stripe的位置、每一個(gè)列的在該stripe的統(tǒng)計(jì)信息以及所有的stream類型和位置克锣。
  5. row group:索引的最小單位茵肃,一個(gè)stripe中包含多個(gè)row group,默認(rèn)為10000個(gè)值組成袭祟。
  6. stream:一個(gè)stream表示文件中一段有效的數(shù)據(jù)验残,包括索引和數(shù)據(jù)兩類。索引stream保存每一個(gè)row group的位置和統(tǒng)計(jì)信息巾乳,數(shù)據(jù)stream包括多種類型的數(shù)據(jù)您没,具體需要哪幾種是由該列類型和編碼方式?jīng)Q定鸟召。
ORC文件結(jié)構(gòu)

從ORC數(shù)據(jù)格式來(lái)看,發(fā)現(xiàn)其結(jié)構(gòu)和parquet很類似氨鹏,也是先分行欧募,每個(gè)row group里面,對(duì)數(shù)據(jù)進(jìn)行列式存儲(chǔ)仆抵,然后提供元數(shù)據(jù)(包括每個(gè)column的offset跟继,字典表,壓縮算法等等)镣丑。在parquet里面我談到还栓,如果想往parquet追加一列數(shù)據(jù)的話,需要往每個(gè)row group里面添加列传轰,需要打破原有的column chunk,并且重新更新footer元數(shù)據(jù)信息谷婆。而ORC也類似慨蛙,需要增添的列打攤到每個(gè)stripe,在row data里面添加一列纪挎,更新index和stream期贫。
因?yàn)镠DFS是一次寫的文件系統(tǒng),ORC是一次寫的文件格式异袄,因此ORC為了支持ACID操作通砍,采用基礎(chǔ)文件和增量文件來(lái)實(shí)現(xiàn)insert,update烤蜕,delete封孙。大致思想是transaction會(huì)被存儲(chǔ)在增量文件中,并且當(dāng)delte變多會(huì)自動(dòng)合并讽营,當(dāng)查詢數(shù)據(jù)時(shí)虎忌,將原數(shù)據(jù)與delta數(shù)據(jù)排序,然后取最近的更改橱鹏。詳情見ACID膜蠢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莉兰,隨后出現(xiàn)的幾起案子挑围,更是在濱河造成了極大的恐慌,老刑警劉巖糖荒,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杉辙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡寂嘉,警方通過(guò)查閱死者的電腦和手機(jī)奏瞬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門枫绅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人硼端,你說(shuō)我怎么就攤上這事并淋。” “怎么了珍昨?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵县耽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我镣典,道長(zhǎng)兔毙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任兄春,我火速辦了婚禮澎剥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赶舆。我一直安慰自己哑姚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布芜茵。 她就那樣靜靜地躺著叙量,像睡著了一般。 火紅的嫁衣襯著肌膚如雪九串。 梳的紋絲不亂的頭發(fā)上绞佩,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音猪钮,去河邊找鬼品山。 笑死,一個(gè)胖子當(dāng)著我的面吹牛烤低,可吹牛的內(nèi)容都是我干的谆奥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼拂玻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼酸些!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起檐蚜,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤魄懂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后闯第,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體市栗,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了填帽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛛淋。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖篡腌,靈堂內(nèi)的尸體忽然破棺而出褐荷,到底是詐尸還是另有隱情,我是刑警寧澤嘹悼,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布叛甫,位于F島的核電站,受9級(jí)特大地震影響杨伙,放射性物質(zhì)發(fā)生泄漏其监。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一限匣、第九天 我趴在偏房一處隱蔽的房頂上張望抖苦。 院中可真熱鬧,春花似錦米死、人聲如沸睛约。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贸伐,卻和暖如春勘天,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捉邢。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工脯丝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人伏伐。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓宠进,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親藐翎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子材蹬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容