轻量级大数据计算java
如今的大数据平台有个明显的问题就是愈来愈沉重。一方面单机性能再也不被关注,而是转向集群规模的堆砌,硬件方面则尽量的避开外存计算困难,期望巨大的内存。另外一方面框架的体系愈加庞大复杂,试图一应俱全。算法
轻量级计算需求数据库
从技术角度来看大数据的核心主要是快速的计算以及性能的提升。这两点其实并不必定只在大数据量的状况下才有需求,即便是几G,几十G这样的规模在高并发的状况下也须要快速的计算和高性能。并且对于临时性的数据处理也不适合建设大数据平台。json
目前不少大数据平台都将努力的方向转向了SQL,由于它是性能比拼的主要性能。通常咱们在学习的时候使用的SQL大多在三、5行以内,而实际开发过程当中碰到的可能更可能是三、5百行的SQL语句,优化SQL性能其实几乎无助于下降这种SQL的开发难度。这时就会致使仍需大量底层的编码,要常常编写UDF。其实提升性能本质上是下降开发难度,若是复杂运算的自动化优化靠不住,那么就须要快速编写高性能的算法。数组
如今的大数据平台也在努力实现集群的透明化,让单机和集群有更高的一致性,提升代码的兼容性,必定程度上下降开发难度。可是在集群不是很大的时候并不必定能得到最优的性能,由于高性能的计算方案因场景而异,其中有些多是相互矛盾的。且透明化只采起最保险的方法,通常是这些方法中性能较差的那个。性能优化
轻量级计算主要有这样一些特征。首先它是面向过程计算,强调可集成性;其次数据源是开放的,并不必定都是在数据库中;另外它更注重单机优化,会尽量的压榨单个机器的计算能力,而后才会考虑集群。最后使用的时候要在集群透明和高性能之间进行权衡,好比节点文件存储,不用网络文件系统;多个单机运算,不用统一集群框架。网络
漏斗运算举例多线程
问题解释电商漏斗分析可以帮助运营人员分析多步骤过程当中每一步的转化和流失状况,上图展现了依次触发登陆、搜索商品、查看商品、生成订单事件的人群转化流式状况。并发
关于上述的功能实现有几个关键点。第一是时间过滤,一次性抽取全部阶段的数据是没有必要的,咱们所关心的每每是某一个时间段内的事件数据。第二是时间窗口,即目标事件得在规定时间内发生才算有效。第三目标事件要具有有序性,如先浏览商品后放入购物车,反之则不算。最后事件属性也须要过滤,好比咱们目前只统计某一品牌的商品。app
以上关键隐藏着几个难点。首先是事件窗口跨天,好比2017年1月1日12点至2017年1月6日12点也被当作5天。其次是目标事件的顺序不肯定,由于是由参数决定目标事件的顺序,所以可能会出现“浏览商品”->“放入购物车”,也可能出现“放入购物车”->“浏览商品”。另外是事件属性不肯定,浏览商品时间有brand属性,登陆事件可能啥属性都没有,而属性又是以json串的形式存在。
针对以上这些问题,显然采用SQL是没法解决的,有序计算是SQL的软肋,所以不适合写这类过程计算。常规的作法是使用UDF,毫无疑问这种方式理论上能够提升性能,可是开发工做量太大了,且缺少通用性,后续的维护很是麻烦。
聚合算法
聚合算法能够算是一个比较好的解决方案,上图为聚合算法(单个用户)的相关参数定义。M表示的是在规定时间窗口也就是T内,所触发的最大事件的序号,当M的值等于K,即等于目标事件个数时,就算完成了一个流程。由于整个流程中可能会不少的序列,且还有可能重复,好比出现12123这样的形式,明显12的事件触发了两次,因而咱们就用A来记录子序列的最大事件序号,这是分别是2和3。S记录的是当前事件的时间戳,若是S超出了T规定的时间窗口,那么其所在的序列将被断定为无效。
上图经过集算器编写的聚合算法的实际代码。
执行聚合算法须要对原始数据进行一些整理,好比获取事件和用户的总数。简单的作法就是直接使用SQL语句的group_by,由于要同时查询用户码表和事件码表,因此咱们须要group_by所有数据两次。众所周知大数据运算的性能瓶颈常常在于硬盘,而在关系型数据库中进行两次group_by,即便是针对同一个表理论上仍是要遍历两次,CPU的计算时间能够忽略不计,可是涉及硬盘的时间则不能忽略。
为此咱们在集算器中设计了一种管道机制。数据在进入游标进行group的时候,同时会有一种机制将数据压入到管道中,在管道中继续进行group,这样一次运算就可以输入两份结果。
这就是实际的代码和效果对比,很明显时间缩短了将近一半。
事件ID
整个过程当中查询事件必不可少,原先事件的ID相似于10000七、1000四、10010这样的形式,事件顺序的判断依据这些ID编号,这种方式对在数组查找成员相对来讲仍是很慢的。
咱们的作法是直接将事件ID序号化,预处理阶段的时候原先的ID号被转换为了简单的id序号,实际计算阶段就能够直接根据id来查找到相关的事件。一样的由于原先的用户ID也是一长串的数字,因此也应该进行序列化以节省查询时间。
事件属性
事件属性主要存在两个问题,一是每一个事件的属性项不一样,二是属性是以json串的形式保存。作过调优的朋友应该都知道文本分析的性能都很是差,不只如此,字符串的比对也很慢,且难以实现复杂的过滤需求,转成json对象或序表又浪费存储空间。
因此咱们将原先的json串形式转成了数组,造事件码表的时候把事件属性整理好,事先规定好属性顺序,这样连属性名也省了,大量节约存储空间,也加快了读取速度。
这是关于用户ID,事件ID序号化,事件属性数组化的全部代码。
多线程和多进程
解决完以上问题以后,再来看下如何对单机性能进行优化。咱们一开始采用的是多线程的方案,由于相对来讲比较简单,可是通过测试发现当多线程并行数达到必定数量后性能却不升反降。在改用多进程以后,并行数一直升到机器核数,性能始终呈上升态势。
因为咱们使用的是java语言,形成这种状况的缘由多是因为多进程的内存事先已经分配好了,以后不会产生冲突。而多线程在分配内存的时候有可能会加锁、抢资源,其余的线程就须要等待,当内存足够大时冲突还不会显现,一旦内存较小就会形成冲突。
单机和多机
接下来咱们就开始搭建集群。最初作的是4台机器的集群,每台机器16个核,采用多进程的方式也就是有64个进程,经过主程序指挥,这里主机要和64个进程进行通讯,相对来讲成本有些高。
后来咱们又从新构建了个三层结构,让每一个机器都有一个子主程序,由子主程序和上端的子程序通信,这样机器间的通信就只剩4次。
总结体会
经过对上述案例的总结,咱们有这样几个体会。首先过程计算其实是一个广泛问题,虽然大数据平台都在努力优化SQL,但却无助于此类问题的解决,而过分依赖UDF又会使平台自己失去意义。另外性能优化是个不断试错迭代的过程,须要敏捷的开发工具快速的作出原先测试,因为开发太慢UDF就变的不太适合了。