做者:闲鱼技术-才思mysql
随着闲鱼业务的发展,用户规模达到数亿级,用户维度的数据指标,达到上百个之多。如何从亿级别的数据中,快速筛选出符合指望的用户人群,进行精细化人群运营,是技术须要解决的问题。业界的不少方案经常须要分钟级甚至小时级才能生成查询结果。本文提供了一种解决大数据场景下的高效数据筛选、统计和分析方法,从亿级别数据中,任意组合查询条件,筛选须要的数据,作到毫秒级返回。sql
从技术角度分析,咱们这个业务场景有以下特色:数据库
下面简单对比下OLTP和OLAP:性能优化
OLTP | OLAP | |
---|---|---|
定义 | 联机事务处理 | 联机分析处理 |
应用场景 | 平常业务操做 | 分析决策,报表统计 |
事务要求 | 须要支持事务 | 无需事务支持 |
经常使用数据操做 | 读/写高并发 | 查询为主,并发要求相对低 |
实时性要求 | 高 | 要求不严格 |
DB大小 | MB-GB | GB-TB |
数据行数 | 几万到几百万 | 亿级别甚至几十亿上百亿 |
数据列数 | 几十至上百列 | 百级别至千级别 |
最多见的数据库,如MySql、Oracle等,都采用行式存储,比较适合OLTP。若是用MySql等行数据库来实现OLAP,通常都会碰到两个瓶颈:架构
综上分析,咱们的应用场景,并不适合采用行存储数据库,所以咱们重点考虑列存数据库。并发
下面简单对比一下行式存储与列式存储的特色:高并发
行式存储 | 列式存储 | |
---|---|---|
存储特色 | 同一行数据一块儿存储 | 同一列数据一块儿存储 |
读取优势 | 一次性读取整行数据快捷 | 读取单列数据快捷 |
读取缺点 | 单列读取,也须要读取整行数据 | 整行查询,须要重组数据 |
数据更新特色 | INSERT/UPDATE比较方便 | INSERT/UPDATE比较麻烦 |
索引 | 须要单独对查询列建索引 | 每一列均可以做为索引 |
压缩特色 | 同一行数据差别大,压缩比率低 | 一列数据因为类似性,压缩比率高 |
行存适合近线数据分析,好比要求查询表中某几条符合条件的记录的全部字段的场景。列存适合用于数据的统计分析。考虑以下场景:一个用于存放用户的表中有20个字段,而咱们要统计用户年龄的平均值,若是是行存,则要全表扫描,遍历全部行。但若是是列存,数据库只要定位到年龄这一列,而后只扫描这一列的数据就能够获得全部的年龄,计算平均值,性能上相比行存理论上就会快20倍。
而在列存数据库中,比较常见的是HBase。HBase应用的核心设计重点是rowkey的设计,通常要把经常使用的筛选条件,组合设计到rowkey中,经过rowkey的get(单条记录)或者scan(范围)查询。所以HBase比较适合有限查询条件下的非结构化数据存储。而咱们的场景,因为全部字段都须要做为筛选条件,因此本质上仍是须要结构化存储,且要求查询低延迟,所以也没法使用HBase。
咱们综合考虑集团内多款列式存储的DB产品(ADS/PostgreSQL/HBase/HybridDB),综合评估读写性能、稳定性、语法完备程度及开发和部署成本,咱们选择了HybridDB for MySQL计算规格来构建人群圈选引擎。性能
HybridDB for MySQL计算规格对咱们的这个场景而言,核心能力主要有:大数据
那么,HybridDB for MySQL计算规格是如何作到大数据场景下的任意维度组合查询的毫秒级响应的呢?优化
综合来讲,HybridDB for MySQL计算规格是以SQL为中心的多功能在线实时仓库系统,很适合咱们的业务场景,所以咱们在此之上构建了咱们的人群圈选底层引擎。
在搭建了人群圈选引擎以后,咱们重点改造了咱们的消息推送系统,做为人群精细化运营的一个重要落地点。
消息推送(PUSH)是信息触达用户最快捷的手段。闲鱼比较经常使用的PUSH方式,是先离线计算好PUSH人群、准备好对应PUSH文案,而后在次日指定的时间推送。通常都是周期性的PUSH任务。可是临时性的、须要马上发送、紧急的PUSH任务,就须要BI同窗介入,每一个PUSH任务平均约须要占用BI同窗半天的开发时间,且操做上也比较麻烦。本次咱们把人群圈选系统与原有的PUSH系统打通,极大地改善了此类PUSH的准备数据以及发送的效率,解放了开发资源。
离线数据层:用户维度数据,分散在各个业务系统的离线表中。咱们经过离线T+1定时任务,把数据汇总导入到实时计算层的用户大宽表中。
实时计算层:根据人群的筛选条件,从用户大宽表中,查询符合的用户数量和用户ID列表,为应用系统提供服务。
人群圈选前台系统:提供可视化的操做界面。运营同窗选择筛选条件,保存为人群,用于分析或者发送PUSH。每个人群,对应一个SQL存储。相似于:
select count(*) from user_big_table where column1> 1 and column2 in ('a','b') and ( column31=1 or column32=2)
同时,SQL能够支持任意字段的多层and/or嵌套组合。
用SQL保存人群的方式,当用户表中的数据变动时,能够随时执行SQL,获取最新的人群用户,来更新人群。
闲鱼PUSH系统:从人群圈选前台系统中获取人群对应的where条件,再从实时计算层,分页获取用户列表,给用户发送PUSH。在实现过程当中,咱们重点解决了分页查询的性能问题。
分页查询性能优化方案:
在分页时,当人群的规模很大(千万级别)时,页码越日后,查询的性能会有明显降低。所以,咱们采用把人群数据增长行号、导出到MySql的方式,来提高性能。表结构以下:
批次号 | 人群ID | 行号 | 用户ID |
---|---|---|---|
1001 | 1 | 1 | 123 |
1001 | 1 | 2 | 234 |
1001 | 1 | 3 | 345 |
1001 | 1 | 4 | 456 |
咱们为"人群ID"+"批次号"+"行号"建组合索引,分页查询时,用索引查询的方式替换分页的方式,从而保证大页码时的查询效率。
另外,为此额外付出的导出数据的开销,得益于HybridDB强大的数据导出能力,数据量在万级别至百万级别,耗时在秒级至几十秒级别。综合权衡以后,采用了本方案。
人群圈选系统为闲鱼精细化用户运营提供了强有力的底层能力支撑。同时,圈选人群,也能够应用到其余的业务场景,好比首页焦点图定投等须要分层用户运营的场景,为闲鱼业务提供了很大的优化空间。
本文实现了海量多维度数据中组合查询的秒级返回结果,是一种OLAP场景下的通用技术实现方案。同时介绍了用该技术方案改造原有业务系统的一个应用案例,取得了很好的业务结果,可供相似需求或场景的参考。
人群圈选引擎中的用户数据,咱们目前是T+1导入的。这是考虑到人群相关的指标,变化频率不是很快,且不少指标(好比用户标签)都是离线T+1计算的,所以T+1的数据更新频度是能够接受的。后续咱们又基于HybridDB构建了更为强大的商品圈选引擎。闲鱼商品数据相比用户数据,变化更快。一方面用户随时会更新本身的商品,另外一方面,因为闲鱼商品单库存(售出即下架)的特性,以及其余缘由,商品状态会随时变动。所以咱们的选品引擎,应该尽快感知到这些数据的变化,并在投放层面作出实时调整。咱们基于HybridDB(存储)和实时计算引擎,构建了更为强大的“马赫”实时选品系统。后续即将推出“马赫”的系列文章,有兴趣的同窗能够关注。另外若是对本文中的具体技术实现(细节)有任何问题,请联系咱们。谢谢。
参考资料:
HybridDB for MySQL介绍: https://www.aliyun.com/product/petadata