HBase内存管理之MemStore进化论

Java工程中内存管理总是一个绕不过去的知识模块,无论HBase、Flink还是Spark等,如果使用的JVM堆比较大同时对读写延迟等性能有较高要求,一般都会选择自己管理内存,而且一般都会选择使用部分堆外内存。HBase系统中有两块大的内存管理模块,一块是MemStore ,一块是BlockCache,这两块内存的管理在HBase的版本迭代过程中不断进行过各种优化,接下来笔者结合自己的理解,将这两个模块的内存管理迭代过程通过几篇文章梳理一遍,相信很多优化方案在各个系统中都有,举一反三,个人觉得对内核开发有很大的学习意义。本篇文章重点集中介绍MemStore内存管理优化。

基于跳表实现的MemStore基础模型

实现MemStore模型的数据结构是SkipList(跳表),跳表可以实现高效的查询\插入\删除操作,这些操作的期望复杂度都是O(logN)。另外,因为跳表本质上是由链表构成,所以理解和实现都更加简单。这是很多KV数据库(Redis、LevelDB等)使用跳表实现有序数据集合的两个主要原因。跳表数据结构不再赘述,网上有比较多的介绍,可以参考。

JDK原生自带的跳表实现目前只有ConcurrentSkipListMap(简称CSLM,注意:ConcurrentSkipListSet是基于ConcurrentSkipListMap实现的)。ConcurrentSkipListMap是JDK Map的一种实现,所以本质上是一种Map,不过这个Map中的元素是有序的。这个有序的保证就是通过跳表实现的。ConcurrentSkipListMap的结构如下图所示:

基于ConcurrentSkipListMap这样的基础数据结构,按照最简单的思路来看,如果写入一个KeyValue到MemStore中,肯定是如下的写入步骤:

  1. 在JVM堆中为KeyValue对象申请一块内存区域。

  2. 调用ConcurrentSkipListMap的put(K key, V value)方法将这个KeyValue对象作为参数传入。

Read more
从一句情话来了解语言模型的发展

问题定义

一段文字,例如:今夜月色真美。代表的是什么含义?如果在春天温度适宜的 9、10 点站在阳台的人对你脱口而出地说出这句话,你会怎么理解这句话,亦或者你会怎么回应他(她)呢?

这句话是十九世纪末的文学家 夏目漱石对 I love you 的英译日标注结果(今夜は月が綺麗ですね)。根据他的标注 我爱你今夜月色真美 表达了相同的意义。但是计算机这么认为吗?输入 今夜月色真美,它理解这句话和 我爱你 是相似的含义吗?这个可以被归类为自然语言理解(NLU)领域的语义匹配问题。

众所周知在计算机中一切都是数字信号,那么如果想让计算机理解一句话的含义,解决 今夜月色真美我爱你 的语义匹配问题,那么先决问题是将一句话表示为一系列的数字信号。一个理所当然的想法是将语料库中的每一个词w用一个唯一的 n 维向量v=[v1​,v2​,…,vn​]来表达,那么数个向量的序列seq={v1​,v2​,…,vm​}就可以表达一句话,这一类方法就是词嵌入模型。本期文章通过对比这个 case 在 one-hot n-gram word2vec BERT 四种语言模型的结果,分析各个方法的优缺点。

one-hot

一个最简单的想法就是使用 one-hot 向量来表达一个词。具体流程:

  1. 遍历语料库,统计词的集合W,集合大小为K
  2. 将每个词在集合W中的下标的元素为 1,其他位置元素为 0,构建长度为K的 0-1 向量

假设我们对 今夜月色真美 的分词结果为:今夜、月色、真、美。那么使用 one-hot 向量就能将这个句子表示为4×K的 0-1 矩阵。我们假设语料库中一共有 10 个词:{我、爱、你、今夜、月色、真、美、今晚、漂亮、喜欢}。那么V今夜​=[0,0,0,1,0,0,0,0,0],V今晚​=[0,0,0,0,0,0,0,1,0,0],V我​=[1,0,0,0,0,0,0,0,0,0]。

one-hot 词嵌入方法优点在于简单,并且最大程度地保留了每个词的信息。但是缺点也很明显:

  1. 对于从未出现在语料库中的未登录词,无法进行兼容。假如词 需要编码,那么只能编码为零向量,否则需要对所有词向量进行更新,扩展向量维度。
  2. 丢失了词与词之间的相关信息。例如 今夜今晚 本身是相近语义的词,但是通过 one-hot 编码的向量差异并没有比 今夜 小。
Read more
Dataflow 模型

Dataflow 模型:是谷歌在处理无边界数据的实践中,总结的一套 SDK 级别的解决方案,其目标是做到在非有序的,无边界的海量数据上,基于事件时间进行运算,并能根据数据自身的属性进行 window 操作,同时数据处理过程的正确性,延迟,代价可根据需求进行灵活的调整配置。

DataFlow 模型核心

和 Spark 通过 micro batch 模型来处理 Streaming 场景的出发点不同,Dataflow 认为 batch 的处理模式只是 streaming 处理模式的一个子集。在无边界数据集的处理过程中,要及时产出数据结果,无限等待显然是不可能的,所以必然需要对要处理的数据划定一个窗口区间,从而对数据及时的进行分段处理和产出,而各种处理模式(stream,micro batch,session,batch),本质上,只是窗口的大小不同,窗口的划分方式不同而已。Batch 的处理模式就只是一个窗口区间涵盖了整个有边界的数据集这样的一种特例场景而已。一个设计良好的能处理无边界数据集的系统,完全能在准确性和正确性上做到和“Batch”系统一样甚至应该更好。

Read more
来美团3个月总结

来美团也三个月了,也渐渐习惯这边的风格。再流程制度上,有让我觉得很不错的地方,也有一些需要改善的地方。自己的能力模型上,还有哪些需要提高的,接下来自己需要在这些地方进步。

好的地方

  • 文档。在文档这方便做的挺好的,把工作中很多都能记录下来,比如会议纪要,产品PRD,技术方案,月度总结等,以及各个系统的详细文档等。而且也能搜到很多技术方案设计,能力模型等。这样比较详细的学习别人怎样去做的,怎样设计的。
  • 各个基础平台和系统。在这边不用什么都从头开始,公司提供了各种基础平台,比如服务发布的CI/CD工具,微服务的RPC框架,服务治理,熔断降级等系统。以及各种存储,KV存储,es平台,大数据平台等,让自己能够专注于业务,而不需要放很多精力在以来的平台开发运维上面。
  • 流程规范。大公司流程还是比较规范的,如开发上线流程,整套流程每一步都细化,大家按照这套流程开发。极大的规避掉一些因为个人理解偏差和信息对齐上面造成的上线问题,能够保质保量的进行新功能迭代上线。
  • 制定绩效合同,后面绩效根据此打,更大化的做得客观。
  • 因为我原来是做大数据平台的,更多的面向的是公司内部,而现在是面向用户。可以感受到这边对监控,线上问题的也紧要重视。(这个主要是我岗位的变化)
Read more
过去,未来

很快,已经大半年没写博客了,这半年内,一直再准备面试,找工作,终于在7月去了自己还认为不错的公司。也发现自己已经工作四年了。如果要给自己前四年以一个词语做总结的话,我想我会选择“混沌”这个词。

过去

在物理学上,混沌(chaos)是指确定性动力学系统因对初值敏感而表现出的不可预测的、类似随机性的运动。又称浑沌。英语词Chaos源于希腊语,原始 含义是宇宙初开之前的景象,基本含义主要指混乱、无序的状态。

而自己曾经的过往也是这样的随机飘荡。没有为自己制定一个人生目标,也不造为何而追求,是我之前很长一段时间的状态。主要体现在我认为的以下几点:

  • 没有制定人生规划。因为没有大方向,所以很多时候更偏向于随遇而安。
  • 工作单位随意性。和人生规划也有关,所以在面临单位的抉择方便没有去更加的努力。没有想好不同的单位能为我带来什么样的背景和能力。
  • 情感的随意性。在情感方面,自己并没有什么优势,所以更需要自己勇于去争取。而为此,需要去提高自己的设计能力,沟通表达能力,自己却没有过多的在意。
  • 到底想要什么。这个问题思考的不够深刻,因为道路上的一些阻碍,也造成前进方向的左右摇摆。
  • 勇敢去争取的心。无论在工作上,情感上,生活上,如果没有一颗勇敢去争取的心,人很容易颓废,然后陷入自我怀疑。社会因为信息的不对等,所以机会更容易给予勇敢去争取的人,而不去争取的人,却只能抱怨“他不如我,为啥是他”这种无聊的问题。
Read more
python协程

这些天,在标签调度上,可预见性的标签调度会越来越多,如果全由调度系统承载,调度系统压力增大,负责调度系统的同事担心会影响其他任务,遂在讨论下,决策开发一个简版的标签执行服务/脚本(相比现有调度系统,只保留任务执行控制,例如池化,并发控制等,任务编排,启动时间都由调度系统控制,整个执行服务就是调度系统的一个task)。而我对此持保持意见,我认为应该增强调度系统能力,2333。

既然决策已定,加上部门现在主要使用python,而调度系统也使用的是airflow,所以主要开发语言也定为python。对于此执行服务/脚本,希望能够并行的执行任务,并且能够控制每次在跑的任务数。调研了下python的并发模型,以及多线程的知识。很多说python多线程是鸡肋,而自己也不想引入第三方并发的库,又看到python支持协程,且相比线程轻量很多,代码易理解,遂最终选定使用协程来实现这个需求。

协程

网上对协程的解释众说纷纭,有说协程是一种用户态的轻量级线程,协程的调度完全由用户控制。wikipedia的定义:
协程是一个无优先级的子程序调度组件,允许子程序在特点的地方挂起恢复。但大家对协程的作用倒挺统一的:

  • 占用资源少,开个协程只需要K级别内存,而新开个线程则需要M级别
  • 线程之间的上下文切换,性能消耗大,而协程间切换非常快
  • 相比事件驱动模型的回调复杂性,协程易于理解,写协程代码就像写同步代码一样。
  • 协程的调度是协作式调度,需要协程自己主动把控制权转让出去之后,其他协程才能被执行到(很难像抢占式调度那样做到强制的 CPU 控制权切换到其他进程/线程)

历史的宿命

在互联网行业面临C10K问题时,线程方案不足以扛住大量的并发,这时的解决方案是epoll() 式的事件循环,nginx在这波潮流中顺利换掉apache上位。同一时间的开发社区为nginx的成绩感到震撼,出现了很多利用事件循环的应用框架,如tornado/ nodejs,也确实能够跑出更高的分数。而且python/ruby 社区受GIL之累,几乎没有并发支持,这时事件循环是一种并发的解放。然而事件循环的异步控制流对开发者并不友好。业务代码中随处可见的mysql/memcache调用,迅速地膨胀成一坨callback hell。这时社区发现了协程,在用户态实现上下文切换的工具,把epoll()事件循环隐藏起来,而且成本不高:用每个协程一个用户态的栈,代替手工的状态管理。似乎同时得到了事件循环和线程同步控制流的好处,既得到了epoll()的高性能,又易于开发。甚至通过monkey patch,旧的同步代码可以几乎无缝地得到异步的高性能,真是太完美了。

Read more
略微糟糕的2018

不在以流水形式记录,而网易云音乐年报体搞笑却不能展示更多。在朋友圈看了别人的发的年终总结,遂借鉴其模板。

2018年让你印象深刻的5件事情是什么?

  1. 定级答辩,完全暴露了自己的弱点,当大领导说自己的ppt很渣时,很懵逼。自己确实没太上心,做的少。虽然后来又重改了,但最终其实效果都不是很满意,包括答辩。表达能力以及技能包装能力都还需要多加练习。最后感谢领导的细心指导。
  2. 不应该老拿别人的弱点说事或者开玩笑。A之前说了个毫无依据的话被大家笑话,然后就竟然被大家拿出来说,以至于大家一旦把焦点给与他,他便有了种不舒服,即便知道大家也是寻乐。自己也会一直拿别人的弱点寻乐,虽然是玩笑,但别人的感受未必是玩笑。当然自己也会被拿着弱点一直攻击你,当然也是寻乐,但自己确实也有不爽,将心比心,也许有时候会意的鼓励会更好。以后自己应该多注意,少娱乐他人的弱点,多提升自己的弱点。
  3. 接近年底,回家办理贷款手续,正好外婆过生,一大家人和和睦睦,开开心心的氛围真好。希望明年能带外婆来北京玩,外婆那些年一路过来不容易,现在也该享享清福了。

你做的引以为傲的5件事?

  1. 终于还是借着低价买的域名,开通的博客,开始记录技术,生活的感悟,以及看书,电影。零零散散的也写了将近20篇文章,虽然内容还有待提供,以及有些是拼凑的。但还是希望自己沉淀一些知识,以及顺便提高自己的写作能力。
  2. 认知的提升。网络上的人云亦云,股市的追涨杀跌,以及自媒体带节奏的十万加,以及各种app推送的信息。让我意识到现在虽然信息变多了,但都是他们为了满足自己的利益而塞给观看者已经加工过的信息。对此,自己的认知的提升,能够对此消息进行独立思考,而不是跟着他人的情感走是非常重要的。而群体更能泛滥此情绪,相比独立思考,会显得更智力低下。
Read more
spark streaming

之前讲解了Google的Dataflow模型。而spark streaming是以micro batch方式来近似streaming数据进行处理。Spark Streaming从实时数据流接入数据,再将其划分为一个个小批量供后续Spark engine处理,所以实际上,Spark Streaming是按一个个小批量来处理数据流的。

spark streaming

离散数据流(DStream)是Spark Streaming最基本的抽象。它代表了一种连续的数据流,要么从某种数据源提取数据,要么从其他数据流映射转换而来。DStream内部是由一系列连续的RDD组成的,每个RDD都是不可变、分布式的数据集。每个RDD都包含了特定时间间隔内的一批数据,如下图所示:

任何作用于DStream的算子,其实都会被转化为对其内部RDD的操作。例如,在代码

1
val words = lines.flatMap(_.split(" "))

中,我们将lines这个DStream转成words DStream对象,其实作用于lines上的flatMap算子,会施加于lines中的每个RDD上,并生成新的对应的RDD,而这些新生成的RDD对象就组成了words这个DStream对象。其过程如下图所示:

Read more
livy指南

Livy是一个基于Spark的开源REST服务,它能够通过REST的方式将代码片段或是序列化的二进制代码提交到Spark集群中去执行。它提供了以下这些基本功能:

  • 提交Scala、Python或是R代码片段到远端的Spark集群上执行;
  • 提交Java、Scala、Python所编写的Spark作业到远端的Spark集群上执行;
  • 提交批处理应用在集群中运行。

livy安装

livy安装很简单,从官网下载zip包。
解压,设置spark和hadoop的环境。

1
2
export SPARK_HOME=spark的路径
export HADOOP_CONF_DIR=/etc/hadoop/conf (hadoop配置路径)

然后在livy的bin目录下执行:
livy-server start

livy配置

在conf/livy.conf文件中有配置,常用的配置:

配置信息 含义
livy.server.host = 0.0.0.0 livy服务启动的host
livy.server.port = 80 启动端口号
livy.spark.master = yarn livy执行spark master
livy.spark.deploy-mode = client spark启动模式
livy.repl.enable-hive-context = true 启动hiveContext
livy.ui.enabled = true livy ui页面

还有其他的配置,用户可以根据自己需求自行配置。
另外,当集群开启了kerberos验证,执行spark任务时报gss错误,需要在配置中加上:

1
2
livy.server.launch.kerberos.keytab = /conf/xxx.keytab
livy.server.launch.kerberos.principal = xxx@HADOOP.COM
Read more
airflow使用指南

airflow 是一个编排、调度和监控workflow的平台,由Airbnb开源。airflow 将workflow编排为tasks组成的DAGs,调度器在一组workers上按照指定的依赖关系执行tasks。同时,airflow 提供了丰富的命令行工具和简单易用的用户界面以便用户查看和操作,并且airflow提供了监控和报警系统。

airflow 核心概念

  • DAGs:即有向无环图(Directed Acyclic Graph),将所有需要运行的tasks按照依赖关系组织起来,描述的是所有tasks执行的顺序。
  • Operators:可以简单理解为一个class,描述了DAG中一个具体的task具体要做的事。其中,airflow内置了很多operators,如BashOperator 执行一个bash 命令,PythonOperator 调用任意的Python 函数,EmailOperator 用于发送邮件,HTTPOperator 用于发送HTTP请求, SqlOperator 用于执行SQL命令…同时,用户可以自定义Operator,这给用户提供了极大的便利性。
  • Tasks:Task 是 Operator的一个实例,也就是DAGs中的一个node。
  • Task Instance:task的一次运行。task instance 有自己的状态,包括”running”, “success”, “failed”, “skipped”, “up for retry”等。
  • Task Relationships:DAGs中的不同Tasks之间可以有依赖关系,如 TaskA >> TaskB,表明TaskB依赖于TaskA。

通过将DAGs和Operators结合起来,用户就可以创建各种复杂的 workflow了。

operators

下面讲解下常见的operator,以及如何使用,注意点。

Read more