Skip to content

06 - Column-Store Databases

Database Workload

目前市面上有常见的三种数据库

On-Line Transaction Processing (OLTP)

联机事务处理
特点:高并发、低延迟、每次操作数据量小、事务性强(ACID)。
典型场景:用户注册、下单、发评论、转账等。
性能要求:快速响应,支持大量并发写入。
技术重点:行式存储(row-oriented)、索引优化、事务日志、锁机制。

Tuple 有几个字段时,查询某一个字段时,不管其他的字段是否需要,都会被读取加载到内存中,即使它们是没有用处的数据,这也就是 OLTP 不适合用于分析的原因。
事实上,除非批量导出,否则分析时几乎不会出现 SELECT *

On-Line Analytical Processing (OLAP)

联机分析处理
特点:读多写少、查询复杂、涉及大量数据扫描和聚合。
典型场景:商业智能(BI)、报表、用户行为分析。
性能要求:高效处理大规模数据聚合,支持复杂查询。
技术重点:列式存储(column-oriented)、数据压缩、向量化执行、MPP 架构。

对于 OLAP 来说,虽然 read 很快,但是相对的 write 就慢,每次 write 都需要将表按字段分离,再进行写入。
同时, OLAP 做点查(point queries) 很慢,因为需要从多个列查询结果,然后进行拼接。

Hybrid Transaction + Analytical Processing

混合事务/分析处理 目标:在同一个系统中同时高效支持 OLTP 和 OLAP。
问题:由于需要兼容两种需求,因此两种需求都不能做得很好,导致市场的选择会出现问题。
代表系统:SingleStore、TiDB、Oracle In-Memory、SQL Server Hekaton。


相对来说
OLTP 大多数操作是写入操作,各种操作也很简单。(实际读取更多,相对 OLAP 写入更多)
OLAP 大多数操作是读取操作,且查询非常复杂。(实际几乎不写入,只会定期批量导入)
HTAP 处于 OLTP 和 OLAP 的中间。

一些特殊的情况,例如在 PostgreSQL 中,它虽然是 OLTP ,但是如果存储了大文本数据,它会单独设置一个 Page ,而不会将数据存到 Slot 中。

Decomposition Storage Model (DSM)

OLAP 的 Tuple 有两种存储方式

Fixed-length Offsets

所有列按相同行序存储,第 i 个元素天然对齐一个 Tuple
要求每个字段大小固定

可能有额外的存储开销

Database compression

目标:

  • 生成固定长度的值
  • 推迟解压数据的时间(不解压就能够知道里面包含了什么)
  • 压缩是无损的

实现方法:

  • Block-level (按 Page 压缩)
  • Tuple-level (按 Tuple 压缩)
  • Attribute-level (对单个属性压缩)
  • Colunm-level (对列压缩)

对于 MySQL(InnoDB) 来说,它使用的 Block-level 压缩,对 Page 进行压缩存储。

在每个数据页的页头(header) 中,有一个叫 “mod log”(修改日志) 的区域。它类似于预写日志(WAL),用于记录对这个页所做的修改,但这些修改尚未应用到压缩数据本身。

当我需要访问一个 Page 时, buffer pool 会像往常一样从磁盘读取它,以压缩的形式读入内存中, 如果需要 Write ,直接定位到它然后覆盖即可,不需要解压,在 mod log 中写一条记录,对这个 Tuple 的这个字段,用新值覆盖,当 mod log 写满时,我才需要解压整个页,应用所有修改,然后清空 mod log;
如果需要 Read ,如果刚刚被 Write 过,直接读取 mod log 进行了,但如果需要查看 Tuple 内部的实际值,就必须解压整个页,然后合并 mod log。

这是一种常见模式:系统会有一个快速写入区(delta store / mod log)

当 mod log 满了需要同步的时候,会产生阻塞

对于 OLAP 来说,更常见的是 Colunm-level ,因为一个列里有许多属性,它们都是重复,可以增加压缩的效率。

Encoding
压缩技术 (Compression Scheme)适用场景核心原理优点缺点 / 挑战典型系统 / 备注
行程编码 (RLE, Run-Length Encoding)列中存在大量连续重复值(如布尔标志、状态字段)将连续相同值编码为 (值, 起始偏移, 重复次数) 三元组极高压缩比(当重复值集中时);支持直接在压缩域聚合(如 COUNT)数据排序才能高效;无序数据压缩后反而更大ORC、Parquet;常与 Delta 编码组合使用
位打包 (Bit Packing)数值远小于其数据类型上限(如 age 用 32 位 int 存 0–127)仅存储有效低位,丢弃高位无意义的 0显著节省空间(如 32 位 → 8 位);保持固定长度,利于向量化插入超大值需补丁表(Patch Table);增加读取逻辑复杂度Amazon Redshift(MOSTLY8 编码)
位图编码 (Bitmap Encoding)低基数(Low-Cardinality) 分类列(如 gender, is_active为每个唯一值维护一个位图,第 i 位 = 1 表示第 i 行是该值查询极快(位运算如 AND/OR/POPCNT);压缩率高(1 行 = 1 位)高基数列(如 user_id)会导致空间爆炸ClickHouse、Doris;高基数时需 Roaring Bitmaps
Delta 编码 (Delta Encoding)平缓变化的有序数值(如时间戳、自增 ID、温度)存储与前一个值(或基准值)的差值(delta),而非绝对值差值通常很小,可用更少位表示;适合进一步压缩(如 RLE)要求数据有序;随机访问需从头累加Parquet、ORC;常用于时间序列数据
字典编码 (Dictionary Encoding)重复字符串或枚举值(如 country, product_name将唯一值映射为固定长度整数 ID,列中存 ID,另存字典(值 ↔ ID)高压缩率;整数比较快;支持顺序保留(order-preserving) 范围查询唯一值过多时字典变大;插入新值可能需重建最常用;ORC、Parquet、ClickHouse、Doris

关键设计原则

  • 组合使用:多种压缩技术可叠加(如 Dictionary + RLE、Delta + RLE),效果是乘法级的。
  • 顺序保留:字典按值排序,使压缩数据仍能高效支持 WHERE name > 'Alice' 等范围查询。
  • OLAP 友好:依赖“数据不可变 + 批量写入”假设,避免频繁更新压缩结构。

Embedded Tuple Ids

每列独立存储 + 附加 Tuple ID + 外部索引对齐

不能使用