+++
title = "我们是怎样打造一款分布式数据库的"
weight = 4
chapter = true
+++

## 我们是怎样打造一款分布式数据库的

作者 | 张亮

关系型数据库在过去数十年的数据库领域一直占据着绝对主导的地位，它所带来的稳定性、安全性和易用性，成为了构建现代化系统的基石。随着的互联网高速发展，构架于单机系统的数据库已无法满足越来越高的并发请求和越来越大的数据存储需求，因此，分布式数据库被愈加广泛的采用。  
  
一直以来，数据库领域均由西方的科技公司和社区所主导。而今，越来越多的国产数据库解决方案以分布式为支点，逐渐在此领域有所建树。Apache ShardingSphere 是其中的一个分布式数据库解决方案，也是目前 Apache 软件基金会中唯一的数据库中间件。 

### 1 背景

全面兼容面向传统关系型数据库的 SQL 和事务，并且对分布式的天然友好，是分布式数据库解决方案的设计目标。它的核心功能主要集中在以下几点：

- 分布式存储: 数据存储不受单机磁盘容量限制，可通过增加数据服务器的数量提升存储能力；

- 计算存储分离: 计算节点无状态，可通过水平扩展增加算力。存储节点和计算节点能够分层优化；

- 分布式事务: 高性能、完全支持本地事务 ACID 原义的分布式事务处理引擎；

- 弹性伸缩：可以随时随地的在不影响现有应用的情况下，动态对数据存储节点扩容和缩容；

- 强一致多副本：自动将数据以强一致、高性能的方式复制至跨机房的多个副本，以保证数据的绝对安全；

- HTAP：采用同一套产品混合处理 OLTP 的事务型操作和 OLAP 的分析型操作。


分布式数据库的实现方案可以划分为进取型和稳定型。进取型实现方案是指开发全新架构的 NewSQL。这类产品以追求更高性能换取稳定性的缺失和运维经验的不足；稳定型的实现方案是指在现有数据库的基础上提供增量能力的中间件。这类产品则以牺牲部分性能以保证数据库的稳定性和运维经验的复用。

### 2 架构

Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈，它由 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar（计划中）这 3 款相互独立的产品组成。它们均提供标准化的数据分片、分布式事务和分布式治理等功能，可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。随着 Apache ShardingSphere 在查询优化器和分布式事务引擎的不断探索，它已渐渐地打破了实现方案的产品边界，向集进取型和稳定型于一身的平台级解决方案演进。

**Sharding-JDBC**

定位为轻量级 Java 框架，在 Java 的 JDBC 层提供的额外服务。它使用客户端直连数据库，以 jar 包形式提供服务，无需额外部署和依赖，可理解为增强版的 JDBC 驱动，完全兼容 JDBC 和各种 ORM 框架。

![](https://shardingsphere.apache.org/blog/img/database1.jpg)

**Sharding-Proxy**

定位为透明化的数据库代理端，提供封装了数据库二进制协议的服务端版本，用于完成对异构语言的支持。目前已提供 MySQL 和 PostgreSQL 版本，它可以使用任何兼容 MySQL 和 PostgreSQL 协议的访问客户端 (如：MySQL Command Client, MySQL Workbench, Navicat 等) 操作数据，对 DBA 更加友好。

![](https://shardingsphere.apache.org/blog/img/database2.jpg)

**Sharding-Sidecar（规划中）**

定位为 Kubernetes 的云原生数据库代理，以 Sidecar 的形式代理所有对数据库的访问。通过无中心、零侵入的方案提供与数据库交互的的啮合层，即 `Database Mesh`，又可称数据库网格。

![](https://shardingsphere.apache.org/blog/img/database3.jpg)

**计算存储分离的混合架构**

Sharding-JDBC 采用无中心化架构，适用于 Java 开发的高性能的轻量级 OLTP 应用；Sharding-Proxy 提供静态入口以及异构语言的支持，适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。

每种架构方案都有其各自的优缺点，下面表格对比了各种架构模型的在不同场景下的优劣：

![](https://shardingsphere.apache.org/blog/img/database4.jpg)

Apache ShardingSphere 是多接入端共同组成的生态圈。通过混合使用 Sharding-JDBC 和 Sharding-Proxy，并采用同一配置中心统一配置分片策略，能够灵活的搭建适用于各种场景的应用系统，使得架构师更加自由地调整适合与当前业务的最佳系统架构。

![](https://shardingsphere.apache.org/blog/img/database5.jpg)

Apache ShardingSphere 采用 Share Nothing 架构，它的 JDBC 和 Proxy 接入端均采用无状态设计。作为计算节点，Apache ShardingSphere 负责对获取的数据进行最终的计算汇总。由于本身并不存储数据，因此 Apache ShardingSphere 可以将计算下推至数据节点，以充分利用数据库自身的计算能力。Apache ShardingSphere 可以通过增加部署节点数量来增加计算能力；增加数据库节点数量来增加存储能力。

### 3 核心功能

数据分片、分布式事务、弹性伸缩和分布式治理是 Apache ShardingSphere 目前阶段的 4 个最核心功能。

#### 数据分片

分而治之是 Apache ShardingSphere 用于处理海量数据的方案。Apache ShardingSphere 通过数据分片方案，使数据库具备分布式存储能力。

它可以将 SQL 根据用户预置的分片算法自动路由至相应的数据节点，以达到操作多个数据库的目的。用户可以像使用单机数据库一样使用被 Apache ShardingSphere 所管理的多个数据库。目前支持 MySQL、PostgreSQL、Oracle、SQLServer 以及任何支持 SQL92 标准和 JDBC 标准协议的数据库。数据分片的内核流程见下图：

![](https://shardingsphere.apache.org/blog/img/database6.jpg)

主要流程如下：

1.  通过解析数据库协议包或 JDBC 驱动获取用户输入的 SQL 和参数；
    
2.  根据词法分析器和语法分析器将 SQL 解析为 AST（抽象语法树），并提取分片所需信息；
    
3.  根据用户预置算法匹配分片键，并计算路由路径；
    
4.  将 SQL 改写为分布式可执行 SQL；
    
5.  向各数据节点并行发送 SQL，执行引擎负责连接池与内存资源的平衡；
    
6.  根据 AST 执行流式或全量内存结果集归并计算；
    
7.  封装数据库协议包或 JDBC 结果集，并返回至客户端。
    

#### 分布式事务

事务是数据库系统的最核心功能。分布式的不确定和事务的复杂性，决定了分布式事务领域无法出现放之四海而皆准的方案。

面对百花齐放的现状，Apache ShardingSphere 提供高度开放的方案，采用标准接口统一整合开发者自主选择的第三方分布式事务框架，以满足各种场景的应用需求。除此之外，Apache ShardingSphere 还提供了全新的分布式事务解决方案 JDTX，来弥补现有方案的缺失。

标准化整合接口

Apache ShardingSphere 为本地事务、两阶段事务和柔性事务提供了统一的适配接口，并对接了大量的现有第三方的成熟解决方案。通过标准接口，开发者可以轻松的将其他整合方案融入到 Apache ShardingSphere 平台中。

![](https://shardingsphere.apache.org/blog/img/database7.jpg)

然而，融合大量第三方解决方案，却不能覆盖所有分布式事务需求分支。各种解决方案均有各自的适合及不适合场景。各解决方案间是互斥的，无法将它们的优点叠加使用。针对于当前最常见 2PC（两阶段提交）和柔性事务，分别存在着以下的优势和不足：

- 两阶段提交：基于 XA 协议实现的两阶段分布式事务对业务侵入很小。它最大的优势是对使用方透明，开发者可以像本地事务一样使用基于 XA 协议的分布式事务。XA 协议能够严格保障事务 ACID 特性，但这也是一把双刃剑。事务执行在过程中需要将所需资源全部锁定，它更加适用于执行时间确定的短事务。对于长事务来说，整个事务进行期间对资源的独占，将导致对热点数据依赖的业务系统并发性能衰退明显。因此，在高并发的性能至上场景中，基于 XA 协议两阶段提交类型的分布式事务并不是最佳选择。
    
- 柔性事务：如果将实现了 ACID 的事务要素的事务称为刚性事务的话，那么基于 BASE 事务要素的事务则称为柔性事务。BASE 是基本可用、柔性状态和最终一致性这 3 个要素的缩写。在 ACID 事务中对一致性和隔离性的要求很高，在事务执行过程中，必须将所有的资源占用。柔性事务的理念则是通过业务逻辑将互斥锁操作从资源层面上移至业务层面。通过放宽对强一致性和隔离性的要求，只要求当整个事务最终结束的时候，数据是一致的。而在事务执行期间，任何读取操作得到的数据都有可能被改变。这种弱一致性的设计可以用来换取系统吞吐量的提升。
    

基于 ACID 的两阶段事务和基于 BASE 的最终一致性事务都不是银弹，可通过下表详细对比它们之间的区别。

![](https://shardingsphere.apache.org/blog/img/database8.jpg)

缺乏并发度保障的两阶段事务不能称之为完善的分布式事务解决方案；而缺乏对 ACID 原义支持的柔性事务都甚至不能称之为数据库事务，它更适合服务层的事务处理。

放眼当前，实难找到无需权衡即可放之四海而皆准的分布式事务解决方案。

新一代分布式事务中间件 JDTX

JDTX 是京东数科自研的分布式事务中间件，尚未开源。它的设计目标是强一致（支持 ACID 的事务原义）、高性能（不低于本地事务性能）、1PC（完全摒弃两阶段提交和两阶段锁）的完全分布式事务中间件，目前可用于关系型数据库。它采用完全开放 SPI 的设计方式，提供与 NoSQL 对接的可能，未来能够将多元异构数据维持在同一事务中。

JDTX 通过完全自研的事务处理引擎，将 SQL 操作的事务中数据转化为 KV（键值对），并在其基础上实现了 MVCC（多版本快照）的事务可见性引擎，以及与数据库设计理念类似的 WAL（预写日志系统）存储引擎。可以通过下面的架构图来了解一下 JDTX 的构成：

![](https://shardingsphere.apache.org/blog/img/database9.jpg)

它的设计特点是将在事务中的数据（称之为活跃数据）和不在事务中的数据（称之为落盘数据）分离。活跃数据在落盘至 WAL 之后，再以 KV 的形态保存至 MVCC 内存引擎中。落盘数据则是通过异步刷盘的方式，将 WAL 中的 REDO 日志，以流量可控的方式同步至最终的存储介质中（如：关系型数据库）。事务内存查询引擎负责使用 SQL 从 KV 格式的活跃数据中检索到相关数据，与落盘数据合并，并根据事务隔离级别获取符合当前事务可见的数据版本。

JDTX 以全新的架构重新诠释了数据库的事务模型，主要亮点有：

**1、内化分布式事务为本地事务**

JDTX 的 MVCC 引擎是一个集中式缓存。它能够将两阶段提交内化至一阶段提交，以维持单一节点中数据的原子性和一致性，即将分布式事务的范畴缩减到本地事务的范畴。JDTX 通过保证所有对事务数据的访问都通过 MVCC 引擎的活跃数据 + 最终数据端的落盘数据的合并的方式，以保证事务数据的原子性和一致性。

**2、无损的事务隔离机制**

以 MVCC 的方式实现事务隔离性。目前完整的支持 4 种标准隔离级别中的读已提交和可重复读，已经可以满足绝大部分需求。

**3、高性能**

采用将活跃数据异步刷盘至存储介质的方式，极大地提高了数据写入的性能上限。它的性能瓶颈从数据库写入耗时转移到了落盘至 WAL 和 MVCC 引擎的耗时。

与数据库的 WAL 系统类似，JDTX 的 WAL 也采用顺序日志追加的方式，因此可以简单的理解为 JDTX 的 WAL 耗时 = 数据库系统的 WAL 耗时。而 MVCC 引擎则采用 KV 数据结构，其写入耗时小于维护 BTree 索引的数据库。因此，JDTX 的数据更新性能的上限甚至可以高于不开启事务。

**4、高可用**

WAL 和 MVCC 引擎均可以通过主备 + 分片的方式维持高可用和水平扩展的能力。在 MVCC 引擎完全不可用的情况下，可通过恢复模式将 WAL 中的数据同步至数据库，以保证数据的完整性。

**5、跨多元数据库事务**

将事务活跃数据和落盘数据分离的设计方案，使其落盘数据存储端无任何限制。由于事务活跃数据通过异步的落盘执行器存储至后端存储介质，因此后端是否为同构数据库，其实并无影响。使用 JDTX 能够保证跨多元存储端（如：MySQL、PostgreSQL，甚至是 MongoDB、Redis 等 NoSQL）的分布式事务维持在同一事务语义之中。

通过 Apache ShardingSphere 提供的分布式事务统一适配接口，可以将 JDTX 像其他第三方分布式事务解决方案一样轻松地融入 Apache ShardingSphere 生态，将数据分片与分布式事务无缝结合，使它们具备组成分布式数据库基础设施的能力。架设在产品最前端的 Apache ShardingSphere 用于 SQL 解析、数据库协议和数据分片；位于中层的 JDTX 用于通过 KV 和 MVCC 的方式处理事务活跃数据；最底层的数据库则仅作为最终的数据存储端。下图是 ShardingSphere + JDTX 的架构图。

![](https://shardingsphere.apache.org/blog/img/database10.jpg)

可以说，JDTX 的存在，使得 Apache ShardingSphere 打破了稳定型的数据库中间件的定位，在保持稳定的同时，逐步向进取型 NewSQL 发展。

#### 弹性伸缩

与无状态的服务化应用不同，数据节点中均持有不可丢失的重要用户数据。当数据节点的容量不足以承担快速增长的业务时，数据节点的扩容则在所难免。根据用户预置的分片策略不同，扩容的策略也会随之不同。

弹性伸缩可以让被 Apache ShardingSphere 所管理的数据库在不停止对外服务的情况下进行扩容和缩容。弹性伸缩分为弹性迁移和范围扩容两个组件，目前它们均在孵化中。

弹性迁移

数据迁移是面向用户定制分片策略的标准扩缩容方案。在迁移过程中，需要准备两套数据节点。原数据节点在继续提供服务的同时，将数据以存量和增量的方式，分别写入新数据节点。整个迁移过程无需停止对外服务，可以平顺的过渡新旧数据节点。Apache ShardingSphere 还将提供工作流界面，让迁移过程完全自主可控。弹性迁移的架构图如下：

![](https://shardingsphere.apache.org/blog/img/database11.jpg)

具体流程如下：

1.  通过配置中心修改数据分片配置，以触发迁移流程。
    
2.  记录当前迁移数据开启前的位点之后，开启历史迁移作业，分批迁移全量数据。
    
3.  开启 Binlog 订阅作业，迁移位点之后的增量数据。
    
4.  根据采样率设置比对数据。
    
5.  设置原数据源只读，确保实时数据迁移完成。
    
6.  将应用连接切换至新数据源。
    
7.  旧数据源下线。
    

迁移时长根据数据量的大小可能是几分钟至几周不等。迁移过程中可以随时回退或重新迁移。整个迁移流程完全自主可控，降低迁移过程中的风险；并且通过自动化工具完全屏蔽人工操作，避免繁琐的操作带来的巨大工作量。

范围扩容

如果弹性迁移称之为硬伸缩的话，那么范围扩容则称之为软伸缩。Apache ShardingSphere 的范围扩容不涉及内核改造，也无需迁移数据，它只需通过优化范围分片策略，即可达到自动扩（缩）容的目标。使用范围扩容，用户无需感知分片策略和分片键等分库分表方案中必要概念，让 Apache ShardingSphere 更加接近一体化的分布式数据库。

使用范围扩容的用户，只需向 Apache ShardingSphere 提供数据库资源池。容量巡检器会在表容量到达阈值时，从资源池中依次寻找下一个数据节点，并在新数据节点创建新表之后，修改分片策略的范围元数据。当资源池中没有新数据节点后，Apache ShardingSphere 会按照同样的顺序，在已经创建过表的数据库中增加新表。当表数据大量删除时，之前的数据节点的数据将不再紧凑，垃圾回收器会定时压缩表范围，以释放更多的碎片空间。范围扩容的结构如下：

![](https://shardingsphere.apache.org/blog/img/database12.jpg)

Apache ShardingSphere 为不同的应用场景，提供了更加灵活的弹性伸缩策略。目前仍在孵化中的两个弹性伸缩相关的项目，也力争尽快提供试用版本。

#### 分布式治理

治理模块的设计目标是为了更好管理和使用分布式数据库。

数据库治理

与所有的分布式系统的设计理念一脉相承，分而治之同样是分布式数据库的指导方针。数据库治理能力的存在，能够让管理成本不随着数据库实例数目的增长而提升。

配置动态化

Apache ShardingSphere 使用配置中心来管理配置，可以在配置修改后的极短时间内传播到所有的接入端实例。配置中心采用开放的 SPI 方式，能够充分利用配置中心自身的能力，如配置多版本变更等。

高可用

Apache ShardingSphere 使用注册中心管理接入端实例和数据库实例的运行状态。注册中心同样采用配置中心的开放 SPI 方式。部分注册中心的实现可以涵盖配置中心的能力，因此用户可以叠加使用注册中心和配置中心的能力。

Apache ShardingSphere 提供了数据库实例禁用和接入端熔断的能力，分别用于处理数据库实例不可用和接入端被大流量冲击的场景。

Apache ShardingSphere 目前正在孵化高可用的 SPI，让用户能够复用数据库自身提供的高可用方案。目前正在对接 MySQL 的 MGR 高可用方案。Apache ShardingSphere 可以通过自动探知 MGR 的选举变化并快速传播至所有的应用实例。

可观察性

大量数据库和接入端实例，使得 DBA 和运维人员无法迅速感知当前系统的状况。Apache ShardingSphere 通过对 OpenTracing 协议的实现，将监控数据发送至实现其协议的第三方 APM 系统中；除此之外，Apache ShardingSphere 还提供了 Apache SkyWalking 的自动探针，可以让使用它作为可观察性产品的用户直接观测到 Apache ShardingSphere 的性能、调用链关系以及系统的整体拓扑图。

数据治理

得益于 Apache ShardingSphere 对 SQL 灵活的处理能力和对数据库协议的高度兼容性，数据相关的治理能力也被很方便的加入到了产品生态中。

脱敏

Apache ShardingSphere 可以让用户在无需修改代码的情况下，自动将指定的数据列加密后存储至数据库，并在应用程序获取数据时解密，以保证数据的安全。当数据库的数据被不慎泄露时，敏感数据信息由于被完全加密，从而不会造成更大的安全隐患。

影子库表

Apache ShardingSphere 可以在系统进行全链路压测时，自动将用户标记的数据路由至影子库（表）。影子库（表）压测功能可以让线上压测成为常态，用户无需在关心压测数据的清理。此项功能也正在高速的孵化中。

### 4 线路规划

如您所见，Apache ShardingSphere 正处于高速发展的轨道中，越来越多与“分库分表”这条曾经主线并无强关联的功能被加入其中。但这些产品功能却并不突兀，它们反而能助力 Apache ShardingSphere 成为更加多元化的分布式数据库解决方案。Apache ShardingSphere 未来的线路主要会集中在以下几点。

#### 可插拔平台

越来越多的零散功能需要进一步梳理。Apache ShardingSphere 现有的架构还不足以完全吸收如此广泛的产品功能。弹性化的功能可插拔平台是 Apache ShardingSphere 未来架构的调整方向。

可插拔平台会将 Apache ShardingSphere 从技术和功能两方面完全拆开。Apache ShardingSphere 的全景图如下所示：

![](https://shardingsphere.apache.org/blog/img/database13.jpg)

Apache ShardingSphere 会根据技术架构横向分为 4 层，分别是接入层、SQL 解析层、内核处理层和存储访问层；并且将功能以可插拔的形态融入至 4 层架构中。

Apache ShardingSphere 对数据库类型的支持将完全开放，除了关系型数据库，NoSQL 也会完全开放支持，数据库方言互不影响，完全解耦。在功能方面，Apache ShardingSphere 采用叠加式架构模型，使各个功能可以灵活的组合使用。每个功能模块只需关注自身的核心功能，由 Apache ShardingSphere 架构负责功能的叠加组合使用。即使没有任何功能，Apache ShardingSphere 也可以作为一个空白的接入端直接启动，为开发者的定制化开发提供脚手架以及 SQL 解析等基础设施。融入 Apache ShardingSphere 生态的数据库，将直接获得平台提供的所有基础能力；在 Apache ShardingSphere 平台上开发的功能，也将直接获得已接入平台的数据库类型的全部支持。数据库类型和功能类型将以相乘的方式排列组合，基础设施 + 乐高的组合，将为 Apache ShardingSphere 提供各种想象和提升的空间。

#### 查询引擎

目前 Apache ShardingSphere 只是将 SQL 通过正确的路由和改写，分发至相应的数据库以操作数据。计算下发能够充分利用的数据库的查询引擎，但无法有效的支持复杂关联查询和子查询。基于关系代数实现的 SQL on KV 查询引擎随着 JDTX 的开发日臻成熟，将其积累的经验反哺于 SQL 查询引擎，能够让 Apache ShardingSphere 更好的支持子查询和跨库关联查询等复杂查询。

#### 强一致多副本

分布式数据库所需要的多数据副本能力目前的 Apache ShardingSphere 还未具备。未来 Apache ShardingSphere 将提供基于 Raft 的强一致多副本写入能力。

#### Database Mesh

上文中提到的 Sharding-Sidecar 接入端，是 Apache ShardingSphere 未来的第三个接入端形态，旨在更好的与 Kubernetes 配合，打造云原生数据库。

Database Mesh 的关注重点在于如何将分布式的数据访问应用与数据库有机串联起来，它更加关注的是交互，是将杂乱无章的应用与数据库之间的交互进行有效地梳理。使用 Database Mesh，访问数据库的应用和数据库终将形成一个巨大的网格体系，应用和数据库只需在网格体系中对号入座即可，它们都是被啮合层所治理的对象。

#### Data Federation

支持更多的数据库类型之后，Apache ShardingSphere 会将工作重点放在多元异构的数据库类型的统一查询引擎之上。除此之外，Apache ShardingSphere 还将配合 JDTX，将更加多元的数据存储介质纳入到同一事务中。

### 5 开源与社区

Apache ShardingSphere 在 2016 年 1 月 17 日在 GitHub 平台首次开源，开源项目的初始名称是 Sharding-JDBC。在 2018 年 11 月 10 日，ShardingSphere 改名并正式进入 Apache 软件基金会的孵化器。

在开源至今走过的 4 年里程中，Apache ShardingSphere 的架构模型在不断的演进的同时，整体产品的功能范围也在急速地扩张。它从开源之初的分库分表 Java 开发框架，逐渐演化为分布式数据库解决方案。

随着 Apache ShardingSphere 的生态圈的扩张，由少数开发者掌控项目的状态早已被打破。目前的 Apache ShardingSphere 有近百名贡献者，以及近 20 名核心提交者，他们共同打造着这个遵循着 Apache Way 的社区。Apache ShardingSphere 是一个标准的 Apache 软件基金会开源项目，并不受某一商业公司或某几位核心开发者掌控。

目前有超过 100 家公司明确的声明他们在使用 Apache ShardingSphere，读者可以从官方网站找到采用公司的用户墙。

随着社区的成熟，Apache ShardingSphere 的成长也越来越迅速。我们诚邀感兴趣的开发者一同参与到 Apache ShardingSphere 社区中，完善不断扩大的生态。

**项目地址：**

https://github.com/apache/incubator-shardingsphere

### 6 作者介绍

张亮，京东数科数据研发负责人，Apache ShardingSphere 发起人 & PPMC，JDTX 负责人。  
  
热爱开源，主导开源项目 ShardingSphere(原名 Sharding-JDBC) 和 Elastic-Job。擅长以 java 为主分布式架构，推崇优雅代码，对如何写出具有展现力的代码有较多研究。  
  
目前主要精力投入在将 ShardingSphere 和 JDTX 打造为业界一流的金融级数据解决方案之上。ShardingSphere 已经进入 Apache 孵化器，是京东集团首个进入 Apache 基金会的开源项目，也是 Apache 基金会首个分布式数据库中间件。
