- 当数据遇上AI,Twitter的数据挖掘实战(二)
你好我是程序员贵哥。在上节课里我们一起了解了Twitter整体搭建数据系统的经验。不过那一篇论文的主要内容还是在方法论上一旦我们想要把这个方法论利用到我们当下就在搭建的数据系统里就有些无从下手的感觉。不过好在Twitter还发表了很多有着具体实战经验的论文。那么今天我就请你一起来学习下《The Unified Logging Infrastructure for Data Analytics in Twitter》这篇论文。在这篇论文里Twitter一点儿都没有藏私而是给出了大量具体的实践技巧你完全可以用“抄作业”的方式把里面的做法用到自己的系统里。事实上在我之前搭建的大数据系统中就从里面吸取了大量的经验。希望在学习完这节课之后你可以直接把所看到的具体实战方法用到实践中去。无论是对你现在已有的系统进行对照改进还是在建设新系统的时候把Twitter的方法作为模版都是一个不错的选择。统一的用户行为日志和元数据管理上节课我们就说过Twitter为了减少碎片化的日志文件和日志格式最终在内部启动了一个项目统一从客户端的视角来记录用户行为日志。这个日志的格式自然是通过Thrift的Schema来定义的。而有了这个日志之后所有的工程师和数据分析师都可以在一个共识下工作。大部分的数据分析工作也不再需要大量的Join操作先把数据在Hadoop上搬运一遍。那么我们就先来看看这个统一的日志格式长什么样子。首先Twitter把用户在Twitter内的所有用户行为拆分成了一个固定的6个层级的结构Twitter称之为Click Events。这6个层级分别是来自于什么客户端client是客户端里的哪一个页面page是页面里的哪一个Tab页section是Tab页里的哪一个组件component是组件里的哪一个元素element以及针对这个元素用户的动作action是什么。要注意这里把这种日志叫做是Click Events并不是说我们只是记录用户的点击行为而是包含了所有的用户行为、特定信息的展示或许用户只是将鼠标Hover在某个UI元素上同样会记录Click Events。这个Click Events里面的action就是Impression或者Hover。我们可以来看一个具体的例子。用户使用Web访问Twitter在主页的Mentions这个Tab里的信息流里点击了某个头像来获取Profile信息这个事件就可以通过web:home:mentions:stream:avatar:profile_click来定义。而为了确保全员有一个统一的命名习惯Twitter规定了使用下划线“_”来作为单词的分割而不允许采用其他命名方式比如驼峰命名法。在有了这样一个日志格式之后任何一个工程师或者数据科学家都可以很容易地定位到自己想要统计和分析的特定用户行为。而且因为整个用户行为的结构是层级化的我们可以很容易地通过通配符找到特定类型的行为。比如通过web:home:mentions:*;我们就可以找到用户在Web端访问Mentions的Timeline里面的所有行为或者可以通过*:profile_click就能找到所有客户端里所有流程里用户点击Profile的行为。事实上Twitter内部有一个叫做Oink的程序会按照层级每天自动运行统计出各个层级Schema下的事件数量。而这些数据会直接展现在内部的Dashboard里方便我们一眼就能看到系统的整体数据。我把对应统计的层级Schema放在了下面你可以去看一下。(client,page,section,component,element,action)(client,page,section,component,*,action)(client,page,section,*,*,action)(client,page,*,*,*,action)(client,*,*,*,*,action)Twitter会预先自动给出整个层级结构里各个层级的事件统计数量。当然日志里不光有事件类型Twitter的Click Events里还会包含两部分类型的信息一部分是标准的字段所有的日志都有包括前面这里的event_name以及event_initiator、user_id、session_id、ip、timestamp这些字段在任何日志里都是必不可少的。而对于每个事件可以独有的信息Twitter则是通过一个event_details的字段用key-value对的形式存储下来。这个event_details字段就使得不同团队需要的额外信息可以有一个统一存放的地方但是同时不会去影响我们统一的日志的整体格式。只统一了日志格式还不够在这个6个层级的日志结构下其实这6个层级可能出现的“值”是很多的。比如我们到底有多少个不同的“UI元素”呢在一个系统每天都在不断迭代的团队里其实我们是很难知道这一点的。传统的通过文档记录来传递这些信息的方式往往也是无效的等我们记得把UI元素可能出现的值记录下来的时候某一个值可能已经废弃并不再出现了。而Twitter的做法也很简单就是直接根据现有的数据反向找出它实际会出现哪些值提供给大家来标记每一个值是什么含义。我们前面不是已经按照6个层级一一统计了事件的统计数据吗自然我们也可以统计6个层级结构下每一个值会有哪些不同的情况出现。Twitter直接把这个基于数据反向统计出来的事件层级树状图做成了一个可展开的Web界面。工程师和数据科学家可以直接搜索这个事件树也可以在具体出现的值上加上Comments记录这个字段值是什么意思把信息共享出来。以“事实数据”本身快速生成文档而不是依赖人工去维护文档能够大大提升我们的工作效率。巧妙利用Unicode和正则表达式不过Click Events只能方便我们做一些描述性的统计信息。而在实际的数据分析中很多时候我们重视的是特定的用户行为模式pattern。我拿电商网站来举个例子。我们常常会去做漏斗分析看用户在浏览商品—加入购物车—进入支付—填写地址到最终支付完成的整个环节里每一个步骤的统计数据他们分别是在哪一个步骤流失的。而且我们也需要经常根据不同的需求重新定义整个漏斗环节。比如新上了一个新年促销的营销活动我们就要去看这个营销活动的漏斗分析的数据。根据用户的一个个Session去分析特定的行为模式这个在用户行为日志的分析里是一个非常高频的动作。当然我们有原始的Click Events就可以写一段Java程序或者一个复杂的Pig脚本或者SQL来完成分析。不过这样我们每一次的工作显然没有被“复用”起来而且每一个脚本都需要根据用户的标识去Join大量的数据。而Twitter采取了一个非常巧妙的方式来解决了这个问题。首先它用了一个数据处理程序把所有的用户行为串成了一个个的用户Session。然后它并没有简单地把原始的数据存储下来而是对数据进行了“信息压缩”。Twitter把每一个web:home:mentions:stream:avatar:profile_click这样的事件名称映射到了一个Unicode的字符串。然后整个Session里把用户按照时间先后顺序进行的这些事件组合在一起就是一个长字符串叫做session_sequence。于是一个用户Session就可以简化成一条日志里面会包含这些字段user_id也就是标识哪一个用户session_id也就是标识是哪一个sessionip也就是用户访问时的ip地址session_sequence也就是代表了用户顺序执行的一系列动作duration也就是这个session的时长。有了这个session_sequence我们的漏斗分析就非常简单了。我们只需要通过一个简单的正则表达式就能定义出一个用户特定的行为流。比如我们用 i 代表用户浏览了单个商品a 代表用户把商品加入了购物车。那么通过正则表达式.*i.*我们就能找到所有浏览过单个商品的session通过.*i.*a.*就代表了先浏览过商品再把商品加入过购物车的session。两个数据一比较我们的漏斗数据就计算出来了。而因为正则表达式的灵活性我们不需要预先定义要分析什么样的漏斗而是可以在需要的时候写个正则表达式就完事儿了。并且在实践中Twitter封装好的Pig UDF让你把这一步也省下了你只要自定义一系列的事件流就能快速查找符合这个执行顺序的Session以及他对应的漏斗。defineFunnelClientEventsFunnel(‘$EVENT1’ ‘$EVENT2’,...);你可以通过一个UDF定义一个你想要分析的漏斗。(0,490123)(1,297071)...这个Funnel的计算结果就是这个事件流的每一层漏斗剩下的session数量。而且这个数据还能够直接拿来进行机器学习的建模。我们完全可以把这样按事件顺序发生的序列通过马尔可夫链或者类似的时序分析方案来进行建模去预测在什么样的用户行为之后会发生什么样的用户行为。这样我们去进行预测类的机器学习建模也只是一个Pig脚本的事情了。方便debug很重要除了统一的日志格式和预先生成的session_sequence之外我们还经常会遇到的一类问题是通过日志debug在线上遇到的各种问题。通过日志进行生产环境的debug是最常见的需求之一。但是对于原始的Click Events日志来说想要找到特定的日志虽然容易但是开销很大。我们想要找到特定的日志其实并不麻烦通过Pig写个脚本加上我们需要的筛选条件跑一下就能找到这样的日志。但是因为每一个Pig任务都是在完整地扫描一遍日志这个开销可不小。而且在debug的过程中这个是一个交互式的过程。我们可能先写了一个筛选条件然后查询到了一部分日志但是这些日志可能并不符合我们的需要。于是我们又变化了一下筛选条件这样就要再完整地扫描一遍日志。可能为了调查一个小小的线上问题我们把数十TB的原始日志扫了又扫如果算算实际的硬件开销那可绝对小不了。Twitter的解决办法也很简单那就是为日志文件生成索引。不过这个索引和关系数据库不一样它不是到每一条的日志这个级别的而是到每一个Block级别。Twitter的日志是用LZO的压缩算法压缩的它的索引也只是确认特定字段的值是否在整个Block中出现过。这样索引的尺寸可以大大减少毕竟如果把每一条日志所在的位置都建立索引那么索引的尺寸可能就和日志一样大了并起不到减少开销的作用。而有了这个索引文件以后我们就可以在查询特定日志的时候通过索引大幅度减少需要扫描的数据。事实上Twitter专门开源了一个叫做elephant-twin的项目。你只要在Pig脚本中加载索引、对应的MapReduce任务它就会自动只读取索引找到的对应Block从而就减少了需要扫描的数据。在实践过程中我还这样做过。我们干脆直接基于特定字段的索引比如user_id提供了一个在线查询特定用户行为的日志的内部站点。通过这个站点数据科学家们能够直接观察用户的行为模式来寻找灵感。虽然这样的服务需要用户对于HDFS进行随机访问其实并不符合HDFS设计的数据访问模式。但是因为并发量很小对于HDFS并不会造成什么压力。其实这样的技巧、方法和工具在大数据领域非常实用能够大大提升我们解决问题的效率。小结通过这节课的学习我们其实可以看到在优秀的基础设施、杰出的团队理念之后一个基于用户行为日志的大数据系统是怎么落到实处的。在数据处理层面Twitter通过统一格式的、层级化的日志使得日常的描述性数据分析变得非常容易这可以大大缩减新人想要使用系统的上手成本。通过对数据进行预处理变成session_sequence并将一个个事件映射成UnicodeTwitter使得基于用户时序行为的分析也变得非常容易。无论是进行漏斗分析还是用户建模都变成了简单Pig脚本可以完成的事情。进一步地为了方便系统开发人员排查问题Twitter还为处理完的日志建立了索引。这就让我们在海量日志中进行一些debug工作和特定案例的分析也变得特别容易了。希望通过这节课你能够一窥Twitter这样公司的内部数据分析系统具体是怎么样的。这也是大量工程师在实践过程中找到的一系列最佳实践。如果你刚开始为你的团队和产品搭建大数据体系完全可以依样画葫芦地实践一遍。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2445905.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!