【编者的话】 A /B测试曾在多个领域产生深远的影响,其中包括医药、农业、制造业和广告。 在软件开发中, A/B测试实验提供了一个有价值的方式来评估新特性对客户行为的影响。在这个系列中,我们将描述 Twitter的 A/B测试系统的技术和统计现状。
本文是该系列的第二篇,主要介绍了 Twitter的 A/B系统后端的实现。
注:本文最初发布于 Twitter博客,InfoQ中文站在获得作者授权的基础上对文章进行了翻译。
在 前一篇文章 中,我们讨论了 Twitter进行 A/B测试的动机和 A/B测试如何帮助我们进行创新。在这篇文章中,我们将介绍 Twitter的 A/B系统后端是如何实现的。
Twitter实验工具 Duck Duck Goose (简称 DDG)最早构建于2010年。它已经演变成一个系统,能够聚合许多 TB的数据,比如 Tweets、社交图谱变化、服务器日志和用户在Web和移动端的交互记录,从而对大量的柔性指标进行测量和分析。
在一个很高的层次上,其数据流相当简单。
工程师通过 Web UI配置实验,并指定一些细节:
然后,DDG会提供给工程师一些代码,用于检测哪种实验桶应该推送给客户。对于特性开发人员,这就是一种简单的“特性开关”——一种用于控制特性可用性的通用机制。任何时候当应用决定用户是否是一个实验体时,我们就记录一个 “A/B测试印象(a/b test impression)”。将决定延迟到用户即将收到实验影响之前会增大统计功效。
Twitter应用程序的使用数据将被发送到“事件采集服务(event ingest service)”中。一些轻量级的统计使用运行在 Heron 平台上的 TSAR 通过流处理任务实时计算。大部分工作是离线完成的,使用 Scalding管道,结合了客户端事件交互日志、内部用户模型和其它数据集。
可以认为,Scalding管道具有三个不同的阶段。
首先,我们将原始数据聚合成每个用户每小时的指标值数据集。结果如下:
这些数据将作为下一阶段的输入数据,除了实验外,也将作为分析的数据源——顶层指标计算、“特殊队列(ad-hoc cohorting)”,等等。
然后,我们结合实验 A/B测试印象中每个用户的指标信息,在实验运行时计算每个指标、每个用户的聚合值。由于用户可能在不同的时间进入不同的实验,因此这种聚合在不同的实验中可能有不同的值。
同时我们也会记录用户第一次进入实验的时间,记录他们是否是新的、偶然的或者频繁的用户以及其它元数据。这能够在实验过程中实现实验结果的分割和属性值变化的测量。
第二阶段的结果对深入挖掘实验和研究替代分析方法非常有帮助——它们让我们能够迭代不同的聚合技术、分层方法和各种处理异常值的算法,等等。
最后,第三阶段会聚合所有实验数据。
这就是加载到 Manhattan 的最终实验结果数据,产品团队可以通过内部仪表板进行浏览。
DDG是一个能够衡量显著差异特性的平台,其中一些特性还没有被发明出来。这意味着我们需要平衡柔性指标定义的可预测性和稳定性。
我们提供了三种指标类型,按集中控制和规范降序排序如下:
为了帮助实验者找到合适的指标集,并保持指标定义的正确和通用,指标会被收集和整理成“指标组”。每个指标组由创建团队维护和变更。指标组的历史版本、所有权和其它属性都将被跟踪。这鼓励实验者之间的共享和交流。
随着时间推移,有意义的跟踪事件的组合数量和实验者数量逐渐增长,会出现冗余的指标。这会引起混乱(比如内置的 “Foobar Quality”指标和 Bob定义的 “Quality of Foobar”指标的区别?)。在我们“TODO”列表上有一个有趣的项目是创建一种能够自动识别度量相同内容指标的方法,并建议指标调和。
实现聚合管道高效运行是系统最大的挑战之一。数据源的交互每天就会产生数千亿的事件;相对而言,低效率会显著影响总运行时和处理成本。
我们发现 Hadoop的 Map-Reduce任务的轻量化、恒定分析对性能问题的快速分析很重要。为此,我们与 Hadoop团队合作,在 Hadoop构建中实现按需的 JVM分析任务(实现 YARN-445 及若干后续事项),同时安装一键式线程转储,并为所有任务开启自动 XProf分析。
通过分析我们发现了一些提高效率的机会。比如,我们发现一些地方可以存储自定义指标事件匹配的结果。如果可能,我们用生成的数字 ID替换字符串。针对 Hadoop Map-Reduce我们还应用了一些技巧:map和 reduce阶段的排序和溢出缓冲调优、数据排序实现聚合过程中 map端的最大压缩、“及早规划(early projection)”等等。
在2015年初的Hack Week,我们发现在 Hadoop映射任务的 SpillThread上浪费了大量的时间,SpillThread负责对部分映射输出进行排序,并写入磁盘。SpillThread的大部分时间浪费在反序列化输出键,并对其进行排序。Hadoop提供了一个 RawComparator接口帮助 Hadoop高级用户避免这种情况,但是不适合我们使用的 Thrift对象。
我们构建了一个原型,为序列化 Thrift结构实现了一个通用的 RawComparator,并用基准问题测试了收益。我们的原型做了一些修改,针对最坏情况做了基准测试,但是由此产生的80%的收益已经足够重要,因此我们从 Scalding团队招募了一些工程师,切实地为Thrift、Scala Tuples和 Case类实现这个想法。最终以 OrderedSerialization特性发布在 Scalding 0.15中。在DDG任务中开启该特性能够节省30%的整体计算时间!更多细节可以参考 Scalding团队在 Hadoop Summit 2015上所作的题为 “ Performance Optimization At Scale ”演讲。
最后,我们有两层防护可以确保我们没有引入性能衰退:预防和检测。为了预防衰退,除了常规单元测试,我们还有自动化测试,让我们在交付准备环境中能够运行完整的端到端管道,并比较两者的结果(保证正确性)和所有的 Hadoop计数器(检查是否存在性能衰退)。为了检测生产环境中是否存在性能问题,我们创建了 Scala特性,使得Scalding任务能够向 Twitter的内部 可观测基础设施 导出所有的 Hadoop计数器。这意味着,我们能够轻松地使用公共模板为 Scalding任务生成仪表板,创建问题警报,比如长时间运行或者长时间不运行,快速检查以过高速度发生的特定类的允许误差,等等。
支撑 Twitter实验平台的基础设施非常广泛,主要是由于需要处理大量的数据,以分析实验。该系统必须在可用指标的灵活性和预测性及易于分析之间进行权衡;管道设计实现了多粒度数据的生成,使不同类型的分析成为可能。我们在效率处理方面投入了大量的精力,包括全面的自动化测试和持续改进,以分析和监控 Hadoop的性能。
多年来,许多 Twitter工程师一直从事这些工具的开发;我们要特别感谢 Chuang Liu , Jimmy Chen , Peter Seibel , Zachary Taylor , Nodira Khoussainova , Richard Whitcomb , Luca Clementi , Gera Shegalov , Ian O’Connell , Oscar Boykin , Mansur Ashraf 所做的贡献,以及 PIE,Scalding和 Hadoop团队所作出的贡献。
《他山之石》是InfoQ中文站新推出的一个专栏,精选来自国内外技术社区和个人博客上的技术文章,让更多的读者朋友受益,本栏目转载的内容都经过原作者授权。文章推荐可以发送邮件到editors@cn.infoq.com。
查看英文原文: Twitter experimentation: technical overview