2021年十一月份总结与十二月份计划

Posted by AlstonWilliams on November 27, 2021

幸福如饮水

计划里有一条,学习《故乡》吉他谱,扫弦做的更好。重拾吉他,发现过去学习的谱子都忘记了,只好从头弹起。有一段很简单的旋律,顺便教女朋友弹了。蛮有意思。

想吃提拉米苏,就买了各种原材料。跟女朋友合作制作一个。她做咖啡液,我打发奶油。做的时候由于我的原因,咖啡液有点烫,后来混入奶油打发不起来了。一度气的想把它扔掉。但在女朋友极力抵抗下,还是放到冰箱冷藏了一晚看看效果。第二天发展奶油虽没有成型,但吃起来口感也别有一番风味,相对于正常的口感反而更加细腻。之前的反而不香了。

周末一起去打羽毛球,还偶遇了一位老先生。看我们打的不怎么样,就免费教我们发球,走位。学到了之后,打的比之前进步很多,能有来有回了。还偶遇一位小朋友观战,然后跟他打了十分钟。看到他打球的姿势,像极了发球都不会的我们。就教了一下他怎么发球。现学现卖。

幸福就是从这些小事汲取到的吧。

周末时间一直过得很浪费。好几个周末都是玩好久的游戏度过的。后来列了一个计划表,排了一下,发现即使现有的事情,周末也可以排的满满当当。比如读书,练琴,练习声乐,运动,上课等。

但也需要休闲不是么?本以为休闲只有游戏,看剧,看直播几种形式。而这几种早就腻了,也不会给我带来什么价值。后来想起来休闲还可以是去酒吧饮酒,听乐队演唱。也可以是去参加脱口秀节目,当一个听众,上海这种活动还是很多的。也可以是去看一场话剧,比如开心麻花有很多话剧,这个也还不错。也可以是去看一场艺术展,但这方面实在是没有感兴趣的展览。也可以是看一场演唱会,只是我喜欢的许巍的演唱会一直没有。也可以是去参加歌剧,像马克西姆的巡回演出。也可以是做菜,提拉米苏创始人刚刚去世,他的去世对我没什么触动,只是了解了提拉米苏的历史以后,想起了自己以前做的提拉米苏,就馋了。

本来很喜欢做菜的。现在不做菜了,因为基于ROI计算,成本远比收益高,就放弃了。但如果是和女朋友两个人就不一样了吧,像蛋糕这种,偶尔还是可以做一次的。不要考虑体重这方面的事情了吧。

其他菜不敢说,但蛋糕方面自信自己做的足够好。做过三种蛋糕:提拉米苏,酸奶草莓蛋糕,黑森林。女朋友只吃过黑森林,提拉米苏还没有尝过。第一次一起吃饭,我做了一桌子菜,也只有蛋糕收到了好评。

音乐

月初找了很多音乐方面的公众号,订阅了一波,有声乐的,有钢琴的,有吉他的,还有演唱现场的。

为什么突然这么做呢?因为突然意识到自己过去其实一直都在闭门造车。知道自己做的不怎么样,但具体哪些地方不好,怎么提升不清楚。

而订阅这些公众号,看到分享的内容,很多地方突然有了方向。

但有了方向,还需要大量练习才能做好。

突然暂时放弃了练习钢琴。

一方面是发现钢琴难度实在太高,而我在练习的时候一直没有很投入其中,所以效果一直不理想。另一方面是我现在更喜欢的是能弹唱出来自己喜欢的歌曲,吉他相对来说简单一点,所以与其继续钢琴,还不如重拾吉他,等到吉他能达到自己满意的程度,再把钢琴中流行声乐伴奏学会,这些都做到自己满意的程度以后,再学习古典钢琴。

有了这个目标之后,练习吉他就更加认真地发现自己的不足,也会在B站看更多的教学视频,期待能尽快达到自己满意的程度。

技术学习

想参与到Flink项目里面去,第一件事就是得熟悉代码。

本想先通过文档或者Flink PMC/Committer分享的视频来了解设计原理,当看完第一个关于流批一体在执行引擎上实现难点的视频以后,突然想到一个很不错的入手点是Flink SQL。

通过这个入口,了解完以后,可以进一步了解执行引擎等。

而Flink SQL第一步,就是SQL的解析。

一头扎进相关代码。一脸懵逼。查找了好久的文档才理解了其实现原理。

Flink SQL的解析是用了Apache Calcite这个框架。这个框架是一个通用的SQL解析,生成执行计划,物理执行计划,以及SQL优化的一个框架,但它不会负责数据存储。说白了就是一个计算框架。它有很高的扩展性,Flink在它的基础上又扩展了一些语法。

像SQL解析还有其他的框架,比如Spark里面用的是Antlr。那Flink为什么选择Calcite呢?因为Calcite原生就支持流计算场景,所以基于这个开发会简单一些。

Antlr这个框架大概了解一些。老东家基于这个框架做了一些表达式计算的内容。但是像每个操作符的运算都需要自己去定义。我不知道有没有在这个基础上进行包装,专门适用于SQL解析的框架。如果没有的话,那Spark就是只用到了Antlr进行解析,后面的语法验证,生成执行计划,执行计划优化这方面的工作都是Spark自己实现的。这个工作量就很大。

Apache Calcite这个框架了解起来也很困难。官方文档远远不够。很多时候得结合源代码里的example,以及其他网上的资料才能理解。我大概花了整整两天才理解了SQL解析的部分。

Apache Calcite中,使用了FMPP以及JavaCC。前者是一个通用的模板渲染框架,后者则是一个代码生成器。JavaCC能根据你定义的语法文件,生成对应的语法解析器。在Apache Calcite中,会先通过FMPP以及预定义的JavaCC模板文件生成一个最终的JavaCC模板文件。就是说,预定义的JavaCC里面有一些占位符,会通过FMPP读取另一个数据文件里面的内容,填充好以后生成一个新的模板文件。生成好这个文件以后,再通过JavaCC就可以生成语法解析器。

在Apache Calcite中,这个模板文件里面就包括了SQL的解析规则等。

那Calcite怎么实现灵活扩展呢?Flink怎么实现的语法扩展呢?

上面提到了数据文件。Flink就是通过重写这个文件扩展了语法。

这个过程说起来貌似挺简单。但我是花了两天的时间才把整个流程搞清楚。其中的细节还不是很懂。比如JavaCC模板文件的语法规范。这些都没有深入了解。

经历了这么多,也只是大概了解了SQL语法解析是怎么做的。那语义验证,生成执行计划,优化执行计划这些怎么做呢?

Calcite通过JavaCC生成语法解析器以后,不是说一行代码,传一个SQL,就能完成所有工作。至于语义验证啥的,得调用它的API去做。语法解析只是将一条SQL解析成一个抽象语法树,它会通过解析器中定义的规则看你SQL语法是不是有问题,但是没法验证你语义是不是有问题。比如select的列是否在表中存在。Validator就是做这件事情的。既然它要验证这些信息,那自然就需要一些元数据,比如有哪些表,哪些列。所以在实现时我们就需要通过json或者yaml文件定义这些信息。这步相对来说是最简单,最容易理解的。

那验证完语法符合规范,语义上也没有问题以后,下一步是做什么呢?下一步就是将抽象语法树转换成关系代数表达式。这个一般跟我们关系不大,但在将一个节点转换成CBO的节点时,这里需要进行自定义。

转换完成之后,下一步就是做优化了。

优化方式有两种,一种是RBO,一种是CBO。RBO的缩写是Rule Based Optimizer,CBO的缩写是Cost Based Optimizer。RBO从名称就能看出来,是基于规则去进行优化的。简单来说,就是内部定义了一堆规则,然后在优化时,会对一个节点遍历全部规则,直到用完了所有规则也没有效果。比如投影下推等都是RBO可以做的。

RBO虽然规则很多,但总有一些事情是没法根据规则就进行优化的,而得根据数据具体情况来进行优化的。这就是CBO做的事了。

CBO是在RBO的基础上,引入了代价控制,采用贪婪算法形成的一种优化器。它会计算不同的规则下,每种规则优化后计算的代价,选择最小的那个去执行。它最后得到的并不一定就是全局代价最小的执行计划。但设计者在实现时,认为在SQL中,局部代价最小的规则其实就是全局代价最小的规则,所以采用了贪心算法。

既然CBO要计算代价,那就需要引入一些元数据相关的信息。所以要实现CBO要自己实现好多东西,比如各种算子计算代价的方法,以及提供对外提供这些信息的RelMetaDataProvider。要计算代价模型,常用的几个指标有行数,内存占用,CPU占用等。但由于内存占用和CPU占用都很难获取,所以目前主流的都是通过行数来实现的。

经过优化器之后,就形成了物理计划。那物理计划是怎么执行的呢?

首先,最基本是读表。读表的方式有三种,分别是:

  • ScannableTable: 简单的读表,不做任何操作
  • FilterableTable: 读表之后做过滤
  • TranslatableTable: 读表之后做更多其它操作

其中最难实现的就是TranslatableTable,使用这种实现可以在源头上对数据做很多处理,但代价就是需要自己实现每一种算子。

那表中的数据读出来以后,需要做后面的操作了。比如说有一个操作是where id = 1。 假设我们表的格式是CSV,有两列,id和name:

1,zhangsan
2,lisi

数据读出来以后,要根据id去过滤,找出来id为1的name。那首先,需要将一行数据转换成实体类。

我们在实现Validator的时候,就需要数据库的元数据,schema信息等。这些Schema信息中,表的信息只有里面有哪些字段,是什么类型,比如下面这种:

{
"columnList":[
{
"name":"id",
"type":"integer"
},
{
"name":"name",
"type":"varchar"
}
],
"name":"user_info"
}

所以很明显需要有一个代码生成机制,根据这个配置,生成一些实体类,然后将数据变成这些实体类,再去操作。

在Calcite中,通过Janino实现的这部分机制。

那生成实体类以后,就需要进行操作了。比如id=1这个例子,就是userBeans.filter(id == 1)这种。可以看到,我们需要一个函数库来实现这种操作。

在C++中,有Linq4j这个工具可以很方便的对数据进行操作。在Scala中,也有原生的这种支持。但是在Java中,目前集合框架还做不到这些。所以Calcite中自己实现了Linq4j来做这些操作。

Calcite要完全搞懂很不容易。我目前只是把它的大体流程梳理清楚了。要能灵活用它实现一个数据库,还有很长的路要走。但目前知道的这些已经够用了,本来就是想把Flink SQL作为一个入口,研究Flink的机制。没想到一个入口就花费了这么久的时间。但似乎这部分也是最难的吧,后面的那些应该不至于像这个这么难理解。

代码在https://github.com/AlstonWilliams/CalciteDemo

工作感悟

说起来工作经历,有些人会觉得不尽人意。而我觉得一段工作经历,总有值得学习的地方。就像一个人,如果我们是带着发现美的眼光去看他,那总能发现这个人身上的优点,值得我们学习的地方。

三年半了,到目前我有两段工作经历。每一段工作经历我都是心怀感激。感激在其中的成长,感激遇到的人,感激成就我,改造我的种种人或物。

第一段工作经历,我在那个产品功能还不复杂,数据量不大的时候参与进来。后来就迎来了数据量的暴涨。在这个过程中,积累了很丰富的debug经验,以及性能调优经验。后来慢慢地还收获到了管理经验,收获到了很多我欣赏或者欣赏我的人的友谊,收获到了对这个行业更加深入的了解,收获到了整个广告营销行业闭环产品的了解。在这个过程中,我很累,但成长非常巨大。所以离职以后,谈起我们的团队,谈起我的成长,我都是心怀感激。我相信,到我最终寿终正寝的时候,脑海里回忆起过去的经历,依然会对这段工作经历心怀感激。

第二段工作经历,从广告营销转行到了互联网公司。有太多东西需要学习。比如数据产品都有哪些,它们之间的联系,如何打造数据平台,数仓如何构建,都有哪些业务域,关键指标都有哪些,数据分析师同学都关心什么,如何更好的服务他们。以及和上一家公司不一样的,面向SQL的数据平台,和面向Spark底层编程实现的广告营销产品,这两者各有哪些优缺点?随着参与的产品越来越多,对整个数据平台越来越了解,脑海里的拼图也越来越完善。所以对这份工作我也是心怀感激。需要学的东西还有很多。

工作也好,生活也好,都是冷暖自知。如果只是看到其中琐碎的不尽人意的地方,而不是发掘其中的闪光点,为前路漫漫准备好干粮,那想必一生都会过得很艰难吧。

理财

本月到目前收益为0.72%。

买入了两种基金。一种是医疗,一种是纳斯达克。买入原因都是相信这两个基金的长久发展趋势。医疗主要是因为中国老龄化越来越严重,再过几年,十几年,计划生育下出生的孩子成为社会主力之后,到时候中国的人口结构就是一个倒三角的形状,老年人远比年轻人多。届时很有可能也会像日本一样。而且医疗今年已经跌了很多了,目前处于低估的区间。

买入纳斯达克则是因为一方面目前汇率方面,人民币升值了,目前一美元仅能兑换6.39元人民币,前不久还能兑换6.7元人民币,此时买入以美元计算的纳斯达克能有成本方面的一个优势,但美元现在持续通胀,未来卖出时,如果美元持续贬值,那现在汇率方面的优势反而会变成劣势。另一个更重要的因素是因为目前纳斯达克大多数公司的PE也就是在20左右,这个是很低的。我相信对于互联网行业来说,现在处于被严重低估的阶段。

今年新能源涨了很多。但一直没有太关注经济方面,也没有多想。其实如果是做短线的话,可以关注新能源背后的产业,赚一波走人。但现在就算了吧。估值已经太高了。

十二月计划

  • 《浮生》练习好
  • 扫弦练习到自己满意的程度
  • 声乐方面,解决目前声音过实的问题,突破换声点
  • 继续阅读Flink代码,了解整个数据处理链路(✔)
  • 阅读《天空的另一半》(✔)
  • 学习Spring核心原理(✔)
  • 其它时间给自己放假,毕竟忙了一年了,停下来歇一歇