Skip to content
3 changes: 3 additions & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,9 @@
+ [隔离级别](/transaction-isolation-levels.md) @于帅鹏
+ [乐观事务](/optimistic-transaction.md) @于帅鹏
+ [悲观事务](/pessimistic-transaction.md) @于帅鹏
+ 垃圾回收 (GC)
+ [GC 机制简介](/garbage-collection-overview.md)
+ [GC 配置](/garbage-collection-configuration.md)
+ [视图](/views.md) @徐怀宇
+ [分区表](/partitioned-table.md) @毛康力
+ [字符集和排序规则](/character-set-and-collation.md) @黄文俊
Expand Down
35 changes: 26 additions & 9 deletions garbage-collection-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ TiDB 的 GC 相关的配置存储于 `mysql.tidb` 系统表中,可以通过 SQ
{{< copyable "sql" >}}

```sql
select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb;
select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb where VARIABLE_NAME like "tikv_gc%";
```

```
+--------------------------+----------------------------------------------------------------------------------------------------+
| VARIABLE_NAME | VARIABLE_VALUE |
+--------------------------+----------------------------------------------------------------------------------------------------+
| bootstrapped | True |
| tidb_server_version | 33 |
| system_tz | UTC |
| tikv_gc_leader_uuid | 5afd54a0ea40005 |
| tikv_gc_leader_desc | host:tidb-cluster-tidb-0, pid:215, start at 2019-07-15 11:09:14.029668932 +0000 UTC m=+0.463731223 |
| tikv_gc_leader_lease | 20190715-12:12:14 +0000 |
Expand All @@ -45,11 +42,11 @@ update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_tim

> **注意:**
>
> `mysql.tidb` 系统表中除了下文将要列出的 GC 的配置以外,还包含一些 TiDB 用于储存部分集群状态(包括 GC 状态)的记录。请勿手动更改这些记录。其中,与 GC 有关的记录如下:
> `mysql.tidb` 系统表中除了下文列出的 GC 的配置以外,还包含一些 TiDB 用于储存部分集群状态(包括 GC 状态)的记录。请勿手动更改这些记录。其中,与 GC 有关的记录如下:
>
> - `tikv_gc_leader_uuid`,`tikv_gc_leader_desc` 和 `tikv_gc_leader_lease` 用于记录 GC leader 的状态
> - `tikv_gc_last_run_time`:上次 GC 运行时间
> - `tikv_gc_safe_point`:当前 GC 的 safe point
> - `tikv_gc_last_run_time`:最近一次 GC 运行的时间(每轮 GC 开始时更新)
> - `tikv_gc_safe_point`:当前的 safe point (每轮 GC 开始时更新)

## `tikv_gc_enable`

Expand All @@ -68,11 +65,10 @@ update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_tim

> **注意:**
>
> - `tikv_gc_life_time` 的值必须大于 TiDB 的配置文件中的 [`max-txn-time-use`](/tidb-configuration-file.md#max-txn-time-use) 的值至少 10 秒,且不低于 10 分钟。
>
> - 在数据更新频繁的场景下,如果将 `tikv_gc_life_time` 设置得比较大(如数天甚至数月),可能会有一些潜在的问题,如:
> - 磁盘空间占用较多。
> - 大量的历史版本会在一定程度上影响性能,尤其是范围查询(如 `select count(*) from t`)。
> - 如果存在运行时间很长、超过了 `tikv_gc_life_time` 的事务,那么在 GC 时,会保留自该事务的开始时间 (start_ts) 以来的数据,以允许该事务继续运行。例如,如果 `tikv_gc_life_time` 配置为 10 分钟,而某次 GC 时,集群中正在运行的事务中开始时间最早的一个事务已经运行了 15 分钟,那么本次 GC 便会保留最近 15 分钟的数据。

## `tikv_gc_mode`

Expand All @@ -96,6 +92,25 @@ update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_tim
- 手动设置 GC concurrency。要使用该参数,必须将 [`tikv_gc_auto_concurrency`](#tikv_gc_auto_concurrency) 设为 `false` 。
- 默认值:2

## `tikv_gc_scan_lock_mode` (**实验特性**)

设定 GC 的 Resolve Locks 阶段中,扫描锁的方式,即是否开启 Green GC(实验性特性)。Resolve Locks 阶段需要扫描整个集群的锁。在不开启 Green GC 的情况下,TiDB 会以 Region 为单位进行扫描。Green GC 提供了“物理扫描”的功能,即每台 TiKV 节点分别绕过 Raft 层直接扫描数据。该功能可以有效缓解 [Hibernate Region](/tikv-configuration-file.md#raftstorehibernate-regions-实验特性) 功能开启时,GC 唤醒全部 Region 的现象,并一定程度上提升 Resolve Locks 阶段的执行速度。

- `"legacy"`(默认):使用旧的扫描方式,即关闭 Green GC。
- `"physical"`:使用物理扫描的方式,即开启 Green GC。

> **注意:**
>
> Green GC 目前是实验性功能,不建议在生产环境中使用。
>
> 该项配置是隐藏配置。首次开启需要执行:
>
> {{< copyable "sql" >}}
>
> ```sql
> insert into mysql.tidb values ('tikv_gc_scan_lock_mode', 'legacy', '');
> ```

## 关于 GC 流程的说明

从 TiDB 3.0 版本起,由于对分布式 GC 模式和并行 Resolve Locks 的支持,部分配置选项的作用发生了变化。可根据下表理解不同版本中这些配置的区别:
Expand All @@ -115,6 +130,8 @@ update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_tim
- 自动并行:使用 TiKV 节点的个数作为线程数,并行地向每个 Region 发送请求。
- 分布式:无需 TiDB 通过对 TiKV 发送请求的方式来驱动,而是每台 TiKV 自行工作。

另外,如果 Green GC (实验特性)开启(即 [`tikv_gc_scan_lock_mode`](#tikv_gc_scan_lock_mode-实验特性) 配置项设为 `"physical"`),Resolve Lock 的执行将不受上述并行配置的影响。

## 流控

TiKV 在 3.0.6 版本开始支持 GC 流控,可通过配置 `gc.max-write-bytes-per-sec` 限制 GC worker 每秒数据写入量,降低对正常请求的影响,`0` 为关闭该功能。该配置可通过 tikv-ctl 动态修改:
Expand Down
30 changes: 17 additions & 13 deletions garbage-collection-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,34 @@ TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新

一个 TiDB 集群中会有一个 TiDB 实例被选举为 GC leader,GC 的运行由 GC leader 来控制。

GC 会被定期触发,默认情况下每 10 分钟一次。每次 GC 时,首先,TiDB 会计算一个称为 safe point 的时间戳(默认为当前时间减去 10 分钟),接下来 TiDB 会在保证 safe point 之后的快照全部拥有正确数据的前提下,删除更早的过期数据。具体而言,分为以下三个步骤:
GC 会被定期触发。每次 GC 时,首先,TiDB 会计算一个称为 safe point 的时间戳,接下来 TiDB 会在保证 safe point 之后的快照全部拥有正确数据的前提下,删除更早的过期数据。每一轮 GC 分为以下三个步骤:

1. Resolve Locks
2. Delete Ranges
3. Do GC
1. Resolve Locks。该阶段会对所有 Region 扫描 safe point 之前的锁,并清理这些锁。
2. Delete Ranges。该阶段快速地删除由于 `DROP TABLE`/`DROP INDEX` 等操作产生的整区间的废弃数据。
3. Do GC。该阶段每个 TiKV 节点将会各自扫描该节点上的数据,并对每一个 key 删除其不再需要的旧版本。

### Resolve Locks
默认配置下,GC 每 10 分钟触发一次,每次 GC 会保留最近 10 分钟内的数据(即默认 GC life time 为 10 分钟,safe point 的计算方式为当前时间减去 GC life time)。如果一轮 GC 运行时间太久,那么在一轮 GC 完成之前,即使到了下一次触发 GC 的时间也不会开始下一轮 GC。另外,为了使持续时间较长的事务能在超过 GC life time 之后仍然可以正常运行,safe point 不会超过正在执行中的事务的开始时间 (start_ts)。

TiDB 的事务是基于 [Google Percolator](https://ai.google/research/pubs/pub36726) 模型实现的,事务的提交是一个两阶段提交的过程。第一阶段完成时,所有涉及的 key 会加上一个锁,其中一个锁会被设定为 Primary,其余的锁(Secondary)则会指向 Primary;第二阶段会将 Primary 锁所在的 key 加上一个 Write 记录,并去除锁。这里的 Write 记录就是历史上对该 key 进行写入或删除,或者该 key 上发生事务回滚的记录。Primary 锁被替换为何种 Write 记录标志着该事务提交成功与否。接下来,所有 Secondary 锁也会被依次替换。如果替换这些 Secondary 锁的线程死掉了,锁就残留了下来。
## 实现细节

Resolve Locks 这一步的任务即对 safe point 之前的锁进行回滚或提交,取决于其 Primary 是否被提交。如果一个 Primary 锁也残留了下来,那么该事务应当视为超时并进行回滚。这一步是必不可少的,因为如果其 Primary 的 Write 记录由于太老而被 GC 清除掉了,那么就再也无法知道该事务是否成功。如果该事务存在残留的 Secondary 锁,那么也无法知道它应当被回滚还是提交,也就无法保证一致性。
### Resolve Locks(清理锁)

Resolve Locks 的执行方式是由 GC leader 对所有的 Region 发送请求进行处理。从 3.0 起,这个过程默认会并行地执行,并发数量默认与 TiKV 节点个数相同
TiDB 的事务是基于 [Google Percolator](https://ai.google/research/pubs/pub36726) 模型实现的,事务的提交是一个两阶段提交的过程。第一阶段完成时,所有涉及的 key 都会上锁,其中一个锁会被选为 Primary,其余的锁 (Secondary) 则会存储一个指向 Primary 的指针;第二阶段会将 Primary 锁所在的 key 加上一个 Write 记录,并去除锁。这里的 Write 记录就是历史上对该 key 进行写入或删除,或者该 key 上发生事务回滚的记录。Primary 锁被替换为何种 Write 记录标志着该事务提交成功与否。接下来,所有 Secondary 锁也会被依次替换。如果因为某些原因(如发生故障等),这些 Secondary 锁没有完成替换、残留了下来,那么也可以根据锁中的信息取找到 Primary,并根据 Primary 是否提交来判断整个事务是否提交。但是,如果 Primary 的信息在 GC 中被删除了,而该事务又存在未成功提交的 Secondary 锁,那么就永远无法得知该锁是否可以提交。这样,数据的正确性就无法保证

### Delete Ranges
Resolve Locks 这一步的任务即对 safe point 之前的锁进行清理。即如果一个锁对应的 Primary 已经提交,那么该锁也应该被提交;反之,则应该回滚。而如果 Primary 仍然是上锁的状态(没有提交也没有回滚),则应当将该事务视为超时失败而回滚。

Resolve Locks 的执行方式是由 GC leader 对所有的 Region 发送请求扫描过期的锁,并对扫到的锁查询 Primary 的状态,再发送请求对其进行提交或回滚。这个过程默认会并行地执行,并发数量默认与 TiKV 节点个数相同。

### Delete Ranges(删除区间)

在执行 `DROP TABLE/INDEX` 等操作时,会有大量连续的数据被删除。如果对每个 key 都进行删除操作、再对每个 key 进行 GC 的话,那么执行效率和空间回收速度都可能非常的低下。事实上,这种时候 TiDB 并不会对每个 key 进行删除操作,而是将这些待删除的区间及删除操作的时间戳记录下来。Delete Ranges 会将这些时间戳在 safe point 之前的区间进行快速的物理删除。

### Do GC
### Do GC(进行 GC 清理)

这一步即删除所有 key 的过期版本。为了保证 safe point 之后的任何时间戳都具有一致的快照,这一步删除 safe point 之前提交的数据,但是会保留 safe point 前的最后一次写入(除非最后一次写入是删除)。
这一步即删除所有 key 的过期版本。为了保证 safe point 之后的任何时间戳都具有一致的快照,这一步删除 safe point 之前提交的数据,但是会对每个 key 保留 safe point 前的最后一次写入(除非最后一次写入是删除)。

TiDB 2.1 及更早版本使用的 GC 方式是由 GC leader 向所有 Region 发送 GC 请求。从 3.0 起,GC leader 只需将 safe point 上传至 PD。每个 TiKV 节点都会各自从 PD 获取 safe point。当 TiKV 发现 safe point 发生更新时,便会对当前节点上所有作为 leader 的 Region 进行 GC。与此同时,GC leader 可以继续触发下一轮 GC。
在进行这一步时,TiDB 只需要将 safe point 发送给 PD,即可结束整轮 GC。TiKV 会自行检测到 safe point 发生了更新,会对当前节点上所有作为 Region leader 进行 GC。与此同时,GC leader 可以继续触发下一轮 GC。

> **注意:**
>
> 通过修改配置可以继续使用旧的 GC 方式,详情请参考 [GC 配置](/garbage-collection-configuration.md)。
> TiDB v2.1 以及更早的版本中,Do GC 这一步是通过由 TiDB 对每个 Region 发送请求的方式实现的。在 v3.0 及更新的版本中,通过修改配置可以继续使用旧的 GC 方式,详情请参考 [GC 配置](/garbage-collection-configuration.md#tikv_gc_mode)。