一个 Blink 小白的成长之路

写在前面

写过blink sql的同窗应该都有体会,明明写的时候就很顺滑,小手一抖,洋洋洒洒三百行代码,一鼓作气。结果跑的时候,吞吐量就是上不去。致使数据延迟高,消息严重积压,被业务方疯狂吐槽。这时候,老鸟就会告诉你,同窗,该优化优化你的代码了,再丢过来一个连接,而后留下一脸懵逼的你。笔者就是这么过来的,但愿本文能帮助到跟我有过一样困惑,如今还束手无策的同窗。html

背景故事

先说一下相关背景吧,笔者做为一个刚入职阿里的小白,还处在水土不服的阶段,就被临危受命,改造数据大屏。为何说临危受命呢,首先是此时距双十一仅剩一个月,再者,去年的双十一,这个大屏刚过零点就出现问题,数据一动不动,几个小时后开始恢复,但仍然延迟严重。此前,笔者仅有的实时计算开发经验是storm,用的是stream API,对于blink这种sql式的API彻底没接触过。接到这个需求的时候,脑子里是懵的,灵魂三问来了,我是谁?我即将经历什么?我会死得有多惨?不是“此时此刻,非我莫属”的价值观唤醒了我,是老大的一句话,在阿里,不是先让老板给你资源,你再证实你本身,而是你先证实你本身,再用结果赢得资源,一席话如醍醐灌顶。而后就开始了一段有趣的故事~sql

压测血案

要找性能问题出在哪儿,最好的方法就是压测。这里默认你们都对节点反压有必定的了解,不了解的请先移步典型的节点反压案例及解法数据结构

一开始是跟着大部队进行压测的,压测的结果是不经过!!!一块儿参加压测的有三十多个项目组,就我被点名。双十一演练的初夜,就这样伤心地流走了(╯°□°)╯︵ ┻━┻。西湖的水,全是个人泪啊。不过痛定思痛,我也是经过此次压测终于定位到了瓶颈在哪里。多线程

瓶颈初现

数据倾斜
在作单量统计的时候,不少时候都是按商家维度,行业维度在作aggregate,按商家维度,不可避免会出现热点问题。并发

hbase写瓶颈
当时我在调大source分片数,而且也无脑调大了各个算子的资源以后,发现输出RPS仍是上不去,sink节点也出现了消息积压。当时就判断,hbase有写瓶颈,这个我是无能为力了。后来的事实证实我错了,hbase的确有写瓶颈,但缘由是咱们写的姿式不对。至于该换什么姿式,请继续看下去。框架

神挡杀神

先来分析一下咱们的数据结构(核心字段)
biz_date, order_code, seller_id, seller_layer, order_status, industry_id函数

咱们group by的典型场景有高并发

CREATE VIEW order_day_view AS
    SELECT
        industry_id,
        seller_layer,
        biz_date,
        count(distinct order_code) AS salesCount
    FROM
        order_view
    GROUP BY industry_id,seller_id,seller_layer,biz_date
;

总结下来就是,按卖家维度,行业维度什么的,都很是容易出现数据倾斜。性能

数据倾斜其实有不少解法,这里我不展开讨论,只讲咱们这个案例的解法。
倾斜的缘由,无非就是group by的字段出现了热点,大量的消息都集中在了该字段少数几个取值上。一般的解法是,在消息中选择具有惟一性,或者预估会分布比较均匀的字段。若是这个字段是整型的,能够直接取模(模数通常是节点的并发数),若是是字符串,能够先进行哈希计算,再取模,获得一个分片地址(本文取名为bucket_id)。在接下来的全部aggregate算子中,都要把他做为group by的key之一。学习

在咱们这个案例中,咱们选择了order_code这个具有惟一性的字段。首先在源头把分片地址算出来,加到消息里面,代码以下:

SELECT
o.biz_date, o.order_code, o.seller_id, o.seller_layer, o.order_status, o.industry_id, o.bucket_id
FROM (select *,MOD(hash_code(order_code), 32) AS bucket_id from order_stream) o

而后把这个bucket_id层层传递下去,在每个须要group by的地方都在后面带上bucket_id,例如:

CREATE VIEW order_day_view AS
    SELECT
        industry_id,
        seller_layer,
        biz_date,
        count(distinct order_code) AS salesCount,
        bucket_id
    FROM
        order_view
    GROUP BY industry_id,seller_id,seller_layer,biz_date,bucket_id
;

事实上,我一开始想到的是用下面tips里的方法,结果就杵进垃圾堆里了,性能问题是解了,可是计算出来的数据都翻倍了,明显是错的。至于我是怎么发现这个问题,并分析其缘由,再换了解法,又是另外一段故事了。能够提早预告一下,是踩了blink撤回计算的坑,后面会再出一个专题来说述这个故事哒~

这里还想再延伸一下,讲讲个人学习方法。若是读者中有跟我同样的小白,可能会奇怪,一样是小白,为什么你这么秀,一上来就搞压测,还能准确地分析出性能的瓶颈在哪里。其实有两方面的缘由,一方面是我有过storm的开发经验,对实时计算中会遇到的坑仍是有必定的认识;另外一方面,是我没说出来的多少个日日夜夜苦逼学习充电的故事。个人学习习惯是喜欢追根溯源,就找了不少介绍flink基本概念,发展历史,以及跟流式和批处理计算框架横向对比的各种博客。并且带着kpi去学习和什么包袱都没有去学习,心态和学习效率是不同的。前者虽然效率更高,可是是以损害身心健康为代价的,由于学习过程当中不可避免的会产生急躁情绪,而后就会不可避免的加班,熬夜,咖啡,再而后他们的好朋友,黑眼圈,豆豆,感冒就全来了。后者虽然轻松,可是什么包袱都没有,反而会产生懈怠,没有压力就没有动力,这是人的天性,拗不过的。这就是矛盾的点,因此在阿里,常常提到“既要也要还要”,其实宣扬的是一种学会平衡的价值观。至于怎么平衡,嘻嘻,天知地知我知。对,只能本身去领悟怎么平衡,别人教不会的。

概念有了必定的认知,下面就开始实践了。整个实践的过程,其实就是在不断的试错。我是一开始连反压的概念都不知道的,一直在无脑的调大CU,调大内存,调高并发数,调整每两个节点之间的并发数比例。寄但愿于这样能解决问题,结果固然是不管我怎么调,吞吐量都是都风雨不动安如山。如今想一想仍是太年轻呀,若是这样简单的作法能解决问题,那那个前辈就绝对不会搞砸了,还轮的到我今天来解决。后来也是在无尽的绝望中想通了,不能再这么无脑了,我要找其余法子。想到的就是在代码层面动刀子,固然试错的基本路线没有动摇,前面也提到过,我一开始是想到的“加盐”,也是在试错。

学习方式决定了我作什么事,都不可能一次成功。甚至有不少状况,我明知道这样作是错的,但我就是想弄明白为何行不通,而故意去踩这个坑。不过也正是由于试了不少错,踩了不少坑,才挖出了更多的有价值的知识点,扩大了知识的边界。

此时无声胜有声,送上几句名言,与诸君共勉
因祸得福,焉知非福。---淮南子·人间训
一切过往,皆为序章。---阿里巴巴·行癫
学习就像跑步同样,每一步都算数。---百阿·南秋

tips: 若是在消息自己中找不到分布均匀的字段,能够考虑给每一条消息加上一个时间戳,直接使用系统函数获取当前时间,而后再对时间戳进行哈希取模计算,获得分片地址。至关于强行在时间维度上对消息进行打散,这种作法也被形象的称为“加盐”。

佛挡杀佛

上一段看下来,彷佛只解决了数据倾斜的问题。以前还提到有一个hbase写瓶颈问题,这个该如何解呢?

仍是接着上面的思路继续走下去,当咱们把bucket_id一路传递下去,到了sink任务的时候,假设咱们要按商家维度来统计单量,可是别忘了,咱们统计的结果还按订单号来分片了的,因此为了获得最终的统计值,还须要把全部分片下的值再sum一下才行,这大概也是大多数人能想到的常规作法。并且咱们现有的hbase rowKey设计,也是每一个维度的统计数据对应一个rowKey的,为了兼容现有的设计,必须在写hbase以前sum一下。

可是笔者当时突发奇想,恰恰要反其道而行之,我就不sum,对于rowKey,我也给它分个片,就是在原来rowKey的基础上,后面再追加一个bucket_id。就至关于原来写到一个rowKey上的数据,如今把他们分散写到64个分片上了。
具体实现代码以下:

INSERT INTO hbase_result_sink
    SELECT
        CONCAT(businessRowkey, '|', bucket_id) AS businessRowkey,
        cast(uopAcceptCount as DECIMAL)
    from hashBucket_view

这样一来,API也必须改造了,读的时候采用scan模式,把全部分片都读出来,而后求和,至关于把sum的工做转移到API端了。
这样作的好处在于,一方面能够转移一部分计算压力,另外一方面,由于rowKey只有一个,而咱们写rowKey的任务(即sink节点)并发数可能有多个,Java开发者应该都深有体会,多线程并发对一个变量进行累加的时候,是须要加锁和释放锁的,会有性能损耗,能够猜想,hbase的写瓶颈就在于此。后来的事实也证实,这种作法将输出RPS提高了不止一个两个档次。

赶考当天

人事已尽,接下来就是关二爷的事了( ̄∇ ̄)。双十一零点倒计时结束,大屏数字开始飙升起来,随之一块儿的,还有个人肾上腺素。再看看数据曲线,延迟正常,流量峰值达平常的10倍。其实结果彻底是在预期以内的,由于从最后一次的压测表现来看,100W的输入峰值(平常的333倍),5W的输出峰值(平常的400倍),都能稳稳的扛下来。出于数(懒)据(癌)安(晚)全(期)的角度考虑,不少大屏和数据曲线的截图就不放出来了。

其实如今回过头再看,此时的心里是平静如水的。不是大获全胜后的傲娇,也不是退隐山林的怯懦。只是看待问题的心态变了。没有翻不过的山,没有迈不过的坎。遇事不急躁,走好当下的每一步就好,也没必要思考是对是错,由于每一步都算数,最后总能到达终点。

浮生后记

笔者写文章习惯带一些有故事趣味性的章节在里面,由于我以为纯讲技术,即便是技术人看起来也会至关乏味,再者纯讲技术的前提是做者具有真正透进骨髓去讲述的功底,笔者自认为还相差甚远,只能加点鱼目来混珠了。换个角度来看,纯技术性的文章,观赏性和权威性更强,每一句都是精华,这种咀嚼后的知识虽有养分饱满,可是不是那么容易消化,消化后能吸取多少,还有待确认。因此我力求展现个人咀嚼过程,更可能是面向跟我同样的小白用户,若是以为冗长,请各位读者姥爷见谅~

本文做者:王科,花名伏难,前后经历国内三大电商平台,苏宁,京东,阿里,电商大战深度参与者。现任职阿里巴巴供应链事业部,从事业务平台化改造、实时计算相关工做。信奉技术自由平等,但愿经过简单形象的语言,打破上层建筑构建的知识壁垒,让天下没有难作的技术。


本文做者:王科

阅读原文

本文为阿里云内容,未经容许不得转载。

相关文章
相关标签/搜索