分布式数据库

服务于写多读少、低延时、海量并发 OLTP 场景的,具备海量数据存储能力和高可靠性的关系型数据库

数据分片与复制

一致性

数据一致性关注的是单对象、单操作在多副本上的一致性,事务一致性则是关注多对象、多操作在单副本上的一致性,分布式数据库的一致性是数据一致性与事务一致性的融合

观察数据一致性的两个视角:

分布式事务

架构风格

PostgreSQL-XC

20221212171450

NewSQL

20221212171629

很多产品为了获得更好的计算性能,会尽量将更多计算下压到存储节点执行,更偏向于PostgreSQL-XC风格

全局时钟

分布式数据库的很多设计都和时间有关

TrueTime

时间源是 GPS 和原子钟,所以属于多时间源和物理时钟,同时它也采用了多点授时机制,依赖于特定硬件设备

HLC

每个节点会使用本地时钟作为参照,但不受到时钟回拨的影响,可以保证单调递增。本质上,HLC 还是 Lamport 逻辑时钟的变体,所以对于不同节点上没有调用关系的两个事件,是无法精确判断先后关系的

TSO

20221212172944

STP

stateDiagram    PrimarySTPServer: STP Server (Primary)    SecondarySTPServer1: STP Server (Secondary)    SecondarySTPServer2: STP Server (Secondary)    STPClient1: STP Client    STPClient2: STP Client    STPClient3: STP Client    PrimarySTPServer --> SecondarySTPServer1    PrimarySTPServer --> SecondarySTPServer2    PrimarySTPServer --> STPClient1    PrimarySTPServer --> STPClient2    PrimarySTPServer --> STPClient3

可用性

分布式数据库为了提升可用性:

OLTP

OLTP 场景下,一般使用行式存储,这是传统数据库的做法:

  1. 数据一般以行的形式进行处理与查询,一行数据通常被一起插入、一起查询、一起更新,这样,使用行式存储就能够以最少的磁盘读写代价处理一行业务数据的读写
  2. 关系型数据库需要支持事务,一行数据或者多行数据需要能够一起持久化成功或者一起失败,按行存储能够简化读写的 I/O,提升性能

OLAP

OLAP 场景下,一般使用列式存储,这也是分析型数据库的做法:

  1. 分析型数据库一般都是批量写入数据,同一列数据一起批量写入,这一列的数据类型相同,所以具备更高的压缩率,可以加快数据的读写速度
  2. OLAP 场景一般需要扫描大量数据行,但是基本是对一列或者多列进行统计分析、聚合等,列式存储可以只读取所需的列,从而避免加载整个行的数据,这大大减少了 I/O 操作,提高了查询效率

HTAP

为了解决 OLAP 的时效性问题,有两种思路:

  1. 以 [KAPPA](/数据技术/数据处理.html#Kappa) 架构为代表的准实时数据计算替代批处理
  2. 在 OLTP 内扩展,实现又能 TP 又能 AP,称之为 HTAP

存储设计:

  1. 融合性存储 PAX(Partition Attributes Across)方案
  2. 在原有行式存储的基础上,新增列式存储,通过数据同步的方式,将行数据同步为列数据进行分析,对于分析请求,每次都要确认本地的数据是否足够新,而后才会执行查询操作

分布式查询处理

由于分布式数据库引入了分区,在进行查询时就没那么方便了,执行器要根据 where 条件决定去哪里查,还有 join 也更复杂了,查询符合条件的数据要如何查代价才小,还有查询完成之后的汇总合并

存算分离下的查询优化:

存算分离下的 join 处理:

聚合计算加速:

火山模型:查询执行引擎可以优雅地将任意 Operator 组装在一起,而不需要考虑每个 Operator 的具体处理逻辑

select count(*) from store_sales where ss_item_sk = 1000;
sequenceDiagram  Aggegation ->> Project: Next()  Project ->> Filter: Next()  Project ->> TableScan: Next()  TableScan ->> DataBase: Next()  DataBase ->> TableScan: Tuple  TableScan ->> Filter: Tuple  Filter ->> Project: Tuple  Project ->> Aggegation: Tuple

火山模型的问题在于每次调用 Next 虚函数有一定开销,同时使用这种抽象的模式也没法充分利用 CPU 的循环展开、SIMD 等特性,有一些优化手段:

  1. 运算符融合:简化运算符嵌套关系,从而降低虚函数调用次数
  2. 向量化:每次不止取出一个 Tuple,而是一批 Tuple 进行计算,利用 SIMD 的能力提升速度
  3. 代码生成:通过编译器将 SQL 编译为简单循环,便于 CPU 能进行循环展开优化