Vitess
Vitess 是一种 MySQL 水平扩展中间件,可用于部署、扩展和管理大型 MySQL 实例集群,用 Go 语言实现。
Vitess 源于 YouTube, 从 2011 年起已为 YouTube 管理了几千个 MySQL 结点。
Vitess 是云原生基金会 CNCF 毕业项目。
官网
https://vitess.io/
官网不稳定,出现过半个月都打不开的情况,打不开时可直接在官网的 GitHub 上看文档
https://github.com/vitessio/website
vitessio / vitess
https://github.com/vitessio/vitess
Vitess基础
架构图
Vitess架构图
Vitess架构图
组件
Topology(存储元数组)
拓扑服务(Topology)是一个元数据存储,用于存储 Vitess 集群的分片副本等配置信息,此外,在 master 选举,分布式锁中也会用到。对拓扑服务的要求是满足数据一致性的前提下尽量提高可用性。可选的拓扑服务器包括 etcd, ZooKeeper, consul.
可通过 vtctl (命令行) 和 vtctld (web) 查看拓扑信息.
在 Kubernetes 中,数据存储是 etcd.
Topology Service
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/topology-service.md
VTTablet(代理MySQL实例)
VTTablet 是一个位于 MySQL 数据库前面的代理服务器。 Vitess 实现中每个 MySQL 实例前面都有一个 VTTablet 进程。
VTTablet 是 Vitess 的最小工作单元,扮演一种 边车(sidecar) 进程的角色。
VTTablet 可以分为 master, replica, rdonly 等角色。
MySQL 进程和 VTTablet 进程组合在一起叫做 Tablet, 每个 Tablet 都有个类型 type, type 指定 Tablet 在集群中扮演的角色。
master
replica 类型的 tablet 被选举为他所在 shards 的 master 后成为 master 类型的 tablet.replica
replica 类型的 tablet 有资格被选举为 master. replica 类型的 tablet 会处理用户请求。rdonly
rdonly 类型的 tablet 不会被选举为 master. rdonly 类型的 tablet 通常用于 OLAP, 数据备份等。
执行的任务试图最大化吞吐量,同时保护 MySQL 不受有害查询的影响。它的特性包括连接池、查询重写和重用重复数据。此外,vtTablet 执行 vtcl 启动的管理任务,并提供用于过滤复制和数据导出的流式服务。
通过在 MySQL 数据库前运行 vttablet 并更改您的应用程序以使用 Vitess 客户端而不是 MySQL 驱动程序,您的应用程序将受益于 vttablet 的连接池,查询重写和重用数据集等功能。
VTGate(入口网关/请求分发与合并)
VTGate 是一个轻型代理服务器,它接收客户端请求,将流量路由到正确的vttablet,并将合并的结果返回给客户端。应用程序向vtgate发起查询。客户端使用起来非常简单,它只需要能够找到vtgate实例就能使vitess。
为了路由查询,vtgate综合考虑了分片方案、数据延迟以及vttablet及其对应底层MySQL实例的可用性。
VTGate 支持 MySQL 协议和 grpc 协议,可以直接将 VTGate 当做 MySQL 服务器使用。
vtctl/vtctld(命令行工具/Web界面)
vtctld 是一个 HTTP 服务器,允许您浏览存储在 Topology 中的信息。它对于故障排除或获取服务器及其当前状态的高层概观非常有用。
vtctl 是一个用于管理 Vitess 集群的命令行工具。它允许用户或应用程序轻松地与 Vitess 实现交互。
使用 vtctl 您可以识别主数据库和副本数据库,创建表,启动故障转移,执行分片(和重新分片)操作等。
当 vtctl 执行操作时,它会根据需要更新 Topology 中的元数据。其他 Vitess 服务器会观察这些变化并做出相应的反应。例如,如果使用 vtctl 故障转移到新的主数据库,则 vtgate 会查看更改并将将写入流量切到新主服务器。
概念
Cell 区域
Cell 指的是地理位置隔离数据中心(Data Center, DC), 或者叫做 区域 Zone
每个 Cell 都有个本地的拓扑服务.
Vitess 会限制跨 Cell 的数据流量。
Keyspace 键空间(数据库)
键空间(Keyspace) 是一种逻辑数据库,类似 Cassandra 中的同名概念,相当于 MySQL 中的 Database 概念。对于分片键空间(sharded), 一个键空间映射到多个 MySQL 数据库上;对于未分片键空间(unsharded), 键空间直接映射到单个 MySQL 数据库。Keyspace 可能包含一个或多个 Shards 分片,每个 Shard 包含一个或多个 Replica 副本,其中一个 Replica 会被选举为 master.
从应用的角度看,键空间就是一个单独的数据库,从键空间读数据就像从 MySQL 数据库读数据一样。
根据读操作的不同一致性要求,Vitess 可能从 master 节点读数据,也可能从 replica 副本上读取数据。
Sharding
Sharding 是一种对数据库进行水平分区的方法。
Vitess 中的键空间 Keyspace 可分为 分片的(sharded) 和 未分片的(unsharded).
未分片的键空间直接映射到单个 MySQL 数据库上,分片键空间中的行数据会被拆分到多个相同 Schema 的 MySQL 数据库上。
例如,user 键空间分为两个 shards, 则每个 shard 大约包含一半的数据量。
同一 shard 中的 master 处理写请求,replica 处理只读、批处理请求。只要 replica 和 master 之间不存在延迟(lag),同一 shard 的多个实例数据应该是一致的。
VSchema
VSchema 是 Vitess 对键空间 keyspace 和 分片 shards 的一种抽象,包含键空间 keyspace 的分片副本信息、表的 sharding 字段等信息。是 VTGate 做 SQL 路由的依据。
VSchema
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/vschema.md
Vindexes
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/vindexes.md
特性
Sharding算法
Vitess 使用 key range sharding 算法,和 TiDB 一样。
key range sharding: 一个 shard 过大后需要再次拆分时,只需要拆为更小的 range, 不需要数据迁移。
hash sharding: resharding 时需要 rehash,代价较高。
key range 包含一个起始值 startValue 和结束值 endValue, 落入 [startValue, endValue)
左闭右开区间范围内的 key值 和此 key range 相匹配。
每个 keyspace 中,必须有一个 shard 包含一个空的起始值 startValue(即最小值,所有其他值都大于它),必须有一个 shard 包含空的结束值 endValue(即最大值,所有其他值都小于它),这两个 shard 可能是同一个。
Vitess 将 key 转化为左对齐二进制字符串,再和 key range 匹配。[ , 0x80)
表示 key range 前半部分。[0x80, )
表示 key range 的后半部分。
Vitess 直接将 key range 添加到 shard 的命名上,例如
key range 是 [ , 0x80)
的 shard 命名为 -80
key range 是 [0x40, 0x80)
的 shard 命名为 40-80
resharding
Vitess 支持根据新的分片、副本配置进行 resharding, resharding 过程中,旧的 shards 持续处理读写请求,新 shards 进行数据拷贝和验证。当新 shards 准备好时,通过几秒钟的只读时间即可完成 shards 切换。
Sharding
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/sharding.md
Vindexes
Vindexes
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/vindexes.md
Vindex 将表的列值映射到 keyspace ID
. 由于每个 shard 覆盖一段 keyspace ID
范围,通过这个映射可以判定每行属于哪个 shard.
Sharding Key 是 NoSQL 数据库中引入的概念,它是基于 NoSQL 数据库中只通过 Key 来访问数据这一事实。但对于关系数据库来说,只有一个 Sharding Key 显然是不够的。
Vindexes 的优势:
1、一个表可以有多个 Vindexes
2、Vindexes 可以是不唯一的,这允许一个列值映射到多个 keyspace ID 上。
3、Vindexes 可以是简单的函数
4、Vindexes 可在多个表之间共享
5、可自定义 Vindexes
主键索引(Primary Vindex)
Vitess 中表的 Primary Vindex 类似关系数据库中的主键。
所有分片表必须定义 Primary Vindex. Primary Vindex 必须是唯一的,这里的唯一性指的是:对于输入的给定列值必须映射到唯一的 keyspace ID. 向表中插入数据时,Primary Vindex 产生的唯一映射决定了插入的行数据存储到哪个 shard. 从概念上来说,Primary Vindex 就等价于 NoSQL 中的 Sharding Key.
Primary Vindex 的唯一性和 MySQL 的唯一约束不同,并不要求列值是唯一的。可以有多行数据都映射到同一个 keyspace ID, 只要这些行在同一个 shard 即可(这里有点儿乱)。
二级索引(Secondary Vindexes)
Secondary Vindexes 是在 Primary Vindex 字段之外的字段上建立的额外索引,目的是为了提高 where 查询性能。二级索引将列值映射到一个或有限个 keyspace IDs
上,从而使 VTGate 只需要将请求路由到指定 shard 上。如果没有 Secondary Vindexes, VTGate 会将请求发送到所有 shards(称为 scatter query 分散查询).
唯一和非唯一索引
唯一索引(Unique Vindex) 对于给定的列值只映射到最多一个 keyspace ID 上。 了解一个索引是唯一的很重要,当 VTGate 知道查询范围可以被限制在单个 shard 上时,便可以将查询条件下推到 VTTablet 中判断(索引下推)。 Primary Vindex 必须是唯一的。
非唯一索引(NonUnique Vindex) 对于给定的列值会映射到一个或多个 keyspace ID 上。 假如表的 name 字段有重复值,可以在 name 上定义一个跨分片的非唯一索引,来加快 name 作为 where 条件的查询速度。
函数索引和查找索引
Functional Vindex 函数索引指的是列值到 keyspace ID 的映射关系是通过逻辑函数来定义的。相反,Lookup Vindex 查找索引使用一个 lookup table 将列值和 keyspace ID 关联起来,当需要的时候从 lookup table 中查找。
典型的,主键索引(Primary Vindex) 是函数索引,通常输入值和 keyspace id 之间是个 t->t 的无转换的映射函数。除此之外其他映射函数,例如 hash 函数也是支持的。
查找索引(Lookup Vindex) 使用 MySQL 查找表(lookup table) 来实现,查找表将列值映射到 keyspace ID 上。通常用于提高不含主键的 where 条件查询的性能。向表中插入数据时,列值和计算得出的 keyspace ID 会被存储到 lookup table 中以备查询。
查找索引(Lookup Vindex)的实现
用于实现查找索引(Lookup Vindex) 的查找表(lookup table) 可以是分片的(sharded) 也可以是非分片的(unsharded)。
Vitess 对 lookup table 的维护对用户是透明的。向原表中插入数据时,同时会在 lookup table 中插入映射行(column value -> keyspace ID)。同样,在原表中删除数据时,也会从 lookup table 中删除映射行。 由此必然导致分布式事务的产生,传统上需要两阶段提交来保证原子性。
查找索引(Lookup Vindex) 使用锁和事务序列来保证上述两个操作的一致性,没有使用传统的两阶段提交(2PC)。这样既保证了跨分片索引的数据一致性,又避免了 2PC 的性能损耗。
实现唯一查找索引(Lookup Vindex) 的查找表(lookup table) 只需要创建一个两列的表,第一列 from
的类型应该和主表的查找索引列的类型相同,第二列 to
的类型应该是足够大的 BINARY
或 VARBINARY
来容纳 keyspace id.
对于非唯一查找索引,lookup table 应该包含多个列。第一列还是索引字段列,此外,还需要额外一列来唯一确定原表的行,典型的是原表的主键,但也可以是任何和 from 列联合起来可以唯一确定原表中行的列,最后一列还是 keyspace id 列。
例如,原表列为 (user_id, email)
,其中 user_id
是主键,email
是非唯一查找索引,则 lookup table 应该包含 (email, user_id, keyspace_id)
这三列。
共享索引(Shared Vindexes)
关系数据库提倡数据范式,允许将表拆分为多个表来避免一对多映射中的数据重复。这种情况下,多个表之间会通过共享 key 来表明行数据相关性,也就是所谓的“外键”。
在分片环境中,将这些相关行数据保持在同一个 shard 中可提高性能。如果查找索引(Lookup Vindex) 创建在外键列上,则支持表(也就是 lookup table)对于涉及外键的父表、子表来说是相同的。因此,Vitess 允许在多个表之间共享查找索引(Lookup Vindex)。其中一个表被指定为查找索引的拥有者(owner),负责创建、删除 lookup table 中的映射关系,其他表只是复用这些映射关系。
定义 Vindex
VSchema 中,在 keyspace 的 Vindexes
域定义 Vindex, 例如:
"name_keyspace_idx": {
"type": "lookup",
"params": {
"table": "name_keyspace_idx",
"from": "name",
"to": "keyspace_id"
},
"owner": "user"
}
上面例子中 Vindex 的名字是 name_keyspace_idx
, 类型是 lookup
, 被 user
表拥有。
然后 VSchema 中相同 keyspace 中的 table 的 column_vindexes
域就可以通过索引名 name_keyspace_idx
来引用这个索引了。
每个索引都有可选的 params
字段,定义 key-value 映射关系。
Vindex 如何被使用
Vindex 是有花费(cost) 的。VTGate 路由一个查询时,会选择使用 cost 最低的 Vindex. 不同类型的 Vindex 的花费如下:
Vindex Type | Cost |
---|---|
Identity | 0 |
Functional | 1 |
Lookup Unique | 10 |
Lookup NonUnique | 20 |
1、Select 查询
对于简单的 select 查询,Vitess 扫描 where 条件来匹配最合适的 Vindex. 如果没有可用的索引,并且查询中不包含复杂语句(比如聚集函数),则发送给所欲 shards 执行。
Vitess 可以处理更复杂的查询,V3 high level design 中描述了 Vitess 如何处理复杂查询。
2、Insert 插入
使用主键(Primary Vindex) 来计算数据行的 keyspace ID.
在此表的其他 Vindexes 上验证此 keyspace ID 的合法性。对于其他二级索引来说,必须存在一个从列值到此 keyspace ID的映射。(这句没看懂)
3、Update 更新
使用 where 子句来路由 update 语句。支持修改 Vindex 列值,但有一个限制:列值的改变不能引起行数据从一个 shard 移动到另一个 shard. 一个应变方法(workaround) 是用先 delete 再 insert 来代替 update.
4、Delete 删除
如果此表拥有任何 查找索引(Lookup Vindex), 会先读取要删除的行,然后删除对应查找表(lookup table) 中映射关系,然后根据 where 子句将 delete 语句路由到对应 shard.
预定义 Vindexes
hash
类型是 Functional Unique
lookup
类型是 Lookup NonUnique
VSchema
VSchema 是 Vitess 对键空间 keyspace 和 分片 shards 的一种抽象,对外伪装为一个单点 MySQL 服务器。
例如,VSchema 包含分片表的 sharding key 信息,VTGate 遇到 where 字段包含 sharding key 的 SQL 请求后,会根据 VSchema 信息将请求路由到正确的分片 shard 上。
VSchema 示例:
{
"sharded": true,
"vindexes": {
"hash": {
"type": "hash"
}
},
"tables": {
"customer": {
"column_vindexes": [
{
"column": "customer_id",
"name": "hash"
}
],
"auto_increment": {
"column": "customer_id",
"sequence": "customer_seq"
}
},
"corder": {
"column_vindexes": [
{
"column": "customer_id",
"name": "hash"
}
],
"auto_increment": {
"column": "order_id",
"sequence": "order_seq"
}
}
}
}
连接到指定类型的 Tablet
Vitess 中可以将 Tablet 类型拼接到 keyspace 名上作为数据库名,例如可以通过数据库名 keyspace@replica
指定 keyspace 的 replica 实例。
这种拼接后的数据库名同样可以用在 MySQL 连接串中。
所以,应用可以通过 mysql://localhost:15306/commerce@rdonly
的方式连接到只读副本上
如果没有指定 Tablet 类型,则会使用 VTGate 的 default_tablet_type
参数指定的 Tablet 类型(默认 MASTER
)。
这也解释了为什么我通过 sysbench 压测 Vitess 时,所有流量都会打到 master 节点上
例如 use commerce@replica
切换到示例库 commerce 的 replica 副本,use commerce@rdonly
切换到 rdonly 副本。
mysql> use commerce@replica;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> use commerce@rdonly;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
不指定 keyspace 连接
如果表名在所有 keyspace 中是唯一的,可以不指定 keyspace 名访问表名,Vitess 会根据拓扑服务中的元数组信息自动将请求发送到正确的 keyspace.
如果表名在所有 keyspace 中不唯一,Vitess 会返回错误信息。
在不指定 keyspace 名使用时,也可以指定 Table 类型,比如可以直接连接到 @replica
来访问 replica 实例。
例如,在默认的 1 master 1 replica 1 rdonly 的 commerce 集群中,use @replica
切换到 replica 副本,use @rdonly
切换到 rdonly 副本。
mysql> use @replica
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> use @rdonly
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
有些框架需要在连接的时候显式指定数据库名,此时可以使用 @master
, @master
代替数据库名。
引用表
Vitess 允许在 sharded keyspace 上创建 unsharded table, 此时多个 shard 上此表的数据是完全一致的。将表的类型指定为 reference
且不添加任何 vindex 即可实现。
VSchema 示例
一、无分片表 VSchema 示例:
sql:
# lookup keyspace
create table name_user_idx(name varchar(128), user_id bigint, primary key(name, user_id));
VSchema:
// lookup keyspace
{
"sharded": false,
"tables": {
"name_user_idx": {}
}
}
对于无分片的表来说,VSchema 只需要知道表名,不需要其他的额外元数据信息。
二、只包含一个 Primary Vindex 的分片表
sql:
# user keyspace
create table user(user_id bigint, name varchar(128), primary key(user_id));
VSchema:
// user keyspace
{
"sharded": true,
"vindexes": {
"hash": {
"type": "hash"
}
},
"tables": {
"user": {
"column_vindexes": [
{
"column": "user_id",
"name": "hash"
}
]
}
}
}
由于 Vindexes 是需要被多次引用的字段,所以需要在 json 中用单独的 vindexes
字段将其定义出来,然后在 tables
中通过名字 hash
引用 Vindex. 上面的 VSchema 定义声明了 user_id
字段使用 hash
作为 Primary Vindex. 每个表的第一个 Vindex 一定是 Primary Vindex.
VSchema
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/vschema.md
路由规则
路由规则将流量转发到合适的 keyspace, shards 或 tablet, 通过配置路由规则可实现:
1、resharding 期间,可以手动在新旧 shards 间切换读写流量。比如,可以将只读流量手动切换到新 shards, 以便验证新 shards 的数据正确性。
2、等价表:可在不同的 keyspace 间指定等价表,来共同承担流量。
可通过 vtctlclient
命令来应用路由规则:
ApplyRoutingRules {-rules=<rules> || -rules_file=<rules_json_file>} [-cells=c1,c2,...] [-skip_rebuild] [-dry-run]
一、场景1
resharding 期间的路由规则配置示例:
{"rules": [
{
"from_table": "t@rdonly",
"to_tables": ["target.t"]
}, {
"from_table": "target.t",
"to_tables": ["source.t"]
}, {
"from_table": "t",
"to_tables": ["source.t"]
}
]}
上面的 json 配置了三条路由规则,含义分别是:
1、如果查询请求访问 source keyspace rdonly 实例上的 t 表,则转发到 target keyspace 的 t 表上。
2、如果查询请求访问 target keyspace 的非 rdonly 实例上的 t 表,则转发到 source 的 t 表上。
3、如果访问无任何限定的 t 表,转发到 source 的 t 表上。
假设的场景是我们将 source keyspace 中的 t 表垂直拆分到 target keyspace 中,通过上面的 3 条规则实现将 t 表的 rdonly 流量切换到 target keyspace, 但其他流量继续留在 source keyspace.
二、场景2
表等价规则配置
{"rules": [
{
"from_table": "product",
"to_tables": ["lookup.product", "user.uproduct"]
}
]}
通过上面的路由规则配置,我们声明在 lookup 和 user 键空间中都有 product 表。如果查询请求无任何限定的访问 product 表,则 VTGate 会考虑将请求转发给 lookup 的 product 表或 user 的 uproduct 表(注意两个表名不同)。
假如 user 是一个分片键空间,并且有个查询需要用 product 表关联(join) user 中的其他表,则 VTGate 会判定在 user 键空间中做表关联是更优的。
Schema Routing Rules
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/schema-routing-rules.md
VReplication
VReplication features, design and options in a nutshell
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/vreplication/vreplication.md
VReplication 是 Vitess 的核心组件,很多核心特性都需要 VReplication, 例如 Resharding, 数据迁移
VReplication 本身是一个底层组件,但是也可以通过命令直接使用。
Replication 复制
Vitess 复制需要 MySQL 配置基于行的 binlog 格式,并开启全局事务ID(GTID)。此外 Vitess 只支持 row binlog 的 binlog_row_image
配置为 full
, 即 binlog 需要记录全部改动前后值。
Vitess 利用 MySQL 复制来实现高可用,以及监控 MySQL 库表结构变化,以便更新拓扑服务中的表结构元数据.
Semi-Sync 半同步
Vitess 强烈建议开启用于高可用的半同步,半同步有如下优点:
1、master 节点只有在至少一个 replica 连接且 semi-sync ack 配置正确的情况下才会接受写请求。Vitess 将半同步超时时间配置为无限大所以系统不会降级为异步复制。在网络分区的情况下(network partition)此配置可以阻止脑裂(split brain)。如果能够验证所有 replica 已停止复制,我们就知道 old master 已停止写入。
2、rdonly 类型的节点不会发送 semi-sync ack. 因为 rdonly 类型的节点不是 master 候选人。
一旦 master 节点宕机,至少会有一个 replica 节点包含全部向客户端报告为已完成的事务。然后可以用 vtctl 手动或用 Orchestrator 工具将 GTID 最大的一个 replica 选为 master.
监控
Monitoring
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/monitoring.md
有三种监控 Vitess 集群的方法:
Vitess 状态页
每个 Vitess 组件都有自己的状态页面,访问 http://<host>:<port>/debug/status
地址即可看到。
状态页会显示该组件的一些基本的但重要的信息,比如 vttablet 状态页会显示过去几分钟的 QPS 信息。
Vitess 的状态页面开箱即用,但提供的监控信息有限。
基于拉取(Pull)的指标系统
Vitess 使用 Go 的 expvar package 包来暴露指标,用户可配置一个基于 Pull 拉取的指标系统进行信息诊断。指标会暴露在组件的 http://<host>:<port>/debug/vars
端点(Endpoint)上,数据是 json 格式。
刮取(Scrape) Vitess 指标变量是一种很好的将 Vitess 集成到已有监控系统的方式,可以用来简历详细的监控 dashboard.
基于推送(Push)的指标系统
Vitess 也可以通过安装插件的方式来支持基于 Push 推送的指标系统,前提是给每个 Vitess 组件的启动命令增加 --emit_stats
参数来开启指标推送支持。
默认情况下,指标推送周期是 60s, 也就是每隔一分钟会向选择的收集后端推送一次统计指标,可通过 --stats_emit_period
参数配置推送周期。
Vitess 预置了推送到 OpenTSDB 指标收集后端的插件。
可使用 Go 自定义推送插件,参考 opentsdb.go
消息系统
Vitess 实现了一个基于 MySQL 表的消息系统,提供基本的消息队列功能。支持:
1、创建消息表 my_message
2、发送消息:像普通表操作那样向其中插入数据
3、接收消息:向 VTGate 发送 MessageStream
请求来订阅消息。
4、确认机制(ACK)
Messaging
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/messaging.md
管理界面
vtctl管理界面
访问 http://localhost:15000 打开 vtctld 控制台(Vitess Control Panel), 可以看到拓扑结构、分片信息、Schema 信息、表大小、数据量等。
vtctld 控制台(Vitess Control Panel)
VTGate Web界面
访问 http://localhost:15001 打开 vtgate 控制台,可查看 qps, 节点状态等信息。
集群状态信息
dashboard 上可以看到集群的 qps, 各 vttablet 节点的状态
vtgate dashboard
点开具体的一个 vttablet 节点,可查看此节点的状态:
vttablet status
SQL执行统计
点开 Query Stats 可以看到 SQL 执行统计
Query Stats
可视化动态修改连接池等参数
点开 View/Change Environment variables 能可视化修改连接池等参数
View/Change Environment variables
实际使用中发现修改连接池大小无法生效。
Schema迁移
Vitess 支持两种 Schema 修改方式:
1、Unmanaged Schema 修改,指的是直接在数据库上执行 DDL.
2、Managed/Online Schema 修改,指的是通过 gh-ost 或 pt-online-schema-change 这种第三方 DDL 工具进行 Online Schema 修改。
Unmanaged Schema 修改
Unmanaged Schema Changes
https://vitess.io/docs/user-guides/schema-changes/unmanaged-schema-changes/
通过vtctlclient执行DDL
通过 vtctlclient ApplySchema
命令手动执行 DDL, 例如:
vtctlclient ApplySchema -sql "ALTER TABLE demo modify id bigint unsigned" commerce
1、Vitess 会将 DDL 发送到 commerce 所在的所有主节点(Primary Shards)并执行,所有 ALTER TABLE 完成后再返回。
2、Vitess 会进行基础的 sql 语法校验,例如字段重复等,但校验可能不完整。
3、假如目标表过大,Vitess 会拒绝 DDL 命令,可通过加 -allow_long_unavailability
参数解决:
vtctlclient ApplySchema -allow_long_unavailability -sql "ALTER TABLE demo modify id bigint unsigned" commerce
通过标准mysql客户端连接VTGate执行DDL
1、直接通过 mysql 登录 VTGate 执行 DDL
$ mysql -h 127.0.0.1 -P 15306 commerce
Welcome to the MySQL monitor. Commands end with ; or \g.
mysql> ALTER TABLE demo ADD COLUMN sample INT;
Query OK, 0 rows affected (0.04 sec)
同样,Vitess 会找到所有相关的 Primary Shards 并执行 DDL
2、也可以直接指定具体的节点(shards) 登录后执行 DDL
$ mysql -h 127.0.0.1 -P 15306 commerce/-80
Welcome to the MySQL monitor. Commands end with ; or \g.
mysql> ALTER TABLE demo ADD COLUMN sample INT;
Query OK, 0 rows affected (0.04 sec)
同样,也可以在应用中像连接普通 mysql server 一样连接 VTGate 后执行 DDL
注意:
1、所有 DDL 语法都必须通过 VTGate 的语法检查,否则会拒绝执行。
2、不建议通过这种方式修改大表的 Schema
直接连接mysql主节点执行DDL
也可以直接通过 mysql 客户端连接底层 mysql master 节点执行 DDL
VTTablet 会检测到他所代理的 mysql 实例的 DDL 变更,并更新元数据。-queryserver-config-schema-reload-time
参数配置 VTTablet 检测 mysql schema 变更的时间间隔,默认是 1800 秒。
也可以执行 vtctlclient ReloadSchema
命令立即重新加载 mysql Schema, 例如:
vtctlclient ReloadSchema zone1-0000000100
其中 zone1-0000000100 是一个 tablet 实例名。
Managed/Online Schema 修改
Managed, Online Schema Changes
https://vitess.io/docs/user-guides/schema-changes/managed-online-schema-changes/
Online DDL执行流程和状态
无论是通过 vtctlclient ApplySchema
还是 VTGate
执行 Online DDL, DDL 请求都会首先被持久化到 Topology Service 中(例如 etcd 集群)。
vtctld 会周期性的检查新的 DDL 请求,解析出相关的 shards 和对应的 primary tablets, 并将请求推送给对应的 primary tablets.
如果有未响应的 shards, vtctld 会周期性的重新将 DDL 请求推送给对应的 shards 直到全部 shards 确认请求。
tablets 会将 DDL 请求存储到 _vt
库中的指定表中,注意不要手动操作这个表。
drop table 不会立即删
执行 drop table 时不会立即删除表结构,而是将表 rename 重命名为一个特殊表名,之后再安全的删除。
DDL策略
前面说了 Vitess 支持 Unmanaged Schema 修改 和 Managed/Online Schema 修改,具体 Vitess 使用哪种方式来执行 DDL 命令依赖于 ddl_strategy
配置,Vitess 支持下面几种 DDL 策略:direct
直接在数据库上执行 DDL, 这种是同步阻塞操作会锁表,这是默认的 DDL 策略。online
使用 Vitess 内置的 VReplication 机制。gh-ost
使用 GitHub 的 gh-ost 工具进行 Schema 修改。pt-osc
使用 Percona 的 pt-online-schema-change 工具进行 Schema 修改。
指定 DDL 策略:
1、执行 vtctlclient ApplySchema
命令时指定 -ddl_strategy
参数,例如:
$ vtctlclient ApplySchema -ddl_strategy "online" -sql "ALTER TABLE demo MODIFY id bigint UNSIGNED" commerce
a2994c92_f1d4_11ea_afa3_f875a4d24e90
$ vtctlclient ApplySchema -ddl_strategy "gh-ost --max-load Threads_running=200" -sql "ALTER TABLE demo add column status int" commerce
2、连接 VTGate 后设置 session 变量 @@ddl_strategy
$ mysql -h 127.0.0.1 -P 15306 commerce
Welcome to the MySQL monitor. Commands end with ; or \g.
mysql> SET @@ddl_strategy='online';
Query OK, 0 rows affected (0.00 sec)
MySQL 兼容性
MySQL Compatibility
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/compatibility/mysql-compatibility.md
事务模型
Vitess 默认提供 读已提交 的事务隔离级别。
Vitess 默认不支持跨分片事务。一定要使用的话可通过开启 两阶段提交(2PC) 来启用跨分片事务,但 2PC 开销较大,不建议开启,尽量通过优化 Schema 设计来避免跨分片事务。
开启 2PC 后,跨分片事务可保证原子性,但不保证隔离性,即跨分片事务执行期间第三方可看到部分提交的数据。
Two-Phase Commit
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/two-phase-commit.md
自增id
Sequences
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/features/vitess-sequences.md
Vitess 提供 Sequences 来解决分表情况下的自增 id 需求。
内部实现:为每个表创建一个存储自增id的单行的序列表。为提高性能,支持内存块分配,一次性分配N个id,N个id用完后才需要和序列表交互,N个id之内都是内存操作。类似 Oracle 的 sequence.
关联查询
Vitess 支持跨分片的 inner join.
对于外关联 outer join, 如果没有跨外表和内表的表达式操作,也支持跨分片 join.
排序
聚合函数
Vitess 支持部分跨分片的 group by 操作。
当需要聚合的结果集大于 VTGate 的内存限制时,请求会被拒绝。
子查询
Vitess 支持部分子查询。
但是例如 子查询 + group by 是不支持的。
DDL支持
Vitess 支持动态 DDL, 可动态增删改表结构,会将 DDL 应用到每个内部 MySQL。
但是对于大表的 alter table 操作建议离线进行。
不支持sql create/drop数据库
不支持直接连接 VTGate 后用 sql create/drop 数据库,需要使用 vtctl 命令创建数据库vtctl CreateKeyspace user
通过 Vitess 创建的数据库名会自动增加 vt_ 前缀,比如 user 会变为 vt_user
UDF自定义函数支持
用户和权限管理
User Management and Authentication
https://github.com/vitessio/website/blob/prod/content/en/docs/user-guides/configuration-advanced/user-management.md
Vitess 通过 VTGate 实现自己的用户和权限管理机制,所以通过 VTGate 连接时 CREATE USER....
和 GRANT...
命令不生效。
$ cat > users.json << EOF
{
"vitess": [
{
"UserData": "vitess",
"Password": "supersecretpassword"
}
],
"myuser1": [
{
"UserData": "myuser1",
"Password": "password1"
}
]}
EOF
其中 UserData
字段并不是用户名,用户列表的每个 key 才是用户名, UserData
字段是在 Vitess授权机制 中使用的,为了方便可以直接保持和用户名一致。
mysqlctl 命令
mysqlctl
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/programs/mysqlctl.md
mysqlctl 是一个用于配置和启动 mysql 实例的命令行工具,或者说一个 MySQL 的包装器(wrapper)。
mysqlctl 会生成 my.cnf 配置文件并启动 mysqld 实例。
tablet_uid
和 mysql_port
是 mysqlctl 的两个必须参数。
init
配置并启动MySQL
init [-wait_time=5m] [-init_db_sql_file=(default)]
init
命令启动一个新的 mysqld 实例,会自动检测 MySQL 版本,并应用一个最小 my.cnf 配置文件。
export VTDATAROOT=/tmp
mysqlctl \
-alsologtostderr \
-tablet_uid 101 \
-mysql_port 12345 \
init
自定义mysqlctl启动的MySQL的my.cnf配置文件
VTTablet and MySQL
https://github.com/vitessio/website/blob/prod/content/en/docs/user-guides/configuration-basic/vttablet-mysql.md
mysqlctl 并不读取 /etc/my.cnf
和 /etc/mysql/my.cnf
中的 MySQL 配置文件,而是使用内置的默认配置自动生成 my.cnf
默认 my.cnf 配置为 https://github.com/vitessio/vitess/blob/main/config/mycnf/default.cnf
想覆盖 mysqlctl 默认的 my.cnf 配置文件有如下几种方式:
1、设置 EXTRA_MY_CNF
环境变量,指向逗号分割的一个或多个 my.cnf 配置文件。
2、通过 -mysqlctl_mycnf_template
指定自己的 my.cnf 模板文件,模板格式参考 https://github.com/vitessio/vitess/tree/main/config/mycnf
例如覆盖默认的 max_connections 配置:
1、创建 /path/to/common.cnf
文件,写入:
max_connections = 50000
2、启动 mysqlctl 的命令中增加 EXTRA_MY_CNF
环境变量指向刚创建的配置文件
EXTRA_MY_CNF="/path/to/common.cnf" mysqlctl \
-log_dir=${VTDATAROOT}/tmp \
-tablet_uid=100 \
-mysql_port=17100 \
init
注意:
(1)EXTRA_MY_CNF 中的配置文件必须是绝对路径,经测试相对路径不起作用。
(2)生成的 my.cnf 中原来的 max_connections 没有被覆盖,而是有两个 max_connections 配置项,下面的是 max_connections = 50000
, 登录 VTGate 后看 show variables like '%max_connections%';
值为 50000 是生效的。
注意:有些 MySQL 配置是不可修改的,因为 Vitess 的一些特性依赖这些配置项,比如必须设置 log-bin
参数以开启 binlog
vttablet 命令
vttablet
https://github.com/vitessio/website/blob/prod/content/en/docs/reference/programs/vttablet.md
VTTablet and MySQL
https://github.com/vitessio/website/blob/prod/content/en/docs/user-guides/configuration-basic/vttablet-mysql.md
-queryserver-config-pool-size
oltp连接池(16)
vttablet 的 oltp 读连接池大小,默认值 16workload='oltp'
时会使用此连接池,默认即是 oltp
-queryserver-config-stream-pool-size
olap连接池(200)
vttablet 的流式查询(olap)连接池大小,默认值 200workload='olap'
时会使用此连接池
-queryserver-config-query-timeout
oltp超时时间(30s)
vttablet 的 SQL 超时时间,单位秒,默认值 30s
超过此配置时间的查询会被 VTTablet 杀掉,通常设置为 15-30s
-queryserver-config-transaction-timeout
事务超时时间(30s)
vttablet 的事务超时时间,单位秒
-queryserver-config-transaction-cap
事务最大并发数(20)
vttablet 允许的事务最大并发数,默认值 20
假如设为 100, 则 vttablet 可同时处理 100 个事务,第 101 个事务会被阻塞(block),被阻塞的事务如果在事务超时时间内还无法连接则会失败。
-queryserver-config-max-result-size
结果集个数最大值(10000)
非流式查询中允许从 vttablet 返回的结果集个数最大值,默认 10000
vtctl 命令
vtctl
https://github.com/vitessio/website/tree/prod/content/en/docs/reference/programs/vtctl
vtctl 是管理 Vitess 集群的命令行工具。
vtctl 既可以作为一个独立的工具使用(直接使用 vtctl
命令),也可以以客户端-服务器方式运行(使用 vtctlclient
命令配合 vtctld
服务)。
推荐使用 客户端-服务器 方式,可以提供额外的安全检查。
CreateKeyspace 创建键空间(数据库)
vtctl CreateKeyspace user
创建数据库 user
topo_global_server_address must be configured
执行 vtctl 命令 CreateKeyspace 时报错:
vtctl CreateKeyspace user
F0625 10:52:56.645071 96207 server.go:224] topo_global_server_address must be configured
看了下启动脚本
/usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local/env.sh 中有
TOPOLOGY_FLAGS=”-topo_implementation etcd2 -topo_global_server_address $ETCD_SERVER -topo_global_root /vitess/global”
设置了 topo_global_server_address 参数
etcd-up.sh 中也执行了
vtctl -topo_implementation etcd2 -topo_global_server_address localhost:2379 -topo_global_root /vitess/global AddCellInfo -root /vitess/zone1 -server_address localhost:2379 zone1
不知道为啥还报这个错。
安装Vitess
Mac brew安装Vitess
Local Install via Homebrew
https://vitess.io/docs/get-started/local-brew/
下面这篇文章解释了安装目录中的默认启动脚本都做了什么,相当于 Vitess 的部署文档。
Life of a Vitess Cluster
https://vitess.io/blog/2020-04-27-life-of-a-cluster/
Mac brew安装Vitess
brew install vitess
安装 Vitess
$ brew install vitess
==> Downloading https://ghcr.io/v2/homebrew/core/etcd/manifests/3.5.0
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/etcd/blobs/sha256:e3faeef31e8635edcc01a1940421e8110405a7c04e5058d4c2392584bfa03315
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:e3faeef31e8635edcc01a1940421e8110405a7c04e5058d4c2392584bfa03315?se=2021-06-24T04%3A10%3A00Z&sig=c7Eisjst9p6Dm61pPGNOjTgLaSnJme5iBiEEDJXQx1c%3D&sp=r&spr=h
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/vitess/manifests/10.0.2
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/vitess/blobs/sha256:379e273a0fa00df8967402dbaf3a2ce7d4ee4f34059bed832f22caf1d12a446a
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:379e273a0fa00df8967402dbaf3a2ce7d4ee4f34059bed832f22caf1d12a446a?se=2021-06-24T04%3A10%3A00Z&sig=TWsyzelX%2FTBFZ3Ncn52cpH9qkXsmY7gYMf%2Bhd6tnyBU%3D&sp=r&s
######################################################################## 100.0%
==> Installing dependencies for vitess: etcd
==> Installing vitess dependency: etcd
==> Pouring etcd--3.5.0.mojave.bottle.tar.gz
🍺 /usr/local/Cellar/etcd/3.5.0: 9 files, 75MB
==> Installing vitess
==> Pouring vitess--10.0.2.mojave.bottle.tar.gz
🍺 /usr/local/Cellar/vitess/10.0.2: 270 files, 544.9MB
安装过程中会自动安装 Vitess 依赖的 etcd
安装目录:/usr/local/Cellar/vitess/10.0.2
启动单Keyspace集群
进入目录 /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local
执行 101_initial_cluster.sh
启动默认 Vitess 集群
成功启动信息如下:
./101_initial_cluster.sh
add /vitess/global
add /vitess/zone1
add zone1 CellInfo
etcd start done...
Starting vtctld...
Starting MySQL for tablet zone1-0000000100...
Starting vttablet for zone1-0000000100...
HTTP/1.1 200 OK
Date: Thu, 24 Jun 2021 08:05:35 GMT
Content-Type: text/html; charset=utf-8
Starting MySQL for tablet zone1-0000000101...
Starting vttablet for zone1-0000000101...
HTTP/1.1 200 OK
Date: Thu, 24 Jun 2021 08:05:40 GMT
Content-Type: text/html; charset=utf-8
Starting MySQL for tablet zone1-0000000102...
Starting vttablet for zone1-0000000102...
HTTP/1.1 200 OK
Date: Thu, 24 Jun 2021 08:05:45 GMT
Content-Type: text/html; charset=utf-8
I0624 16:05:46.830993 29700 main.go:67] I0624 08:05:46.829125 tablet_executor.go:249] Received DDL request. strategy=direct
I0624 16:05:46.874232 29700 main.go:67] I0624 08:05:46.874079 tablet_executor.go:249] Received DDL request. strategy=direct
I0624 16:05:46.913929 29700 main.go:67] I0624 08:05:46.913794 tablet_executor.go:249] Received DDL request. strategy=direct
New VSchema object:
{
"tables": {
"corder": {
},
"customer": {
},
"product": {
}
}
}
If this is not what you expected, check the input data (as JSON parsing will skip unexpected fields).
Waiting for vtgate to be up...
vtgate is up!
Access vtgate at http://MyMacHostname:15001/debug/status
设置mysql命令别名
启动过程中 Vitess 会设置如下几个 alias 简化命令,方便使用
alias mysql="command mysql -h 127.0.0.1 -P 15306"
alias vtctlclient="command vtctlclient -server localhost:15999 -log_dir ${VTDATAROOT}/tmp -alsologtostderr"
alias vtctldclient="command vtctldclient --server localhost:15999"
这些在别名设置在 /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local/env.sh 中
比如查看 Vitess 相关表(Vitess 丰富了 MySQL 的 show 语句)
mysql -e "show vitess_tablets"
+-------+----------+-------+------------+---------+------------------+-----------+----------------------+
| Cell | Keyspace | Shard | TabletType | State | Alias | Hostname | MasterTermStartTime |
+-------+----------+-------+------------+---------+------------------+-----------+----------------------+
| zone1 | commerce | 0 | MASTER | SERVING | zone1-0000000100 | localhost | 2021-06-24T08:05:46Z |
| zone1 | commerce | 0 | REPLICA | SERVING | zone1-0000000101 | localhost | |
| zone1 | commerce | 0 | RDONLY | SERVING | zone1-0000000102 | localhost | |
+-------+----------+-------+------------+---------+------------------+-----------+----------------------+
如果不想使用别名,可以执行 unalias mysql && unalias vtctlclient
来删除 alias
通过VTGate连接MySQL集群
直接本地执行 mysql
命令(已经被设置为别名,相当于 mysql -h 127.0.0.1 -P 15306
)即可连接到 VTGate 服务器,可直接当做 MySQL 来使用。
直接登录Vitess启动的MySQL单例
101_initial_cluster.sh 脚本中默认会通过 mysqlctl 启动 3 个 MySQL 实例,端口号分别为 17100, 17200, 17300
mysql -h localhost -P 17100
可直接登录每个 MySQL 实例。
访问vtgate和vtctld控制台界面
访问 http://localhost:15000 打开 vtctld 控制台(Vitess Control Panel), 可以看到拓扑结构、分片信息、Schema 信息等。
vtctld 控制台(Vitess Control Panel)
访问 http://localhost:15001 打开 vtgate 控制台,可以看到查询 QPS 监控等信息。
vtgate 控制台
关闭Vitess集群
进入目录 /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local
执行 401_teardown.sh
关闭 Vitess 集群
可能有这个脚本杀不掉的进程,仔细看提示,手动 kill 掉。
或者直接执行:
pkill -9 -f '(vtdataroot|VTDATAROOT)' # kill Vitess processes
rm -rf /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local/vtdataroot
遇到的问题
旧版本brew仓库中可能没有Vitess
注意:如果brew没有更新最新的formulae, 可能找不到vitess, 我这里就遇到了这个问题,报错如下:
$ brew install vitess
Error: No available formula with the name "vitess"
==> Searching for a previously deleted formula (in the last month)...
Error: No previously deleted formula found.
==> Searching for similarly named formulae...
Error: No similarly named formulae found.
==> Searching taps...
==> Searching taps on GitHub...
Error: No formulae found in taps.
解决:brew update
更新 brew formulae 仓库
比如我 Mac 上执行 brew update
后(2021.6.24)可以看到输出信息中 New Formulae 里有了 vitess, 看来是近期刚加进去的
$ brew update
Updated 3 taps (homebrew/core, homebrew/services and mongodb/brew).
==> New Formulae
...
caire gallery-dl libfontenc
mongocli qt-libiodbc vitess
...
Could not resolve host: MyMacHostname
执行 101_initial_cluster.sh 报错
./101_initial_cluster.sh
add /vitess/global
add /vitess/zone1
add zone1 CellInfo
etcd start done...
Starting vtctld...
Starting MySQL for tablet zone1-0000000100...
Starting vttablet for zone1-0000000100...
curl: (6) Could not resolve host: MyMacHostname
ERROR: tablet could not be started!
Starting MySQL for tablet zone1-0000000101...
Starting vttablet for zone1-0000000101...
curl: (6) Could not resolve host: MyMacHostname
ERROR: tablet could not be started!
Starting MySQL for tablet zone1-0000000102...
Starting vttablet for zone1-0000000102...
curl: (6) Could not resolve host: MyMacHostname
ERROR: tablet could not be started!
解决:
ping 了下自己 Mac 的 hostname 发现果然 ping 不同
编辑 /etc/hosts 增加一行即可
127.0.0.1 MyMacHostname
卡在 Waiting for vtgate to be up…
执行 101_initial_cluster.sh
可成功启动 MySQL 和 vttablet, 但卡在 Waiting for vtgate to be up… 这一步不动了
查看 /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local/scripts/vtgate-up.sh 脚本
可以看到日志目录是 /usr/local/Cellar/vitess/10.0.2/share/vitess/examples/local/vtdataroot/tmp
进去看看 vtgate 日志,看到 vtgate.ERROR 中有报错:
E0624 15:26:27.783632 7229 plugin_mysql_server.go:476] Existent socket '/tmp/mysql.sock' is still accepting connections, aborting
F0624 15:26:27.784005 7229 plugin_mysql_server.go:452] mysql.NewListener failed: listen unix /tmp/mysql.sock: bind: address already in use
我本地还启动着一个日常使用的 3306 端口的 MySQL 5.7, 可能是这个引起的,停掉后果然成功了。
机器上已有etcd时如何启动vitess集群
机器上已有 etcd 时,101_initial_cluster.sh 启动 vitess 集群报错
etcd is already running. Exiting.
此时可以修改 vitess-11/examples/local/env.sh 将 vitess 启动的 etcd 改成另一个端口:
ETCD_SERVER="localhost:2479"
修改vtgate的web页面端口
修改 vitess-11/examples/local/scripts/vtgate-up.sh 脚本
web_port=8081
Mac Docker安装Vitess
Local Install via Docker
https://vitess.io/docs/get-started/local-docker/
Mac 系统版本:10.14.6 Mojave
1、clone 代码
git clone git@github.com:vitessio/vitess.git
2、构建
cd vitess
make docker_local
总是报错,按网上说的添加代理环境变量也不行
=> ERROR [11/15] RUN make install PREFIX=/vt/install 363.1s
------
> [11/15] RUN make install PREFIX=/vt/install:
#16 3.187 Wed Jun 23 07:33:01 UTC 2021: Building source tree
#16 123.2 go: cloud.google.com/go/storage@v1.10.0: Get "https://proxy.golang.org/cloud.google.com/go/storage/@v/v1.10.0.mod": dial tcp 172.217.24.17:443: i/o timeout
#16 243.1 go: cloud.google.com/go/storage@v1.10.0: Get "https://proxy.golang.org/cloud.google.com/go/storage/@v/v1.10.0.mod": dial tcp 172.217.24.17:443: i/o timeout
#16 363.0 go: cloud.google.com/go/storage@v1.10.0: Get "https://proxy.golang.org/cloud.google.com/go/storage/@v/v1.10.0.mod": dial tcp 172.217.24.17:443: i/o timeout
#16 363.0 make: *** [Makefile:69: build] Error 1
3、改为下载官方打好的 docker 镜像,下载 vitess/lite 镜像
docker pull vitess/lite
各个版本的 docker 镜像的区别见下面的文档
https://github.com/vitessio/vitess/tree/main/docker
4、启动镜像
docker run -p 15000:15000 -p 15001:15001 -p 15991:15991 -p 15999:15999 --rm -d vitess/lite
启动不成功,也看不出有什么错误。
k8s helm 安装 Vitess
Helm Chart (deprecated)
https://vitess.io/docs/get-started/helm/
Run Vitess on Kubernetes
https://www.bookstack.cn/read/vitess-en/6b1bc138da17993c.md
安装并启动 k8s
Mac 本地使用 minikube 安装单节点 k8s 集群并启动,具体参考 Kubernetes/K8S-安装部署
helm 启动 vitess
1、拉取 Vitess 代码 git clone git@github.com:vitessio/vitess.git
2、进入 vitess/examples/helm 目录使用 101_initial_cluster.yaml 配置启动 vitesshelm install vitess ../../helm/vitess -f 101_initial_cluster.yaml
结果如下
helm install vitess ../../helm/vitess -f 101_initial_cluster.yaml
NAME: vitess
LAST DEPLOYED: Tue Jun 29 17:53:11 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Release name: vitess
To access administrative web pages, start a proxy with:
kubectl proxy --port=8001
Then use the following URLs:
vtctld: http://localhost:8001/api/v1/namespaces/default/services/vtctld:web/proxy/app/
vtgate: http://localhost:8001/api/v1/namespaces/default/services/vtgate-zone1:web/proxy/
helm list 结果如下:
helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
vitess default 1 2021-06-29 17:53:11.324643 +0800 CST deployed vitess-2.0.1-0
pod 和 job 信息如下:
kubectl get pod,job -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/commerce-apply-vschema-initial-jwhqq 0/1 Completed 0 56m 172.17.0.3 minikube <none> <none>
pod/vtctld-574d58959-29fz6 1/1 Running 1 56m 172.17.0.4 minikube <none> <none>
pod/vtgate-zone1-568499d679-57c5z 1/1 Running 5 56m 172.17.0.5 minikube <none> <none>
pod/zone1-commerce-0-replica-0 1/2 Running 2 56m 172.17.0.10 minikube <none> <none>
pod/zone1-commerce-0-replica-1 1/2 Running 6 56m 172.17.0.8 minikube <none> <none>
pod/zone1-commerce-0-replica-2 1/2 Running 6 56m 172.17.0.9 minikube <none> <none>
NAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTOR
job.batch/commerce-apply-schema-initial 0/1 56m 56m apply-schema vitess/vtctlclient:helm-2.0.2-0 controller-uid=6c032aab-b21f-40c5-a369-169af6edd912
job.batch/commerce-apply-vschema-initial 1/1 6m14s 56m apply-vschema vitess/vtctlclient:helm-2.0.2-0 controller-uid=56cea201-1019-43fa-b3fd-bce30df6a404
job.batch/zone1-commerce-0-init-shard-master 0/1 56m 56m init-shard-master vitess/vtctlclient:helm-2.0.2-0 controller-uid=0cd31da1-0dcb-42a2-8b33-4778f656e155
设置端口转发
为了方便使用,Vitess 提供一个端口转发脚本 vitess/examples/helm/pf.sh./pf.sh &
运行此脚本,之后可直接在本地连接容器内的 vtgate
同时提示创建别名 mysql 指向 mysql -h 127.0.0.1 -P 15306
, 创建后如果不想使用别名,可以执行 unalias mysql && unalias vtctlclient
来删除 alias
#!/bin/sh
kubectl port-forward service/vtctld 15000 15999 &
process_id1=$!
kubectl port-forward service/vtgate-zone1 15306:3306 15001 &
process_id2=$!
sleep 2
echo "You may point your browser to http://localhost:15000 for vtctld."
echo "You may point your browser to http://localhost:15001 for vtgate, use the following aliases as shortcuts:"
echo 'alias vtctlclient="vtctlclient -server=localhost:15999"'
echo 'alias mysql="mysql -h 127.0.0.1 -P 15306"'
echo "Hit Ctrl-C to stop the port forwards"
wait $process_id1
wait $process_id2
关闭Vitess集群
helm delete vitess
kubectl delete pvc -l "app=vitess"
kubectl delete vitesstoponodes --all
k8s operator 安装 Vitess
https://github.com/vitessio/website/blob/prod/content/en/docs/get-started/operator.md
安装并启动 k8s
Mac 本地使用 minikube 安装单节点 k8s 集群并启动,具体参考 Kubernetes/K8S-安装部署
安装operator
1、拉取 Vitess 代码 git clone git@github.com:vitessio/vitess.git
2、进入 vitess/examples/operator 目录,apply 安装 operator.yamlkubectl apply -f operator.yaml
结果如下,会创建一些自定义资源 CRD
kubectl apply -f operator.yaml
customresourcedefinition.apiextensions.k8s.io/etcdlockservers.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitessbackups.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitessbackupstorages.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitesscells.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitessclusters.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitesskeyspaces.planetscale.com created
customresourcedefinition.apiextensions.k8s.io/vitessshards.planetscale.com created
serviceaccount/vitess-operator created
role.rbac.authorization.k8s.io/vitess-operator created
rolebinding.rbac.authorization.k8s.io/vitess-operator created
priorityclass.scheduling.k8s.io/vitess created
priorityclass.scheduling.k8s.io/vitess-operator-control-plane created
deployment.apps/vitess-operator created
启动Vitess
apply 安装 101_initial_cluster.yamlkubectl apply -f 101_initial_cluster.yaml
这个配置中描述了一个 VitessCluster 类型的自定义 k8s 资源对象(CRD),所以必须先应用 operator.yaml 来描述此 CRD. 如果想定制化的修改 101_initial_cluster.yaml 中的内容,可以去 operator.yaml 看各个配置项的含义,operator.yaml 内容很长,容易看晕。
kubectl apply -f 101_initial_cluster.yaml
vitesscluster.planetscale.com/example created
secret/example-cluster-config created
查看 pod
# kubectl get pod
NAME READY STATUS RESTARTS AGE
example-etcd-faf13de3-1 1/1 Running 0 37m
example-etcd-faf13de3-2 1/1 Running 0 37m
example-etcd-faf13de3-3 1/1 Running 0 37m
example-vttablet-zone1-2469782763-bfadd780 3/3 Running 0 37s
example-vttablet-zone1-2548885007-46a852d0 3/3 Running 1 37m
example-zone1-vtctld-1d4dcad0-6886b4f86-k9kcb 1/1 Running 2 37m
example-zone1-vtgate-bc6cde92-66bdbf79f6-b7b9t 1/1 Running 2 37m
vitess-operator-77d56b98dd-k89cr 1/1 Running 0 14h
设置端口转发
为了方便使用,Vitess 提供一个端口转发脚本 vitess/examples/helm/pf.sh./pf.sh &
运行此脚本,之后可直接在本地连接容器内的 vtgate
连接MySQL
通过 VTGate 连接
./mysql -h 127.0.0.1 -P 15306 -uuser
也可以使用 vtctlclient 进行操作
vtctlclient -server=localhost:15999 -logtostderr
关闭Vitess集群
kubectl delete -f 101_initial_cluster.yaml
遇到的问题
其中一个vttablet pod不断重启
问题:
使用 vitess 源码中的 vitess/examples/operator 启动 vitess 集群,有一个 vttablet pod 不断重启kubectl logs -f example-vttablet-zone1-2548885007-46a852d0 -c vttablet
查看错误日志如下:
W0706 07:42:02.200507 1 tm_init.go:531] Cannot get current mysql port, will keep retrying every 1s: net.Dial(/vt/socket/mysql.sock) to local server failed: dial unix /vt/socket/mysql.sock: connect: no such file or directory (errno 2002) (sqlstate HY000)
E0706 07:42:02.285406 1 engine.go:213] Error starting vreplication engine: error in connecting to mysql db with connection <nil>, err net.Dial(/vt/socket/mysql.sock) to local server failed: dial unix /vt/socket/mysql.sock: connect: no such file or directory (errno 2002) (sqlstate HY000), will keep retrying.
E0706 07:42:02.285504 1 state_manager.go:276] Error transitioning to the desired state: MASTER, Serving, will keep retrying: net.Dial(/vt/socket/mysql.sock) to local server failed: dial unix /vt/socket/mysql.sock: connect: no such file or directory (errno 2002) (sqlstate HY000)
I0706 07:42:02.285527 1 state_manager.go:661] State: exiting lameduck
E0706 07:42:02.285539 1 tm_state.go:258] Cannot start query service: net.Dial(/vt/socket/mysql.sock) to local server failed: dial unix /vt/socket/mysql.sock: connect: no such file or directory (errno 2002) (sqlstate HY000)
I0706 07:42:02.285553 1 tm_state.go:305] Publishing state: alias:<cell:"zone1" uid:2548885007 > hostname:"10.233.107.217" port_map:<key:"grpc" value:15999 > port_map:<key:"vt" value:15000 > keyspace:"commerce" shard:"-" key_range:<> type:MASTER db_name_override:"vt_commerce" mysql_hostname:"10.233.107.217" master_term_start_time:<seconds:1625527268 nanoseconds:196807555 >
在 Stack Overflow 上提了个问题
Error starting vreplication engine: error in connecting to mysql db with connection
https://stackoverflow.com/questions/68266523/error-starting-vreplication-engine-error-in-connecting-to-mysql-db-with-connect
原因:
不知道,猜测和内部的master选举机制有关。
解决:
默认 101_initial_cluster.yaml 中 keyspaces 的副本数 replicas 是 2, 启动后有一个 vttablet pod 不断重启。但是这种情况是可用的,能通过 vtgate 连接上数据库读写数据。
replicas 改成 1 也不行,单个 vttablet pod 自己不断重启,服务直接不可用。
replicas 改成 3 之后好了,三个 vttablet pod 很稳定。
修改默认库名commerce后无法启动
修改初始化脚本后无法启动
配置mysql数据目录
通过vschema创建sbtest1测试表(失败)
一开始想使用 vschema 的方式,创建 vschema_sysbench_sbtest1.json, 内容为
{
"sharded": true,
"vindexes": {
"hash": {
"type": "hash"
}
},
"tables": {
"sbtest1": {
"column_vindexes": [
{
"column": "id",
"name": "hash"
}
],
"columns": [
{
"name": "c",
"type": "CHAR"
},
{
"name": "pad",
"type": "CHAR"
}
]
}
}
}
这是 sysbench 中的默认测试用表。
然后利用 vtctlclient ApplyVSchema -vschema
将刚才的 schema json 格式应用到 commerce 数据库上。
vtctlclient -server=localhost:15999 -logtostderr ApplyVSchema -vschema="$(cat vschema_sysbench_sbtest1.json)" commerce
但执行后 show tables;
看不到刚创建的 sbtest1 表。
参考
Support sysbench oltp test vitess #3864
https://github.com/vitessio/vitess/issues/3864
直接sql创建sbtest1测试表(失败)
sbtest1 表结构如下:
CREATE TABLE sbtest1(
id INTEGER NOT NULL AUTO_INCREMENT,
k INTEGER DEFAULT '0' NOT NULL,
c CHAR(120) DEFAULT '' NOT NULL,
pad CHAR(60) DEFAULT '' NOT NULL,
PRIMARY KEY (id)
)
通过 vtctlclient ApplyVSchema -sql
将建表sql应用到 commerce 数据库上:
vtctlclient -server=localhost:15999 -logtostderr ApplySchema -sql "CREATE TABLE sbtest1( id INTEGER NOT NULL AUTO_INCREMENT, k INTEGER DEFAULT '0' NOT NULL, c CHAR(120) DEFAULT '' NOT NULL, pad CHAR(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) );" commerce
或者先通过 VTGate 登录 ./mysql -h 127.0.0.1 -P 15306 -uuser
再执行 create table 语句。
提示建表成功了,show tables;
可以看到,但增删查改数据时又提示表不存在
mysql> select * from sbtest1;
ERROR 1105 (HY000): table sbtest1 not found
又会提示表不存在。
stackoverflow 提了个问题
vitess kubernetes ERROR 1105 (HY000): table xxx not found
https://stackoverflow.com/questions/68392949/vitess-kubernetes-error-1105-hy000-table-xxx-not-found
将已存在的MySQL服务器添加到Vitess集群
Unmanaged Tablet
https://vitess.io/docs/user-guides/configuration-advanced/unmanaged-tablet/
问题与调优
事务超时回滚(默认30秒)
debug 时代码报错事务超时
15:01:29.133 [http-nio-8380-exec-10] DEBUG org.springframework.orm.jpa.JpaTransactionManager [902] - Initiating transaction rollback after commit exception
org.springframework.orm.jpa.JpaSystemException: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: vttablet: rpc error: code = Aborted desc = transaction 1624590190640986082: ended at 2021-06-25 15:00:36.039 CST (exceeded timeout: 30s) (CallerID: userData1); nested exception is javax.persistence.PersistenceException: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: vttablet: rpc error: code = Aborted desc = transaction 1624590190640986082: ended at 2021-06-25 15:00:36.039 CST (exceeded timeout: 30s) (CallerID: userData1)
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:418)
...
Caused by: javax.persistence.PersistenceException: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: vttablet: rpc error: code = Aborted desc = transaction 1624590190640986082: ended at 2021-06-25 15:00:36.039 CST (exceeded timeout: 30s) (CallerID: userData1)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:74)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
... 51 common frames omitted
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: vttablet: rpc error: code = Aborted desc = transaction 1624590190640986082: ended at 2021-06-25 15:00:36.039 CST (exceeded timeout: 30s) (CallerID: userData1)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
原因:
默认事务超时时间 30 s
解决:
设置 vttablet 的 -queryserver-config-transaction-timeout
参数,默认值 30
修改 vitess-11/examples/local/scripts/vttablet-up.sh 改为和 MySQL 相同的 50s
vttablet 启动命令中增加如下配置:
-queryserver-config-transaction-timeout 50 \
重启 Vitess 集群。
默认最大结果集10000
一个 SQL 中增删改的行数超过 10000 时报错:
mysql> delete from sbtest1 where id > 100005430;
ERROR 10001 (HY000): target: commerce.0.master: vttablet: rpc error: code = ResourceExhausted desc = caller id: userData1: row count exceeded 10000 (errno 10001) (sqlstate HY000) (CallerID: userData1): Sql: "delete from sbtest1 where id > :vtg1", BindVars: {#maxLimit: "type:INT64 value:\"10001\""vtg1: "type:INT64 value:\"100005430\""}
解决:
调整 -queryserver-config-max-result-size
参数,默认值 10000
事务并发数限制(默认值20)
超出事务连接池限制
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: target: abf.0.master: vttablet: rpc error: code = ResourceExhausted desc = transaction pool connection limit exceeded (CallerID: userData1)
at sun.reflect.GeneratedConstructorAccessor99.newInstance(Unknown Source)
解决:
设置 vttablet 的 queryserver-config-txpool-timeout
参数,增加事务超时时间
设置 vttablet 的 queryserver-config-transaction-cap
参数,增加连接数。默认值 20
修改 vitess-11/examples/local/scripts/vttablet-up.sh
vttablet 启动命令中增加如下配置:
-queryserver-config-transaction-cap 50000 \
重启 Vitess 集群。
Troubleshooting - Transaction connection limit errors
https://github.com/vitessio/website/blob/prod/content/en/docs/user-guides/configuration-basic/troubleshooting.md
修改Vitess启动的MySQL连接数等其他配置
只增大 vttablet 的事务并发数并不能解决问题,sysbench 压测并发增加时还会报错:
FATAL: mysql_stmt_execute() returned error 1040 (target: commerce.0.master: vttablet: rpc error: code = ResourceExhausted desc = Too many connections (errno 1040) (sqlstate 08004) (CallerID: userData1): Sql: "begin", BindVars: {}) for query 'UPDATE sbtest1 SET k=k+1 WHERE id=?'
FATAL: `thread_run' function failed: /usr/local/share/sysbench/oltp_common.lua:458: SQL error, errno = 1040, state = '08004': target: commerce.0.master: vttablet: rpc error: code = ResourceExhausted desc = Too many connections (errno 1040) (sqlstate 08004) (CallerID: userData1): Sql: "begin", BindVars: {}
原因:
MySQL 连接数过大,因为 mysqlctl 默认启动的 MySQL 实例最大连接数为 500 max_connections = 500
在 vtdataroot/vt_0000000100/my.cnf 中可看到。
解决:
1、/home/vitess/vitess-11/examples/local/scripts 目录中创建 my-custom.cnf
配置:
max_connections = 50000
innodb_buffer_pool_size=50G
max_prepared_stmt_count = 50000
2、修改 /home/vitess/vitess-11/examples/local/scripts/mysqlctl-up.sh 最后的启动命令前加上 EXTRA_MY_CNF="/home/vitess/vitess-11/examples/local/scripts/my-custom.cnf"
EXTRA_MY_CNF="/home/vitess/vitess-11/examples/local/scripts/my-custom.cnf" mysqlctl \
-log_dir $VTDATAROOT/tmp \
-tablet_uid $uid \
-mysql_port $mysql_port \
$action
重启 Vitess 集群。
注意:(1)这里 EXTRA_MY_CNF 中必须是绝对路径,相对路径不起作用。(2)生成的 my.cnf 中原来的 max_connections 没有被覆盖,而是有两个 max_connections 配置项,下面的是 max_connections = 50000
, 登录 VTGate 后看 show variables like '%max_connections%';
值为 50000 是生效的。
vitess用户最大线程数限制
sysbench 压测时还会报错
FATAL: mysql_stmt_execute() returned error 2012 (target: commerce.0.master: vttablet: rpc error: code = Unknown desc = immediate error from server errorCode=1135 errorMsg=Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug (errno 2012) (sqlstate HY000) (CallerID: userData1): Sql: "begin", BindVars: {}) for query 'UPDATE sbtest1 SET k=k+1 WHERE id=?'
FATAL: `thread_run' function failed: /usr/local/share/sysbench/oltp_common.lua:458: SQL error, errno = 2012, state = 'HY000': target: commerce.0.master: vttablet: rpc error: code = Unknown desc = immediate error from server errorCode=1135 errorMsg=Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug (errno 2012) (sqlstate HY000) (CallerID: userData1): Sql: "begin", BindVars: {}
原因:
达到 vitess 用户最大进程/线程数限制,注意这里是用 vitess 用户启动的 MySQL 实例。
vitess 用户下 ulimit -a
看到最大进程数为 4096
max user processes (-u) 4096
解决:
1、CentOS 5 及之前版本:vi /etc/security/limits.conf
添加:
* hard nproc 65536
* soft nproc 65536
2、CentOS 6 及之后版本中,修改 /etc/security/limits.conf
并不会生效,因为被 /etc/security/limits.d/20-nproc.conf
中下面的配置覆盖了
* soft nproc 4096
root soft nproc unlimited
应该将上述配置添加到 /etc/security/limits.d/20-nproc.conf
中
Can’t create a new thread (errno 11)
https://sites.google.com/site/sysknife8/database/cantcreateanewthreaderrno11
Can’t Create Thread: Errno 11 (A Tale of Two Ulimits)
https://www.percona.com/blog/2013/02/04/cant_create_thread_errno_11/
直接高并发压测会报错
vitess 集群启动后,直接使用 200+(具体阈值不清楚) 的并发压测,会报下面的错误,但如果从 100 并发逐渐加并发,就没有问题,逐渐增加到 4000+ 并发都没问题。
单机版的 MySQL 好像也有这个问题。
FATAL: mysql_stmt_execute() returned error 2002 (target: commerce.0.master: vttablet: rpc error: code = Unknown desc = net.Dial(/home/vitess/vtdataroot/vt_0000000100/mysql.sock) to local server failed: dial unix /home/vitess/vtdataroot/vt_0000000100/mysql.sock: connect: resource temporarily unavailable (errno 2002) (sqlstate HY000) (CallerID: userData1): Sql: "begin", BindVars: {}) for query 'UPDATE sbtest1 SET k=k+1 WHERE id=?'
FATAL: `thread_run' function failed: /usr/local/share/sysbench/oltp_common.lua:458: SQL error, errno = 1105, state = 'HY000': target: commerce.0.master: vttablet: rpc error: code = Unavailable desc = tx engine can't accept new connections in state Transitioning (CallerID: userData1)
FATAL: mysql_stmt_execute() returned error 1105 (target: commerce.0.master: vttablet: rpc error: code = Unavailable desc = tx engine can't accept new connections in state Transitioning (CallerID: userData1)) for query 'UPDATE sbtest1 SET k=k+1 WHERE id=?'
报出上面错误时,vttablet 启动参数中已经将下面几个连接池设为 10000 了,没有用,还是会报错:
-queryserver-config-transaction-cap 10000 \
-queryserver-config-pool-size 10000 \
-queryserver-config-stream-pool-size 10000 \
-app_pool_size 10000 \
优劣势
优势:
- 自动增减分片扩缩容量
- 丰富的运维工具
- 兼容MySQL协议,应用无需改动
劣势:
- 架构复杂,部署、运维难度大
- 资料较少,社区不活跃,官网曾半个月无法打开,遇到问题很难排查
使用sysbench对Vitess进行基准测试
Vitess官方benchmark
Are We Fast Yet 页面上的测试结果分为 micro 和 macro:
micro 是单函数微型测试,用 go 实现,数据日更。
macro 是端到端整体测试,用 sysbench 实现。
京东如何基于Vitess管理大型MySQL实例集群
https://developer.jdcloud.com/article/1159
上一篇 Snack3
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: