MySQL与OLAP:分析型SQL查询最佳实践探索
搞点多维分析,糙快猛的解决方式就是使用ROLAP(关系型OLAP)了。数据经维度建模后存储在MySQL,ROLAP引擎(比方开源的Mondrian)负责将OLAP请求转化为SQL语句提交给数据库。OLAP计算分析功能导致MySQL须要进行较多复杂SQL查询,性能调优不可缺少,本文总结了一些有用原则。
OLAP特点
OLAP的典型应用包含复杂动态报表,须要支持钻取(上卷和下钻)、切片、切块和旋转操作。下表总结了OLAP和OLTP系统的主要差别。OLAP的特点决定了SQL的查询场景和优化方案,下文将从索引、聚合、子查询、表连接和Pivoting等几个方面分别介绍。
OLAP |
OLTP |
|
用户量 |
分析人员用户量相对小 |
高并发 |
数据库设计 |
维度模型:星型、雪花型号 |
规范化 |
数据量 |
大,动辄千万级别 |
小,一般不超过百万级别 |
SQL读写场景 |
定期导入,一般无更新,复杂查询每次检索大量数据 |
以事务为单位每次读写少量数据 |
老生常谈之索引
在权衡数据容错恢复和性能之后,存储引擎选择的是Innodb。Innodb索引的特性是主键聚集索引和B+Tree数据结构。利用这两个特性,可以提升数据导入和多维度组合切片的性能。
1) 数据导入速度
下图为Innodb表主键索引示意图,聚集索引使表中全部数据必须依照主键顺序存储在主键索引叶子节点上。假设不依照主键顺序导入数据,会导致额外的分页、数据查找、移动IO操作,这样,Innodb表的插入速度严重依赖于插入顺序。解决方法比較简单:主键使用Auto_Increment列。
2) 多维度切片
多维度组合查询、分组和汇总操作很常见,那么在多个维度字段上加入复合索引是不可缺少的,而复合索引的字段选择和顺序尤为重要。
谁排NO.1?一般遵循下面原则:
a) Mysql仅仅进行索引最左前缀匹配,能够选择最常查询的字段排首位。特殊情况:如果少量查询场景不存在该字段怎么处理?须要另外再建索引吗?如果在盘古系统中,运营单位通常会出如今全部查询中,所以会建立[运营单位,行业,产品线……]的复合索引,但某些高级别管理人员的查询语句中,不包括运营单位,那么须要再建立[行业,产品线……]的复合索引吗?答案是看情况,提供小技巧:应用层处理,在不包括运营单位条件的查询SQL中增加“运营单位 in(全部运营单位)”条件
b) 最佳性能优化原则决定索引区分度最大的字段排首位(可用count(distinct column)/count(*)计算)
还有个大家往往会忽略的问题,谁排最后呢?答案是:将可能存在范围条件检索的字段放最后。来个案例
……WHERE avg_csm_weekly >100 AND trade_id= 19 ORDER BY balance
如果建立的复合索引为[avg_cms_weekly,trade_id, ,balance],那么因为在avg_csm_weekly上存在范围条件,MySQL不会使用剩余的索引。
聚合
MySQL不支持Hash聚合,仅支持流聚合。流聚合会先依据GROUP BY的字段进行排序,然后流式訪问排序好的数据,进行分组聚合。假设在explain的extra列中看到Using temporary和Using filesort,说明聚合使用了暂时表和文件排序操作,这可能导致性能低下。最佳优化目标是让聚合操作使用Covering Index,即全然不用查询表数据,仅仅在索引上完毕聚合查询。
以下查询语句会使用复合索引 [trade_id,product_line_id]
select trade_id,product_line_id,count(*) from data_acct_info_weekly group bytrade_id,product_line_id
观察查询计划,在extra列显示Using index,说明该操作为Covering
Index查询。
在OLAP分析中,时间范围上的聚合操作很普遍。以下以账号每日消费表为演示样例,总结几种常见的时间聚合查询模板
account_id(账户) |
stdate(数据日期) |
click_pay(点击消费) |
1 |
2013-08-01 |
100 |
1 |
2013-08-02 |
150 |
2 |
2013-08-01 |
125 |
1)累计聚合
返回账户增加某度以来累计消费和平均值。
SELECT a.account_id,a.stdate ,SUM(click_pay),AVG(click_pay)
FROM data_account_csm_daily a INNER JOIN data_account_csm_daily b
ON a.account_id=b.account_id ANDb.stdate<=a.stdate
GROUP BY a.account_id,a.stdate
ORDER BY a.account_id,a.stdate
2)滑动累计
返回账户固定窗体时间内累计消费和平均值
SELECT a.account_id,a.stdate ,SUM(click_pay),AVG(click_pay)
FROM data_account_csm_daily a INNER JOIN data_account_csm_daily b
ON a.account_id=b.account_id ANDb.stdate<=a.stdate
AND b.stdate>=DATE_ADD(a.stdate,INTERVAL -30 DAY)
GROUP BY a.account_id,a.stdate
ORDER BY a.account_id,a.stdate
3)MTD累计
返回账户月初以来累计消费和平均值
SELECT a.account_id,a.stdate,SUM(click_pay),AVG(click_pay)
FROM data_account_csm_daily a INNER JOIN data_account_csm_daily b
ON a.account_id=b.account_id ANDb.stdate<=a.stdate
AND b.stdate>=DATE_FORMAT(a.stdate,”%Y-%M-01”)
GROUP BY a.account_id,a.stdate
ORDER BY a.account_id,a.stdate
再探讨下ROLLUP和CUBE。如果用户须要对N个维度进行聚合操作,须要进行N次GROUP BY再将结果进行UNION,而使用ROLLUP能够一次查询出N次GROUP BY 操作的结果。以下的两条语句查询结果一致,运行计划上却不同,前者仅仅须要扫描一次,后者则须要扫描表四次。
语句1:
SELECT col1,col2,col3,SUM(col4) FROM table
GROUP BYcol1,col2,col3
WITH ROLLUP
语句2:
SELECT col1,col2,col3,SUM(col4) FROM table
GROUP BYcol1,col2,col3
UNION
SELECT col1,col2,NULL,SUM(col4) FROM table
GROUP BYcol1,col2
UNION
SELECT col1,NULL,NULL ,SUM(col4) FROM table
GROUP BY col1
UNION
SELECT NULL,NULL,NULL,SUM(col4) FROM table
与ROLLUP仅仅在同一层次上对维度进行汇总不同,CUBE对全部维度进行汇总,N个维度CUBE须要2的N次方分组操作。当前版本号的MySQL还不支持CUBE操作,但和用多个GROUP操作UNION模拟ROLLUP同理,也能够用多个ROLLUP操作UNION模拟CUBE。
子查询vs JOIN
复杂的需求场景导致某些子查询场景不可避免。关于子查询,存在不少性能陷阱和认识误区值得关注。
1)MySQL子查询性能差的主要原因是子查询产生暂时表吗?不全然正确,暂时表并不可怕,一个完整的SQL语句,FROM/JOIN/GROUP/WHERE/ORDER等操作,不考虑索引优化的情况下,都有可能产生暂时表。所以更严格的表述是在子查询产生的暂时表上查询无法利用索引导致性能低下。
2)IN子查询往往性能不佳的真实原因是什么?是IN查询的暂时表数据量太大,MySQL太弱,仅仅能支持极少数量的IN子查询吗?不一定,显示列表IN(a,b,c)查询的性能并不算差,IN子查询真正的性能陷阱在于Mysql优化器往往将IN独立子查询优化成EXISTS相关子查询!所以当观察SELECT * FROM table1 WHERE table1.id IN(SELECT id FROM table2)的查询计划,会发现table2的查询为DEPEDENTSUBQUERY,原因事实上是MySQL优化策略+历史原因。
3)子查询的性能一定弱于JOIN吗?未必,因为Mysql不支持Semi Join(注),所以在某些须要场景下,使用子查询性能优于JOIN。比方A表和B表一对多关系,假设只想查询在B表中存在相应记录的A表记录,假设使用JOIN,须要用DISTINCT或者GROUP操作进行去重操作。使用关联子查询能够避免这部分开销。SELECT id FROM table1 WHERE EXISTS(SELECT table2.id FROM table2WHERE table2.id=table1.id)
关于Join,Mysql使用Nested Loop算法(注)。在典型的星型维度模型中,维度表数据量远小于事实表,JOIN操作往往是大小表连接,性能问题不大,这方面不多讲。结合前面提到的Covering Index,介绍一个利用JOIN提高分页效率的歪招:
分页往往须要用到LIMIT OFFSET,在偏移量非常大的时候,比方LIMIT 100000,50,MySQL须要检索100050数据,性能严重下降。常见的处理方式是a)添加排序辅助列,将LIMIT转化为在辅助列上范围查找操作
b)应用层缓存机制 c)需求折中,没有人会翻到100000页。以上皆不灵的时候,能够选择Covering
Index+Join。
SELECT * FROM table1 INNER JOIN
(SELECT id FROM table1 ORDER BY indexed_col limit 100000,50) AS a
ON table1.id = a.id
这样的方式效率较高,由于暂时表a仅在索引上进行操作(Innodb索引叶子节点上存储了主键值),取得所需行id之后,再和完整的表进行Join获取其它所需列。
注:MySQL的著名分支MarioDB支持Semi
Join和Hash Join
其它
Pivoting&Unpivoting主要关注行列旋转变化,还能够用来对聚合数据进行格式化用于报表展现,在此不再复述
MySQL与OLAP:分析型SQL查询最佳实践探索的更多相关文章
- MySQL 使用profile分析慢sql,group left join效率高于子查询
MySQL 使用profile分析慢sql,group left join效率高于子查询 http://blog.csdn.net/mchdba/article/details/54380221 -- ...
- Bug预防体系(上千bug分析后总结的最佳实践)
Bug预防体系(上千bug分析后总结的最佳实践) 原创 2017-08-16俞美玲 光荣之路 吴老的<selenium webdriver 实战宝典>出版了! web常见产品问题及预防 ...
- SQL Server - 最佳实践 - 参数嗅探问题 转。
文章来自:https://yq.aliyun.com/articles/61767 先说我的问题,最近某个存储过程,暂定名字:sp_a 总是执行超时,sp_a带有一个参数,暂定名为 para1 var ...
- paip.前端加载时间分析之道优化最佳实践
paip.前端加载时间分析之道优化最佳实践 1.另存为 ,查看文件尺寸..和图片. 2.view the 另存为的htm静态的文件单个的加载,看时间...可以排除编程语言的问题and 数据库.. ## ...
- paip.前端载入时间分析之道优化最佳实践
paip.前端载入时间分析之道优化最佳实践 1.另存为 ,查看文件尺寸..和图片. 2.view the 另存为的htm静态的文件单个的载入,看时间...能够排除编程语言的问题and 数据库.. ## ...
- MySQL源码分析之SQL函数执行
1.MySQL中执行一条SQL的总体流程 2.SQL函数执行过程 1.MySQL中执行一条SQL的总体流程 一条包含函数的SQL语句,在mysql中会经过: 客户端发送,服务器连接,语法解析,语句执行 ...
- 敏捷遇上UML-需求分析及软件设计最佳实践(郑州站 2014-6-7)
邀请函: 尊敬的阁下:我们将在郑州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实 ...
- SpringMVC源码分析和一些常用最佳实践
前言 本文分两部分,第一部分剖析SpringMVC的源代码,看看一个请求响应是如何处理,第二部分主要介绍一些使用中的最佳实践,这些best practices有些比较common,有些比较tricky ...
- MySQL表行数查询最佳实践
日常应用运维工作中,Dev或者db本身都需要统计表的行数,以此作为应用或者维护的一个信息参考.也许很多人会忽略select count(*) from table_name类似的sql对数据库性能的影 ...
随机推荐
- JavaScript:String 对象
ylbtech-JavaScript:String 对象 1.返回顶部 String 对象 String 对象用于处理文本(字符串). 创建 String 对象的语法: new String(s); ...
- boost并发编程boost::atomic
三个用于并发编程的组件: atomic,thread,asio(用于同步和异步io操作) atomic atomic,封装了不同计算机硬件的底层操作原语,提供了跨平台的原子操作功能,解决并发竞争读 ...
- 算法: 实现LRU缓存,读取、写入O(1)实现
这题应该见的不少了,写写记录一下. 实现该功能分析: (1) O(1) 时间完成查找,那除了 hash 别无选择. (2) LRU 最近最少使用算法,为了方便数据的淘汰.需要对最近访问的数据放未访问数 ...
- go语言之进阶篇通过select实现斐波那契数列
一.select作用 Go里面提供了一个关键字select,通过select可以监听channel上的数据流动. select的用法与switch语言非常类似,由select开始一个新的选择块,每个选 ...
- Maven自定义Archetype(zz)
原文地址:http://www.cnblogs.com/javalouvre/p/5858162.html Maven提供了archetype帮助我们快速构建项目骨架,很便捷.但是,中央仓库中的arc ...
- asp.net网站发布
1.iis里面新建一个网站,目录可以新建(例如:F:\dotNetWeb),还可以创建子文件夹如:F:\dotNetWeb\my,网站路径是可以自己设置的,也可以使用IIS默认的网站. 2.vs201 ...
- CentOS6 安装并破解Jira 7
CentOS6 安装并破解Jira 7 JIRA软件是为您的软件团队的每个成员构建的,用来规划,跟踪和发布优秀的软件. https://confluence.atlassian.... 最低硬件要求及 ...
- Java Math.sqrt()方法
描述 java.lang.Math.sqrt(double a) 返回正确舍入的一个double值的正平方根.特殊情况: 如果参数是NaN或小于为零,那么结果是NaN. 如果参数是正无穷大,那么结果为 ...
- 不能从const char *转换为LPCWSTR --VS经常碰到
不能从const char *转换为LPCWSTR 在VC 6.0中编译成功的项目在VS2005 vs2005.vs2008.vs2010中常会出现类型错误. 经常出现的错误是:不能从const ch ...
- LSTM 文本情感分析/序列分类 Keras
LSTM 文本情感分析/序列分类 Keras 请参考 http://spaces.ac.cn/archives/3414/ neg.xls是这样的 pos.xls是这样的neg=pd.read_e ...