简介:PolarDB MySQL 是因云而生的一个数据库系统, 除了云上 OLTP 场景,大量客户也对 PolarDB 提出了实时数据分析的性能需求。对此 PolarDB 技术团队提出了 In-Memory Column Index(IMCI)的技术计划,在简单剖析查问场景取得的数百倍的减速成果。本文论述了 IMCI 背地技术路线的思考和具体计划的取舍。
作者 | 北楼
起源 | 阿里技术公众号
前言
最近剖析型数据库在资本市场和技术社区都十分的炽热,各种守业公司的创新型产品如雨后春笋般呈现。这一方面是因为以后阶段企业日益依赖从数据中寻找增长后劲带来需要的增长,另一方面云原生技术的倒退带来现有技术体系的进化和改革,诸如 Snowflakes 这类产品的胜利证实,应用云原生技术再造剖析型数据库技术体系是必要的且存在很大的市场机会。
PolarDB MySQL 是因云而生的一个数据库系统, 除了云上 OLTP 场景,大量客户也对 PolarDB 提出了实时数据分析的性能需求。对此 PolarDB 技术团队提出了 In-Memory Column Index(IMCI)的技术计划,在简单剖析查问场景取得的数百倍的减速成果。
本文论述了 IMCI 背地技术路线的思考和具体计划的取舍。PolarDB MySQL 列存剖析性能行将在阿里云上线,敬请期待。
一 MySQL 生态 HTAP 数据库解决方案
MySQL 是一款次要面向 OLTP 型场景设计的开源数据库,开源社区的研发方向侧重于增强其事务处理的能力,如晋升单核性能 / 多核扩展性 / 加强集群能力以晋升可用性等。在解决大数据量下简单查问所须要的能力方面,如优化器解决子查问的能力,高性能算子 HashJoin, SQL 并行执行等,社区始终将其放在比拟低优先级上,因而 MySQL 的数据分析能力晋升停顿迟缓。
随着 MySQL 倒退为世界上最为风行的开源数据库系统,用户在其中存储了大量的数据,并且运行着要害的业务逻辑,对这些数据进行实时剖析成为一个日益增长的需要。当单机 MySQL 不能满足需要时,用户寻求一个更好的解决方案。
1 MySQL + 专用 AP 数据库的搭积木计划
专用剖析型数据库产品选项泛滥,一个可选计划是应用两套零碎来别离满足的 OLTP 和 OLAP 型需要,在两套零碎两头通过数据同步工具等进行数据的实时同步。更进一步,用户甚至能够减少一层 proxy,主动将 TP 型负载路由到 MySQL 上,而将剖析型负载路由到 OLAP 数据库上,对应用层屏蔽底层数据库的部署拓扑。
这样的架构有其灵便之处,例如对于 TP 数据库和 AP 数据库都能够各自抉择最好的计划,而且实现了 TP/AP 负载的齐全隔离。然而其毛病也是不言而喻的。首先,在技术上须要保护两套不同技术体系的数据库系统,其次因为两套零碎解决机制的差别,保护上下游的数据实时一致性也十分具备挑战。而且因为同步提早的存在,上游 AP 零碎存储的常常是过期的数据,导致无奈满足实时剖析的需要。
2 基于多正本的 Divergent Design 办法
随着互联网而衰亡的新兴数据库产品很多都兼容了 MySQL 协定,因而成为代替 MySQL 的一个可选项。而这些分布式数据库产品大部分采纳了分布式 Share Nothing 的计划,其一个外围特点是应用分布式一致性协定来保障单个 partition 多正本之间的数据一致性。因为一份数据在多个正本之间上齐全独立,因而在不同正本上应用不同格局进行存储,来服务不同的查问负载是一个易于施行的计划。典型的如 TiDB,其从 TiDB4.0 开始,在一个 Raft Group 中的其中一个正本上,应用列式存储 (TiFlash) 来响应 AP 型负载, 并通过 TiDB 的智能路由性能来主动选取数据起源。这样实现了一套数据库系统同时服务 OLTP 型负载和 OLAP 型负载。
该办法在诸多 Research 及 Industry 畛域的工作中都被借鉴并应用,并日益成为分布式数据畛域一体化 HTAP 的事实标准计划。然而利用这个计划的前提是用户须要迁徙到对应的 NewSQL 数据库系统,而这往往带来各种兼容性适配问题。
3 一体化的行列混合存储计划
比多正本 Divergent Design 办法更进一步的,是在同一个数据库实例中采纳行列混合存储的计划,同时响应 TP 型和 AP 型负载。这是传统商用数据库 Oracle/SQL Server/DB2 等不谋而合采纳的计划。
Oracle 公司在在 2013 年发表的 Oracle 12C 上,公布了 Database In-Memory 套件,其最外围的性能即为 In-Memory Column Store,通过提供行列混合存储 / 高级查问优化 (物化表达式,JoinGroup) 等技术晋升 OLAP 性能。
微软在 SQL Server 2016 SP1 上,开始提供 Column Store Indexs 性能,用户能够依据负载特色,灵便的应用纯行存表,纯列存表,行列混合表,列存表 + 行存索引等多种模式。
IBM 在 2013 年公布的 10.5 版本 (Kepler) 中,减少了 DB2 BLU Acceleration 组件,通过列式数据存储配合内存计算以及 DataSkipping 技术,大幅晋升剖析场景的性能。
三家当先的商用数据库厂商,均同时采纳了行列混合存储联合内存计算的技术路线,这是有其底层技术逻辑的:列式存储因为有更好的 IO 效率 (压缩,DataSkipping, 列裁剪) 以及 CPU 计算效率(Cache Friendly), 因而要达到最极致的剖析性能必须应用列式存储,而列式存储中索引稠密导致的索引精准度问题决定它不可能成为 TP 场景的存储格局,如此行列混合存储成为一个必选计划。但在行列混合存储架构中,行存索引和列存索引在解决随机更新时存在性能鸿沟, 必须借助 DRAM 的低读写延时来补救列式存储更新效率低的问题。因而在低延时在线事务处理和高性能实时数据分析两大前提下,行列混合存储联合内存计算是惟一计划。
比照上述三种办法,从组合搭积木的办法到 Divergent Design 办法再到一体化的行列混合存储,其集成度越来越高,用户的应用体验也越来越好。然而其对内核工程实现上的挑战也一个比一个大。根底软件的作用就是把简单留给本人把简略留给用户,因而一体化的办法是合乎技术发展趋势的。
二 PolarDB MySQL AP 能力的演进
PolarDB MySQL 能力栈与开源 MySQL 相似,长于 TP 但 AP 能力较弱。因为 PolarDB 提供了最大单实例 100TB 的存储能力,同时其事务处理能力远超用户自建 MySQL。因而 PolarDB 用户偏向于在单实例上存储更多的数据,同时会在这些数据上运行一些简单聚合查问。借助于 PolarDB 一写多读的架构,用户能够减少只读的 RO 节点以运行简单只读查问,从而防止剖析型查问对 TP 负载的烦扰。
1 MySQL 的架构在 AP 场景的缺点
MySQL 的实现架构在执行简单查问时性能差有多个方面的起因,比照专用的 OLAP 零碎,其性能瓶颈体现多个方面:
MySQL 的 SQL 执行引擎基于流式迭代器模型 (Volcano Iterator) 实现, 这个架构在工程实现上依赖大量深层次的函数嵌套及虚函数调用,在解决海量数据时,这种架构会影响古代 CPU 流水线的 pipline 效率,导致 CPU Cache 效率低下。同时 Iterator 执行模型也无奈充分发挥古代 CPU 提供的 SIMD 指令来做执行减速。
执行引擎只能串行执行,无奈施展古代多核 CPU 的并行话能力。官网从 MySQL 8.0 开始,在一些 count(*)等根本查问上减少并行执行的能力,然而简单 SQL 的并行执行能力构建仍然任重道远。
MySQL 最罕用的存储引擎都是按行存储,在按列进行海量数据分析时,按行从磁盘读取数据存在十分大的 IO 带宽节约。其次行式存储格局在解决大量数据时大量拷贝不必要的列数据,对内存读写效率也存在冲击。
2 PolarDB 并行查问冲破 CPU 瓶颈
PolarDB 团队开发的并行查问框架(Parallel Query), 能够在当查问数据量达到肯定阈值时,主动启动并行执行,在存储层将数据分片到不同的线程上,多个线程并行计算,将后果流水线汇总到总线程,最初总线程做些简略归并返回给用户,进步查问效率。
并行查问的退出使得 PolarDB 冲破了单核执行性能的限度,利用多核 CPU 的并行处理能力,在 PolarDB 上局部 SQL 查问耗时成指数级降落。
3 Why We Need Column-Store
并行执行框架冲破了 CPU 扩大能力的限度,带来了显著的性能晋升。然而受限于行式存储及行式执行器的效率限度,单核执行性能存在天花板,其峰值性能仍然与专用的 OLAP 零碎存在差距。要更进一步的晋升 PolarDB MySQL 的剖析性能,咱们须要引入列式存储:
在剖析场景常常须要拜访某个列的大量记录,而列存按列拆分存储的形式会防止读取不须要的列。其次列存因为把雷同属性的列间断保留,其压缩效率也远超行存,通常能够达到 10 倍以上。最初列存中大块存储的构造,联合 MIN/MAX 等毛糙索引信息能够实现大范畴的数据过滤。所有这些行为都极大的晋升了 IO 的效率。在现今存储计算拆散的架构下,缩小通过网络读取的数据量能够对查询处理的响应工夫带来空谷传声的晋升。
列式存储同样能进步 CPU 在解决数据时的执行效率,首先列存的紧凑排列形式可晋升 CPU 拜访内存效率,缩小 L1/L2 Cache miss 导致的执行进展。其次在列式存储上能够应用利用 SIMD 技术进一步晋升单核吞吐能力,而这是古代高性能剖析执行引擎的通用技术路线(Oracle/SQL Server/ClickHouse).
三 PolarDB In-Memory Column Index
PolarDB In-Memory Column Index 性能为 PolarDB 带来列式存储以及内存计算能力,让用户能够在一套 PolarDB 数据库上同时运行 TP 和 AP 型混合负载,在保障现有 PolarDB 优异的 OLTP 性能的同时,大幅晋升 PolarDB 在大数据量上运行简单查问的性能。
In-Memory Column Index 应用行列混合存储技术,同时联合了 PolarDB 基于共享存储一写多读的架构特色,其蕴含如下几个要害的技术创新点:
在 PolarDB 的存储引擎(InnoDB)上新增对列式索引(Columnar Index)的反对,用户能够抉择通过 DDL 将一张表的全部列或者局部列创立为列索引,列索引采纳列压缩存储,其存储空间耗费会远小于行存格局。默认列索引会全副常驻内存以实现最大化剖析性能,然而当内存不够时也反对将其长久化到共享存储上。
在 PolarDB 的 SQL 执行器层,咱们重写了一套面向列存的执行器引擎框架(Column-oriented), 该执行器框架充分利用列式存储的劣势,如以 4096 行的一个 Batch 为单位拜访存储层的数据,应用 SIMD 指令晋升 CPU 单核心解决数据的吞吐,所有要害算子均反对并行执行。在列式存储上,新的执行器比照 MySQL 原有的行存执行器性有几个数量级的性能晋升。
反对行列混合执行的优化器框架,该优化器框架会依据下发的 SQL 是否能在列索引上执行笼罩查问,并且其所依赖的的函数及算子能被列式执行器所反对来决定是否启动列式执行。优化器会同时对行存执行打算和列存执行打算做代价估算,并选中代价交代的执行打算。
用户能够应用 PolarDB 集群中的一个 RO 节点作为剖析型节点,在该 RO 节点上配置生成列存索引,简单查问运行在列存索引上并应用所有可用 CPU 的计算能力,在取得最大执行性能的同时不影响该集群上的 TP 型负载的可用内存和 CPU 资源。
几个要害关键技术联合使得 PolarDB 成为了一个真正的 HTAP 数据库系统,其在大数据量上运行简单查问的性能能够与 Oracle/SQL Server 等业界最顶尖的商用数据库系统处在同一程度。
四 In-Memory Column Index 的技术架构
1 行列混合的优化器
PolarDB 原生有一套面向行存的优化器组件,在引擎层减少对列存性能反对之后,此局部须要进行性能加强,优化器须要可能判断一个查问应该被调度到行存执行还是列存执行。咱们通过一套白名单机制和执行代价计算框架来实现此项工作。零碎保障对反对的 SQL 进行性减速,同时兼容运行不反对的 SQL.
如何实现 100% 的 MySQL 兼容性
咱们通过一套白名单机制来实现兼容性指标。应用白名单机制是基于如下几点考量。第一点思考到零碎可用资源(次要是内存)的限度,个别不会在所有的表的所有上都创立列索引,当一个查问语句须要应用到列不在列存中存在时,其不能在列存上执行。第二点,基于性能的的考量,咱们齐全重写了一套面向列存的 SQL 执行引擎,包含其中所有的物理执行算子和表达式计算,其所笼罩的场景绝对 MySQL 原生行存可能反对的范畴有欠缺。当下发的 SQL 中蕴含一些 IMCI 执行引擎不能反对的算子片段或者列类型时,须要能可能辨认拦挡并切换回行存执行。
查问打算转换
Plan 转换的目标是将 MySQL 的原生逻辑执行打算示意形式 AST 转换为 IMCI 的 Logical Plan。在生成 IMCI 的 Logical Plan 之后,会通过一轮 Optimize 过程,生成 Physical Plan。Plan 转换的办法简略间接,只须要遍历这个执行打算树,将 mysql 优化后的 AST 转换成 IMCI 以 relation operator 位节点的树状构造即可,是一个比拟间接的翻译过程。不过在这个过程中,也会做一部分额定的事件,如进行类型的隐式转换,以兼容 MySQL 灵便的类型零碎。
兼顾行列混合执行的优化器
有行存和列存两套执行引擎的存在,优化器在抉择执行打算时有了更多的抉择,其能够比照行存执行打算的 Cost 和列存执行打算的 Cost,并应用代价最低的那个执行打算.
在 PolarDB 中除了有原生 MySQL 的行存串行执行,还有可能施展多核计算能力的基于行存的 Paralle Query 性能。因而理论优化器会在 1)行存串行执行,2)行存 Paralle Query 3)IMCI 三个选项之中抉择。在目前的迭代阶段,优化器按如下的流程操作:
- 执行 SQL 的 Parse 过程并生成 LogicalPlan,而后调用 MySQL 原生优化器依照执行肯定优化操作,如 join order 调整等。同时该阶段取得的逻辑执行打算会转给 IMCI 的执行打算编译模块, 尝试生成一个列存的执行打算(此处可能会被白名单拦挡并 fallback 回行存)。
- PolarDB 的 Optimizer 会依据行存的 Plan, 计算得出一个面向行存的执行 Cost。如果此 Cost 超过肯定阈值,则会尝试下推到 IMCI 执行器应用 IMCI_Plan 进行执行。
- 如果 IMCI 无奈执行此 SQL,则 PolarDB 会尝试编译出一个 Parallel Query 的执行打算并执行。如果无奈生成 PQ 的执行打算,则阐明 IMCI 和 PQ 均无奈反对此 SQL,fallback 回行存执行。
上述策略是基于这样一个判断,从执行性能比照,行存串行执行 < 行存并行执行 < IMCI。从 SQL 兼容性上看,IMCI < 行存并行执行 < 行存串行执行。然而理论状况会更简单,例如某些状况下,基于行存有序索引笼罩的并行 Index Join 会比基于列存的 Sort Merge join 有更低的 Cost. 目前的策略下可能就抉择了 IMCI 列存执行。
2 面向列式存储的执行引擎
IMCI 执行引擎是一套面向列存优化, 并齐全独立于现有 MySQL 行式执行器的一个实现,重写执行器的目标是为了打消现有行存执行引擎在执行剖析型 SQL 时效率低两个要害瓶颈点:按行拜访导致的虚函数拜访开销以及无奈并行执行。
反对 BATCH 并行的算子
IMCI 执行器引擎应用经典的火山模型,然而借助了列存存储以及向量执行来晋升执行性能。
火山模型里,SQL 生成的语法树所对应的关系代数中,每一种操作会形象为一个 Operator,执行引擎会将整个 SQL 构建成一个 Operator 树,查问树自顶向下的调用 Next()接口,数据则自底向上的被拉取解决。该办法的长处是其计算模型简略间接,通过把不同物理算子形象成一个个迭代器。每一个算子只关怀本人外部的逻辑即可,让各个算子之间的耦合性升高,从而比拟容易写出一个逻辑正确的执行引擎。
在 IMCI 的执行引擎中,每个 Operator 也应用迭代器函数来拜访数据,但不同的是每次调用迭代器会返回一批的数据,而不是一行,能够认为这是一个反对 batch 解决的火山模型。
串行执行受制于单核计算效率,访存延时,IO 提早等限度,执行能力无限。而 IMCI 执行器在几个要害物理算子(Scan/Join/Agg 等)上均反对并行执行。除物理算子须要反对并行外,IMCI 的优化器须要反对生成并行执行打算,优化器在确定一个表的拜访形式时,会依据须要拜访的数据量来决定是否启用并行执行,如果确定启用并行执行,则会参考一系列状态数据决定并行度:包含以后零碎可用的 CPU/Memory/IO 资源, 目前曾经调度和在排队的工作信息, 统计信息, query 的复杂程度, 用户可配置的参数等。依据这些数据计算出一个举荐的 DOP 值给算子, 而一个算子外部会应用雷同的 DOP。同时 DOP 也反对用户应用 Hint 的形式进行设定。
向量化执行解决了单核执行效率的问题,而并行执行冲破了单核的计算瓶颈。二者联合使得 IMCI 执行速度相比传统 MySQL 行式执行有了数量级的速度晋升。
SIMD 向量化计算减速
AP 型场景,SQL 中常常会蕴含很多波及到一个或者多个值 / 运算符 / 函数组成的计算过程,这都是属于表达式计算的领域。表达式的求值是一个计算密集型的工作,因而表达式的计算效率是影响整体性能的一个要害的因素。
传统 MySQL 的表达式计算体系以一行为一个单位的逐行运算,个别称其为迭代器模型实现。因为迭代器对整张表进行了形象,整个表达式实现为一个树形构造,其实现代码易于了解,整个解决的过程十分清晰。
但这种形象会同时带来性能上的损耗,因为在迭代器进行迭代的过程中,每一行数据的获取都会引发多层的函数调用,同时逐行地获取数据会带来过多的 I/O,对缓存也不敌对。MySQL 采纳树形迭代器模型,是受到存储引擎拜访办法的限度,这导致其很难对简单的逻辑计算进行优化。
在列存格局下,因为每一列的数据都独自顺序存储,波及到某一个特定列上的表达式计算过程都能够批量进行。对每一个计算表达式,其输出和输入都以 Batch 为单位,在 Batch 的解决模式下,计算过程能够应用 SIMD 指令进行减速。新表达式零碎有两项要害优化:
充分利用列式存储的劣势,应用分批解决的模型代替迭代器模型,咱们应用 SIMD 指令重写了大部分罕用数据类型的表达式内核实现,例如所有数字类型 (int, decimal, double) 的根本数学运算(+, -, *, /, abs),全副都有对应的 SIMD 指令实现。在 AVX512 指令集的加持下, 单核运算性能取得会数倍的晋升。
采纳了与 Postgres 相似表达式实现办法:在 SQL 编译及优化阶段,IMCI 的表达式以一个树形构造来存储(与现有行式迭代器模型的体现办法相似),然而在执行之前会对该表达式树进行一个后序遍历,将其转换为一维数组来存储,在后续计算时只须要遍历该一维数组构造即能够实现运算。因为打消了树形迭代器模型中的递归过程,计算效率更高。同时该办法对计算过程提供简洁的形象,将数据和计算过程拆散,人造适宜并行计算。
3 反对行列混合存储的存储引擎
事务型利用和剖析型利用对存储引擎有着截然不同的要求,前者要求索引能够精确定位到每一行并反对高效的增删改,而后者则须要反对高效批量扫描解决,这两个场景对存储引擎的设计要求齐全不同,有时甚至是矛盾的。
因而设计一个一体化的存储引擎能同时服务 OLTP 型和 OLAP 型负载十分具备挑战性。目前市场上 HTAP 存储引擎做的比拟好的只有几家有几十年研发积攒的大厂,如 Oracle (In-Memory Column Store)/Sql Server(In Memory Column index)/DB2(BLU)等。如 TiDB 等只能通过将多正本集群中的一个正本调整为列存来反对 HTAP 需要。
一体化的 HTAP 存储引擎个别应用行列混合的存储计划,即引擎中同时存在行存和列存,行存服务于 TP,列存服务于 AP。相比于部署独立一套 OLTP 数据库 加一套 OLAP 数据库来满足业务需要,繁多 HTAP 引擎具备如下的劣势:
- 行存数据和列存数据具备实时一致性,能满足很多刻薄的业务需要,所有数据写入即可见于剖析型查问。
- 更低的老本,用户能够十分不便的指定哪些列甚至一张表哪个范畴的存储为列存格局用于剖析。全量数据持续以行存存储。
- 治理运维不便,用户无需关注数据在两套零碎之间同步及数据一致性问题。
PolarDB 采纳了和 Oracle/Sql Server 等商用数据库相似的行列混合存储技术,咱们称之为 In-Memory Column Index:
- 建表时能够指定局部表或者列为列存格局,或者对已有的表能够应用 Alter table 语句为其减少列存属性,剖析型查问会主动应用列存格局来进行查问减速。
- 列存数据默认压缩格局存储在磁盘上,并能够应用 In-Memory Columbia Store Area 来做缓存减速并减速查问,传统的行格局仍然保留在 BufferPool 中供 OLTP 性负载应用。
- 所有事务的增删改操作都会实时反馈到列存存储上,保障事务级别的数据一致性。
实现一个行列混合的存储引擎技术上十分艰难,然而在 InnoDB 这样一个成熟的面向 OLTP 负载优化的存储引擎中减少列存反对,又面临不同的状况:
- 满足 OLTP 业务的需要是第一优先的,因而减少列存反对不能对 TP 性能太大影响。这要求咱们保护列存必须足够轻量,必要时须要就义 AP 性能保 TP 性能。
- 列存的设计无需思考事务并发对数据的批改, 数据的 unique check 等问题,这些问题在行存零碎中曾经被解决,而这些问题对 ClickHouse 等独自的列存引擎是十分难以解决的。
- 因为有一个久经考验的行存零碎的存在,列存零碎呈现任何问题,都能够切换回行存零碎响应查问申请。
上述条件堪称有利有弊,这也影响了对 PolarDB 整个行列混合存储的方案设计。
体现为 Index 的列存
在 MySQL 插件式的存储引擎框架的架构下,减少列存反对最简略计划是实现一个独自的存储引擎,如 Inforbright 以及 MarinaDB 的 ColumnStore 都采纳了这种计划。而 PolarDB 采纳了将列存实现为 InnoDB 的二级索引的计划,次要基于如下几点考量:
- InnoDB 原生是反对多索引的,Insert/Update/Delete 操作都会以行粒度 apply 到 Primary Index 和所有的 Secondary Index 上,并且保障事务。将列存实现为一个二级索引能够复用这套事务处理框架。
- 在数据编码格局上,实现为二级索引的列存能够和其余行存索引应用齐全一样的内格局,间接内存拷贝即可,不须要思考 charset 和 collation 等信息,这对下层执行器也是齐全通明的。
- 二级索引操作非常灵活,能够在建表时即指定索引所蕴含的列,也能够后续通过 DDL 语句对一个二级索引中蕴含的列进行减少或者删除操作。例如用户能够将须要剖析的 int/float/Double 列退出列索引,而对于个别只须要点查然而又占用大量空间的 text/blob 字段,则能够保留在行存中。
- 解体复原过程能够复用 InnoDB 的 Redo 事务日志模块, 与现有实现无缝兼容。同时也不便反对 PolarDB 的物理复制过程,反对在独立 RO 节点或者 Standby 节点上生成列存索引提供剖析服务。
- 同时二级索引与主表有一样的生命周期,方便管理。
如上图所示,在 PolarDB 中所有 Primary Index 和 Seconary Index 都实现为一个 B +Tree。而列索引在定义上是一个 Index,但其实是一个虚构的索引,用于捕捉对该索引笼罩列的增删改操作。
对于下面的表其主表 (Primary Index)蕴含(C1,C2,C3,C4,C5) 5 列数据, Seconary Index 索引蕴含(C2,C1) 两列数据, 在一般二级索引中,C2 与 C1 编码成一行保留在 B +tree 中。而其中的列存索引蕴含(C2,C3,C4) 三列数据. 在理论物理存储时,会对三列进行拆分独立存储,每一列都会按写入程序转成列存格局。
列存实现为二级索引的另一个益处是执行器的工程实现非常简单,在 MySQL 中曾经存在笼罩索引的概念,即一个查问所须要的列都在一个二级索引中存储,则能够间接利用这个二级索引中的数据满足查问需要,应用二级索引绝对于应用 Primary Index 能够极大缩小读取的数据量进而晋升查问性能。当一个查问所须要的列都被列索引笼罩时,借助列存的减速作用,能够数十倍甚至数百倍的晋升查问性能。
列存数据组织
对 ColumnIndex 中每一列,其存储都应用了无序且追加写的格局,联合标记删除及后盾异步 compaction 实现空间回收。其具体实现上有如下几个关键点:
- 列索引中记录按 RowGroup 进行组织,每个 RowGroup 中不同的列会各自打包造成 DataPack。
- 每个 RowGroup 都采纳追加写,分属每个列的 DataPack 也是采纳追加写模式。对于一个列索引,只有个 Active RowGroup 负责承受新的写入。当该 RowGroup 写满之后即解冻,其蕴含的所有 Datapack 会转为压缩格保留到磁盘上,同时记录每个数据块的统计信息便于过滤。
- 列存 RowGroup 中每新写入一行都会调配一个 RowID 用作定位,属于一行的所有列都能够用该 RowID 计算定位,同时系统维护 PK 到 RowID 的映射索引,以反对后续的删除和批改操作。
- 更新操作采纳标记删除的形式来反对,对于更新操作,首先依据 RowID 计算出其原始地位并设置删除标记,而后在 ActiveRowGroup 中写入新的数据版本。
- 当一个 RowGroup 中的有效记录超过肯定阈值,则会触发后盾异步 compaction 操作,其作用一方面是回收空间,另一方面能够让无效数据存储更加紧凑,晋升剖析型查问单的效率。
采纳这种数据组织形式一方面满足了剖析型查问按列进行批量扫描过滤的要求。另一方面对于 TP 型事务操作影响十分小,写入操作只须要按列追加写到内存即可,删除操作只须要设置一个删除标记位。而更新操作则是一个标记删除附加一个追加写。列存能够做到反对事务级别的更新同时,做到简直不影响 OLTP 的性能。
全量及增量行转列
行转列操作在两种状况下会产生,第一种状况是应用 DDL 语句对局部列创立列索引(个别是业务对一个已有的表有新增剖析型需要),此时须要扫描全表数据以创立列索引。另一种状况是在事务操作过程中对于波及到的列实时行专列。
对于全表行转列的情景,咱们应用并行扫描的形式对 InnoDB 的 Primary Key 进行扫描,并顺次将所有波及到的列转换为列存模式,这一操作的速度十分快,其根本只受限于服务器可用的 IO 吞吐速度和可用 CPU 资源。该操作是一个 online-DDL 过程,不会阻塞在线业务的运行。
在一个表上建设列索引之后,所有的更新事务将会同步更新行存和列存数据,以保障二者的事务一致性。下图演示了在 IMCI 性能敞开和开启之间的差异性。在未开启 IMCI 性能时,事务对所有行的更新都会先加锁,而后再对数据页进行批改,在事务提交之前会对所有加锁的记录一次性方所。在开启 IMCI 性能之后,事务零碎会创立一个列存更新缓存,在所有数据页被批改的同时,会记录所波及到的列存的批改操作,在事务提交完结前,该更新缓存会利用到列存零碎。
在此实现下,列存存储提供了与行存一样的事务隔离级别。对于每个写操作, RowGroup 中的每一行都会记录批改该行的事务编号,而对于每个标记删除操作也会记录该设置动作的事务编号。借助写入事务号和删除事务号,AP 型查问能够用十分轻量级的形式取得一个全局一致性的快照。
列索引毛糙索引
由前述列的存储格局能够看出, IMCI 中所有的 Datapack 都采纳无序且追加写的形式, 因而无奈像 InnoDB 的一般有序索引那样的能够精准的过滤掉不符合要求的数据。在 IMCI 中,咱们借助统计信息来进行数据块过滤,以此来达到升高数据拜访单价的目标。
- 在每个 Active Datapack 终结写入的时候,会事后进行计算,并生成 Datapack 所蕴含数据的最小值 / 最大值 / 数值的总和 / 空值的个数 / 记录总条数等信息。所有这些信息会保护在 DataPacks Meta 元信息区域并常驻内存。因为解冻的 Datapack 中还会存在数据的删除操作,因而统计信息的更新保护会放到后盾实现。
- 对于查问申请,会依据查问条件将 Datapacks 分为相干、不相干、可能相干三大类,从而缩小理论的数据块拜访。而对于一些聚合查问操作,如 count/sum 等,能够通过事后计算好的统计值进行简略的运算得出,这些数据块甚至都不须要进行解压。
采纳基于统计信息的毛糙索引计划对于一些须要精准定位局部数据的查问并不是很敌对。然而在一个行列混合存储引擎中,列索引只须要辅助减速那些会波及到大量数据扫描的查问,在这个场景下应用列会具备显著的劣势。而对于那些只会拜访到大量数据的 SQL,优化器通常会基于代价模型计算得出基于行存会失去一个老本更低的计划。
行列混合存储下的 TP 和 AP 资源隔离
PolarDB 行列混合存储能够反对在一个实例中同时反对 AP 型查问和 TP 型查问。但很多业务有很高的 OLTP 型负载,而突发性的 OLAP 性负载可能烦扰到 TP 型业务的响应时延。因而反对负载隔离在 HTAP 数据库中是一个必须反对的性能。借助 PolarDB 一写多读的架构,咱们能够十分不便对 AP 型负载和 TP 型负载进行隔离。在 PolarDB 的技术架构下,咱们有如下几个部署形式:
- 第一种形式,RW 上开启行列混合存储,此种模式部署能够反对轻量级的 AP 查问,在次要为 TP 负载,且 AP 型申请比拟少时能够采纳。或者应用 PolarDB 进行报表查问,然而数据来自批量数据导入的场景。
- 第二种形式,RW 反对 OLTP 型负载,并启动一个 AP 型 RO 开启行列混合存储以反对查问,此种部署模式下 CPU 资源能够实现 100% 隔离,同时该 AP 型 RO 节点上的内存能够 100% 调配给列存存储和执行器。然而因为应用的雷同的共享存储,因而在 IO 上会互相产生肯定影响,对于这个问题咱们在将来会反对将列存数据写入到内部存储如 OSS 等,实现 IO 的资源隔离,同时晋升 AP 型 RO 上的 IO 吞吐速率。
- 第三种形式,RW/RO 反对 OLTP 型负载,在独自的 Standby 节点开启行列混合存储以反对 AP 型查问,因为 standby 是应用独立的共享存储集群,这种计划在第二种计划反对 CPU 和内存资源隔离的根底上,还能够实现 IO 资源的隔离。
除了上述部署架构上不同能够反对的资源局隔离之外。在 PolarDB 外部对于一些须要应用并行执行的大查问反对动静并行度调整(Auto DOP),这个机制会综合思考以后零碎的负载以及可用的 CPU 和内存资源,对单个查问所用的资源进行限度,以防止单个查问耗费的资源太多,影响其余申请的解决。
五 PolarDB IMCI 的 OLAP 性能
为了验证 IMCI 技术的成果,咱们对 PolarDB MySQL IMCI 的进行了 TPC- H 场景的测试。同时在雷同的场景下将其与原生 MySQL 的行存执行引擎以及以后 OLAP 引擎单机性能最强的 ClickHouse 进行了比照。测试参数简要介绍如下:
- 数据量 TPC-H 100GB, 22 条 Query
- CPU Intel(R) Xeon(R) CPU E5-2682 2 socket
- 内存 512G, 启动后数据都灌进内存。
1 PolarDB IMCI VS MySQL 串行
在 TPC- H 场景下,所有 22 条 Query,IMCI 解决延时绝对比原生 MySQL 都有数十倍到数百倍不等的减速成果。其中 Q6 的的成果将近 400 倍。体现出了 IMCI 的微小劣势。
2 PolarDB IMCI VS ClickHouse
而在比照以后社区最炽热的剖析型数据库 ClickHouse 时, IMCI 在 TPC- H 场景下的性能也与其根本在同一程度。局部 SQL 的解决延时各有优劣。用户齐全能够应用 IMCI 代替 ClickHouse 应用,同时其数据管理也更加不便。
FutureWork
IMCI 是 PolarDB 迈向数据分析市场的第一步,它迭代脚步不会进行,接下里咱们会在如下几个方向进一步钻研和摸索,给客户带来更好的应用体验:
- 自动化的索引举荐零碎,目前列存的创立和删除须要用户手动指定,这减少了 DBA 的工作量,目前咱们正在钻研引入自动化举荐技术,依据用户的 SQL 申请特色,主动创立列存索引,升高保护累赘。
- 独自的列存表以及 OSS 存储,目前 IMCI 只是一个索引,对纯剖析型场景,去除行存能够更进一步的升高存储大小,而 IMCI 执行器反对读写 OSS 对象存储能将存储老本降到最低。
- 行列混合执行,即一个 SQl 的执行打算局部片段在行存执行,局部片段在列存执行。以取得最大化的执行减速成果。
原文链接
本文为阿里云原创内容,未经容许不得转载。