架构
JED 平台包括许多服务器进程,命令行工具, 基于 Web 的工具和一致的元数据存储支持
基本概念
KeySpace
keyspace 是逻辑上的数据库,在非 shard 场景下,一个 keyspace 对应一个 MYSQL DataBase。从 Keyspace 中读取数据和从一个 MYSQL DataBase 中读取数据很像。但是根据读取数据时不同的一致性要求,可以从一个 master database 或者从一个 replica 读取数据。当一个 keySpace 被 sharding 成多个 Shard 时,一个 keyspace 会对应多个 MYSQL database。在这种情况下一个查询会被路由到一个或者多个 shard 上,这取决于请求的数据所在的位置。
Shard
一个 shard 就是在一个 keyspace 中的一个 horizontal partition 。每个 shard 都会包含一个 master 实例和多个 replica 实例,目前一个 shard 包含一个 Master 实例,一个Replica 实例和一个 ReadOnly 实例,但是若支持半同步复制则需要在一个 Shard 中包含一个 Master 实例,两个 Replica 实例和一个 ReadOnly 实例。而且不同的 shard 之间绝对不会存在数据重叠的现象。
综上所属 DataBase、KeySpace 和 Shard 的关系图如下所示:
从上面的图中可以看出一个 KeySpace 可以跨越多个 Mysql 实例,而且每个 Shard 中的数据都是 KeySpace 中的一个数据子集,一个 KeySpace 由分散在各个 Shard 中的数据子集组成。每个 Shard 在物理硬件上由多个 MySQL 实例和 JED-Tablet 实例组成,一个 shard 具体包括:一个 Master Mysql 实例、一个 Replica MySQL 实例、一个 ReadOnly MySQL 实例和三个 JED-Tablet 实例。
a. Topology
Topology 服务是一个包含服务器信息,分片方案和主从信息的元数据信息存储服务。Topology 服务是基于一致性存储方案来实现数据一致性, 例如:zookeeper 和 etcd。用户可以通过使用 vtctl(命令行)和vtctld(web)访问 Topology。
b. JED-Gate
JED-Gate 是一个轻量代理服务器,它将查询路由到正确的 JED-Tablet 并将合并的结果返回给客户端。 JED-Gate 接收到请求时会对 sql 进行解析,根据 Topology 服务中的元数据判断路由到哪个 JED-Tablet;JED-Gate 是直接接受应用程序发起的查询的服务。 因为客户端只需要能够找到一个vtgate 实例即可正常访问,所以客户端的实现可以非常简单。
c. JED-Tablet
Vttablet是一个位于 MySQL 数据库实例前面的代理服务器,并且要求与对应的 MYSQL 实例部署在同一个 Pod 上。在 JED 中对于每个 MySQL 实例对应的都有一个 JED-Tablet 实现;
d. Vtctl
Vtctl 是用于管理弹性数据库集群的命令行工具。 它允许人或应用程序轻松地与弹性数据库实现交互。通过 Vtctl 可以标识主从数据库,创建表,启动故障转移,执行分片(重新分片)等操作。
e. vtctld
vtctld 是一个 HTTP 服务器,允许您浏览存储在锁服务器中的信息。
客户端
客户端相关
a. vtctlclient
弹性数据库环境搭建起来之后可以直接通过官方提供的客户端进行基本功能的校验, 以下对主要的功能点进行说明。
使用方法:
vtctlclient -server 127.0.0.1:15999 command param
i. Tablets
1. InitTablet
在 topoServer 上初始化 tablet
例如:
vtctlclient -server localhost:15999 InitTablet -hostname=172.22.189.67 -port=9999 -keyspace=test_keyspace -shard=0 test-105 RDONLY
2. GetTablet
获取 tablet 信息
例如:
vtctlclient -server localhost:15999 GetTablet test-100
返回:
{ “alias”: { “cell”: “test”, “uid”: 100 }, “hostname”: “A01-R06-I189-67.JD.LOCAL”, “ip”: “127.0.0.1”, “port_map”: { “grpc”: 16100, “mysql”: 17100, “vt”: 15100 }, “keyspace”: “test_keyspace”, “shard”: “0”, “type”: 1 }
3. UpdateTabletAddrs
更新 tablet 地址信息, 可以更新 hostname, ip, 端口等信息
例如:
vtctlclient -server localhost:15999 UpdateTabletAddrs -hostname 172.22.189.67 -ip-addr 172.22.189.67 test-100
4. DeleteTablet
删除 tablet
例如:
vtctlclient -server localhost:15999 DeleteTablet test-105
5. SetReadOnly
设置 tablet 为 ReadOnly
例如:
vtctlclient -server localhost:15999 SetReadOnly test-104
6. SetReadWrite
设置 table 为 ReadWrite
例如:
vtctlclient -server localhost:15999 SetReadWrite test-104
这个命令貌似不太好用, 调用后状态没有改变
7. 其他
其他还有很多相关命令, 参见
http://jed.io/reference/vtctl.html#tablets
ii. Shards
1. CreateShard
创建分片
例如:
vtctlclient -server localhost:15999 CreateShard test_keyspace/-b0
2. GetShard
获取分片信息
例如:
vtctlclient -server localhost:15999 GetShard test_keyspace/-80
返回:
{“master_alias”:{“cell”:“test”,“uid”:200},“key_range”: {“end”: “gA==”},“served_types”: [{“tablet_type”: 3},“tablet_type”: 2}],“source_shards”: [{“keyspace”: “test_keyspace”,“shard”: “0”}],“cells”: [“test”]}
3. ListShardTablets
列出指定分片中的所有 tablet
4. DeleteShard
删除指定的分片。在递归模式下,它还会删除属于该分片的 tablet。否则,分片中必须
没有 tablet。
5. InitShardMaster
初始化分片集群中的主从关系
6. 其他
其他命令详细说明参见 http://jed.io/reference/vtctl.html#shards
iii. Keyspaces
1. CreateKeyspace
创建指定的键空间
例如:
vtctlclient -server localhost:15999 CreateKeyspace new_keyspacke
2. DeleteKeyspace
删除指定的键空间
例如:
vtctlclient -server localhost:15999 DeleteKeyspace new_keyspacke
3.其他
其他命令详细参见官方参考
http://jed.io/reference/vtctl.html#keyspaces
服务器
a. 连接池
JED 连接池实现是基于自己实现的一个资源池创建的。 资源池的实现是基于 chan 实现的支持生产者消费者模型的同步队列, 而且是个不需要销毁的队列,golang 会自动将不再使用的 channel 当垃圾回收掉。其实池也是生产者消费者模型;所以需要外部提供生产资源的方法。 具体的资源池的实现可以参考源码实现 ResourcePool 的实现。
基于通用池 ResourcePool 的实现 JED 中的连接池实现很简洁, JED 连接池中的连接都是针对具体数据库的连接(这点和 jproxy 不同, jproxy 使用的是基于 MySQL 实例的连接池而不是具体 database 的连接);对于连接池中连接的存取操作, 获取连接的时候如果有连接就会直接返回, 如果当前连接不够用了就会一直在等待,直到有可用连接了才返回。 如下图所示: JED 连接池同时包含 connections 和 DBAConnPool, DBAConnPool 是用于终止连接的 dba 连接池。连接池的主要结构如下:
b. 网络模型
JED 服务通过 proto3 服务定义方式对外提供接口支持。 JED 支持 gRPC, 各主要服务间的通信是通过 gRPC 框架来实现。 gRPC 是一个现代开源的高性能 RPC 框架,可以在任何环境中运行。 它可以提供负载均衡、健康检查和身份验证等可插拔式的插件支持,他可以有效地连接数据中心内和数据中心之间的服务; 同时他也可以应用于设备间的分布式计算、移动端、web 端等。
grpc 主要有以下特点:
- 强大的接口描述语言(Powerful IDL)Protocol Buffers 是一个强大的二进制序列化
工具集和语言,你可以使用 Protocol Buffers 定义你的接口 - 支持十种语言的类库为各种语言编写的服务自动生成相应语言的客户端和服务端接口
- 基于 HTTP2 协议基于 HTTP2 标准设计,带了许多诸如双向流、流程控制、头部压缩、
单 TCP 连接上的多路复用请求等特性。 这些特性使得其在移动设备上表现更好,更省电
和节省空间占用,同时加速了运行在 cloud 上的服务和 web 应用。
由于 JED 的高性能服务器都是使用 gRPC, 这里只对 gRPC 做个简单的介绍, 有关 gRPC的使用和介绍详见 grpc.io。gRPC 网络高性能主要是基于新的协议 HTTP2, http2 是一个二进制协议, 每个流都包含一个优先级(也就是“权重”),它被用来告诉对端哪个流更重要。当资源有限的时候,服务器会根据优先级来选择应该先发送哪些流。http2 同时还支持多路复用, 可以极大的提升性能。
路由
分片类型:
a. Range-based Sharding
该分片方式跟 jproxy 类似,每个分库代表一定的范围值(即计算出的路由值如果在该分库所代表的范围内,那么就可以确定该数据属于该分库)确定分库所使用的路由值是由路由字段的原始值经过计算得到的字节数组,jed 将[0x80]作为拆分键的中间值。
例如:假设共两个分库,那么路由值小于 0x80 的在一个分库,路由值大于或等于 0x80 的在另一个分库。
分库名字与所代表值的范围保持一致:
例如:假设共四个分库,那么四个分库名分别为:-40;40-80; 80-c0; c0-
b. Custom Sharding
该分片方式类似于 jproxy 的特殊路由,为某些特定的路由字段值,指定某些分库,这种分片方式不支持 resharding。
Range-based 路由计算:
- GetPlan:这个阶段中,会进行 sql 的解析并创建 sql 的执行计划;
- ExecuteRoute:根据*engine.Route.Opcode(创建执行计划时进行赋值)选择相应的
方法; - Map:对路由字段值进行 hash 计算,得到 keyspaceid,进而确定具体分库;
c. 路由配置
zookeeper
(1) CreateKeyspace:创建一个 keyspace(目前理解是逻辑库),伴随着会创建 文件
夹用于存放相关信息,包括该逻辑库后面的物理分库信息及发生的相关操作日志;除此,
还有相关的改,查及删除 keyspace 方法。
(2) SaveVSchema:配置某个 keyspace 的表及其路由字段,同样会伴随创建文件夹用于
存放表及其路由字段的信息。
目录结构:
keyspace
├── action
├── actionlog
├── shards
└── vschema
案例: 假设需要将数据库 db_order 中的 order 表拆分到两个分库: 根据现有脚本做
修改:
keyspace='db_order' shard=${SHARD:-'-80'} uid_base=${UID_BASE:-'100'} 执行
vttablet 脚本,启动 mysql 服务及其绑定的 vttablet 服务 再次修改:
keyspace='db_order' shard=${SHARD:-'80-'} uid_base=${UID_BASE:-'200'} 执行
vttablet 脚本 为第一个分库选主:./lvtctl.sh InitShardMaster -force db_order/-
80 test-100 为第一个分库选主:./lvtctl.sh InitShardMaster -force db_order/80-
test-200 建表:./lvtctl.sh ApplySchema -sql "$(cat create_order.sql)"
db_order 编辑 vschema.json:{ "sharded": true, "vindexes": { "hash": { "type":
"hash" } }, "tables": { "order": { "column_vindexes": [ { "column": "id",
"name": "hash" } ] } } } 配置拆分键信息:./lvtctl.sh ApplyVSchema -vschema
"$(cat vschema.json)" order_db
Sharding
归纳为水平拆分和垂直拆分,重点水平拆分包括连续的 key_range 拆分和多个 key_range集拆分
a. 垂直拆分
将业务相关的表放在同一个 keyspace 里边,相当于一个库。实际只是做了业务拆分。举例如用户逻辑和商品逻辑分开。在 keyspace 内部可以指定一个 key 关键字来做行数据的关联(是否和缓存有关)单个 keyspace 内部可以 join,跨 keyspace 的 join 不建议。不建议使用跨 keyspace 的事务。keyspace 可以称作“特征域”或“特征集”
b. 水平拆分
1. 元素定义
shard,即分片表示是一个 keyspace 内的水平拆分块。一个 shard 相当于一套独立的主从、只读的 mysql 实例。keyid 标识数据特征。
2. 拆分方案
一个 keyspace 即表示一个 shard,属于 noshard。多个 shard,可以通过不同的连接池连接,应用将查询语句指定到特定的分片。定义为custom,自定义路由表,类似“特殊路由”,但是无法兼容 range-based 的管理工具,且只有 executeShard , BatchShard, StreamShard 几种执行方式(细节?)基于 range 的拆分。每个记录包含一个拆分键,每个 shard 即分片包含了设定的范围的拆分键,拆分范围可以动态重构。定义为 range-based。注意每个 keyspace 有个起始范围和结束范围皆为空的 shard(作用)重新改拆分 resharding
3. 特征
不中断,有短暂不可写。多数据中心过滤复制,包含源 tablet 添加 binlog 事务标注,目标 tablet 执行过滤后的语句。
4. 过程
定义新的拆分规则 vschema。创建或配置新的 tablet。复制旧的数据。迁移配置,中间切换主库,短暂主库 master 不可写。
join 实现
join 实现过程
limitation: 只支持单库 join
advantages: query plan & pushing down to filter row on back-storage [like
MySQL or others]
排序实现
order by 实现
limitation: 只支持单路由的排序 advantages: no pressure an caculate on vtgate