From b9fbe9683a9bc61c104bd8bab22e01c308cd578b Mon Sep 17 00:00:00 2001 From: disksing Date: Tue, 31 Mar 2020 19:47:39 +0800 Subject: [PATCH 01/16] how-to: add placement rule document Signed-off-by: disksing --- TOC.md | 1 + how-to/configure/placement-rules.md | 294 ++++++++++++++++++++++++++++ media/placement-rules-1.png | Bin 0 -> 50843 bytes 3 files changed, 295 insertions(+) create mode 100644 how-to/configure/placement-rules.md create mode 100644 media/placement-rules-1.png diff --git a/TOC.md b/TOC.md index 5a5dc5e36362..6800d390f891 100644 --- a/TOC.md +++ b/TOC.md @@ -42,6 +42,7 @@ + 配置 - [时区](/how-to/configure/time-zone.md) - [内存控制](/how-to/configure/memory-control.md) + - [Placement Rules](/how-to/configure/placement-rules.md) + 安全 + 安全传输层协议 (TLS) - [为 MySQL 客户端开启 TLS](/how-to/secure/enable-tls-clients.md) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md new file mode 100644 index 000000000000..790a490a8c46 --- /dev/null +++ b/how-to/configure/placement-rules.md @@ -0,0 +1,294 @@ +--- +title: Placement Rules 使用文档 +summary: 如何配置 Placement Rules +category: how-to +--- + +# Placement Rules 使用文档 + +Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本规则系统,用于指导 PD 针对不同类型的数据生成对应的调度。通过组合不同的调度规则,用户可以精细地控制任何一段连续数据的副本数量、存放位置、主机类型、是否参与 Raft 投票、是否可以担任 Raft leader 等。 + +## 规则系统介绍 + +整个系统的配置由多条规则组成,我们把规则叫做 Rule。每条 Rule 可以指定不同的副本数量、Raft 角色、放置位置等属性,以及这条规则生效的 key range。PD 在进行调度时,会先根据 Region 的 key range 在规则系统中查到它对应的规则,然后再生成对应的调度,来使得 Region 副本的分布情况符合 Rule。 + +值得注意的是,多条规则的 key range 是允许有重叠部分的,即一个 Region 能匹配到多条规则,这种情况下 PD 根据 Rule 的属性来决定规则是相互覆盖还是同时生效,如果有多条规则同时生效,PD 会按照规则的堆叠次序依次去生成调度进行规则匹配。 + +此外,为了满足不同来源的规则相互隔离的需求,还引入了*分组(Group)*的概念,如果某条规则不希望与系统中的其他规则相互影响(比如被覆盖),可以使用单独的分组。 + +![Placement rules overview](/media/placement-rules-1.png) + +### 规则字段 + +以下是每条规则各个字段的具体含义: + +| 字段名 | 类型及约束 | 说明 | +| :--- | :--- | :--- | +| GroupID | string | 分组 ID,标识规则的来源 | +| ID | string | 分组内唯一 ID | +| Index | int | 分组内堆叠次序 | +| Override | true/false | 是否覆盖 index 更小的 Rule (限分组内) | +| StartKey | string,hex 编码 | 适用 Range 起始 key | +| EndKey | string,hex 编码 | 适用 Range 终止 key | +| Role | string,leader/follower/learner | 副本角色 | +| Count | int,正整数 | 副本数量 | +| LabelConstraint | []Constraint | 用于按 label 筛选节点 | +| LocationLabels | []string | 用于物理隔离 | + +其中 LabelConstraint 之前没有介绍过,它参考了 Kubernetes 中类似的功能,支持通过 in, notIn, exists, notExists 四种原语来筛选 label,这四种原语分别对应 “给定 key 的 label value 包含在给定列表中”,“给定 key 的 label value 不包含在给定列表中”,“包含给定的 label key”,以及“不包含给定的 label key”。 + +LocationLabels 的意义和作用与之前的版本相同。比如配置 `[zone,rack,host]` 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则保证放置在不同的 rack;假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。 + +## 配置规则操作步骤 + +本节的操作步骤以使用 [pd-ctl](/reference/tools/pd-control.md) 工具为例,涉及到的命令也支持通过 HTTP API 进行调用,具体 API 这里不一一列举了。 + +### 开启 placement rules 特性 + +默认情况下,placement rules 特性是关闭的。要开启这个特性,可以集群初始化以前设置 PD 配置文件: + +{{< copyable "" >}} + +```toml +[replication] +enable-placement-rules = true +``` + +这样,PD 在初始化成功后会开启这个特性,并根据 `max-replicas` 及 `location-labels` 配置生成对应的规则: + +```json +{ + "group_id": "pd", + "id": "default", + "start_key": "", + "end_key": "", + "role": "voter", + "count": 3, + "location_labels": ["zone", "rack", "host"] +} +``` + +如果是已经初始化过的集群,也可以通过 pd-ctl 进行在线开启: + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules enable +``` + +PD 同样将根据系统的 `max-replicas` 及 `location-labels` 生成默认的规则。 + +### 关闭 placement rules 特性 + +使用 pd-ctl 可以关闭 placement rules 特性,切换为之前的调度策略。 + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules disable +``` + +**注意:关闭 placement rules 后,PD 将使用原先的 `max-replicas` 及 `location-labels` 配置,在 placement rules 开启期间对 Rule 的修改不会导致这两项配置的同步更新。此外,设置好的所有 Rule 都会保留在系统中,会在下次开启 placement rules 时被使用。** + +### 使用 pd-ctl 设置规则 + +pd-ctl 支持使用多种方式查看系统中的 Rule,输出是 json 格式的 Rule 或 Rule 列表: + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules show // 查看所有规则列表 +pd-ctl config placement-rules show --group=pd // 查看 pd Group 的所有规则列表 +pd-ctl config placement-rules show --group=pd --id=default // 查看对应 Group 和 ID 的一条规则 +pd-ctl config placement-rules show --region=2 // 查看 Region 2 所匹配的规则列表 +``` + +新增和编辑规则是类似的,需要把对应的规则写进文件,然后使用 `save` 命令保存至 PD: + +{{< copyable "" >}} + +```bash +cat > rules.json <}} + +```bash +cat > rules.json <}} + +```bash +pd-ctl config placement-rules load // 将所有规则转存至 rules.json +pd-ctl config placement-rules load --group=pd --out=rule.txt // 将 pd Group 的规则转存至 rule.txt +``` + +### 使用 tidb-ctl 查询 table 相关 key range + +一种常见的需求是针对元数据或某个特定的表进行特殊配置,此时可以通过 [tidb-ctl](https://github.com/pingcap/tidb-ctl) 的 [keyrange 命令](https://github.com/pingcap/tidb-ctl/blob/master/doc/tidb-ctl_keyrange.md) 来查询相关的 key。注意要添加 `--encode` 返回 PD 中的表示形式。 + +{{< copyable "" >}} + +```bash +tidb-ctl keyrange --database test --table ttt --encode +``` + +```text +global ranges: + meta: (6d00000000000000f8, 6e00000000000000f8) + table: (7400000000000000f8, 7500000000000000f8) +table ttt ranges: (NOTE: key range might be changed after DDL) + table: (7480000000000000ff2d00000000000000f8, 7480000000000000ff2e00000000000000f8) + table indexes: (7480000000000000ff2d5f690000000000fa, 7480000000000000ff2d5f720000000000fa) + index c2: (7480000000000000ff2d5f698000000000ff0000010000000000fa, 7480000000000000ff2d5f698000000000ff0000020000000000fa) + index c3: (7480000000000000ff2d5f698000000000ff0000020000000000fa, 7480000000000000ff2d5f698000000000ff0000030000000000fa) + index c4: (7480000000000000ff2d5f698000000000ff0000030000000000fa, 7480000000000000ff2d5f698000000000ff0000040000000000fa) + table rows: (7480000000000000ff2d5f720000000000fa, 7480000000000000ff2e00000000000000f8) +``` + +**注意:DDL 等操作会导致 table ID 发生变化,需要同步更新对应的规则。** + +## 典型场景示例 + +### 场景一:普通的表使用 3 副本,元数据使用 5 副本提升集群容灾能力 + +只需要增加一条规则,key range 限定在 meta 数据的范围,并把 `count` 值设为 5。 + +{{< copyable "" >}} + +```json +{ + "group_id": "pd", + "id": "meta", + "index": 1, + "override": true, + "start_key": "6d00000000000000f8", + "end_key": "6e00000000000000f8", + "role": "voter", + "count": "5", + "location_labels": ["zone", "rack", "host"] +} +``` + +### 场景二:5 副本按 2-2-1 的比例放置在 3 个数据中心,且第 3 个中心不产生 Leader + +思路是创建三条规则,分别设置副本数为 2、2、1,并且在每个规则内通过 `label_constraint` 将副本限定在对应的数据中心内。另外,不需要 leader 的数据中心将 `role` 改为 `follower`。 + +{{< copyable "" >}} + +```json +[ + { + "group_id": "pd", + "id": "zone1", + "start_key": "", + "end_key": "", + "role": "voter", + "count": 2, + "label_constraints": [ + {"key": "zone", "op": "in", "values": ["zone1"]} + ], + "location_labels": ["rack", "host"] + }, + { + "group_id": "pd", + "id": "zone2", + "start_key": "", + "end_key": "", + "role": "voter", + "count": 2, + "label_constraints": [ + {"key": "zone", "op": "in", "values": ["zone2"]} + ], + "location_labels": ["rack", "host"] + }, + { + "group_id": "pd", + "id": "zone3", + "start_key": "", + "end_key": "", + "role": "follower", + "count": 1, + "label_constraints": [ + {"key": "zone", "op": "in", "values": ["zone3"]} + ], + "location_labels": ["rack", "host"] + } +] +``` + +### 场景三:为某个 table 添加 2 个 TiFlash Learner 副本 + +为 table 的 row key 单独添加一条规则,限定数量为 2,并且通过 `label_constraint` 保证副本产生在 `engine=tiflash` 的节点。注意这里使用了单独的 `group_id`,保证这条规则不会与系统中其他来源的规则互相覆盖或产生冲突。 + +{{< copyable "" >}} + +```json +{ + "group_id": "tiflash", + "id": "learner-replica-table-ttt", + "start_key": "7480000000000000ff2d5f720000000000fa", + "end_key": "7480000000000000ff2e00000000000000f8", + "role": "learner", + "count": 2, + "label_constraints": [ + {"key": "engine", "op": "in", "values": ["tiflash"]} + ], + "location_labels": ["host"] +} +``` + +### 场景四:为某个 table 在有高性能磁盘的北京节点添加 2 个 Follower 副本 + +这个例子展示了比较复杂的 `label_constaint` 配置,下面的例子限定了副本放置在 bj1 或 bj2 机房,且磁盘类型不能为 hdd。 + +{{< copyable "" >}} + +```json +{ + "group_id": "follower-read", + "id": "follower-read-table-ttt", + "start_key": "7480000000000000ff2d00000000000000f8", + "end_key": "7480000000000000ff2e00000000000000f8", + "role": "follower", + "count": 2, + "label_constraints": [ + {"key": "zone", "op": "in", "values": ["bj1", "bj2"]}, + {"key": "disk", "op": "notIn", "values": ["hdd"]} + ], + "location_labels": ["host"] +} +``` diff --git a/media/placement-rules-1.png b/media/placement-rules-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2342c89e8559eff898efcab0ed6c752d5306ca6c GIT binary patch literal 50843 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 literal 0 HcmV?d00001 From c7d83536dda537b8468d81734cf2a7f689f894bb Mon Sep 17 00:00:00 2001 From: disksing Date: Tue, 31 Mar 2020 19:58:15 +0800 Subject: [PATCH 02/16] minor update Signed-off-by: disksing --- how-to/configure/placement-rules.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 790a490a8c46..5440a71a007a 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -92,6 +92,8 @@ pd-ctl config placement-rules disable ### 使用 pd-ctl 设置规则 +**注意:规则的变更将实时地影响 PD 调度,不恰当的规则设置可能导致副本数较少影响系统的高可用。** + pd-ctl 支持使用多种方式查看系统中的 Rule,输出是 json 格式的 Rule 或 Rule 列表: {{< copyable "" >}} From 34e8cd291cb53adf472358bfa07fec49d512021e Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:54:07 +0800 Subject: [PATCH 03/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 5440a71a007a..52577e1e0644 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -10,7 +10,7 @@ Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本 ## 规则系统介绍 -整个系统的配置由多条规则组成,我们把规则叫做 Rule。每条 Rule 可以指定不同的副本数量、Raft 角色、放置位置等属性,以及这条规则生效的 key range。PD 在进行调度时,会先根据 Region 的 key range 在规则系统中查到它对应的规则,然后再生成对应的调度,来使得 Region 副本的分布情况符合 Rule。 +整个规则系统的配置由多条规则即 Rule 组成。每条 Rule 可以指定不同的副本数量、Raft 角色、放置位置等属性,以及这条规则生效的 key range。PD 在进行调度时,会先根据 Region 的 key range 在规则系统中查到它对应的规则,然后再生成对应的调度,来使得 Region 副本的分布情况符合 Rule。 值得注意的是,多条规则的 key range 是允许有重叠部分的,即一个 Region 能匹配到多条规则,这种情况下 PD 根据 Rule 的属性来决定规则是相互覆盖还是同时生效,如果有多条规则同时生效,PD 会按照规则的堆叠次序依次去生成调度进行规则匹配。 From 1aa5bfe9c5dca55d71a82db0d87c0455628f886a Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:54:19 +0800 Subject: [PATCH 04/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 52577e1e0644..733f96b7b2ba 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -27,7 +27,7 @@ Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本 | GroupID | string | 分组 ID,标识规则的来源 | | ID | string | 分组内唯一 ID | | Index | int | 分组内堆叠次序 | -| Override | true/false | 是否覆盖 index 更小的 Rule (限分组内) | +| Override | true/false | 是否覆盖 index 的更小 Rule (限分组内) | | StartKey | string,hex 编码 | 适用 Range 起始 key | | EndKey | string,hex 编码 | 适用 Range 终止 key | | Role | string,leader/follower/learner | 副本角色 | From 4fb46ff20ddbc8c2fcfb2548ce9df4807a0c788f Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:55:08 +0800 Subject: [PATCH 05/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 733f96b7b2ba..9a1c5e99ca2e 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -43,7 +43,7 @@ LocationLabels 的意义和作用与之前的版本相同。比如配置 `[zone, 本节的操作步骤以使用 [pd-ctl](/reference/tools/pd-control.md) 工具为例,涉及到的命令也支持通过 HTTP API 进行调用,具体 API 这里不一一列举了。 -### 开启 placement rules 特性 +### 开启 Placement Rules 特性 默认情况下,placement rules 特性是关闭的。要开启这个特性,可以集群初始化以前设置 PD 配置文件: From edd324d82a9d89bd402573826a2c13c7becee3d6 Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:55:28 +0800 Subject: [PATCH 06/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 9a1c5e99ca2e..726a0589b3e1 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -88,7 +88,9 @@ PD 同样将根据系统的 `max-replicas` 及 `location-labels` 生成默认的 pd-ctl config placement-rules disable ``` -**注意:关闭 placement rules 后,PD 将使用原先的 `max-replicas` 及 `location-labels` 配置,在 placement rules 开启期间对 Rule 的修改不会导致这两项配置的同步更新。此外,设置好的所有 Rule 都会保留在系统中,会在下次开启 placement rules 时被使用。** +> **注意:** +> +> 关闭 Placement Rules 后,PD 将使用原先的 `max-replicas` 及 `location-labels` 配置。在 Placement Rules 开启期间对 Rule 的修改不会导致这两项配置的同步更新。此外,设置好的所有 Rule 都会保留在系统中,会在下次开启 Placement Rules 时被使用。 ### 使用 pd-ctl 设置规则 From 53a9f7d23c1ec4a677ff9603e6215f1f23abf709 Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:55:38 +0800 Subject: [PATCH 07/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 726a0589b3e1..8b1fb8518d6e 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -94,7 +94,9 @@ pd-ctl config placement-rules disable ### 使用 pd-ctl 设置规则 -**注意:规则的变更将实时地影响 PD 调度,不恰当的规则设置可能导致副本数较少影响系统的高可用。** +> **注意:** +> +> 规则的变更将实时地影响 PD 调度,不恰当的规则设置可能导致副本数较少,影响系统的高可用。 pd-ctl 支持使用多种方式查看系统中的 Rule,输出是 json 格式的 Rule 或 Rule 列表: From a0cbab7614a7fa77e2165827500d7060a3be8c47 Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:55:57 +0800 Subject: [PATCH 08/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 8b1fb8518d6e..e6d1bc8bf08d 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -185,7 +185,9 @@ table ttt ranges: (NOTE: key range might be changed after DDL) table rows: (7480000000000000ff2d5f720000000000fa, 7480000000000000ff2e00000000000000f8) ``` -**注意:DDL 等操作会导致 table ID 发生变化,需要同步更新对应的规则。** +> **注意:** +> +> DDL 等操作会导致 table ID 发生变化,需要同步更新对应的规则。 ## 典型场景示例 From ae9035dde4f7b293087d10d26ad32ac811fb850e Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:56:13 +0800 Subject: [PATCH 09/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index e6d1bc8bf08d..c2b598879164 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -193,7 +193,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) ### 场景一:普通的表使用 3 副本,元数据使用 5 副本提升集群容灾能力 -只需要增加一条规则,key range 限定在 meta 数据的范围,并把 `count` 值设为 5。 +只需要增加一条规则,key range 限定在 meta 数据的范围,并把 `count` 值设为 5。添加规则示例如下: {{< copyable "" >}} From 7b09e126407424a9dba925f71c077e55a8cfe20a Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 12:56:25 +0800 Subject: [PATCH 10/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index c2b598879164..b441befa9be8 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -258,7 +258,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) ] ``` -### 场景三:为某个 table 添加 2 个 TiFlash Learner 副本 +### 场景三:为某张表添加 2 个 TiFlash Learner 副本 为 table 的 row key 单独添加一条规则,限定数量为 2,并且通过 `label_constraint` 保证副本产生在 `engine=tiflash` 的节点。注意这里使用了单独的 `group_id`,保证这条规则不会与系统中其他来源的规则互相覆盖或产生冲突。 From 7f7c1b296a5de1de0fb05f6b417654211dfa66c8 Mon Sep 17 00:00:00 2001 From: disksing Date: Wed, 1 Apr 2020 13:02:03 +0800 Subject: [PATCH 11/16] address comment Signed-off-by: disksing --- how-to/configure/placement-rules.md | 31 +++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index b441befa9be8..ef1db2539b56 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -100,13 +100,36 @@ pd-ctl config placement-rules disable pd-ctl 支持使用多种方式查看系统中的 Rule,输出是 json 格式的 Rule 或 Rule 列表: +**查看所有规则列表** + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules show +``` + +**查看 pd Group 的所有规则列表** + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules show --group=pd +``` + +**查看对应 Group 和 ID 的一条规则** + +{{< copyable "" >}} + +```bash +pd-ctl config placement-rules show --group=pd --id=default +``` + +**查看 Region 2 所匹配的规则列表** + {{< copyable "" >}} ```bash -pd-ctl config placement-rules show // 查看所有规则列表 -pd-ctl config placement-rules show --group=pd // 查看 pd Group 的所有规则列表 -pd-ctl config placement-rules show --group=pd --id=default // 查看对应 Group 和 ID 的一条规则 -pd-ctl config placement-rules show --region=2 // 查看 Region 2 所匹配的规则列表 +pd-ctl config placement-rules show --region=2 ``` 新增和编辑规则是类似的,需要把对应的规则写进文件,然后使用 `save` 命令保存至 PD: From 7764f11a05876171763a8241750c1936b87f520c Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 2 Apr 2020 11:59:06 +0800 Subject: [PATCH 12/16] Update how-to/configure/placement-rules.md Co-Authored-By: TomShawn <41534398+TomShawn@users.noreply.github.com> --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index ef1db2539b56..af1f9d849260 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -37,7 +37,7 @@ Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本 其中 LabelConstraint 之前没有介绍过,它参考了 Kubernetes 中类似的功能,支持通过 in, notIn, exists, notExists 四种原语来筛选 label,这四种原语分别对应 “给定 key 的 label value 包含在给定列表中”,“给定 key 的 label value 不包含在给定列表中”,“包含给定的 label key”,以及“不包含给定的 label key”。 -LocationLabels 的意义和作用与之前的版本相同。比如配置 `[zone,rack,host]` 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则保证放置在不同的 rack;假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。 +LocationLabels 的意义和作用与 PD v4.0 之前的版本相同。比如配置 `[zone,rack,host]` 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则保证放置在不同的 rack;假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。 ## 配置规则操作步骤 From f7fa3e7f0998c5de2e7a4f8311b28e728cec746b Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 2 Apr 2020 14:30:49 +0800 Subject: [PATCH 13/16] Update placement-rules.md --- how-to/configure/placement-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index af1f9d849260..6a9dbc4d8228 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -124,7 +124,7 @@ pd-ctl config placement-rules show --group=pd pd-ctl config placement-rules show --group=pd --id=default ``` -**查看 Region 2 所匹配的规则列表** +**查看 Region 所匹配的规则列表** {{< copyable "" >}} From 6c0740dc930d7d2c6b42763fe87f0fc20f9ed124 Mon Sep 17 00:00:00 2001 From: disksing Date: Thu, 2 Apr 2020 16:32:25 +0800 Subject: [PATCH 14/16] Update placement-rules.md --- how-to/configure/placement-rules.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 6a9dbc4d8228..405a90c33a7c 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -132,6 +132,8 @@ pd-ctl config placement-rules show --group=pd --id=default pd-ctl config placement-rules show --region=2 ``` +上面的例子中 2 为 region id。 + 新增和编辑规则是类似的,需要把对应的规则写进文件,然后使用 `save` 命令保存至 PD: {{< copyable "" >}} From ca1f509b3a736049305b36e56b8a8a37ac4aea8d Mon Sep 17 00:00:00 2001 From: TomShawn <1135243111@qq.com> Date: Thu, 2 Apr 2020 17:39:41 +0800 Subject: [PATCH 15/16] refine format and language --- how-to/configure/placement-rules.md | 116 ++++++++++++++++------------ 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index 405a90c33a7c..cd036825c4cd 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -6,46 +6,53 @@ category: how-to # Placement Rules 使用文档 -Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本规则系统,用于指导 PD 针对不同类型的数据生成对应的调度。通过组合不同的调度规则,用户可以精细地控制任何一段连续数据的副本数量、存放位置、主机类型、是否参与 Raft 投票、是否可以担任 Raft leader 等。 +Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本规则系统,用于指导 PD 针对不同类型的数据生成对应的调度。通过组合不同的调度规则,用户可以精细地控制任何一段连续数据的副本数量、存放位置、主机类型、是否参与 Raft 投票、是否可以担任 Raft leader 等属性。 ## 规则系统介绍 -整个规则系统的配置由多条规则即 Rule 组成。每条 Rule 可以指定不同的副本数量、Raft 角色、放置位置等属性,以及这条规则生效的 key range。PD 在进行调度时,会先根据 Region 的 key range 在规则系统中查到它对应的规则,然后再生成对应的调度,来使得 Region 副本的分布情况符合 Rule。 +整个规则系统的配置由多条规则即 Rule 组成。每条 Rule 可以指定不同的副本数量、Raft 角色、放置位置等属性,以及这条规则生效的 key range。PD 在进行调度时,会先根据 Region 的 key range 在规则系统中查到该 Region 对应的规则,然后再生成对应的调度,来使得 Region 副本的分布情况符合 Rule。 -值得注意的是,多条规则的 key range 是允许有重叠部分的,即一个 Region 能匹配到多条规则,这种情况下 PD 根据 Rule 的属性来决定规则是相互覆盖还是同时生效,如果有多条规则同时生效,PD 会按照规则的堆叠次序依次去生成调度进行规则匹配。 +多条规则的 key range 可以有重叠部分的,即一个 Region 能匹配到多条规则。这种情况下 PD 根据 Rule 的属性来决定规则是相互覆盖还是同时生效。如果有多条规则同时生效,PD 会按照规则的堆叠次序依次去生成调度进行规则匹配。 -此外,为了满足不同来源的规则相互隔离的需求,还引入了*分组(Group)*的概念,如果某条规则不希望与系统中的其他规则相互影响(比如被覆盖),可以使用单独的分组。 +此外,为了满足不同来源的规则相互隔离的需求,还引入了*分组(Group)*的概念。如果某条规则不希望与系统中的其他规则相互影响(比如被覆盖),可以使用单独的分组。 + +Placement Rules 示意图如下所示: ![Placement rules overview](/media/placement-rules-1.png) ### 规则字段 -以下是每条规则各个字段的具体含义: +以下是每条规则中各个字段的具体含义: | 字段名 | 类型及约束 | 说明 | | :--- | :--- | :--- | -| GroupID | string | 分组 ID,标识规则的来源 | -| ID | string | 分组内唯一 ID | -| Index | int | 分组内堆叠次序 | -| Override | true/false | 是否覆盖 index 的更小 Rule (限分组内) | -| StartKey | string,hex 编码 | 适用 Range 起始 key | -| EndKey | string,hex 编码 | 适用 Range 终止 key | -| Role | string,leader/follower/learner | 副本角色 | -| Count | int,正整数 | 副本数量 | -| LabelConstraint | []Constraint | 用于按 label 筛选节点 | -| LocationLabels | []string | 用于物理隔离 | - -其中 LabelConstraint 之前没有介绍过,它参考了 Kubernetes 中类似的功能,支持通过 in, notIn, exists, notExists 四种原语来筛选 label,这四种原语分别对应 “给定 key 的 label value 包含在给定列表中”,“给定 key 的 label value 不包含在给定列表中”,“包含给定的 label key”,以及“不包含给定的 label key”。 - -LocationLabels 的意义和作用与 PD v4.0 之前的版本相同。比如配置 `[zone,rack,host]` 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则保证放置在不同的 rack;假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。 +| `GroupID` | `string` | 分组 ID,标识规则的来源 | +| `ID` | `string` | 分组内唯一 ID | +| `Index` | `int` | 分组内堆叠次序 | +| `Override` | `true`/`false` | 是否覆盖 index 的更小 Rule(限分组内) | +| `StartKey` | `string`,十六进制编码 | 适用 Range 起始 key | +| `EndKey` | `string`,十六进制编码 | 适用 Range 终止 key | +| `Role` | `string` | 副本角色,包括 leader/follower/learner | +| `Count` | `int`,正整数 | 副本数量 | +| `LabelConstraint` | `[]Constraint` | 用于按 label 筛选节点 | +| `LocationLabels` | `[]string` | 用于物理隔离 | + +`LabelConstraint` 与 Kubernetes 中的功能类似,支持通过 `in`、`notIn`、`exists` 和 `notExists` 四种原语来筛选 label。这四种原语的意义如下: + ++ `in`:给定 key 的 label value 包含在给定列表中。 ++ `notIn`:给定 key 的 label value 不包含在给定列表中。 ++ `exists`:包含给定的 label key。 ++ `notExists`:不包含给定的 label key。 + +`LocationLabels` 的意义和作用与 PD v4.0 之前的版本相同。比如配置 `[zone,rack,host]` 定义了三层的拓扑结构:集群分为多个 zone(可用区),每个 zone 下有多个 rack(机架),每个 rack 下有多个 host(主机)。PD 在调度时首先会尝试将 Region 的 Peer 放置在不同的 zone,假如无法满足(比如配置 3 副本但总共只有 2 个 zone)则保证放置在不同的 rack;假如 rack 的数量也不足以保证隔离,那么再尝试 host 级别的隔离,以此类推。 ## 配置规则操作步骤 -本节的操作步骤以使用 [pd-ctl](/reference/tools/pd-control.md) 工具为例,涉及到的命令也支持通过 HTTP API 进行调用,具体 API 这里不一一列举了。 +本节的操作步骤以使用 [pd-ctl](/reference/tools/pd-control.md) 工具为例,涉及到的命令也支持通过 HTTP API 进行调用。 ### 开启 Placement Rules 特性 -默认情况下,placement rules 特性是关闭的。要开启这个特性,可以集群初始化以前设置 PD 配置文件: +默认情况下,Placement Rules 特性是关闭的。要开启这个特性,可以集群初始化以前设置 PD 配置文件: {{< copyable "" >}} @@ -70,7 +77,7 @@ enable-placement-rules = true 如果是已经初始化过的集群,也可以通过 pd-ctl 进行在线开启: -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules enable @@ -78,11 +85,11 @@ pd-ctl config placement-rules enable PD 同样将根据系统的 `max-replicas` 及 `location-labels` 生成默认的规则。 -### 关闭 placement rules 特性 +### 关闭 Placement Rules 特性 -使用 pd-ctl 可以关闭 placement rules 特性,切换为之前的调度策略。 +使用 pd-ctl 可以关闭 Placement Rules 特性,切换为之前的调度策略。 -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules disable @@ -102,23 +109,23 @@ pd-ctl 支持使用多种方式查看系统中的 Rule,输出是 json 格式 **查看所有规则列表** -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules show ``` -**查看 pd Group 的所有规则列表** +**查看 PD Group 的所有规则列表** -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules show --group=pd ``` -**查看对应 Group 和 ID 的一条规则** +**查看对应 Group 和 ID 的某条规则** -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules show --group=pd --id=default @@ -126,17 +133,17 @@ pd-ctl config placement-rules show --group=pd --id=default **查看 Region 所匹配的规则列表** -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash pd-ctl config placement-rules show --region=2 ``` -上面的例子中 2 为 region id。 +上面的例子中 `2` 为 Region ID。 新增和编辑规则是类似的,需要把对应的规则写进文件,然后使用 `save` 命令保存至 PD: -{{< copyable "" >}} +{{< copyable "shell-regular" >}} ```bash cat > rules.json < rules.json <}} +{{< copyable "shell-regular" >}} ```bash cat > rules.json <}} +{{< copyable "shell-regular" >}} ```bash -pd-ctl config placement-rules load // 将所有规则转存至 rules.json -pd-ctl config placement-rules load --group=pd --out=rule.txt // 将 pd Group 的规则转存至 rule.txt +pd-ctl config placement-rules load ``` -### 使用 tidb-ctl 查询 table 相关 key range +以上命令将所有规则转存至 rules.json 文件。 -一种常见的需求是针对元数据或某个特定的表进行特殊配置,此时可以通过 [tidb-ctl](https://github.com/pingcap/tidb-ctl) 的 [keyrange 命令](https://github.com/pingcap/tidb-ctl/blob/master/doc/tidb-ctl_keyrange.md) 来查询相关的 key。注意要添加 `--encode` 返回 PD 中的表示形式。 +{{< copyable "shell-regular" >}} -{{< copyable "" >}} +```bash +pd-ctl config placement-rules load --group=pd --out=rule.txt +``` + +以上命令将 PD Group 的规则转存至 rule.txt 文件。 + +### 使用 tidb-ctl 查询表相关的 key range + +若需要针对元数据或某个特定的表进行特殊配置,可以通过 [tidb-ctl](https://github.com/pingcap/tidb-ctl) 的 [`keyrange` 命令](https://github.com/pingcap/tidb-ctl/blob/master/doc/tidb-ctl_keyrange.md) 来查询相关的 key。注意要添加 `--encode` 返回 PD 中的表示形式。 + +{{< copyable "shell-regular" >}} ```bash tidb-ctl keyrange --database test --table ttt --encode @@ -216,9 +232,11 @@ table ttt ranges: (NOTE: key range might be changed after DDL) ## 典型场景示例 +本部分介绍 Placement Rules 的使用场景示例。 + ### 场景一:普通的表使用 3 副本,元数据使用 5 副本提升集群容灾能力 -只需要增加一条规则,key range 限定在 meta 数据的范围,并把 `count` 值设为 5。添加规则示例如下: +只需要增加一条规则,将 key range 限定在 meta 数据的范围,并把 `count` 值设为 `5`。添加规则示例如下: {{< copyable "" >}} @@ -238,7 +256,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) ### 场景二:5 副本按 2-2-1 的比例放置在 3 个数据中心,且第 3 个中心不产生 Leader -思路是创建三条规则,分别设置副本数为 2、2、1,并且在每个规则内通过 `label_constraint` 将副本限定在对应的数据中心内。另外,不需要 leader 的数据中心将 `role` 改为 `follower`。 +创建三条规则,分别设置副本数为 2、2、1,并且在每个规则内通过 `label_constraint` 将副本限定在对应的数据中心内。另外,不需要 leader 的数据中心将 `role` 改为 `follower`。 {{< copyable "" >}} @@ -285,7 +303,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) ### 场景三:为某张表添加 2 个 TiFlash Learner 副本 -为 table 的 row key 单独添加一条规则,限定数量为 2,并且通过 `label_constraint` 保证副本产生在 `engine=tiflash` 的节点。注意这里使用了单独的 `group_id`,保证这条规则不会与系统中其他来源的规则互相覆盖或产生冲突。 +为表的 row key 单独添加一条规则,限定数量为 2,并且通过 `label_constraint` 保证副本产生在 `engine=tiflash` 的节点。注意这里使用了单独的 `group_id`,保证这条规则不会与系统中其他来源的规则互相覆盖或产生冲突。 {{< copyable "" >}} @@ -294,7 +312,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) "group_id": "tiflash", "id": "learner-replica-table-ttt", "start_key": "7480000000000000ff2d5f720000000000fa", - "end_key": "7480000000000000ff2e00000000000000f8", + "end_key": "7480000000000000ff2e00000000000000f8", "role": "learner", "count": 2, "label_constraints": [ @@ -304,7 +322,7 @@ table ttt ranges: (NOTE: key range might be changed after DDL) } ``` -### 场景四:为某个 table 在有高性能磁盘的北京节点添加 2 个 Follower 副本 +### 场景四:为某张表在有高性能磁盘的北京节点添加 2 个 Follower 副本 这个例子展示了比较复杂的 `label_constaint` 配置,下面的例子限定了副本放置在 bj1 或 bj2 机房,且磁盘类型不能为 hdd。 From 6742f61ce55ab90f50f47cbbb2abc95ac241512b Mon Sep 17 00:00:00 2001 From: TomShawn <1135243111@qq.com> Date: Thu, 2 Apr 2020 17:49:36 +0800 Subject: [PATCH 16/16] fix a typo --- how-to/configure/placement-rules.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/how-to/configure/placement-rules.md b/how-to/configure/placement-rules.md index cd036825c4cd..44dd393a3e97 100644 --- a/how-to/configure/placement-rules.md +++ b/how-to/configure/placement-rules.md @@ -14,7 +14,7 @@ Placement Rules 是 PD 在 4.0 版本引入的试验特性,它是一套副本 多条规则的 key range 可以有重叠部分的,即一个 Region 能匹配到多条规则。这种情况下 PD 根据 Rule 的属性来决定规则是相互覆盖还是同时生效。如果有多条规则同时生效,PD 会按照规则的堆叠次序依次去生成调度进行规则匹配。 -此外,为了满足不同来源的规则相互隔离的需求,还引入了*分组(Group)*的概念。如果某条规则不希望与系统中的其他规则相互影响(比如被覆盖),可以使用单独的分组。 +此外,为了满足不同来源的规则相互隔离的需求,还引入了分组(Group)的概念。如果某条规则不希望与系统中的其他规则相互影响(比如被覆盖),可以使用单独的分组。 Placement Rules 示意图如下所示: @@ -63,6 +63,8 @@ enable-placement-rules = true 这样,PD 在初始化成功后会开启这个特性,并根据 `max-replicas` 及 `location-labels` 配置生成对应的规则: +{{< copyable "" >}} + ```json { "group_id": "pd",