OLAP数仓入门问答-基础篇

2020-05-31 00:00:00 查询 数据 操作 维度 立方体

前言

过去近七年在网易杭研一直从事数据库相关的开发工作,主要是MySQL和MongoDB这两种数据库,去年开始涉及图数据库Neo4J。上述几种,都可认为是OLTP类数据处理,由于工作需要,需要调研学习OLAP技术和相关系统,本文开始逐步进行轮总结,很多东西还只是片面理解,权当做个笔记。

对事物的认识总是螺旋式的,先有个大致的认识,再逐渐丰富其血肉。这个过程,会有片面性、也可能否定之前的理解,但只要一直用心用脑,总会不断进步的。当然,多参考的资料,会大大缩短过程中花费的时间。

基础问答

什么是OLAP,其与OLTP有什么区别?

如果展开来说,这个问题估计可以写好几篇文章,这里简单谈谈个人的理解。

OLTP是Online transaction processing的英文缩写,指在线/联机事务处理,这么说其实还是比抽象的。OLTP典型的应用领域包括银行、证劵等金融行业,电子商务系统等,在此举经典的银行例子,我们在招商银行APP上查询账户余额、收支信息和转账记录,在ATM机上存钱,取钱,将招行账号的钱转到工行账号上。这些都是典型的OLTP类操作,这些操作都比较简单,主要是对数据库中的数据进行增删改查。操作主体一般是产品的用户。

OLAP是Online analytical processing的英文缩写,指联机分析处理。从字面上我们能看出是做分析类操作。通过分析数据库中的数据来得出一些结论性的东西。比如给老总们看的报表,用于进行市场开拓的用户行为统计,不同维度的汇总分析结果等等。操作主体一般是运营、销售和市场等团队人员而不是用户。

单次OLTP处理的数据量比较小,所涉及的表非常有限,一般仅一两张表。而OLAP是为了从大量的数据中找出某种规律性的东西,经常用到count()、sum()和avg()等聚合方法,用于了解现状并为将来的计划/决策提供数据支撑,所以对多张表的数据进行连接汇总非常普遍。

为了表示跟OLTP的数据库(database)在数据量和复杂度上的不同,一般称OLAP的操作对象为数据仓库(data warehouse),简称数仓。数据库仓库中的数据,往往来源于多个数据库,以及相应的业务日志。

下表是对OLTP和OLAP的简单总结。

网易杭研OLTP数据库团队为业界培养了多位数据库专家,在线数仓团队也同样如此,在Impala、Kudu等技术上有深厚的积累,是网易猛犸、有数等网易大数据产品的核心基础设施。

MySQL等OLTP数据库能处理OLAP业务吗?

MySQL是当前流行的开源数据库,一般作为OLTP数据库使用。在MySQL上也能执行一些OLAP操作,但这不是MySQL擅长的领域。虽然OLTP和OLAP都是通过SQL来执行,但SQL语句只是描述了我想要什么,而并没有说明应该怎么做(不考虑hint等),即确定优的执行计划。由于OLTP操作比较简单,所涉及的表也少,因此不需要相应的数据库具有强大的执行优化能力,比如说MySQL在查询优化这块就比较弱,但这其实没有给它的大规模普及使用造成多大伤害。

当然,MySQL也在快速进步,尤其是新的8.0版本,在查询优化模块添加了很多众望所归的功能特性,包括窗口函数,通用表达式和更强大的Join能力等。

而OLAP类操作不一样,没有强大的执行计划产生和优化能力,执行这类操作肯定不会有多高的效率,甚至会寸步难行。当然,如果总数据量较小,SQL也相对简单,那MySQL也是能够应付的。在MySQL高可用实例的从库做些报表类查询也有不少案例。

OLAP的查询跟OLTP查询具体有那些不一样?

上文简要提及,OLTP查询一般仅涉及单表,点查为主,返回的是记录本身或该记录的多个列。即使是范围查询,基本上也会通过limit来限制返回的记录数。

而OLAP则不同,表中单条记录本身并不是查询所关心的,比较典型的特点包括有聚合类算子、涉及多表Join,查询所用谓语/条件没有索引,玩玩不是返回记录。由于这些操作都非常耗计算资源,而且数据仓库相比数据库在数据量上大很多,因此,OLAP类查询经常表现为cpu-bound而不是io-bound。

这样说可能还不够直观,下面换种形式。OLTP和OLAP发展到现在已经比较成熟,业界也有些公认的benchmark来进行性能评估。我们可以通过这些benchmark中的对应sql来了解两位服务的典型查询语句。 对于OLTP来说,有sysbench和tpcc测试套件,对于OLAP来说,有tpch和tpcds 2种。这里分别例举sysbench oltp和tpcds的sql作为参考

sysbench oltp查询

可以从sysbench的lua脚本中获取都有哪些查询类型。如下所示:

local stmt_defs = {
   point_selects = {
      "SELECT c FROM sbtest%u WHERE id=?",
      t.INT},
   simple_ranges = {
      "SELECT c FROM sbtest%u WHERE id BETWEEN ? AND ?",
      t.INT, t.INT},
   sum_ranges = {
      "SELECT SUM(k) FROM sbtest%u WHERE id BETWEEN ? AND ?",
       t.INT, t.INT},
   order_ranges = {
      "SELECT c FROM sbtest%u WHERE id BETWEEN ? AND ? ORDER BY c",
       t.INT, t.INT},
   distinct_ranges = {
      "SELECT DISTINCT c FROM sbtest%u WHERE id BETWEEN ? AND ? ORDER BY c",
      t.INT, t.INT},

相关文章