一文读懂Vitess

概述

什么是Vitess

Vitess是一个为部署、扩展、管理大型集群的开源数据库解决方案。当前支持MySQL和MariaDB。在专用硬件上为高效的运行公有、私有云架构而设计。Vitess结合并扩展了许多重要的SQL功能和NoSQL数据库的可扩展性。Vitess可以解决下面的问题:

  1. 通过对SQL数据库进行分片来扩展SQ 数据库,同时将应用程序更改保持在最低限度。
  2. 支持从裸机部署到共有或私有云。
  3. 支持部署和管理大量的SQL实例。

Vitess支持JDBC和Go数据库驱动使用native查询协议。此外,它实现了几乎与任何其他语言兼容的MySQL server协议。

Vitess在YouTube使用超过5年,许多企业也考虑在生产环境Vitess。

特性

性能

  • Connection pooling: 将前端应用程序查询多路复用到 MySQL 连接池以优化性能。
  • Query de-duping: 对于重复的查询请求,重用结果。
  • Transaction Manager: 限制并发事务的数量并管理超时,优化整体吞吐量。

保护

  • Query rewriting and sanitization: 添加限制并避免非确定性更新。
  • Query blacklisting: 自定义规则,防止有问题的查询攻击数据库
  • Query killer: 中断长时间的查询
  • Table ACLs: 对数据表指定访问控制列表

监控

  • 性能分析工具帮助你监控、诊断、分析数据库性能

拓扑管理工具

  • Master管理工具
  • 基于Web的GUI
  • 支持管理多数据中心

分片

  • 几乎无缝的动态重新分片
  • 支持水平、垂直分片
  • 多种分片方案,支持自定义实现

架构

Vitess平台由许多服务器进程、命令行工具和Web工具组成,并由一致的元数据存储提供支持。

通过一系列流程可以实现一个完整的Vitess。比如,如果要从头开始构建服务,那么使用 Vitess 的第一步就是定义数据库拓扑。但是,如果需要扩展现有数据库,可能会从部署连接代理开始。

Vitess的工具或服务可以提供帮助,无论是从一开始快速开始就创建一个完整的数据或是一步一步从小处着手。往小处讲,vttablet的连接池和查询重新特性可以让你体验到它的好处;往大处说,Vitess的自动化工具可以提供快速创建一个数据库。

下面的图展示了Vitess的组件:
Vitess Runtime

支持的数据库

Vitess当前支持MySQL、Percona和MariaDB数据库。

MySQL 5.6-8.0

Vitess支持MySQL 5.6-8.0的关键特性,但是也有一些限制。Vitess同样支持Percona Server for MySQL 5.6-8.0版本。MySQL 5.6在2021年2月不再维护,建议使用MySQL 5.7及以上版本。

MariaDB 10.0-10.3

Vitess支持MariaDB 10.0-10.3的关键特性,暂不支持10.4版本。

可扩展性理念

许多方式都可以解决扩展性问题,下面看看Vitess是怎么做的:

小实例

一般数据库要分片,自然想到的就是适合一台物理机的大小的分片。通常做法是一台物理机一个实例。

Vitess 建议将实例分解为可管理的块(每个 MySQL 服务器 250GB),并且不回避在每个主机上运行多个实例。净资源使用量将大致相同。但是当 MySQL 实例较小时,可管理性大大提高。跟踪端口和分离 MySQL 实例的路径很复杂。然而,一旦跨越了这个障碍,其他一切都会变得更简单。

需要担心的是会有锁竞争,复制会更频繁;但中断对生产的影响变得更小,备份和恢复运行得更快,并且可以实现更多次要优势。例如,您可以对实例进行混洗以获得更好的机器或机架多样性,从而减少对生产中断的影响,并提高资源使用率。

通过复制获得持久性

传统数据存储软件在数据刷新到磁盘后立即将其视为持久数据。然而,这种方法在当今的商品硬件世界中是不切实际的。这种方法也不能解决灾难场景。

新的持久性方法是通过将数据复制到多台机器甚至地理位置来实现的。这种形式的持久性解决了设备故障和灾难的现代问题。

Vitess 中的许多工作流都是用这种方法构建的。例如,强烈建议开启半同步复制。这允许 Vitess 在 master 宕机时故障转移到新副本,而不会丢失数据。 Vitess 还建议避免恢复崩溃的数据库。而是从最近的备份中创建一个新的并让它赶上。

依靠复制还允许您放宽一些基于磁盘的持久性设置。例如,您可以关闭sync_binlog,这将大大减少对磁盘的IOPS数,从而提高有效吞吐量。

一致性模型

在将表分片或移动到不同的键空间之前,需要验证(或更改)应用程序,以便它可以容忍以下更改:

  • 跨分片读取可能彼此不一致。相反,分片决策也应尽量减少此类事件的发生,因为跨分片读取的成本更高。
  • best-effort mode模式下,跨分片事务可能会在中间失败并导致部分提交。您可以改为使用2PC mode事务,提供分布式原子保证。但是,选择此选项会使写入成本增加约 50%。

单个分片事务仍然保持 ACID。

如果可以容忍轻微陈旧数据的只读情况,则应将查询发送到用于 OLTP 的 REPLICA 片,以及用于 OLAP 工作负载的 RDONLY 片。这可以更轻松地扩展读流量,并使您能够在地理上分布它们。

这种权衡允许以过时或可能不一致的读取为代价获得更好的吞吐量,因为随着数据的变化(并且可能在不同分片上具有不同的延迟),读取可能落后于 MASTER 服务器。为了缓解这种情况,VTGate 服务器能够监控副本滞后,并且可以配置为避免副本滞后超过 X 秒的数据。

对于真正的快照,必须在事务中将查询发送到 master。对于read-after-write一致性,在没有事务的情况下从 master 读取就足够了。

总而言之,支持的各种等级的一致性:

  • __REPLICA/RDONLY read__:服务器可以在地理上扩展。本地读取速度很快,但可能会因副本滞后而过时。
  • __MASTER read__:每个分片只有一个Master。来自远程位置的读取将受到网络延迟和可靠性的影响,但数据将是最新的(read-after-write一致性)。隔离级别为 READ_COMMITTED。
  • __MASTER transactions__:它们表现出与MASTER read相同的属性。但是,对于单个分片,您可以获得 REPEATABLE_READ 一致性和 ACID 写入。对跨分片原子交易的支持正在进行中。

至于原子性,支持以下级别:

  • __SINGLE__:禁止多数据库事务。
  • __MULTI__:尽最而为的多数据库事务。
  • __TWOPC__:具有 2PC 提交的多数据库事务。

不支持多主(Master)

Vitess 不支持多主配置。它具有解决大多数通常由多主解决的用例的替代方法:

  • 可扩展性:在某些情况下,多Master会为您提供一些额外的运行方式。但是,由于这些语句最终必须应用于所有Master,因此这不是一个可持续的策略。 Vitess 通过分片解决了这个问题,分片可以无限扩展。
  • 高可用性:Vitess 与 Orchestrator 集成,能够在检测到故障后几秒钟内执行故障转移到新主服务器。这对于大多数应用程序来说通常是足够的。
  • 低延迟地理分布式写入:这是 Vitess 未解决的一种情况。当前的建议是避免长距离往返的延迟成本的写入。如果数据分布允许,您仍然可以选择基于地理亲和力进行分片。然后,您可以为不同的分片设置主节点,使其位于不同的地理位置。这样,大多数 master 写入仍然可以是本地的。

多cell

Vitess 旨在在多个数据中心/区域/单元中运行。在这里,我们将使用Cell来表示一组非常接近的服务器,并共享相同的区域可用性。

一个 cell 通常包含一组 tablet、一个 vtgate 池和使用 Vitess 集群的应用服务器。使用 Vitess 可以根据需要配置和启动所有组件:

  • 分片的主节点可以在任何 cell 中。如果需要cross-cell master 访问,可以通过 vtgate 轻松配置(通过将包含 master 的 cell 作为要监视的单元传递)。
  • master cell比read-only cell配置得更多的情况也不少见。这些具有master-capable的单元可能需要一个更多的副本来处理可能的故障转移,同时仍保持相同的副本服务容量。
  • 从一个cell中的主节点故障转移到不同cell中的主节点与本地故障转移没有区别。它对流量和延迟有影响,但如果应用流量也被重定向到新cell,最终结果是稳定的。
  • 也可以有一些分片与master在一个cell中,而其他一些分片与其他master在另一个cell中。vtgate 只会将流量路由到正确的位置,仅在远程访问时会产生额外的延迟成本。例如,在拥有美国的master数据库中创建美国用户记录,在欧洲master的数据库中创建欧洲用户记录很容易做到。副本无论如何都可以存在于每个cell中,并快速为副本流量提供服务。
  • 副本服务单元是减少用户可见延迟的一个很好的折衷方案:它们只包含副本服务器,并且master访问始终是远程完成的。如果应用程序的主要场景是读取,这非常有效。
  • 并非所有cell都需要 rdonly(或批处理)实例。只有运行批处理作业或 OLAP 作业的cell才真正需要。

注意 Vitess 首先使用本地cell数据,并且对于任何cell宕机都非常有弹性,Vitess的大多数进程都会优雅地处理这种情况。

Cloud Native

Vitess 非常适合云部署,因为它使数据库能够逐步增加容量。 运行 Vitess 的最简单方法是通过 Kubernetes。

Kubernetes 可以使用 Docker 容器编排系统,Vitess 可以感知 Kubernetes 云原生环境运行分布式数据库。

Kubernetes 处理计算集群中节点的调度,主动管理这些节点上的工作负载,并将包含应用程序的容器分组以便于管理和发现。这为 Vitess 在 YouTube 中运行的方式提供了一个类似的开源环境,这也是 Kubernetes 的前身。

历史

Vitess 创建于 2010 年,旨在解决 YouTube 团队面临的 MySQL 可扩展性挑战。本节简要总结了 Vitess 发展的一系列事件:

  1. YouTube 的 MySQL 数据库达到了高峰流量并将很快超过数据库服务能力的地步。为了暂时缓解这个问题,YouTube 创建了一个用于写入流量的主数据库和一个用于读取流量的副本数据库。
  2. 由于对视频的需求空前高涨,只读流量仍然使副本数据库过载。所以 YouTube 增加了更多的副本,再次提供了一个临时解决方案。
  3. 最终,写入流量变得太高,主数据库无法处理,需要 YouTube 对数据进行分片来处理传入流量。顺便说一句,如果数据库的整体大小对于单个 MySQL 实例来说变得太大,分片也将变得必要。
  4. YouTube 的应用层经过修改,以便在执行任何数据库操作之前,代码可以识别正确的数据库分片以接收特定查询。

Vitess 让 YouTube 从源代码中删除了该逻辑,在应用程序和数据库之间引入了一个代理来路由和管理数据库交互。从那时起,YouTube 将其用户群扩大了 50 多倍,大大提高了其访问页面、处理新上传视频等的能力。更重要的是,Vitess 是一个不断扩展的平台。

CNCF 是许多快速增长的开源项目的中立供应商。2018 年 2 月,技术监督委员会 (TOC) 投票接受 Vitess 作为 CNCF 孵化项目。 Vitess 成为 2019 年 11 月毕业的第八个 CNCF 项目,加入了 Kubernetes、Prometheus、Envoy、CoreDNS、containerd、Fluentd 和 Jaeger的 CNCF。

开始

通过Docker本地安装

本指南说明了如何通过 Docker 运行本地 Vitess 测试环境。 Vitess 环境与本地安装相同,除了 Docker 无需安装其他软件。

检出仓库

  • SSH:git clone git@github.com:vitessio/vitess.git,或者:

  • HTTP:git clone https://github.com/vitessio/vitess.git

    cd vitess

构建docker镜像

make docker_local

将创建一个名为vitess/local的docker镜像(vitess/local:lateest)

运行docker镜像

./docker/local/run.sh

这一步将安装 MySQL replication拓扑,以及 etcdvtctldvtgate 服务。

为方便起见,在 docker shell 中设置了别名。尝试使用以下 mysql 命令连接到各种 tablets:

  • mysql commerce
  • mysql commerce@master
  • mysql commerce@replica
  • mysql commerce@rdonly

你会发现 Vitess 运行在一个单keyspace、单分片的集群。

总结

在这个例子中,我们部署了一个名为 commerce 的未分片的keyspace。未分片的keyspace有一个名为 0 的分片。以下schema反映了由脚本创建的常见电子商务场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table product (
sku varbinary(128),
description varbinary(128),
price bigint,
primary key(sku)
);
create table customer (
customer_id bigint not null auto_increment,
email varbinary(128),
primary key(customer_id)
);
create table corder (
order_id bigint not null auto_increment,
customer_id bigint,
sku varbinary(128),
price bigint,
primary key(order_id)
);

上面的schema仅仅列出比较重要的属性:

  • product 表包含所有产品的产品信息。
  • customer 表有一个 auto_increment 的customer_id列,一个典型的customer表包含很多列,甚至还包含扩展表。
  • corder 表(应该命名为order,因为和SQL关键字冲突)有一个 auto_increment 的order_id列,同时有两个外键customer(customer_id)product(sku)

本地安装

通过Homebrew本地安装

Vitess Operator for Kubernetes

PlanetScale 为 Kubernetes 提供了一个 Vitess Operator,在 Apache 2.0 许可下发布。 以下步骤显示了如何开始使用 Minikube 安装:

前置条件

在开始之前,需要准备 Kubernetes环境:

  1. 安装 Minikube 并启动 Minikube,推荐使用1.14版本,方便跨公有云:

    minikube start –kubernetes-version=v1.14.10 –cpus=8 –memory=11000 –disk-size=50g

如果你没有一台有 11GB 内存的机器,也可以考虑使用 GKE 来代替。可以使用以下命令从 Cloud Shell 部署等效设置:

gcloud container clusters create vitess --cluster-version 1.14 --zone us-east1-b --num-nodes 5
  1. 安装 kubectl 并确保在PATH下。比如,Linux下:

    curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.9/bin/linux/amd64/kubectl

  2. 本地安装 MySQL 客户端。比如,Ubuntu下:

    apt install mysql-client

  3. 本地安装 vtctlclient :

如果熟悉 GO 开发环境,最简单的方式是:

go get vitess.io/vitess/go/cmd/vtctlclient

如果没有,可以下载最新的 Vitess 版本并从中提取 vtctlclient。

安装 Operator

切换到operator example目录下:

git clone git@github.com:vitessio/vitess.git
cd vitess/examples/operator

安装operator:

kubectl apply -f operator.yaml

初始化集群

在此目录中,您将看到一组 yaml 文件。每个文件名的第一位数字表示示例的阶段。接下来的两位数字表示执行它们的顺序。例如,101_initial_cluster.yaml 是第一阶段的第一个文件。现在将执行:

kubectl apply -f 101_initial_cluster.yaml

我们提供了一个示例 yaml,用于使用实验性 vtorc 组件启动 Vitess。您可以使用以下命令进行尝试:kubectl apply -f vtorc_example.yaml。一旦 vtorc 正式发布,示例将相应更新。

验证集群

可以使用 kubectl get pods 检查集群的状态。几分钟后,它应该显示所有 Pod 都处于运行状态:

$ kubectl get pods
NAME                                             READY   STATUS    RESTARTS   AGE
example-etcd-faf13de3-1                          1/1     Running   0          78s
example-etcd-faf13de3-2                          1/1     Running   0          78s
example-etcd-faf13de3-3                          1/1     Running   0          78s
example-vttablet-zone1-2469782763-bfadd780       3/3     Running   1          78s
example-vttablet-zone1-2548885007-46a852d0       3/3     Running   1          78s
example-zone1-vtctld-1d4dcad0-59d8498459-kwz6b   1/1     Running   2          78s
example-zone1-vtgate-bc6cde92-6bd99c6888-vwcj5   1/1     Running   2          78s
vitess-operator-8454d86687-4wfnc                 1/1     Running   0          2m29s

设置 Port-forward

port-forward 只会转发到特定的 pod。目前,由于应用/升级操作导致 pod 消失,kubectl 不会自动终止端口转发。需要手动重新配置端口转发。

为了方便使用,Vitess 提供了一个脚本来将 Kubernetes 端口转发到您的本地机器。此脚本还建议为 mysqlvtctlclient 设置别名:

./pf.sh &
alias vtctlclient="vtctlclient -server=localhost:15999"
alias mysql="mysql -h 127.0.0.1 -P 15306 -u user"

设置别名将 mysql 更改为始终连接到 Vitess 以进行当前会话。要还原这个,输入 unalias mysql && unalias vtctlclient 或关闭会话。

创建 Schema

载入初始化schema:

vtctlclient ApplySchema -sql="$(cat create_commerce_schema.sql)" commerce
vtctlclient ApplyVSchema -vschema="$(cat vschema_commerce_initial.json)" commerce

连接到集群

现在应该能够使用 MySQL 客户端连接到集群中的 VTGate 服务器:

~/vitess/examples/operator$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.9-Vitess MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+-----------+
| Databases |
+-----------+
| commerce  |
+-----------+
1 row in set (0.00 sec)

总结

在这个例子中,我们部署了一个名为 commerce 的未分片的keyspace。未分片的keyspace有一个名为 0 的分片。以下schema反映了由脚本创建的常见电子商务场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table product (
sku varbinary(128),
description varbinary(128),
price bigint,
primary key(sku)
);
create table customer (
customer_id bigint not null auto_increment,
email varbinary(128),
primary key(customer_id)
);
create table corder (
order_id bigint not null auto_increment,
customer_id bigint,
sku varbinary(128),
price bigint,
primary key(order_id)
);

上面的schema仅仅列出比较重要的属性:

  • product 表包含所有产品的产品信息。
  • customer 表有一个 auto_increment 的customer_id列,一个典型的customer表包含很多列,甚至还包含扩展表。
  • corder 表(应该命名为order,因为和SQL关键字冲突)有一个 auto_increment 的order_id列,同时有两个外键customer(customer_id)product(sku)

删除集群

kubectl delete -f 101_initial_cluster.yaml

Vttestserver Docker镜像

本指南涵盖使用 vttestserver docker 映像进行测试。这也是我们在 Vitess Framewok Testing 中用来测试的 docker 镜像。

获取docker镜像

第一步是获取docker镜像,有两种方式获取:

  1. 从vitessio/vitess仓库获取

检出仓库

  • SSH:git clone git@github.com:vitessio/vitess.git,或者:

  • HTTP:git clone https://github.com/vitessio/vitess.git

    cd vitess

构建docker镜像

make docker_vttestserver

这将创建 2 个名为 vitess/vttestserver:mysql57vitess/vttestserver:mysql80 的 docker 镜像。

  1. 从docker hub拉取
    或者,您可以从 docker hub 获取最新的 docker 镜像。在 shell 中,执行:

    docker pull vitess/vttestserver:mysql57
    docker pull vitess/vttestserver:mysql80

运行docker镜像

此时,您应该有一个名为 vitess/vttestserver:mysql57vitess/vttestserver:mysql80 的 docker 镜像。

环境变量

docker 镜像需要设置一些环境变量才能正常运行。下表列出了所有可用的环境变量及其用法。

Environment variable Required Use
KEYSPACES yes Specifies the names of the keyspaces to be created as a comma separated value.
NUM_SHARDS yes Specifies the number of shards in each keyspace. It is a comma separated value as well, read in conjunction with the KEYSPACES.
PORT yes The starting of the port addresses that vitess will use to register its components like vtgate, etc.
MYSQL_MAX_CONNECTIONS no Maximum number of connections that the MySQL instance will support. If unspecified, it defaults to 1000.
MYSQL_BIND_HOST no Which host to bind the MySQL listener to. If unspecified, it defaults to 127.0.0.1.
MYSQL_SERVER_VERSION no MySQL server version to advertise. If unspecified, it defaults to 8.0.21-vitess or 5.7.9-vitess according to the version of vttestserver run.
CHARSET no Default charset to use. If unspecified, it defaults to utf8mb4.
FOREIGN_KEY_MODE no This is to provide how to handle foreign key constraint in create/alter table. Valid values are: allow (default), disallow.
ENABLE_ONLINE_DDL no Allow users to submit, review and control Online DDL. Valid values are: true (default), false.
ENABLE_DIRECT_DDL no Allow users to submit direct DDL statements. Valid values are: true (default), false.

在docker环境变量可以通过 -e--env 指定。

从外面发生请求给 vttestserver

vtgate 在3个以上的 PORT 环境变量上侦听 MySQL 连接。即,如果您将 PORT 指定为 33574,则 vtgate 将在 33577 上监听连接,主机地址 MYSQL_BIND_HOST 默认为 localhost,但是这个端口将在 docker 容器端。要从 MySQL 客户端外部连接到 vtgate,还需要发布该端口并将 MYSQL_BIND_HOST 指定为 0.0.0.0,这可以通过 docker 的 -p--publish 标志来完成。例如:将 -p 33577:33577 添加到 docker run 命令会将容器的 33577 端口发布到本地的 33577 端口,就可用于连接到 vtgate。

示例

运行 docker 镜像的示例命令如下:

docker run --name=vttestserver -p 33577:33577 -e PORT=33574 -e KEYSPACES=test,unsharded -e NUM_SHARDS=2,1 -e MYSQL_MAX_CONNECTIONS=70000 -e MYSQL_BIND_HOST=0.0.0.0 --health-cmd="mysqladmin ping -h127.0.0.1 -P33577" --health-interval=5s --health-timeout=2s --health-retries=5 vitess/vttestserver:mysql57

现在,可以从 MySQL 客户端连接到 vtgate,如下所示:

mysql --host 127.0.0.1 --port 33577 --user "root"

这里有 2 个可以使用的键空间,test 有2个分片,unsharded 有1个分片。

概念

Cell

数据中心、可用区或计算资源组

cell是一组服务器和网络基础设施并置在一个区域中,并与其他单元中的故障隔离。它通常是完整的数据中心或数据中心的子集,有时称为区域或可用性区域。 Vitess 可以优雅地处理cell-level故障,例如当一个cell断网时。

Vitess 实现中的每个单元都有一个本地拓扑服务,该服务托管在该单元中。拓扑服务在其单元中包含有关 Vitess tablet的大部分信息。这使得一个单元能够被删除或重建。

Vitess 限制数据和元数据的跨单元访问。虽然具有将读取流量路由到单个单元格的能力可能很有用,但 Vitess 目前仅支持来自本地单元格的读取流量。必要时,写入将跨单元进行,写入该分片的主节点所在的任何位置。

Execution Plans

Vitess 在 VTGate 和 VTablet 层解析查询,以评估执行查询的最佳方法。这种评估称为查询计划,并产生查询执行计划。

执行计划取决于查询和关联的 VSchema。 Vitess 规划策略的基本目标之一是将尽可能多的工作下推到底层 MySQL 实例。当这不可能时,Vitess 将使用从多个来源收集输入并合并结果以生成正确查询结果的计划。

评估模型

一个执行计划由操作符组成,每个操作符执行一个特定的工作。运算符组合成一个树状结构,代表整体执行计划。该计划将每个运算符表示为树中的一个节点。每个运算符将零或更多行作为输入,并产生零或更多行作为输出。这意味着一个操作符的输出成为下一个操作符的输入。连接树中两个分支的运算符组合来自两个传入流的输入并产生单个输出。

执行计划的评估从树的叶节点开始。叶节点从 VTablet、拓扑服务中提取数据,并且在某些情况下还能够在本地评估表达式值。每个叶节点不会有来自其他算子的输入,并且将它们产生的任何节点通过管道传输到其父节点。然后,父节点将通过管道将节点传送到它们的父节点,一直到根节点。根节点产生查询的最终结果并将结果传递给用户。

观察执行计划

通过浏览 /queryz 端点,可以在 VTGate 级别观察缓存的执行计划。

从 Vitess 6 开始,还可以使用 EXPLAIN FORMAT=vitess <query> 观察单个语句计划。

Keyspace

键空间是一个逻辑数据库。如果你使用分片,一个键空间映射到多个 MySQL 数据库;如果您不使用分片,键空间将直接映射到 MySQL 数据库名称。在任何一种情况下,从应用程序的角度来看,键空间都显示为单个数据库。

从键空间读取数据就像从 MySQL 数据库读取数据。但是,根据读取操作的一致性要求,Vitess 可能会从主数据库或副本中获取数据。 通过将每个查询路由到适当的数据库,Vitess 允许您的代码像从单个 MySQL 数据库读取一样。

Keyspace ID

键空间 ID 是用于决定给定行所在的分片的值。基于范围的分片是指创建每个覆盖特定范围的键空间 ID 的分片。

使用此技术意味着您可以通过用两个或更多新分片替换、拆分给定分片,这些新分片组合在一起以覆盖键空间 ID 的原始范围,而无需移动其他分片中的任何记录。

键空间 ID 本身是使用数据中某些列的函数计算的,例如用户 ID。Vitess 允许您从各种函数(vindexes)中进行选择来执行此映射。这使您可以选择正确的方法来实现数据在分片之间的最佳分布。

MoveTables

MoveTables 是一种基于 VReplication 的新工作流。它使您能够在键空间之间重新定位表,从而在不停机的情况下重新定位物理 MySQL 实例。

识别候选表

建议将需要相互连接的表保留在同一键空间中,因此 MoveTables 操作的典型候选对象是一组逻辑上组合在一起或以其他方式隔离的表。

如果您有多组表作为候选,最有意义的移动可能取决于您的环境的具体情况。例如,一个更大的表将需要更多的时间来移动,但移动之后你能够利用额外的或更新的硬件,这些硬件在你需要执行额外的操作(如分片)之前有更多的空间。

同样,更新频率高的表也可能会增加移动时间。

对生产交通的影响

在内部,MoveTables 操作由表副本和对表所做的所有更改的订阅组成。Vitess 使用批处理来提高表复制和应用订阅更改的性能,更新率较低的表移动得更快。

在主动移动过程中,数据是从副本而不是主服务器复制的。这有助于确保最小的生产流量影响。

在 MoveTables 操作的 SwitchWrites 阶段,Vitess 可能暂时不可用。这种不可用性通常是几秒钟,取决于主服务器到副本的复制延迟。

Query Rewriting

Vitess 努力营造一种用户与单个数据库的单一连接的错觉。实际上,单个查询可能与多个数据库交互,也可能多个连接连接到同一数据库。在这里,我们将讨论 Vitess 的作用以及它对您的影响。

查询拆分

具有交叉分片连接的复杂查询可能需要首先从 vindex 查找表的 tablet 中获取信息。然后使用此信息查询两个不同的分片以获取更多数据,然后将传入的结果连接到用户接收的单个结果中。 MySQL 获取的查询通常只是原始查询的一部分,最终结果将在 vtgate 级别组装。

连接池

当 tablet 与 MySQL 执行查询时,它不会为每个用户使用专用连接,而是会在用户之间共享底层连接。这意味着在会话中存储任何状态都是不安全的,因为你不能确定它会继续在同一个连接上执行查询,你也不能确定这个连接以后是否会被其他用户使用。

用户定义变量

使用 MySQL 时,用户定义的变量在会话状态保持。可以使用 SET 为它们赋值:

SET @my_user_variable = 'foobar'

可以使用 SELECT 进行查询:

> SELECT @my_user_variable;
+-------------------+
| @my_user_variable |
+-------------------+
| foobar            |
+-------------------+

如果您针对 VTGate 执行这些查询,则第一个 SET 查询不会发送到 MySQL。而是在 VTGate 中进行计算,并且 VTGate 将为您保留此状态。第二个查询也没有发送下来。像这样的琐碎查询实际上完全在 VTGate 上执行。

如果我们尝试需要来自 MySQL 的数据的更复杂的查询,VTGate 将在发送之前重写查询。 如果我们要写这样的东西:

WHERE col = @my_user_variable

MySQL看到的是:

WHERE col = 'foobar'

这样,就不需要会话状态来计算 MySQL 中的查询。

服务器系统变量

用户可能还想更改 MySQL 公开的许多不同系统变量。Vitess 以四种不同方式之一处理系统变量:

  • No op。对于某些设置,Vitess 会默默地忽略该设置。这适用于在分片设置中没有多大意义的系统变量,并且不会改变 MySQL 的行为。
  • Check and fail if not already set。这些是不应更改的设置,但 Vitess 将允许 SET 语句尝试将变量设置为已经是的任何值。
  • Not supported。对于这些设置,尝试更改它们总是会导致错误。
  • Vitess aware。这些是改变 Vitess 行为的设置,不会发送到 MySQL
  • Reserved connection。对于某些设置,允许设置它们是有意义的,但这也意味着我们不能为此用户使用共享连接。这意味着代表该用户完成的每个连接都需要首先设置这些系统变量,然后保持连接专用。连接池对 Vitess 的性能很重要,保留的连接不能被池化,所以这不应该是在 Vitess 上运行应用程序的正常方式。只需确保将全局变量设置为应用程序将设置的相同值,Vitess 就可以使用连接池。

除此之外,Vitess 确保@@version 包含 MySQL 版本和 Vitess 版本,例如:5.7.9-vitess-10.0.0-SNAPSHOT。可以使用 vtgate 标志 -mysql_server_version 更改此值。

特殊功能

Vitess 可以处理一些特殊功能,而无需委托给 MySQL。

  • DATABASE() - 键空间名称和基础数据库名称不必相等。Vitess 将重写这些调用以使用文本字符串作为键空间名称。(这也适用于同义词 SCHEMA())
  • ROW_COUNT() 和 FOUND_ROWS() - 这些函数返回上次查询影响/返回的行数。因为这可能是在不同的连接上执行的,所以这些被重写为使用返回行数的文字值。
  • LAST_INSERT_ID() - 与 FOUND_ROWS() 非常相似,我们不能相信这些函数调用的池化连接,因此它们在命中 MySQL 之前会被重写。

Replication Graph

Replication Graph标识了主数据库与其各自副本之间的关系。在主故障转移期间,Replication Graph使 Vitess 能够将所有现有副本指向新指定的主数据库,以便复制可以继续。

Shard

shard 是 keyspace 的子集。一个键空间将始终包含一个或多个分片。一个分片通常包含一个 MySQL 主节点和潜在的多个 MySQL 副本。

分片中的每个 MySQL 实例都具有相同的数据(如果忽略任何复制延迟)。副本可以提供只读流量(具有最终一致性保证)、执行长时间运行的数据分析查询或执行管理任务(备份、恢复、差异等)。

未分片的键空间是只有一个分片的键空间。 Vitess 按照惯例将分片命名为 0(或有时 -)。分片时,一个键空间有 N 个不重叠数据的分片。一个键空间中的分片数量可以根据用例和负载特性而变化,一些 Vitess 用户在某些键空间中有数百个分片。

分片命名

分片名称具有以下特征:

  • 代表无符号整数空间中的一个范围,其中包括左边的数字,但不包括右边的数字。
  • 符号是十六进制的。
  • 它们左对齐,右填充零。
  • 一个 - 前缀意味着:任何小于右边值的意思。
  • 一个 - 后缀表示:任何大于或等于左边值的意思。
  • 普通 - 表示完整的键范围。

因此:-80 == 00-80 == 0000-8000 == 000000-800000 == 0000000000000000-8000000000000000

80-80-FF 不等价。为什么呢?

80-FF == 8000-FF00。因此 FFFF 将超出 80-FF 范围。

80- 表示:任何大于或等于 0x80

散列类型 vindex 产生一个无符号的 64 位整数作为输出。这意味着所有小于 0x8000000000000000 的整数都将落入分片 -80。任何设置了最高位的数字将 >= 0x8000000000000000,因此属于分片 80-

这种左对齐的方法允许您拥有任意长度的键空间 ID。因此最重要的位是左边的位。

例如,一个 md5 哈希产生 16 个字节。这也可以用作键空间 ID。

任意长度的 varbinary 也可以按原样映射到 keyspace id。这就是二进制 vindex 所做的。

重新分片

Vitess 支持重新分片,即在实时集群上更改分片的数量。这可以是将一个或多个分片拆分为更小的部分,或者将相邻的分片合并为更大的部分。

在重新分片期间,源分片中的数据被复制到目标分片中,允许赶上复制,然后与原始分片进行比较以确保数据完整性。然后将实时服务基础设施转移到目标分片,并删除源分片。

Tablet

一个tablet是一个mysqld进程和一个对应的vttablet进程的组合,通常运行在同一台机器上。每个tablet都被分配了一个tablet类型,它指定了它当前执行的角色。

查询通过 VTGate 服务器路由到tablet。

tablet类型

  • master - 一个副本tablet,恰好当前是其分片的 MySQL master。
  • replica - 有资格提升为主节点的 MySQL 副本。通常,这些副本作为候选者用于服务实时的、面向用户的请求(例如来自网站的前端)。
  • rdonly - 无法提升为 master 的 MySQL 副本。通常,这些用于后台处理作业,例如备份、将数据转储到其他系统、大量分析查询、MapReduce 和重新分片。
  • backup - 已在一致性快照处停止复制的tablet,因此它可以为其分片上传新备份。完成后,它将恢复复制并返回到以前的类型。
  • restore - 启动时没有数据的tablet,并且正在从最新备份中恢复自身。完成后,它将在备份的 GTID 位置开始复制,并成为 replica 或 rdonly。
  • drained - 由 Vitess 后台进程保留的tablet(例如用于重新分片的 rdonly tablet)。

Topology Service

也称为 TOPO 或 lock service

拓扑服务是一组运行在不同服务器上的后端进程。这些服务器存储了拓扑数据并提供分布式锁定服务。

Vitess 使用plug-in系统来支持存储拓扑数据的各种后端,前提是这些后端提供分布式、一致的键值存储。默认的拓扑服务插件是 etcd2。

拓扑服务的存在有几个原因:

  • 它使tablet能够作为一个集群在它们之间进行协调。
  • 它使 Vitess 能够发现tablet,因此它知道将查询路由到哪里。
  • 它存储集群中许多不同服务器需要的数据库管理员提供的 Vitess 配置,并且必须在服务器重新启动期间保持不变。

一个 Vitess 集群有一个全局拓扑服务,每个单元有一个本地拓扑服务。

全局拓扑

全局拓扑服务存储不经常更改的 Vitess 级别的数据。具体来说,它包含有关密钥空间和分片的数据以及每个分片的主tablet别名。

全局拓扑用于一些操作,包括 reparenting 和 resharding。按照设计,全局拓扑服务的使用并不多。

为了在任何单个单元发生故障时容灾,全局拓扑服务应该在多个单元中具有节点,以便在单元发生故障时维持仲裁。

本地拓扑

每个本地拓扑都包含与其自身单元相关的信息。具体来说,它包含有关单元格中tablet的数据、该单元的 keyspace graph 以及该单元的 replication graph。

本地拓扑服务必须可供 Vitess 使用以发现tablet并在tablet往返时调整路由。但是,在稳定状态下提供查询服务的关键路径中不会调用拓扑服务。这意味着在拓扑暂时不可用期间仍会提供查询服务。

VSchema

VSchema 允许您描述数据在键空间和分片中的组织方式。此信息用于路由查询,也用于重新分片操作。

对于 Keyspace,您可以指定它是否被分片。对于sharded keyspace,您可以为每个表指定 vindexes 列表。

Vitess 还支持序列生成器,可用于生成新的 id,其工作方式类似于 MySQL 自动增量列。 VSchema 允许您将table columns关联到sequence tables。如果没有为这样的列指定值,那么 VTGate 将使用sequence table为它生成一个新值。

VStream

VStream 是一种可通过 VTGate 访问的更改通知服务。 VStream 的目的是从 Vitess 集群的底层 MySQL 分片提供与 MySQL 二进制日志等效的信息。gRPC 客户端,包括 Vitess 组件,如 VTablets,可以订阅 VStream 以接收来自其他分片的更改事件。VStream从VTTablet实例上的一个或多个VStreamer实例拉取事件,后者又从底层MySQL实例的二进制日志拉取事件。这允许有效执行诸如 VReplication 之类的功能,其中订阅者可以从一个或多个 MySQL 实例分片的二进制日志中间接接收事件,然后将其应用于目标实例。用户可以利用 VStream 获取有关给定 Vitess 键空间、分片和位置的数据更改事件的详细信息。单个 VStream 还可以整合来自键空间中多个分片的更改事件,使其成为从 Vitess 数据存储向下游提供 CDC(Change Data Capture)过程的便捷工具。

作为参考,请参考下图:
VStream

注意:VStream 不同于 VStreamer。前者位于VTGate,后者位于VTTablet。

vtctl

vtctl 是一个命令行工具,用于管理 Vitess 集群。它可作为独立工具 (vtctl) 和客户端-服务器(vtctlclient 与 vtctld 结合使用)。建议使用客户端-服务器,因为它在远程使用客户端时提供了额外的安全层。

使用 vtctl,您可以识别主数据库和副本数据库、创建表、启动故障转移、执行重新分片操作等。

随着 vtctl 执行操作,拓扑服务会根据需要进行更新。其他 Vitess 服务器会观察这些变化并做出相应的反应。例如,如果您使用 vtctl 故障转移到新的 master 数据库,vtgate 会看到更改并将未来的写入操作定向到新的 master。

vtctld

vtctld 是一个 HTTP 服务器,可让您浏览存储在拓扑服务中的信息。它对于故障排除或获取服务器及其当前状态的概览信息很有用。

vtctld 还充当 vtctlclient 连接的服务器。

VTGate

VTGate 是一个轻量级的代理服务器,它可以将流量路由到正确的 VTTablet 服务器并将合并的结果返回给客户端。它同时使用 MySQL 协议和 Vitess gRPC 协议。因此,您的应用程序可以像连接 MySQL 服务器一样连接到 VTGate。

在将查询路由到适当的 VTablet 服务器时,VTGate 会考虑分片方案、所需的延迟以及表及其底层 MySQL 实例的可用性。

用户手册

参考

设计文档

FAQ