## ColumnStore 在大数据中的应用实践 文 / 陈兴隆 随着企业数据量急速增长,为了满足业务需求,大数据统计早已成为迫切的需求。在引擎排行榜上 MySQL 已经长期处于第二,但大数据统计并没有明显突破。 MySQL 解决方案包括 Infobright、Greenplum、Spark 等,与之更为密切的是 Infobright,但是多表连接场景下,性能会大幅下降(且特殊功能需要付费)。而 ColumnStore 的出现则弥补了此处的空缺,是 MariaDB 在 OLAP 领域解决方案的突破。ColumnStore 是 InfiniDB 与 MariaDB 10.1 的结合体,目前已经 GA,拥有计算能力及存储线性扩展、高压缩比、MySQL 协议兼容、自动水平和垂直分区、扩展窗口函数等特点。如果你正在寻找性能及存储线性扩展、学习成本低、易维护且兼具数据安全及审计的数据仓库解决方案,相信 ColumnStore 会给你带来不小的惊喜。 图1是来自官方的 ColumnStore 结构图。 图1  ColumnStore架构图 图1 ColumnStore 架构图 UM:MariaDB SQL FRONT End(User Module); PM:Distributed Query Engine(Performance Module); 整体架构分为计算层和存储层,都是可扩展的,计算层需由以下几项主要进程构成: - MariaDB(mysqld):收集用户请求的一个 SQL 入口,存储元数据信息; - Execution Manager:解析语法树,转化成任务列表(JOB LIST),包括优化、取数据、(HASH)JOIN、汇总、分组; - DMLProc/DDLProc:将 DML/DDL 语句发送到指定的 PM 执行; - Performance Module:接受 Execution Manager 发送过来的任务调度,分布式扫描,(HASH)JOIN 与汇总。 由此,我们能清晰地理解整个处理流程:MariaDB 收到用户的 SQL 请求后,通过执行管理器将 SQL 转化为任务列表,再由 DMLProc/DDLProc 发送任务给P M,最后 PM 将结果返回给 UM 进行汇总,返回结果给客户端。 ### 优势 #### 存储、性能、兼容、扩展 为了解决在大数据统计性能上的问题,对比了 Infobright 和 ColumnStore 两种解决方案,最终选取了 ColumnStore,对比如下: 表1  数仓方案对比表 表1 数仓方案对比表 - 对于存储而言,InnoDB 本身的压缩率并不高,有1倍左右。相比之下,Infobright 的压缩率相对最高,有20倍之多;而 ColumnStore 则比较适中,为5倍左右。如果为了追求高压缩比的历史数据存档,很明显使用 Infobright 社区版是很好的方案。 - InnoDB 自身的扩展性并不高,需要外部中间件来实现分库分表,因此给予扩展性低的评价;Infobright 也可以部署集群,因此扩展性给予高评分;同样 ColumnStore 也是易于扩展的分布式方案。 - 在性能方面,InnoDB 的 OLTP 性能远高于后两者,在数据仓库中提供在线服务,可以考虑用 InnoDB 来存储,但统计性能则相差甚远;由于 Infobright 中存在 Knowledge Grid(知识网格),又使用了列式存储来压缩数据,在数据量超过内存容量的情况下,对单列的统计表现卓越,100倍性能提升就成了极轻松的事,但如果统计字段增加或多表查询,性能也就极速下降;ColumnStore 不仅拥有单列高速统计的优势,而且在多表连接的场景下也表现不俗,当然字段越多性能同样会下降,但下降后的速度仍可以接受。 - Infobright 的社区版不支持 DML,因此语法兼容性存在很大问题,对查询的支持尚佳;ColumnStore 本身开源,但语法也略有不同,例如多表连接时,不能对同一个表进行多次连接,分组查询字段(SELECT COLUMN LIST)必须要在分组列表(GROUP BY LIST)中。 #### 窗口函数 窗口函数(Windowing Function)在数仓的场景下非常有用,当分析 App 中的行为路径时,很简单地就能把用户的使用路径串联起来。 #### 性能优势 想了解 ColumnStore 的性能表现,请移步 [Percona 的性能测试对比。](https://www.percona.com/blog/2010/01/07/star-schema-bechmark-infobright-infinidb-and-luciddb/) ### 限制 #### 数据类型不一致 InnoDB 支持的数据类型有: - TINYINT/SMALLINT/MEDIUMINT/INT或INTEGER/BIGINT - FLOAT/DOUBLE/DECIMAL - CHAR/VARCHAR/TINYBLOB/BLOB/MEDIUMBLOB/LOGNGBLOB/TINYTEXT/TEXT/MEDIUMTEXT/LONGTEXT/VARBINARY/BINARY - DATE/TIME/YEAR/DATETIME/TIMESTAMP - ENUM/SET ColumnStore 支持的数据类型为: - TINYINT/SMALLINT/INT或INTERGER/BIGINT - FLOAT/DOUBLE/DEAL/DECIMAL或NUMBER - CHAR/VARCHAR - DATE/DATETIME 通过对比可知,ColumnStore 不支持的数据类型有:MEDIUMINT、BLOB、TEXT、BINARY、TIMESTAMP/TIME/YEAR、ENUM/SET 不支持的数据类型,我们通过类型转换来保证兼容,规则如表2所示: 表2  类型转换对照表 表2 类型转换对照表 #### 字符类型长度限制 ColumnStore 的字符串类型字段长度最长为8000,否则报语法错误。也许在 ColumnStore 工程师的眼中,过长的字符不具有很好的分析意义,加之如果文本过大,应该不利于列式存储发挥压缩统计的优势,这可能正是8000长度限制的由来(纯属个人猜测)。大多数场景下,需要把过长的文本截断以保证系统之间的兼容性。如果业务对此类文本内容依赖较强,建议在原数据中拆分为多行处理。 #### 表总字段总长度限制 对于长度的限制,不仅字段的长度有限制,一个表中多个字段长度总和也有限制。虽然碰见的情况比较少,但也不容忽视。 #### 中文字符长度的限制 MySQL 的老司机都知道,MySQL 在早期版本里字符串长度的定义为字节长度(一个汉字占用三个字节),后面的版本才定义为字符数(一个汉字占用一个长度),但 ColumnStore 或者 InfiniDB 都占用三个字节。如果你的中文字符串莫名其妙地被截断了,很可能是出现了这个问题,即 VARCHAR(3) 类型字段只能存储一个汉字。 #### 窗口函数的视图 如果 SQL 中包含了窗口函数,那么在创建视图时会报错。 #### 建表限制 不支持 primary key/auto_increment/index 等 DDL 语句,表名不支持关键字。 ### 建议 由于碰到了 ColumnStore 如此多的限制,使用者很容易出错,建议自己写脚本将建表、数据抽取做到自动化,省时省力,其中可能包含的功能建议: - 创建表结构 - 字段数据类型转换 - 字段长度转换 - 数据库/表名称变更 有了以上四点,就不用人工审核每个字段应该用什么类型和多大的长度,如果表名字还是个关键字(那就更糟糕了),只有把名字改掉了,比如可以加个前缀。 - 多线程数据抽取 - 数据增量抽取 当数据量到一定程度,从原数据库抽取数据会成为瓶颈,此时如果能多线程抽取,那可谓如虎添翼。之前等2个小时才能抽取完的任务,现在10分钟就搞定。如果再加上增量抽取,那便可以再节省5分钟。 - 字段忽略 前面讲过,ColumnStore的字段长度和表总宽度有限制,但我们不能控制业务表设计,因而只能适应,所以如果有了字段忽略的功能,把没有分析统计意义的字段直接忽略掉,数据就可以很愉快地抽取起来了。 #### 参考 [窗口函数](https://www.percona.com/live/mysql-conference-2014/sessions/windowing-functions-mysql-infinidb) [性能对比1](https://www.percona.com/blog/2010/01/07/star-schema-bechmark-infobright-infinidb-and-luciddb/) [性能对比2](https://www.percona.com/blog/2017/03/17/column-store-database-benchmarks-mariadb-columnstore-vs-clickhouse-vs-apache-spark/) [结构简介](http://mt.sohu.com/20161216/n476022933.shtml)