Hive 2.3 decimal精度損失問題

1.準(zhǔn)備測試數(shù)據(jù)

????使用如下建表語句,并插入測試數(shù)據(jù):

CREATE?TABLE?IF?NOT?EXISTS test_decimal(

? ? md5 string,

? ? id int,

? ? ty int,

? ? amount decimal(38, 12)

) stored as orc?;

insert into table test_decimal values

('9F99855A44BD41FE592B69E0D36BF3E8', 4591, 2, 188593.210890000000),

('9F99855A44BD41FE592B69E0D36BF3E8', 4592, 2, 177918.123481132049),

('9F99855A44BD41FE592B69E0D36BF3E8', 4593, 2, 10675.087408867951);

2.使用測試sql測試(在2.3.x版本中執(zhí)行的)

????使用測試sql穴豫,發(fā)現(xiàn)測試的結(jié)果有精度損失:

hive> select id, sum(amount) from test_decimal group by id;

OK

4591 188593.210890000000

4592 177918.123481132049

4593 10675.087408867951

Time taken: 28.013 seconds, Fetched: 3 row(s)


hive>? select id, sum(amount) * -1 from test_decimal group by id;

OK

4591 -188593.2108900000

4592 -177918.1234811320

4593 -10675.0874088680

Time taken: 26.016 seconds, Fetched: 3 row(s)

????通過比較測試結(jié)果發(fā)現(xiàn),在sum函數(shù)之后乘以 -1 導(dǎo)致精度損失了2位。

3.通過分析執(zhí)行計(jì)劃查找兩條sql的執(zhí)行計(jì)劃的區(qū)別习柠,查找原因(在2.3.x版本中執(zhí)行的)

????直接輸出sum的sql的執(zhí)行計(jì)劃:

hive> explain select id, sum(amount) from test_decimal group by id;

OK

STAGE DEPENDENCIES:

? Stage-1 is a root stage

? Stage-0 depends on stages: Stage-1

STAGE PLANS:

? Stage: Stage-1

? ? Map Reduce

? ? ? Map Operator Tree:

? ? ? ? ? TableScan

? ? ? ? ? ? alias: test_decimal

? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? Select Operator

? ? ? ? ? ? ? expressions: id (type: int), amount (type: decimal(38,12))

? ? ? ? ? ? ? outputColumnNames: id, amount

? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? Group By Operator

? ? ? ? ? ? ? ? aggregations: sum(amount)

? ? ? ? ? ? ? ? keys: id (type: int)

? ? ? ? ? ? ? ? mode: hash

? ? ? ? ? ? ? ? outputColumnNames: _col0, _col1

? ? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? ? Reduce Output Operator

? ? ? ? ? ? ? ? ? key expressions: _col0 (type: int)

? ? ? ? ? ? ? ? ? sort order: +

? ? ? ? ? ? ? ? ? Map-reduce partition columns: _col0 (type: int)

? ? ? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? ? ? value expressions: _col1 (type: decimal(38,12))

? ? ? Reduce Operator Tree:

? ? ? ? Group By Operator

? ? ? ? ? aggregations: sum(VALUE._col0)

? ? ? ? ? keys: KEY._col0 (type: int)

? ? ? ? ? mode: mergepartial

? ? ? ? ? outputColumnNames: _col0, _col1

? ? ? ? ? Statistics: Num rows: 1 Data size: 236 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? File Output Operator

? ? ? ? ? ? compressed: false

? ? ? ? ? ? Statistics: Num rows: 1 Data size: 236 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? table:

? ? ? ? ? ? ? ? input format: org.apache.hadoop.mapred.SequenceFileInputFormat

? ? ? ? ? ? ? ? output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat

? ? ? ? ? ? ? ? serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe

? Stage: Stage-0

? ? Fetch Operator

? ? ? limit: -1

? ? ? Processor Tree:

? ? ? ? ListSink

Time taken: 0.16 seconds, Fetched: 48 row(s)

sum后乘以 -1 的sql的執(zhí)行計(jì)劃:

hive> explain select id, sum(amount)*-1 from test_decimal group by id;

OK

STAGE DEPENDENCIES:

? Stage-1 is a root stage

? Stage-0 depends on stages: Stage-1

STAGE PLANS:

? Stage: Stage-1

? ? Map Reduce

? ? ? Map Operator Tree:

? ? ? ? ? TableScan

? ? ? ? ? ? alias: test_decimal

? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? Select Operator

? ? ? ? ? ? ? expressions: id (type: int), amount (type: decimal(38,12))

? ? ? ? ? ? ? outputColumnNames: id, amount

? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? Group By Operator

? ? ? ? ? ? ? ? aggregations: sum(amount)

? ? ? ? ? ? ? ? keys: id (type: int)

? ? ? ? ? ? ? ? mode: hash

? ? ? ? ? ? ? ? outputColumnNames: _col0, _col1

? ? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? ? Reduce Output Operator

? ? ? ? ? ? ? ? ? key expressions: _col0 (type: int)

? ? ? ? ? ? ? ? ? sort order: +

? ? ? ? ? ? ? ? ? Map-reduce partition columns: _col0 (type: int)

? ? ? ? ? ? ? ? ? Statistics: Num rows: 3 Data size: 708 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? ? ? value expressions: _col1 (type: decimal(38,12))

? ? ? Reduce Operator Tree:

? ? ? ? Group By Operator

? ? ? ? ? aggregations: sum(VALUE._col0)

? ? ? ? ? keys: KEY._col0 (type: int)

? ? ? ? ? mode: mergepartial

? ? ? ? ? outputColumnNames: _col0, _col1

? ? ? ? ? Statistics: Num rows: 1 Data size: 236 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? Select Operator

? ? ? ? ? ? expressions: _col0 (type: int), (_col1 * -1) (type: decimal(38,10))

? ? ? ? ? ? outputColumnNames: _col0, _col1

? ? ? ? ? ? Statistics: Num rows: 1 Data size: 236 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? File Output Operator

? ? ? ? ? ? ? compressed: false

? ? ? ? ? ? ? Statistics: Num rows: 1 Data size: 236 Basic stats: COMPLETE Column stats: NONE

? ? ? ? ? ? ? table:

? ? ? ? ? ? ? ? ? input format: org.apache.hadoop.mapred.SequenceFileInputFormat

? ? ? ? ? ? ? ? ? output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat

? ? ? ? ? ? ? ? ? serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe

? Stage: Stage-0

? ? Fetch Operator

? ? ? limit: -1

? ? ? Processor Tree:

? ? ? ? ListSink

Time taken: 4.656 seconds, Fetched: 52 row(s)

????通過查看兩條sql(兩條sql的差別是在sum函數(shù)后面有沒有乘以 -1)的執(zhí)行計(jì)劃發(fā)現(xiàn)暖途,直接輸出sum的結(jié)果是 (value expressions: _col1 (type:decimal(38,12)))類型膏执,輸出乘以 -1 的結(jié)果是 ( expressions: _col0 (type: int), (_col1 * -1) (type:decimal(38,10)) )類型驻售,說明乘以 -1 之后欺栗,精度損失了2位。

4.分析源碼类腮,找原因

????通過分析sum后乘以 -1 的代碼蚜枢,其中關(guān)鍵的代碼如下:

????2.3.x 版本的GenericUDFOPMultiply 類的關(guān)鍵代碼


????prec1 和 scale1 代表的是 decimal(38,12)

????prec2 和 scale2 代表的是 -1被轉(zhuǎn)換成decimal后的類型為 decimal(1, 0)

????其中 adjustPrecScale()方法的代碼在其父類GenericUDFBaseNumeric中筷凤,代碼如下:


????decimal支持的最大精度為38,而通過上面的計(jì)算莽鸭,發(fā)現(xiàn)精度precision字段的值已經(jīng)達(dá)到了40足淆,超過的最大精度丹鸿,因此廊敌,需要重新計(jì)算精度肋殴,計(jì)算后的結(jié)果是护锤,將小數(shù)的精度減少了2位為10拧粪,精度使用最大精度值38。

????在hive2.3.x中,算術(shù)運(yùn)算的精度的計(jì)算公式如下:


????至此正卧,精度損失的原因已經(jīng)找到蠢熄,是因?yàn)槌朔ㄟ\(yùn)算,將兩個(gè)精度進(jìn)行相加后再加1炉旷,超出了最大精度签孔,重新計(jì)算精度時(shí)叉讥,將小數(shù)位的精度改成了10導(dǎo)致的。

5.解決方案

1)針對(duì)這個(gè)乘以 -1 的操作饥追,可以改成使用單目運(yùn)算負(fù)號(hào) - 的方式图仓,將負(fù)號(hào) - 加到sum前即可。

2)降低建表語句中decimal類型的精度字段的值但绕,根據(jù)上面計(jì)算精度的表算出一個(gè)滿足需要的最小精度值救崔。

6.為啥相同的sql在1.2.x版本中結(jié)果就沒有損失精度

????1.2.x 版本的GenericUDFOPMultiply 類的關(guān)鍵代碼

public class GenericUDFOPMultiply extends GenericUDFBaseNumeric {

.....

? @Override

? protected DecimalTypeInfo deriveResultDecimalTypeInfo(int prec1, int scale1, int prec2, int scale2) {

? ? int scale = Math.min(HiveDecimal.MAX_SCALE, scale1 + scale2 );

? ? int prec = Math.min(HiveDecimal.MAX_PRECISION, prec1 + prec2 + 1);

? ? return TypeInfoFactory.getDecimalTypeInfo(prec, scale);

? }

}

????其中 HiveDecimal.MAX_SCALE 和 HiveDecimal.MAX_PRECISION 的值都是38。

????從上面的關(guān)鍵代碼中可以看到捏顺,在1.2.x中帚豪,沒有重新校準(zhǔn)精度的地方,而是使用簡單粗暴的方式草丧,各自計(jì)算precision和scale的精度,這就會(huì)導(dǎo)致在真實(shí)數(shù)據(jù)很大的時(shí)候莹桅,計(jì)算出來的值的精度達(dá)不到預(yù)期昌执,也就是會(huì)不準(zhǔn)確。诈泼,

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懂拾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子铐达,更是在濱河造成了極大的恐慌岖赋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓮孙,死亡現(xiàn)場離奇詭異唐断,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)杭抠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門脸甘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人偏灿,你說我怎么就攤上這事丹诀。” “怎么了翁垂?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵铆遭,是天一觀的道長。 經(jīng)常有香客問我沿猜,道長枚荣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任邢疙,我火速辦了婚禮棍弄,結(jié)果婚禮上望薄,老公的妹妹穿的比我還像新娘。我一直安慰自己呼畸,他們只是感情好痕支,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛮原,像睡著了一般卧须。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上儒陨,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天花嘶,我揣著相機(jī)與錄音,去河邊找鬼蹦漠。 笑死椭员,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笛园。 我是一名探鬼主播隘击,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼研铆!你這毒婦竟也來了埋同?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤棵红,失蹤者是張志新(化名)和其女友劉穎凶赁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逆甜,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虱肄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忆绰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浩峡。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖错敢,靈堂內(nèi)的尸體忽然破棺而出翰灾,到底是詐尸還是另有隱情,我是刑警寧澤稚茅,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布纸淮,位于F島的核電站,受9級(jí)特大地震影響亚享,放射性物質(zhì)發(fā)生泄漏咽块。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一欺税、第九天 我趴在偏房一處隱蔽的房頂上張望侈沪。 院中可真熱鬧揭璃,春花似錦、人聲如沸亭罪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽应役。三九已至情组,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箩祥,已是汗流浹背院崇。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袍祖,地道東北人底瓣。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像蕉陋,于是被迫代替她去往敵國和親濒持。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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