---
title: 站在地球表面
taxonomy:
    category: docs
---

![](https://qn.lvwenhan.com/2023-02-08-16758712688201.jpg)
<center>图 12-1 一望无际的华北平原</center>

如图 12-1 所示就是地球表面的代表——一望无际的华北平原。

北京位于美丽的华北平原北端，生活着两千多万人，在巅峰的 2020 年双 11，天猫平台北京地区销售额为 216 亿，全国总额为 4982 亿，占比为 4.33%，略高于北京占全国 3.44% 的 GDP 比例，数据比较可信。使用这些数字我们可以计算得出，北京的两千多万人，给天猫贡献了`583000 * 0.0433 = 25243.9`笔/秒的并发。

虽然全国订单数看起来十分惊人，但是北京这一个地方的压力却只有 2.5 万单每秒，这个哪怕不用奇技淫巧，纯靠数据库硬抗，十万数据库 QPS 只用主从架构可能都能抗住。但是，系统能基于地理位置划分吗？系统不是必须全国一盘棋吗？不是的，可以划分。

下面我们讨论一下怎么划分。

### 基于地理位置对应用和数据库分区

为什么非要全国的用户访问同一个数据库呢？我们可以利用微服务思想对业务系统和数据进行拆分：北京的用户和上海的用户，理论上讲都可以只访问“本地天猫”。

接下来我们分析一下，在一个标准的电商业务中，哪些地方会让一个北京的用户和一个上海的用户发生联系。

1. 用户表自增 ID
2. 商品库存检查
3. 商家订单聚合
4. 离线数据分析

实际上，地理上被隔开的两个人，在系统内还真没什么机会需要相互查询对方的数据，这就是我们能基于地理位置对应用和数据库进行分区的现实原因。下面我们一一拆除这几个单点：

1. 用户表自增 ID：可以预分配 ID 段，也可以用算法保证，例如一奇一偶。
2. 商品库存检查：预分配库存，再异步刷新缓存。这个部分能玩出花，甚至有在客户端上提前下发抢购结果的骚操作，大家可以自己探索。
3. 商家订单聚合：简单地从两个地方各拉一次即可。
4. 离线数据分析：更不用说了，都离线了，本身也是要做很多数据同步和聚合的，不差这一点。

基于地理位置对应用和数据库进行划分，产生出两个“本地天猫”后，就需要我们老朋友 DNS 出来表演了。

### 进击的 DNS

域名当初可能是为了方便记忆而发明的，但是域名背后的 DNS 服务却几乎是最重要的互联网高并发基础设施：不同地区的人，对同一个域名进行访问，可以获得两个公网 ip，这样“本地天猫”就实现了。

#### 类 DNS 哲学思想：Consul 和 Kong

DNS 几乎完全放弃了一致性，但却实现了极高的可用性和分区容错性。其实，gossip 协议也是这个思想：让消息像病毒一样传播，能够实现最终一致性就行了，要啥自行车。

异曲同工的 Kong 集群的设计思想也十分令人震惊：所有节点每 5 秒从数据库读取最新的配置文件，然后，这些节点就成了一个行为完全一致的集群啦。“想那么多干什么，短时间内多个节点的行为不一致，就让它们不一致好了，5 秒之后不就一致了。”

### 高性能计算第一原则

! 高性能计算第一原则：数据离 CPU 越近，性能越高，容量越小

在我们熟悉的存储器山中，这是一个大家都理解的基本特性，而这个特性引申到分布式系统中，就是：一定不能让应用和数据库分离。

和 InnoDB 一样，很多时候其实是“局部性”这个我们宇宙的基本属性在帮助我们提升系统的性能，让应用和数据库分布在同一个地域，也是在利用局部性获得性能增益。

所以，让应用去隔壁区域的数据库读数据是要极力避免的——我们应该用 API 网关直接把请求发给隔壁区域的应用服务器，这显然是在今天这个异地网络传输速度接近光速的时代最佳的选择。

### 向 Clickhouse 学习高并发

Clickhouse 在亿级数据量面前丝毫不怵：MySQL、MongoDB、Hadoop，谁也没有老子快。为什么 Clickhouse 这么快呢？

#### 列式存储

首先，它将数据以列为单位组织起来，压缩后存入磁盘上一个又一个的 block，这些 block 就像 InnoDB 的 16KB 页一样，只是它更大（64KB~1MB）。这样，当我们 select 某个 column 的时候，Clickhouse 就能顺序读出磁盘上这个 column 下面所有行的数据。

#### 高效压缩

由于同一列通常具有高度的数据相似性，所以列式压缩的效果在大部分情况下都非常好，这也能算作“局部性”在数据库层面的一种应用。

#### 充分利用多 CPU 并行计算

除了列存储之外，每个 block 内，Clickhouse 还用“稀疏索引”的方式，将每一列的数据划分为了多个 granularity（颗粒度），然后给每个 granularity 分配一个 CPU 核心进行并行计算，并且它还利用 SSE4.2 指令集，利用 CPU 的 SIMD（Single Instruction Multiple Data）指令，在 CPU 寄存器层面进行并行操作。

#### 放弃内存缓存

这是 Clickhouse 整个架构中笔者最喜欢的部分。我们通过前面的章节可以看出，所有的分布式数据库，其本质都是在搞“内存缓存的数据同步”，Clickhouse 直接掀桌子——老子不要内存缓存了。由于所有数据都在磁盘上，而节点的 CPU 又直接和磁盘数据打交道，所以 Clickhouse 实现了真正的并行：增加 CPU 核心数就能提升系统容量，无论在不在同一台机器上都行，反正 CPU 相互之间完全不需要通信。这样，Clickhouse 通过堆核心数就能够实现系统容量的“近线性扩展”。

#### 太暴力了，我喜欢

我们可以学习这种思想，打造一个可以线性扩展的系统架构：只要不同地区的本地系统之间完全没有“数据实时同步”需求，那其实它们就是两个系统，就可以实现线性的性能提升。

### 理论无限容量

我们说过，关系型数据库的关系，指的就是两行数据之间的关系。现实世界中，位于异地甚至是异国的两个人之间，几乎是不会发生实时相互数据读取的。

站在地球表面来思考，你会发现人类社会和自然规律都是契合高并发“找出单点，进行拆分”哲学原理的：每一个人类居所，本质上都是散落在整个地球上的一个又一个点。因为这些点的存在，我们发明了国省市县乡村逐级政府，同级政府之间几乎没有相互通信。

> 将一个大系统拆成不需要实时相互通信的多个小系统，可以获得线性的性能提升。

当你的系统顶不住的时候，按照这个原理来拆就行了，绝对顶得住。别说区区一百万 QPS 了，服务全人类也做得到，毕竟全球 80 亿人都生活在如图 12-2 所示的球体上嘛，这个球体的半径也只有六千多公里，不算是很大的一个球。

![](https://qn.lvwenhan.com/2023-02-09-16759581159212.jpg)
<center>图 12-2 我们的地球</center>

价值上完了，我们最后再讨论一下高可用。
