列存索引(IMCI)

本文介绍了PolarDB PostgreSQL的列存索引特性,可以让您更好地适应复杂查询的需求。

简介

PolarDB PostgreSQL的列存索引(In-Memory Column Index,简称IMCI)功能,让您可以在一套系统中同时处理高并发的在线交易(OLTP)和复杂的数据分析(OLAP)。您不再需要为了分析查询,而维护一个独立、昂贵且架构复杂的外部系统。这极大地简化了您的数据架构,在降低运维成本的同时,实现对海量业务数据的实时分析。

相对于行存引擎,主要在两个层面进行优化:存储层面的列存索引和执行引擎层面的向量化算子(即:向量化执行引擎),从而弥补行存引擎在复杂查询场景的不足。以TPC-H 100GB为例,在32256 GB集群规格中,PolarDB PostgreSQL的列存引擎比行存引擎性能提升60倍以上,详细介绍请参见列存索引性能测试结果

技术原理

架构优化

PolarDB PostgreSQL列存索引在执行引擎和存储层面进行了优化,以更好地适应复杂查询的需求。

  • 执行引擎层面

    • 不同于行存引擎,向量化执行引擎利用CPUSIMD指令批量化处理数据,即一条CPU指令可并行处理多条数据,从而减少函数调用耗时以及Cache Miss问题。

    • 实现完整的向量化算子。如ScanGroup ByOrder ByHash JoinFilterCountSum等算子的向量化,使其能够接收批量化的数据输入并利用SIMD指令进行处理。

  • 存储层面

    • 采用更适合向量化算子的列存格式,而非Heap行存结构。

    • 列存格式目前以索引的形式存在,即列存索引。列存索引与B-tree索引、GiST索引具有相似性,仅在存储结构和适用场景上存在差异。列存索引可被直接使用,而B-tree、GiST等索引则被行存引擎使用。一个表中可以同时存在列存索引和其他类型的索引来应对不同的查询,PolarDB PostgreSQL优化器会根据查询计划的代价来选择不同的索引。

如下图所示,您可以为表tc2列创建B-tree索引来应对点查(SELECT * FROM t WHERE c2=10),为c4c5列创建列存索引来应对统计类查询(SELECT c4,  SUM(c5) FROM t GROUP BY c4),查询优化器会根据查询SQL的查询代价选择合适的索引。

image

行列数据实时同步

列存索引中的数据是以列式存储在数据库中的。数据会先写入到行存表中,然后通过索引机制更新至列存索引,此过程被称为行列数据的同步。PolarDB PostgreSQL的列存索引提供高效、实时且自动化的行列数据同步机制,在使用时无需为此过程搭建额外链路,也无需手动刷新列存数据。

行列数据同步机制的基本原理是通过解析WAL日志来获得变化的数据,进而将其异步写入到列存索引。该过程对行存数据的负载和性能几乎没有影响,对行存性能的影响保持在3%以下。由于PolarDB PostgreSQL列存索引与行存引擎可以部署在同一个节点中,因此在解析WAL的过程中进行了大量优化。尽管行列转换的过程为异步,但依旧可以实现毫秒级至秒级的实时同步(取决于写入负载)。行列数据实时同步性能调优请参见提升列存索引的实时性

产品形态

PolarDB PostgreSQL的列存索引将在所有节点上部署,因此集群中的所有计算节点均同时具备行存引擎和列存索引。在这种模式下,一条SQL语句在执行时会面临两个选择:

  1. 该语句将被分配到哪个节点执行,即选择计算节点。

  2. 该语句由节点内的哪个引擎执行,即选择执行引擎。

选择计算节点

PolarDB集群存在多个节点,与列存索引相关的SQL语句将面临由哪个计算节点执行的问题。

  • 所有涉及数据更改的语句均由RW节点执行,如DDLDML语句。在RW节点内部将进一步根据具体条件选择执行引擎。

  • 创建列存索引、列存索引实时同步都在RW节点执行。

  • 所有只读SQL语句,可通过设置数据库代理来决定使用哪个节点来执行。

选择执行引擎

在计算节点内部,执行语句需要选择使用的执行引擎。

  • 对于DDL类型语句,如CREATE TABLEALTER TABLE等,一般使用行存引擎。但CREATE TABLE AS SELECT语句会根据SELECT子语句的复杂度来选择是否使用列存索引。

  • 对于DML类型语句,如INSERTUPDATEDELETE等,使用行存引擎。

  • 对于DQL类型语句,如SELECT语句,将根据查询代价和参数来决定是否使用列存索引。一般来说SELECT语句的查询代价越大,使用列存索引的概率越大。如果列存索引执行SELECT语句失败,将会使用行存引擎重新执行。

image

功能优势

  • 高性能

    相比于行存引擎,列存索引能够将SQL查询性能普遍提升一个数量级,针对复杂查询提供百倍以上的性能提升。

  • 低成本

    • 只需为查询涉及列创建列存索引,无需将整个表转换为列存。

    • 列存索引的存储空间占用较小。对于同一列,列存索引的空间占用仅为行存的10%~50%(具体取决于数据类型)。

  • 使用方式简单

    使用流程与原生PostgreSQL一致。

    • 列存索引继承原生PostgreSQL索引管理方式,支持CREATE INDEXDROP INDEX等语法,无额外的使用语法,详情请参见开启和使用列存索引

    • 高度兼容PostgreSQL数据类型和语法,无需修改现有SQL语句即可使用列存索引。

    • 支持通过参数配置,精细化控制哪些SQL使用列存索引,包括全局使用列存索引、会话级使用列存索引,以及通过Hint等方式指定SQL使用列存索引,详情请参见开启和使用列存索引

  • 实时维护列存索引

    • 自动维护行存数据与列存索引之间的一致性,无需额外构建行列数据转换链路,无需手动刷新同步列存索引等操作。

    • 行存中新插入的数据可在毫秒/秒级同步到列存索引中,可根据不同的业务负载调整同步性能,详情请参见开启和使用列存索引

  • 一致性

    为列存数据和行存数据提供两种一致性级别,以满足不同业务需求。

    • 最终一致性(默认):适用于写入负载高,但对数据实时性要求低的查询。

    • 强一致性:在列存数据与行存数据完全一致后再返回查询结果,详情请参见开启和使用列存索引

  • 兼容多种使用方式

    • 支持Prepared Statement语法。

    • 支持事务块中的SELECT语句加速。

      说明

      SELECT语句必须为事务块内的“写前读”SQL。

    • 支持分区表和pg_pathman管理的分区表,并支持分区裁剪能力,详情请参见分区表使用列存索引

    • 支持时空多模态查询加速。

适用场景

PolarDB PostgreSQL的列存索引特性提供了一站式HTAP产品体验,可以应用于多种业务场景:

  • HTAP场景:例如每天需要对大量的交易数据进行增删改查,同时也需要实时统计出过去一小时的交易报表。PolarDB PostgreSQL列存索引不仅能够高效应对这两类负载,还可以简化系统架构,无需为实时统计部分OLAP查询维护额外的系统。

  • 各类慢SQL:在高并发事务场景中出现的慢SQL,如:

    • 全表统计,如COUNTSUMAVERAGE等操作。

    • 对列进行 GROUP BYORDER BY等操作。

    • 对多个表做JOIN操作。

    • 查询时过滤的列较多且顺序不定,复合索引不灵活且容易失效,使用列存索引更优。

  • 多模类型与时空查询:在嵌套JSON中提取KEYVALUE、基于地理网格统计时空热力图等。

  • ETL数据加速计算场景:依托PolarDB基于列存索引提供的强大而灵活的计算能力,在PolarDB中使用SQL来实现ETL功能。

费用说明

列存索引可直接在行存节点或在添加的列存索引只读节点上执行。

  • 在行存节点上使用列存索引:可免费使用。

  • 添加列存索引只读节点:需收取相关的节点费用,列存索引只读节点按照普通计算节点收计费。同时,列存索引将占用一定的存储空间,因此也会产生相应的存储空间费用

说明

在行存节点上直接使用列存索引的能力可能会有业务影响。添加列存索引只读节点后,您可以实现TP(事务)与AP(分析)业务在不同节点上的相互隔离,确保彼此之间互不影响。