作者归档:Chaozhong Yang

LeanCloud 离线数据分析的服务架构:异步处理

一般来讲,绝大多数互联网服务都可以划分为 OLTP (On-line Transaction Processing) 与 OLAP (On-line Analytical Processing)。OLTP vs. OLAP 这篇文章对两者之间的差异给出了非常清晰的解释。如果我们只考虑 Query 这个维度,OLTP 最好只处理相当简单、耗时极短的查询或操作,而 OLAP 则处理比较复杂、耗时较长的查询(通常包含数据聚合)。

LeanCloud 提供的离线数据分析服务,大致上可以归类为 OLAP。用户提交 SQL 查询语句,我们的系统为其完成查询并返回相应结果。如果用户提交的 SQL 较为简单,系统处理时间就比较短。反过来,如果用户提交的 SQL 较为复杂(多表 join、数据聚合),那么系统处理时间会更长一些。既然如此,我们就不能像 OLTP 那样在一次请求里面给出查询结果,而是将查询拆分成「创建分析 job」与「获取分析 job 处理结果」。简言之,从 API 层面看,用户的查询是由两个接口来实现的,即 POST /jobsGET /jobs/:jobId。这样的 API 设计意味着所有的数据分析任务都会异步地处理。

继续阅读

LeanCloud 离线数据分析功能介绍

可能不少用户还不知道,我们上个月中旬就已经对外发布了离线数据分析功能。用户可以用它来做什么,它适用于哪些应用场景,有没有应用实例?若要回答这些问题,首先让我们看看部分用户曾经向我们提出过的一些痛点或需求。为了对用户数据保密,后面提到的应用名字与数据均为虚构。

  • 某应用 TestA 发布几个月之后,由于用户量暴涨,存储在我们这里的数据规模急剧上涨。这就导致了一个无法忽视的问题:他们在应用刚发布的那段时间会每天从我们这里导出数据,自己写程序做数据分析。而当数据规模(几十 GB)越来越大,受限于网络文件下载速度,每日导出数据再自己做分析已经变得越来越困难。因此,他们希望我们能提供数据分析服务,支持 SQL-like 的查询。
  • 某应用 TestB 每天都会针对应用数据做一些分析报告,主要目的是知晓应用当前的关键数据指标。按照最初的方案,他们需要每日导出数据,自己写脚本程序处理应用数据。实际上,这样的工作对一个需要快速迭代产品的团队来说颇为繁琐,也比较消耗时间。
  • 我们的工单系统 也是构建在 Leancloud 之上,每一条工单数据都是通过我们的存储组件写入我们的云端数据库。工单系统同时也作为我们的一个示例公开了源代码,从源代码就可以了解到它和其他用户的应用没有本质区别(从数据存储这个角度看)。我们的工程师除了需要非常及时地处理用户提交的工单,也需要了解工单系统的一些统计指标,例如自己最近回复了多少工单、不同平台工单的处理速度等等。如果有一个支持 SQL-like 的查询服务,那么要统计这些指标就会变得很容易。

相信上面的例子对很多用户来讲都不陌生,它揭示的痛点无非是大量的数据无法通过便捷的手段进行分析。考虑到用户的需求,我们开发了支持 SQL-like 查询的离线数据分析服务。用户要做的不过是按照我们的离线数据分析使用指南开启离线数据分析服务,输入合法的 SQL 语句即可分析数据。顺便说一下,这个服务后端核心组件为 Spark SQLParquet

接下来,就以我们的工单系统为例子,介绍如何利用离线数据分析服务来解决我们日常工作中遇到的一些问题。当然,为了更好地了解工单系统内部的结构,你可以访问这里的源代码。

首先让我们看看整个界面:

bigquery-0

接下来就让我们针对一些统计指标分析工单系统数据:

  • 统计不同平台类型(type)的工单数量,并按大小排序
select count(*) as `count`, type from Ticket where type is not null group by type order by `count` desc

结果如图所示(如果图中文字不够清晰,请点击查看大图):

bigquery-1

  • 统计每个工单(Ticket)各有多少条回复(Thread),并按回复数大小排序
select Ticket.objectId, Ticket.title, count(Thread.content) as `count` from Ticket inner join Thread where Ticket.objectId=Thread.ticket.objectId group by Thread.content, Ticket.objectId, Ticket.title order by `count` desc

结果如图所示:

bigquery-2

若想知晓 iOS 平台的每条工单各有多少条回复,只需把上面的 SQL 语句做简单修改:

select Ticket.objectId, Ticket.title, count(Thread.content) as `count` from Ticket inner join Thread where Ticket.objectId=Thread.ticket.objectId and Ticket.type='ios' group by Thread.content, Ticket.objectId, Ticket.title order by `count` desc
  • 统计所有工程师最近一个多月处理了多少条工单(Ticket),并按处理数量排序。Admin、Thread、Ticket 这三张表参与了 join 计算,其中 Admin 存放了工程师的个人信息,它和回复(Thread)通过 cid 关联。而回复(Thread)与工单(Ticket)则是通过工单的 objectId 来做关联。
# 注:slackName 其实就是这个工程师的用户名,因为我们还使用 Slack 来实时接收消息。
select count(distinct Ticket.objectId) as `count`, Admin.slackName from Ticket inner join Thread inner join Admin where Thread.ticket.objectId = Ticket.objectId and Admin.cid = Thread.cid and Admin.slackName is not null and unix_timestamp(Ticket.createdAt) > unix_timestamp('2014-12-01T00:00:00.000Z') group by Admin.slackName order by `count` desc

结果如图所示:

bigquery-3

  • 统计所有工程师总共有多少次回复(Thread),并按回复数排序。同工单数的查询类似,这里也需要三张表做 join。
select count(distinct Thread.objectId) as `count`, Admin.slackName from Ticket inner join Thread inner join Admin where Thread.ticket.objectId = Ticket.objectId and Admin.cid = Thread.cid and Admin.slackName is not null group by Admin.slackName order by `count` desc

结果如图所示:

bigquery-4

  • 统计每天各有多少条回复(Thread)
# substr(createdAt, 1, 10) 用来获取回复的日期(yyyy-MM-dd)
select count(*) as `count`, substr(createdAt, 1, 10) as date from Thread group by substr(createdAt, 1, 10) order by date desc

结果如图所示:

bigquery-5

考虑到本文篇幅有限,就不再列举其他查询示例。如果您在使用离线数据分析服务的过程中遇到各种各样的问题,都可以通过工单或者邮件向我们反馈,我们会及时处理相关问题。

Swift 基础运算符

对任何一门高级编程语言,基础运算符都是不可或缺的。为了让更多开发者了解 Swift 的基础运算符,我们翻译了《The Swift Programming》的《Basic Operators》一章,如有错误,敬请指正。转载请在开始位置注明出处。

基础运算符

运算符是一种可以检查、修改或结合数值的特殊符号或短语。例如,加法运算符可以将两个数字相加(let i = 1 + 2)。更复杂的例子还包括逻辑与运算符 &&if enteredDoorCode && passedRetinaScan)以及可以简化增量操作的运算符 ++i

Swift 支持大多数标准 C 运算符,并增强了避免常见编码错误的能力。赋值运算符(=)没有返回值,这可以防止误用(=)和等于符号(==)。算数运算符(+*)会检测且禁止数值溢出,避免因处理数据不当而导致的意外结果。你可以使用 Swift 的溢出运算符来自定义溢出行为,详情请参考 溢出运算符

继续阅读