本文作者:吴永健 https://tidb.net/u/banana_jian
TiDB 6.0 版本正式提供了基于 SQL 接口的数据放置框架(Placement Rules in SQL), 特性用于通过 SQL 接口配置数据在 TiKV 集群中的放置位置。通过该功能,用户可以将表和分区指定部署至不同的地域、机房、机柜、主机。适用场景包括低成本优化数据高可用策略、保证本地的数据副本可用于本地 Stale Read 读取、遵守数据本地要求等。它支持针对任意数据提供副本数、角色类型、放置位置等维度的灵活调度管理能力,这使得在多业务共享集群、跨 AZ 部署等场景下,TiDB 得以提供更灵活的数据管理能力,满足多样的业务诉求。
该功能可以实现以下业务场景:
使用放置规则时有 2 种方式
(1) 直接放置
直接放置是指在 create table 时或使用 alter table 方式直接在表的 DDL 语句中使用放置选项
create table jian(id int) primary_region='bj' folllowers=4
(2) 放置策略
使用放置策略时首先通过 create placement policy 建立放置策略,然后在 create table 或 alter table 中直接指定放置策略。
create placement policy location_policy primary_region='bj' folllowers=4;
alter table jian placement policy location_policy;
使用时: 创建放置策略会使 placement policy 更加易于管理,通过修改放置策略可以直接更新所有使用该策略的对象。另一方面对于 create table 时使用和 alter table 时指定,这里也建议大家能注意以下两点:
由于 Placement Rules in SQL 的灵活性,在使用时可以“因地适宜”。以下是几个可以考虑的场景:
下面我们来详细看看 placement policy 的使用方法:
当前 tikv 节点以及集群的信息如下:
ID Role Host Ports OS/Arch Status Data Dir Deploy Dir
\\-- ---- ---- ----- ------- ------ -------- ----------
192.168.135.148:9093 alertmanager 192.168.135.148 9093/9094 linux/x86_64 Up /tidb-data/alertmanager-9093 /tidb-deploy/alertmanager-9093
192.168.135.148:3000 grafana 192.168.135.148 3000 linux/x86_64 Up - /tidb-deploy/grafana-3000
192.168.135.148:2379 pd 192.168.135.148 2379/2380 linux/x86_64 Up|L|UI /tidb-data/pd-2379 /tidb-deploy/pd-2379
192.168.135.148:9090 prometheus 192.168.135.148 9090/12020 linux/x86_64 Up /tidb-data/prometheus-9090 /tidb-deploy/prometheus-9090
192.168.135.148:4000 tidb 192.168.135.148 4000/10080 linux/x86_64 Up - /tidb-deploy/tidb-4000
192.168.135.148:20160 tikv 192.168.135.148 20160/20180 linux/x86_64 Up /tidb-data/tikv-20160 /tidb-deploy/tikv-20160
192.168.135.148:20161 tikv 192.168.135.148 20161/20181 linux/x86_64 Up /tidb-data/tikv-20161 /tidb-deploy/tikv-20161
192.168.135.148:20162 tikv 192.168.135.148 20162/20182 linux/x86_64 Up /tidb-data/tikv-20162 /tidb-deploy/tikv-20162
这里有一点需要大家注意一下:默认的 PLACEMENT POLICY 是需要以 region 来作为区分标签的,所以在创建 tikv 的时候这里需要明确的指定 tikv 的 region 的标签,不然的话在show placement labels 是无法看到 region lable 的。这里可以参照官方文档的建议
这里创建一个 PLACEMENT POLICY 使其 PRIMARY_REGION 放置在 region lable 为 bj 的 tikv 节点上,其余副本放置在 region lable 为 dl,sz 的 tikv 节点上
注意:primary region 必须包含在 region 的定义中
此处的 Raft leader 在 4 号 store 上,看之前开头的环境信息可以验证 PLACEMENT POLICY 已经生效
(root\\@127.0.0.1) \\[test] 12:00:14> CREATE PLACEMENT POLICY jianplacementpolicy PRIMARY_REGION="bj" REGIONS="bj,dl,sz";
Query OK, 0 rows affected (0.10 sec)
(root\\@127.0.0.1) \\[test] 12:00:32> CREATE TABLE jian1 (id INT) PLACEMENT POLICY=jianplacementpolicy;
Query OK, 0 rows affected (0.10 sec)
(root\\@127.0.0.1) \\[test] 12:03:36> show table jian1 regions\\G
REGION_ID: 135
START_KEY: t_68\\_
END_KEY: t_69\\_
LEADER_ID: 137
LEADER_STORE_ID: 4 这里可以看到store_id是4
PEERS: 136, 137, 138
SCATTERING: 0
WRITTEN_BYTES: 39
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.00 sec)
leader 的 store 节点由原来的 1 变为了 4,看之前开头的环境信息可以验证 PLACEMENT POLICY 已经生效,可使用这个特性来修改表的leader节点或者当有热点问题时也可以变相的通过这种方式去修改频繁访问的表的leader所在的tikv的节点位置
(root\\@127.0.0.1) \\[test] 12:03:39> create table jian2(id int);
Query OK, 0 rows affected (0.10 sec)
(root\\@127.0.0.1) \\[test] 12:05:14> show table jian2 regions\\G
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 1. row \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
REGION_ID: 2
START_KEY: t_70\\_
END_KEY:
LEADER_ID: 3
LEADER_STORE_ID: 1
PEERS: 3, 63, 85
SCATTERING: 0
WRITTEN_BYTES: 0
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.00 sec)
(root\\@127.0.0.1) \\[test] 12:05:16> alter table jian2 PLACEMENT POLICY=jianplacementpolicy;
Query OK, 0 rows affected (0.09 sec)
(root\\@127.0.0.1) \\[test] 12:05:50> show table jian2 regions\\G
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 1. row \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
REGION_ID: 143
START_KEY: t_70\\_
END_KEY: t_71\\_
LEADER_ID: 145
LEADER_STORE_ID: 4
PEERS: 144, 145, 146
SCATTERING: 0
WRITTEN_BYTES: 0
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.00 sec)
Follower 的数量。例如 FOLLOWERS=2 表示数据有 3 个副本(2 个 follower 和 1 个 leader)。
(root\\@127.0.0.1) \\[test] 12:10:34> alter PLACEMENT POLICY jianplacementpolicy FOLLOWERS=1;
Query OK, 0 rows affected (0.11 sec)
(root\\@127.0.0.1) \\[test] 12:10:40> show table jian2 regions\\G
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 1. row \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
REGION_ID: 143
START_KEY: t_70\\_
END_KEY: t_71\\_
LEADER_ID: 145
LEADER_STORE_ID: 4
PEERS: 144, 145 **副本数从 3 个已经调整到了 2 个
**
SCATTERING: 0
WRITTEN_BYTES: 0
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.00 sec)
(root\\@127.0.0.1) \\[test] 12:10:44> alter PLACEMENT POLICY jianplacementpolicy FOLLOWERS=2;
Query OK, 0 rows affected (0.09 sec)
(root\\@127.0.0.1) \\[test] 12:10:59> show table jian2 regions\\G
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 1. row \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
REGION_ID: 143
START_KEY: t_70\\_
END_KEY: t_71\\_
LEADER_ID: 145
LEADER_STORE_ID: 4
PEERS: 144, 145, 148
SCATTERING: 0
WRITTEN_BYTES: 0
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.02 sec)
注意:修改定义时需要将原来的定义都带上否则会将其覆盖 这一点在官方文档中并没有特殊说明,也是自己在测试这个功能的时候偶然的发现,目前官方也没有直接修改的语法,所以大家在修改放置规则的时候一定要注意之前的定义以免将之前的定义覆盖。
##########################################################
之前的 PRIMARY_REGION="bj" REGIONS="bj,dl,sz" 定义已经被覆盖了
##########################################################
如果 PRIMARY_REGION 的 tikv 节点宕机,那么 leader 节点也会转移到非 PRIMARY_REGION 节点,当 tikv 节点恢复正常后 leader 节点也会随之转移回来
以下的过程
leader 节点:store4-- 停止 store 的 tikv ---》store1 -- 恢复 tikv 节点-- 》store4
如果更改表当前 palcement policy 定义的 primary region 那么表的 leader 也会随 PRIMARY_REGION 的改变而改变
下图 jian1 表一开始的 region 1005 的 leader 是在 store4(bj)上边,之后修改其 PRIMARY_REGION 为 dl(store 1),可以看到 region 1005 的 leader 也确实随之发生了改变
以下样例中我们手动指定了每一个分区的 PLACEMENT POLICY,使其每个分区的 leader 都存放于不同的 store 上。
CREATE PLACEMENT POLICY policy_table FOLLOWERS=3;
CREATE PLACEMENT POLICY policy_dl PRIMARY_REGION="dl" REGIONS="dl,bj,sz";
CREATE PLACEMENT POLICY policy_bj PRIMARY_REGION="bj" REGIONS="dl,bj,sz";
CREATE PLACEMENT POLICY policy_sz PRIMARY_REGION="sz" REGIONS="dl,bj,sz" FOLLOWERS=1;
SET tidb_enable_list_partition = 1;
CREATE TABLE t1 (
location VARCHAR(10) NOT NULL,
userdata VARCHAR(100) NOT NULL
) PLACEMENT POLICY=policy_table PARTITION BY LIST COLUMNS (location) (
PARTITION p_dl VALUES IN ('dl') PLACEMENT POLICY=policy_dl,
PARTITION p_bj VALUES IN ('bj') PLACEMENT POLICY=policy_bj,
PARTITION p_sz VALUES IN ('sz') PLACEMENT POLICY=policy_sz
);
下图可一看到t1的region分别存放在store 1(dl),4(bj),5(sz)上边
更改默认的放置选项,但更改不影响已有的表
创建新表会自动继承当前数据的放置规则
表级别的放置规则要优先于数据库级别的放置规则
注意:PRIMARY_REGION、REGIONS 和 SCHEDULE 选项不可与 CONSTRAINTS 选项同时指定,否则会报错
以下 placement policy 的解读为:
- 使用该规则的表的 region 只可以放置在含有 rack 标签且等于 rack1 的 tikv 节点上
- 使用该规则的表的 leader region 只可以放置在含有 dc 标签且等于 bja 的 tikv 节点上
- 使用该规则的表的 follower region 只可以放置在含有 dc 标签且等于 dla 的 tikv 节点上
(root\\@127.0.0.1) \\[(none)] 16:34:28> create placement policy localtion_policy CONSTRAINTS="\\[+rack=rack1]" LEADER_CONSTRAINTS="\\[+dc=bja]" FOLLOWER_CONSTRAINTS="{+dc=dla: 1}";
Query OK, 0 rows affected (0.10 sec)
(root\\@127.0.0.1) \\[(none)] 16:35:41> create table testdb.jian2(id int) placement policy=localtion_policy;
Query OK, 0 rows affected (0.19 sec)
(root\\@127.0.0.1) \\[(none)] 16:35:49> show table testdb.jian2 regions\\G
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* 1. row \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
REGION_ID: 1127
START_KEY: t_167\\_
END_KEY: t_168\\_
LEADER_ID: 1129
LEADER_STORE_ID: 4
PEERS: 1128, 1129
SCATTERING: 0
WRITTEN_BYTES: 0
READ_BYTES: 0
APPROXIMATE_SIZE(MB): 1
APPROXIMATE_KEYS: 0
1 row in set (0.01 sec)
注意:虽然 placement policy 高级匹配规则的默认 followers 是 2(三副本)但是实际的副本数还是要看符合 lable 的 tikv 的数量,如果实际的tikv节点数量无法满足2followers 那么最终也只会有两个副本(也就是只有一个followers和一个leader)上边的查询结果可以看到实际 region 的副本只有两个,但是当查询 localtion_policy 这个规则定义的时候 followers 为 2
POLICY_ID: 17
CATALOG_NAME: def
POLICY_NAME: localtion_policy
PRIMARY_REGION:
REGIONS:
CONSTRAINTS: \\[+rack=rack1]
LEADER_CONSTRAINTS: \\[+dc=bja]
FOLLOWER_CONSTRAINTS: {+dc=dla: 1}
LEARNER_CONSTRAINTS:
SCHEDULE:
FOLLOWERS: 2
LEARNERS: 0
| 选项名 | 描述 |
| PRIMARY_REGION | Raft leader 被放置在有 region 标签的节点上,且这些 region 标签匹配本选项的值。 |
| REGIONS | Raft followers 被放置在有 region 标签的节点上,且这些 region 标签匹配本选项的值。 |
| SCHEDULE | 用于调度 follower 放置位置的策略。可选值为 EVEN(默认值)或 MAJORITY_IN_PRIMARY。 |
| FOLLOWERS | Follower 的数量。例如 FOLLOWERS=2 表示数据有 3 个副本(2 个 follower 和 1 个 leader)。 |
| CONSTRAINTS | 适用于所有角色 (role) 的约束列表。例如,CONSTRAINTS="[+disk=ssd]"。 |
| LEADER_CONSTRAINTS | 仅适用于 leader 的约束列表。 |
| FOLLOWER_CONSTRAINTS | 仅适用于 follower 的约束列表。 |
| LEARNER_CONSTRAINTS | 仅适用于 learner 的约束列表。 |
| LEARNERS | 指定 learner 的数量。 |
删除 placement policy 时一定要确保没有任何表在使用当前的 placement policy 否则会报错
(root\\@127.0.0.1) \\[test] 12:11:43> drop PLACEMENT POLICY jianplacementpolicy;
ERROR 8241 (HY000): Placement policy 'jianplacementpolicy' is still in use
(root\\@127.0.0.1) \\[test] 12:16:20> alter table jian1 PLACEMENT POLICY=default;
Query OK, 0 rows affected (0.08 sec)
查看某个 placement policy 是否正在被表使用
SELECT table\\_schema, table\\_name FROM information\\_schema.tables WHERE tidb\\_placement\\_policy\\_name='jianplacementpolicy';
SELECT table\\_schema, table\\_name FROM information\\_schema.partitions WHERE tidb\\_placement\\_policy\\_name='jianplacementpolicy';
当前版本在使用 Placement Rules in SQL 时如果使用基本的放置规则那么只可以使用 PRIMARY_REGION 和 REGIONS 来进行放置规则的设置,但是如果使用高级放置规则那么 tikv 的 label 标签不需要必须设置 region 层级的标签,可以灵活使用和定义已存在或者需要的标签。
高级放置规则的默认 followers 的数量为 2,但是如果在设置规则 FOLLOWER_CONSTRAINTS 时如果满足的节点不满足 2 时只会在 FOLLOWER_CONSTRAINTS 匹配的节点上创建副本,这一点在创建时一定要规划好自己的集群中的 tikv 节点的标签设计,以免导致 region 的副本数过少。
Placement Rules in SQL 可以通过它对分区 / 表 / 库不同级别的数据进行基于标签的自由放置。
总之 TiDB 6.0 的 Placement Rules In SQL 暴露了以往用户无法控制的内部调度能力,并提供了方便的 SQL 接口,这开启了诸多以往不可能实现的场景,更多的运用方式与使用场景还期待各位的发掘。
如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!
赞0
添加新评论0 条评论