diff --git a/best-practices/tidb-best-practices.md b/best-practices/tidb-best-practices.md index 5b8df7abde65..a9e418e3c93e 100644 --- a/best-practices/tidb-best-practices.md +++ b/best-practices/tidb-best-practices.md @@ -1,197 +1,197 @@ ---- -title: TiDB 最佳实践 ---- - -# TiDB 最佳实践 - -本文档总结使用 TiDB 时的一些最佳实践,主要涉及 SQL 使用和 OLAP/OLTP 优化技巧,特别是一些 TiDB 专有的优化开关。 - -建议先阅读讲解 TiDB 原理的三篇文章([讲存储](https://pingcap.com/blog-cn/tidb-internal-1/),[说计算](https://pingcap.com/blog-cn/tidb-internal-2/),[谈调度](https://pingcap.com/blog-cn/tidb-internal-3/)),再来看这篇文章。 - -## 前言 - -数据库是一个通用的基础组件,在开发过程中会考虑到多种目标场景,在具体的业务场景中,需要根据业务的实际情况对数据的参数或者使用方式进行调整。 - -TiDB 是一个兼容 MySQL 协议和语法的分布式数据库,但是由于其内部实现,特别是支持分布式存储以及分布式事务,使得一些使用方法和 MySQL 有所区别。 - -## 基本概念 - -TiDB 的最佳实践与其实现原理密切相关,建议读者先了解一些基本的实现机制,包括 Raft、分布式事务、数据分片、负载均衡、SQL 到 KV 的映射方案、二级索引的实现方法、分布式执行引擎。下面会做一点简单的介绍,更详细的信息可以参考 PingCAP 公众号以及知乎专栏的一些文章。 - -### Raft - -Raft 是一种一致性协议,能提供强一致的数据复制保证,TiDB 最底层用 Raft 来同步数据。每次写入都要写入多数副本,才能对外返回成功,这样即使丢掉少数副本,也能保证系统中还有最新的数据。比如最大 3 副本的话,每次写入 2 副本才算成功,任何时候,只丢失一个副本的情况下,存活的两个副本中至少有一个具有最新的数据。 - -相比 Master-Slave 方式的同步,同样是保存三副本,Raft 的方式更为高效,写入的延迟取决于最快的两个副本,而不是最慢的那个副本。所以使用 Raft 同步的情况下,异地多活成为可能。在典型的两地三中心场景下,每次写入只需要本数据中心以及离得近的一个数据中心写入成功就能保证数据的一致性,而并不需要三个数据中心都写成功。但是这并不意味着在任何场景都能构建跨机房部署的业务,当写入量比较大时候,机房之间的带宽和延迟成为关键因素,如果写入速度超过机房之间的带宽,或者是机房之间延迟过大,整个 Raft 同步机制依然无法很好的运转。 - -### 分布式事务 - -TiDB 提供完整的分布式事务,事务模型是在 [Google Percolator](https://research.google.com/pubs/pub36726.html) 的基础上做了一些优化。具体的实现可以参考[《Percolator 和 TiDB 事务算法》](https://pingcap.com/blog-cn/percolator-and-txn/)这篇文章。本文档只讨论以下几点: - -+ 乐观锁 - - TiDB 的乐观事务模型,只有在真正提交的时候,才会做冲突检测。如果有冲突,则需要重试。这种模型在冲突严重的场景下,会比较低效,因为重试之前的操作都是无效的,需要重复做。举一个比较极端的例子,就是把数据库当做计数器用,如果访问的并发度比较高,那么一定会有严重的冲突,导致大量的重试甚至是超时。但是如果访问冲突并不十分严重,那么乐观锁模型具备较高的效率。在冲突严重的场景下,推荐使用悲观锁,或在系统架构层面解决问题,比如将计数器放在 Redis 中。 - -+ 悲观锁 - - TiDB 的悲观事务模型,悲观事务的行为和 MySQL 基本一致,在执行阶段就会上锁,先到先得,避免冲突情况下的重试,可以保证有较多冲突的事务的成功率。悲观锁同时解决了希望通过 `select for update` 对数据提前锁定的场景。但如果业务场景本身冲突较少,乐观锁的性能会更有优势。 - -+ 事务大小限制 - - 由于分布式事务要做两阶段提交,并且底层还需要做 Raft 复制,如果一个事务非常大,会使得提交过程非常慢,并且会卡住下面的 Raft 复制流程。为了避免系统出现被卡住的情况,我们对事务的大小做了限制: - - - 单个事务包含的 SQL 语句不超过 5000 条(默认) - - 单条 KV entry 不超过 6MB(默认) - - KV entry 的总大小不超过 10G - - 在 Google 的 Cloud Spanner 上面,也有[类似的限制](https://cloud.google.com/spanner/docs/limits)。 - -### 数据分片 - -TiKV 自动将底层数据按照 Key 的 Range 进行分片。每个 Region 是一个 Key 的范围,从 `StartKey` 到 `EndKey` 的左闭右开区间。Region 中的 Key-Value 总量超过一定值,就会自动分裂。这部分用户不需要担心。 - -### 负载均衡 - -PD 会根据整个 TiKV 集群的状态,对集群的负载进行调度。调度是以 Region 为单位,以 PD 配置的策略为调度逻辑,自动完成。 - -### SQL on KV - -TiDB 自动将 SQL 结构映射为 KV 结构。具体的可以参考[《三篇文章了解 TiDB 技术内幕 - 说计算》](https://pingcap.com/blog-cn/tidb-internal-2/)这篇文档。简单来说,TiDB 执行了以下操作: - -+ 一行数据映射为一个 KV,Key 以 `TableID` 构造前缀,以行 ID 为后缀 -+ 一条索引映射为一个 KV,Key 以 `TableID+IndexID` 构造前缀,以索引值构造后缀 - -可以看到,对于一个表中的数据或者索引,会具有相同的前缀,这样在 TiKV 的 Key 空间内,这些 Key-Value 会在相邻的位置。那么当写入量很大,并且集中在一个表上面时,就会造成写入的热点,特别是连续写入的数据中某些索引值也是连续的(比如 update time 这种按时间递增的字段),会在很少的几个 Region 上形成写入热点,成为整个系统的瓶颈。同样,如果所有的数据读取操作也都集中在很小的一个范围内 (比如在连续的几万或者十几万行数据上),那么可能造成数据的访问热点。 - -### 二级索引 - -TiDB 支持完整的二级索引,并且是全局索引,很多查询可以通过索引来优化。如果利用好二级索引,对业务非常重要,很多 MySQL 上的经验在 TiDB 这里依然适用,不过 TiDB 还有一些自己的特点,需要注意,这一节主要讨论在 TiDB 上使用二级索引的一些注意事项。 - -+ 二级索引是否越多越好 - - 二级索引能加速查询,但是要注意新增一个索引是有副作用的,上一节介绍了索引的存储模型,那么每增加一个索引,在插入一条数据的时候,就要新增一个 Key-Value,所以索引越多,写入越慢,并且空间占用越大。另外过多的索引也会影响优化器运行时间,并且不合适的索引会误导优化器。所以索引并不是越多越好。 - -+ 对哪些列建索引比较合适 - - 上文提到,索引很重要但不是越多越好,因此需要根据具体的业务特点创建合适的索引。原则上需要对查询中需要用到的列创建索引,目的是提高性能。下面几种情况适合创建索引: - - - 区分度比较大的列,通过索引能显著地减少过滤后的行数 - - 有多个查询条件时,可以选择组合索引,注意需要把等值条件的列放在组合索引的前面 - - 这里举一个例子,假设常用的查询是 `select * from t where c1 = 10 and c2 = 100 and c3 > 10`, 那么可以考虑建立组合索引 `Index cidx (c1, c2, c3)`,这样可以用查询条件构造出一个索引前缀进行 Scan。 - -+ 通过索引查询和直接扫描 Table 的区别 - - TiDB 实现了全局索引,所以索引和 Table 中的数据并不一定在一个数据分片上,通过索引查询的时候,需要先扫描索引,得到对应的行 ID,然后通过行 ID 去取数据,所以可能会涉及到两次网络请求,会有一定的性能开销。 - - 如果查询涉及到大量的行,那么扫描索引是并发进行,只要第一批结果已经返回,就可以开始去取 Table 的数据,所以这里是一个并行 + Pipeline 的模式,虽然有两次访问的开销,但是延迟并不会很大。 - - 以下情况不会涉及到两次访问的问题: - - - 索引中的列已经满足了查询需求。比如 Table `t` 上面的列 `c` 有索引,查询是 `select c from t where c > 10;`,这个时候,只需要访问索引,就可以拿到所需要的全部数据。这种情况称之为覆盖索引 (Covering Index)。所以如果很关注查询性能,可以将部分不需要过滤但是需要在查询结果中返回的列放入索引中,构造成组合索引,比如这个例子: `select c1, c2 from t where c1 > 10;`,要优化这个查询可以创建组合索引 `Index c12 (c1, c2)`。 - - 表的 Primary Key 是整数类型。在这种情况下,TiDB 会将 Primary Key 的值当做行 ID,所以如果查询条件是在 PK 上面,那么可以直接构造出行 ID 的范围,直接扫描 Table 数据,获取结果。 - -+ 查询并发度 - - 数据分散在很多 Region 上,所以 TiDB 在做查询的时候会并发进行,默认的并发度比较保守,因为过高的并发度会消耗大量的系统资源,且对于 OLTP 类型的查询,往往不会涉及到大量的数据,较低的并发度已经可以满足需求。对于 OLAP 类型的 Query,往往需要较高的并发度。所以 TiDB 支持通过 System Variable 来调整查询并发度。 - - - [tidb_distsql_scan_concurrency](/system-variables.md#tidb_distsql_scan_concurrency) - - 在进行扫描数据的时候的并发度,这里包括扫描 Table 以及索引数据。 - - - [tidb_index_lookup_size](/system-variables.md#tidb_index_lookup_size) - - 如果是需要访问索引获取行 ID 之后再访问 Table 数据,那么每次会把一批行 ID 作为一次请求去访问 Table 数据,这个参数可以设置 Batch 的大小,较大的 Batch 会使得延迟增加,较小的 Batch 可能会造成更多的查询次数。这个参数的合适大小与查询涉及的数据量有关。一般不需要调整。 - - - [tidb_index_lookup_concurrency](/system-variables.md#tidb_index_lookup_concurrency) - - 如果是需要访问索引获取行 ID 之后再访问 Table 数据,每次通过行 ID 获取数据时候的并发度通过这个参数调节。 - -+ 通过索引保证结果顺序 - - 索引除了可以用来过滤数据之外,还能用来对数据排序,首先按照索引的顺序获取行 ID,然后再按照行 ID 的返回顺序返回行的内容,这样可以保证返回结果按照索引列有序。前面提到了扫索引和获取 Row 之间是并行 + Pipeline 模式,如果要求按照索引的顺序返回 Row,那么这两次查询之间的并发度设置的太高并不会降低延迟,所以默认的并发度比较保守。可以通过 [tidb_index_serial_scan_concurrency](/system-variables.md#tidb_index_serial_scan_concurrency) 变量进行并发度调整。 - -+ 逆序索引 - - 目前 TiDB 支持对索引进行逆序 Scan,目前速度比顺序 Scan 慢一些,通常情况下慢 20%,在数据频繁修改造成版本较多的情况下,会慢的更多。如果可能,建议避免对索引的逆序 Scan。 - -## 场景与实践 - -上一节我们讨论了一些 TiDB 基本的实现机制及其对使用带来的影响,本节我们从具体的使用场景出发,谈一些更为具体的操作实践。我们以从部署到支撑业务这条链路为序,进行讨论。 - -### 部署 - -在部署之前请务必阅读 [TiDB 部署建议以及对硬件的需求](/hardware-and-software-requirements.md)。 - -推荐通过 [TiUP](/production-deployment-using-tiup.md) 部署 TiDB 集群,这个工具可以部署、停止、销毁、升级整个集群,非常方便易用。非常不推荐手动部署,后期的维护和升级会很麻烦。 - -### 导入数据 - -为了提高导入数据期间的写入性能,可以对 TiKV 的参数进行调优,具体的文档查看 [TiKV 性能参数调优](/tune-tikv-memory-performance.md)。 - -### 写入 - -上面提到了 TiDB 对单个事务的大小有限制,这层限制是在 KV 层面,反映在 SQL 层面的话,简单来说一行数据会映射为一个 KV entry,每多一个索引,也会增加一个 KV entry。 - -> **注意:**: -> -> 对事务的大小限制,要考虑 TiDB 做编码以及事务额外 Key 的开销,在使用的时候,**建议每个事务的行数不超过 200 行,且单行数据小于 100k**,否则可能性能不佳。 - -建议无论是 Insert,Update 还是 Delete 语句,都通过分 Batch 或者是加 Limit 的方式限制。 - -在删除大量数据的时候,建议使用 `Delete from t where xx limit 5000;` 这样的方案,通过循环来删除,用 `Affected Rows == 0` 作为循环结束条件。 - -如果一次删除的数据量非常大,这种循环的方式会越来越慢,因为每次删除都是从前向后遍历,前面的删除之后,短时间内会残留不少删除标记(后续会被 GC 清理掉),影响后面的 `Delete` 语句。如果有可能,建议把 `Where` 条件细化。举个例子,假设要删除 2017-05-26 当天的所有数据,那么可以这样做: - -```SQL -for i from 0 to 23: - while affected_rows > 0: - delete from t where insert_time >= i:00:00 and insert_time < (i+1):00:00 limit 5000; - affected_rows = select affected_rows() -``` - -上面是一段伪代码,意思就是要把大块的数据拆成小块删除,以避免删除过程中前面的 Delete 语句影响后面的 Delete 语句。 - -### 查询 - -看业务的查询需求以及具体的语句,可以参考 [TiDB 专用系统变量和语法](/system-variables.md)这篇文档。可以通过 SET 语句控制 SQL 执行的并发度,另外通过 Hint 控制 Join 物理算子选择。 - -另外 MySQL 标准的索引选择 Hint 语法,也可以用,通过 `Use Index/Ignore Index hint` 控制优化器选择索引。 - -如果是个 OLTP 和 OLAP 混合类型的业务,可以把 TP 请求和 AP 请求发送到不同的 tidb-server 上,这样能够减小 AP 业务对于 TP 业务的影响。 承载 AP 业务的 tidb-server 推荐使用高配的机器,比如 CPU 核数比较多,内存比较大。 - -但彻底的隔离 OLTP 和 OLAP,推荐将 OLAP 的业务跑在 TiFlash 上。TiFlash 是列存引擎,在 OLAP 的分析查询场景上,性能极具亮点,TiFlash 可以在存储层上做到物理隔离,并可做到一致性读取。 - -### 监控和日志 - -Metrics 系统是了解系统状态的最佳方法,建议所有的用户都部署监控系统。 - -TiDB [使用 Grafana + Prometheus 监控系统状态](/tidb-monitoring-framework.md)。如果使用 TiUP 部署集群,那么会自动部署和配置监控系统。 - -监控系统中的监控项很多,大部分是给 TiDB 开发者查看的内容,如果没有对源代码比较深入的了解,并没有必要了解这些监控项。我们会精简出一些和业务相关或者是系统关键组件状态相关的监控项,放在一个独立的 `overview` 面板中,供用户使用。 - -除了监控之外,查看日志也是了解系统状态的常用方法。TiDB 的三个组件 tidb-server/tikv-server/pd-server 都有一个 `--log-file` 的参数。如果启动的时候设置了这个参数,那么日志会保存着参数所设置的文件的位置,另外会自动的按天对 Log 文件做归档。如果没有设置 `--log-file` 参数,日志会输出在 `stderr` 中。 - -从 4.0 版本开始,从解决易用性的角度出发,提供了 [TiDB Dashboard](/dashboard/dashboard-intro.md) UI 系统,通过浏览器访问 `http://PD_IP:PD_PORT/dashboard` 即可打开 TiDB Dashboard。TiDB Dashboard 可以提供集群状态、性能分析、流量可视化、SQL 诊断、日志搜索等功能。 - -### 文档 - -了解一个系统或者解决使用中的问题最好的方法是阅读文档,明白实现原理。TiDB 有大量的官方文档,希望大家在遇到问题的时候能先尝试通过文档或者搜索 Issue list 寻找解决方案。官方文档查看 [docs-cn](https://github.com/pingcap/docs-cn)。如果希望阅读英文文档,可以查看 [docs](https://github.com/pingcap/docs)。 - -其中的 [FAQ](/faq/tidb-faq.md) 和[故障诊断](/troubleshoot-tidb-cluster.md)章节建议大家仔细阅读。另外 TiDB 还有一些不错的工具,也有配套的文档,具体的见各项工具的 GitHub 页面。 - -除了文档之外,还有很多不错的文章介绍 TiDB 的各项技术细节内幕,大家可以关注下面这些文章发布渠道: - -+ 公众号:微信搜索 PingCAP -+ 知乎专栏:[TiDB 的后花园](https://zhuanlan.zhihu.com/newsql) -+ [官方博客](https://pingcap.com/blog-cn/) - -## TiDB 的最佳适用场景 - -简单来说,TiDB 适合具备下面这些特点的场景: - -+ 数据量大,单机保存不下 -+ 不希望做 Sharding 或者懒得做 Sharding -+ 访问模式上没有明显的热点 -+ 需要事务、需要强一致、需要灾备 -+ 希望 Real-Time HTAP,减少存储链路 +--- +title: TiDB 最佳实践 +--- + +# TiDB 最佳实践 + +本文档总结使用 TiDB 时的一些最佳实践,主要涉及 SQL 使用和 OLAP/OLTP 优化技巧,特别是一些 TiDB 专有的优化开关。 + +建议先阅读讲解 TiDB 原理的三篇文章([讲存储](https://pingcap.com/blog-cn/tidb-internal-1/),[说计算](https://pingcap.com/blog-cn/tidb-internal-2/),[谈调度](https://pingcap.com/blog-cn/tidb-internal-3/)),再来看这篇文章。 + +## 前言 + +数据库是一个通用的基础组件,在开发过程中会考虑到多种目标场景,在具体的业务场景中,需要根据业务的实际情况对数据的参数或者使用方式进行调整。 + +TiDB 是一个兼容 MySQL 协议和语法的分布式数据库,但是由于其内部实现,特别是支持分布式存储以及分布式事务,使得一些使用方法和 MySQL 有所区别。 + +## 基本概念 + +TiDB 的最佳实践与其实现原理密切相关,建议读者先了解一些基本的实现机制,包括 Raft、分布式事务、数据分片、负载均衡、SQL 到 KV 的映射方案、二级索引的实现方法、分布式执行引擎。下面会做一点简单的介绍,更详细的信息可以参考 PingCAP 公众号以及知乎专栏的一些文章。 + +### Raft + +Raft 是一种一致性协议,能提供强一致的数据复制保证,TiDB 最底层用 Raft 来同步数据。每次写入都要写入多数副本,才能对外返回成功,这样即使丢掉少数副本,也能保证系统中还有最新的数据。比如最大 3 副本的话,每次写入 2 副本才算成功,任何时候,只丢失一个副本的情况下,存活的两个副本中至少有一个具有最新的数据。 + +相比 Master-Slave 方式的同步,同样是保存三副本,Raft 的方式更为高效,写入的延迟取决于最快的两个副本,而不是最慢的那个副本。所以使用 Raft 同步的情况下,异地多活成为可能。在典型的两地三中心场景下,每次写入只需要本数据中心以及离得近的一个数据中心写入成功就能保证数据的一致性,而并不需要三个数据中心都写成功。但是这并不意味着在任何场景都能构建跨机房部署的业务,当写入量比较大时候,机房之间的带宽和延迟成为关键因素,如果写入速度超过机房之间的带宽,或者是机房之间延迟过大,整个 Raft 同步机制依然无法很好的运转。 + +### 分布式事务 + +TiDB 提供完整的分布式事务,事务模型是在 [Google Percolator](https://research.google.com/pubs/pub36726.html) 的基础上做了一些优化。具体的实现可以参考[《Percolator 和 TiDB 事务算法》](https://pingcap.com/blog-cn/percolator-and-txn/)这篇文章。本文档只讨论以下几点: + ++ 乐观锁 + + TiDB 的乐观事务模型,只有在真正提交的时候,才会做冲突检测。如果有冲突,则需要重试。这种模型在冲突严重的场景下,会比较低效,因为重试之前的操作都是无效的,需要重复做。举一个比较极端的例子,就是把数据库当做计数器用,如果访问的并发度比较高,那么一定会有严重的冲突,导致大量的重试甚至是超时。但是如果访问冲突并不十分严重,那么乐观锁模型具备较高的效率。在冲突严重的场景下,推荐使用悲观锁,或在系统架构层面解决问题,比如将计数器放在 Redis 中。 + ++ 悲观锁 + + TiDB 的悲观事务模型,悲观事务的行为和 MySQL 基本一致,在执行阶段就会上锁,先到先得,避免冲突情况下的重试,可以保证有较多冲突的事务的成功率。悲观锁同时解决了希望通过 `select for update` 对数据提前锁定的场景。但如果业务场景本身冲突较少,乐观锁的性能会更有优势。 + ++ 事务大小限制 + + 由于分布式事务要做两阶段提交,并且底层还需要做 Raft 复制,如果一个事务非常大,会使得提交过程非常慢,并且会卡住下面的 Raft 复制流程。为了避免系统出现被卡住的情况,我们对事务的大小做了限制: + + - 单个事务包含的 SQL 语句不超过 5000 条(默认) + - 单条 KV entry 不超过 6MB(默认) + - KV entry 的总大小不超过 10G + + 在 Google 的 Cloud Spanner 上面,也有[类似的限制](https://cloud.google.com/spanner/docs/limits)。 + +### 数据分片 + +TiKV 自动将底层数据按照 Key 的 Range 进行分片。每个 Region 是一个 Key 的范围,从 `StartKey` 到 `EndKey` 的左闭右开区间。Region 中的 Key-Value 总量超过一定值,就会自动分裂。这部分用户不需要担心。 + +### 负载均衡 + +PD 会根据整个 TiKV 集群的状态,对集群的负载进行调度。调度是以 Region 为单位,以 PD 配置的策略为调度逻辑,自动完成。 + +### SQL on KV + +TiDB 自动将 SQL 结构映射为 KV 结构。具体的可以参考[《三篇文章了解 TiDB 技术内幕 - 说计算》](https://pingcap.com/blog-cn/tidb-internal-2/)这篇文档。简单来说,TiDB 执行了以下操作: + ++ 一行数据映射为一个 KV,Key 以 `TableID` 构造前缀,以行 ID 为后缀 ++ 一条索引映射为一个 KV,Key 以 `TableID+IndexID` 构造前缀,以索引值构造后缀 + +可以看到,对于一个表中的数据或者索引,会具有相同的前缀,这样在 TiKV 的 Key 空间内,这些 Key-Value 会在相邻的位置。那么当写入量很大,并且集中在一个表上面时,就会造成写入的热点,特别是连续写入的数据中某些索引值也是连续的(比如 update time 这种按时间递增的字段),会在很少的几个 Region 上形成写入热点,成为整个系统的瓶颈。同样,如果所有的数据读取操作也都集中在很小的一个范围内 (比如在连续的几万或者十几万行数据上),那么可能造成数据的访问热点。 + +### 二级索引 + +TiDB 支持完整的二级索引,并且是全局索引,很多查询可以通过索引来优化。如果利用好二级索引,对业务非常重要,很多 MySQL 上的经验在 TiDB 这里依然适用,不过 TiDB 还有一些自己的特点,需要注意,这一节主要讨论在 TiDB 上使用二级索引的一些注意事项。 + ++ 二级索引是否越多越好 + + 二级索引能加速查询,但是要注意新增一个索引是有副作用的,上一节介绍了索引的存储模型,那么每增加一个索引,在插入一条数据的时候,就要新增一个 Key-Value,所以索引越多,写入越慢,并且空间占用越大。另外过多的索引也会影响优化器运行时间,并且不合适的索引会误导优化器。所以索引并不是越多越好。 + ++ 对哪些列建索引比较合适 + + 上文提到,索引很重要但不是越多越好,因此需要根据具体的业务特点创建合适的索引。原则上需要对查询中需要用到的列创建索引,目的是提高性能。下面几种情况适合创建索引: + + - 区分度比较大的列,通过索引能显著地减少过滤后的行数 + - 有多个查询条件时,可以选择组合索引,注意需要把等值条件的列放在组合索引的前面 + + 这里举一个例子,假设常用的查询是 `select * from t where c1 = 10 and c2 = 100 and c3 > 10`, 那么可以考虑建立组合索引 `Index cidx (c1, c2, c3)`,这样可以用查询条件构造出一个索引前缀进行 Scan。 + ++ 通过索引查询和直接扫描 Table 的区别 + + TiDB 实现了全局索引,所以索引和 Table 中的数据并不一定在一个数据分片上,通过索引查询的时候,需要先扫描索引,得到对应的行 ID,然后通过行 ID 去取数据,所以可能会涉及到两次网络请求,会有一定的性能开销。 + + 如果查询涉及到大量的行,那么扫描索引是并发进行,只要第一批结果已经返回,就可以开始去取 Table 的数据,所以这里是一个并行 + Pipeline 的模式,虽然有两次访问的开销,但是延迟并不会很大。 + + 以下情况不会涉及到两次访问的问题: + + - 索引中的列已经满足了查询需求。比如 Table `t` 上面的列 `c` 有索引,查询是 `select c from t where c > 10;`,这个时候,只需要访问索引,就可以拿到所需要的全部数据。这种情况称之为覆盖索引 (Covering Index)。所以如果很关注查询性能,可以将部分不需要过滤但是需要在查询结果中返回的列放入索引中,构造成组合索引,比如这个例子: `select c1, c2 from t where c1 > 10;`,要优化这个查询可以创建组合索引 `Index c12 (c1, c2)`。 + - 表的 Primary Key 是整数类型。在这种情况下,TiDB 会将 Primary Key 的值当做行 ID,所以如果查询条件是在 PK 上面,那么可以直接构造出行 ID 的范围,直接扫描 Table 数据,获取结果。 + ++ 查询并发度 + + 数据分散在很多 Region 上,所以 TiDB 在做查询的时候会并发进行,默认的并发度比较保守,因为过高的并发度会消耗大量的系统资源,且对于 OLTP 类型的查询,往往不会涉及到大量的数据,较低的并发度已经可以满足需求。对于 OLAP 类型的 Query,往往需要较高的并发度。所以 TiDB 支持通过 System Variable 来调整查询并发度。 + + - [tidb_distsql_scan_concurrency](/system-variables.md#tidb_distsql_scan_concurrency) + + 在进行扫描数据的时候的并发度,这里包括扫描 Table 以及索引数据。 + + - [tidb_index_lookup_size](/system-variables.md#tidb_index_lookup_size) + + 如果是需要访问索引获取行 ID 之后再访问 Table 数据,那么每次会把一批行 ID 作为一次请求去访问 Table 数据,这个参数可以设置 Batch 的大小,较大的 Batch 会使得延迟增加,较小的 Batch 可能会造成更多的查询次数。这个参数的合适大小与查询涉及的数据量有关。一般不需要调整。 + + - [tidb_index_lookup_concurrency](/system-variables.md#tidb_index_lookup_concurrency) + + 如果是需要访问索引获取行 ID 之后再访问 Table 数据,每次通过行 ID 获取数据时候的并发度通过这个参数调节。 + ++ 通过索引保证结果顺序 + + 索引除了可以用来过滤数据之外,还能用来对数据排序,首先按照索引的顺序获取行 ID,然后再按照行 ID 的返回顺序返回行的内容,这样可以保证返回结果按照索引列有序。前面提到了扫索引和获取 Row 之间是并行 + Pipeline 模式,如果要求按照索引的顺序返回 Row,那么这两次查询之间的并发度设置的太高并不会降低延迟,所以默认的并发度比较保守。可以通过 [tidb_index_serial_scan_concurrency](/system-variables.md#tidb_index_serial_scan_concurrency) 变量进行并发度调整。 + ++ 逆序索引 + + 目前 TiDB 支持对索引进行逆序 Scan,目前速度比顺序 Scan 慢一些,通常情况下慢 20%,在数据频繁修改造成版本较多的情况下,会慢的更多。如果可能,建议避免对索引的逆序 Scan。 + +## 场景与实践 + +上一节我们讨论了一些 TiDB 基本的实现机制及其对使用带来的影响,本节我们从具体的使用场景出发,谈一些更为具体的操作实践。我们以从部署到支撑业务这条链路为序,进行讨论。 + +### 部署 + +在部署之前请务必阅读 [TiDB 部署建议以及对硬件的需求](/hardware-and-software-requirements.md)。 + +推荐通过 [TiUP](/production-deployment-using-tiup.md) 部署 TiDB 集群,这个工具可以部署、停止、销毁、升级整个集群,非常方便易用。非常不推荐手动部署,后期的维护和升级会很麻烦。 + +### 导入数据 + +为了提高导入数据期间的写入性能,可以对 TiKV 的参数进行调优,具体的文档查看 [TiKV 性能参数调优](/tune-tikv-memory-performance.md)。 + +### 写入 + +上面提到了 TiDB 对单个事务的大小有限制,这层限制是在 KV 层面,反映在 SQL 层面的话,简单来说一行数据会映射为一个 KV entry,每多一个索引,也会增加一个 KV entry。 + +> **注意:**: +> +> 对事务的大小限制,要考虑 TiDB 做编码以及事务额外 Key 的开销,在使用的时候,**建议每个事务的行数不超过 200 行,且单行数据小于 100k**,否则可能性能不佳。 + +建议无论是 Insert,Update 还是 Delete 语句,都通过分 Batch 或者是加 Limit 的方式限制。 + +在删除大量数据的时候,建议使用 `Delete from t where xx limit 5000;` 这样的方案,通过循环来删除,用 `Affected Rows == 0` 作为循环结束条件。 + +如果一次删除的数据量非常大,这种循环的方式会越来越慢,因为每次删除都是从前向后遍历,前面的删除之后,短时间内会残留不少删除标记(后续会被 GC 清理掉),影响后面的 `Delete` 语句。如果有可能,建议把 `Where` 条件细化。举个例子,假设要删除 2017-05-26 当天的所有数据,那么可以这样做: + +```SQL +for i from 0 to 23: + while affected_rows > 0: + delete from t where insert_time >= i:00:00 and insert_time < (i+1):00:00 limit 5000; + affected_rows = select affected_rows() +``` + +上面是一段伪代码,意思就是要把大块的数据拆成小块删除,以避免删除过程中前面的 Delete 语句影响后面的 Delete 语句。 + +### 查询 + +看业务的查询需求以及具体的语句,可以参考 [TiDB 专用系统变量和语法](/system-variables.md)这篇文档。可以通过 SET 语句控制 SQL 执行的并发度,另外通过 Hint 控制 Join 物理算子选择。 + +另外 MySQL 标准的索引选择 Hint 语法,也可以用,通过 `Use Index/Ignore Index hint` 控制优化器选择索引。 + +如果是个 OLTP 和 OLAP 混合类型的业务,可以把 TP 请求和 AP 请求发送到不同的 tidb-server 上,这样能够减小 AP 业务对于 TP 业务的影响。 承载 AP 业务的 tidb-server 推荐使用高配的机器,比如 CPU 核数比较多,内存比较大。 + +但彻底的隔离 OLTP 和 OLAP,推荐将 OLAP 的业务跑在 TiFlash 上。TiFlash 是列存引擎,在 OLAP 的分析查询场景上,性能极具亮点,TiFlash 可以在存储层上做到物理隔离,并可做到一致性读取。 + +### 监控和日志 + +Metrics 系统是了解系统状态的最佳方法,建议所有的用户都部署监控系统。 + +TiDB [使用 Grafana + Prometheus 监控系统状态](/tidb-monitoring-framework.md)。如果使用 TiUP 部署集群,那么会自动部署和配置监控系统。 + +监控系统中的监控项很多,大部分是给 TiDB 开发者查看的内容,如果没有对源代码比较深入的了解,并没有必要了解这些监控项。我们会精简出一些和业务相关或者是系统关键组件状态相关的监控项,放在一个独立的 `overview` 面板中,供用户使用。 + +除了监控之外,查看日志也是了解系统状态的常用方法。TiDB 的三个组件 tidb-server/tikv-server/pd-server 都有一个 `--log-file` 的参数。如果启动的时候设置了这个参数,那么日志会保存着参数所设置的文件的位置,另外会自动的按天对 Log 文件做归档。如果没有设置 `--log-file` 参数,日志会输出在 `stderr` 中。 + +从 4.0 版本开始,从解决易用性的角度出发,提供了 [TiDB Dashboard](/dashboard/dashboard-intro.md) UI 系统,通过浏览器访问 `http://PD_IP:PD_PORT/dashboard` 即可打开 TiDB Dashboard。TiDB Dashboard 可以提供集群状态、性能分析、流量可视化、SQL 诊断、日志搜索等功能。 + +### 文档 + +了解一个系统或者解决使用中的问题最好的方法是阅读文档,明白实现原理。TiDB 有大量的官方文档,希望大家在遇到问题的时候能先尝试通过文档或者搜索 Issue list 寻找解决方案。官方文档查看 [docs-cn](https://github.com/pingcap/docs-cn)。如果希望阅读英文文档,可以查看 [docs](https://github.com/pingcap/docs)。 + +其中的 [FAQ](/faq/tidb-faq.md) 和[故障诊断](/troubleshoot-tidb-cluster.md)章节建议大家仔细阅读。另外 TiDB 还有一些不错的工具,也有配套的文档,具体的见各项工具的 GitHub 页面。 + +除了文档之外,还有很多不错的文章介绍 TiDB 的各项技术细节内幕,大家可以关注下面这些文章发布渠道: + ++ 公众号:微信搜索 PingCAP ++ 知乎专栏:[TiDB 的后花园](https://zhuanlan.zhihu.com/newsql) ++ [官方博客](https://pingcap.com/blog-cn/) + +## TiDB 的最佳适用场景 + +简单来说,TiDB 适合具备下面这些特点的场景: + ++ 数据量大,单机保存不下 ++ 不希望做 Sharding 或者懒得做 Sharding ++ 访问模式上没有明显的热点 ++ 需要事务、需要强一致、需要灾备 ++ 希望 Real-Time HTAP,减少存储链路 diff --git a/blocklist-control-plan.md b/blocklist-control-plan.md index 66255c9adf66..ffb21cc7837f 100644 --- a/blocklist-control-plan.md +++ b/blocklist-control-plan.md @@ -72,14 +72,30 @@ summary: 了解优化规则与表达式下推的黑名单。 ### 已支持下推的表达式 -| 表达式分类 | 具体操作 | -| :-------------- | :------------------------------------- | -| [逻辑运算](/functions-and-operators/operators.md#逻辑操作符) | AND (&&), OR (||), NOT (!) | -| [比较运算](/functions-and-operators/operators.md#比较方法和操作符) | <, <=, =, != (`<>`), >, >=, [`<=>`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_equal-to), [`IN()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in), IS NULL, LIKE, IS TRUE, IS FALSE, [`COALESCE()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce) | -| [数值运算](/functions-and-operators/numeric-functions-and-operators.md) | +, -, *, /, [`ABS()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_abs), [`CEIL()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceil), [`CEILING()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceiling), [`FLOOR()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_floor) | -| [控制流运算](/functions-and-operators/control-flow-functions.md) | [`CASE`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#operator_case), [`IF()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_if), [`IFNULL()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_ifnull) | -| [JSON 运算](/functions-and-operators/json-functions.md) | [JSON_TYPE(json_val)](https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-type),
[JSON_EXTRACT(json_doc, path[, path] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-search-functions.html#function_json-extract),
[JSON_UNQUOTE(json_val)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-unquote),
[JSON_OBJECT(key, val[, key, val] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html#function_json-object),
[JSON_ARRAY([val[, val] ...])](https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html#function_json-array),
[JSON_MERGE(json_doc, json_doc[, json_doc] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-merge),
[JSON_SET(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-set),
[JSON_INSERT(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-insert),
[JSON_REPLACE(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-replace),
[JSON_REMOVE(json_doc, path[, path] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-remove) | -| [日期运算](/functions-and-operators/date-and-time-functions.md) | [`DATE_FORMAT()`](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 表达式分类 | 具体操作 | ++:========================================================================+:==============================================================================================================================================================================================================================================================================================================================================================================================================+ +| [逻辑运算](/functions-and-operators/operators.md#逻辑操作符) | AND (&&), OR (||), NOT (!) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [比较运算](/functions-and-operators/operators.md#比较方法和操作符) | \<, \<=, =, != (`<>`), \>, \>=, [`<=>`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_equal-to), [`IN()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in), IS NULL, LIKE, IS TRUE, IS FALSE, [`COALESCE()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [数值运算](/functions-and-operators/numeric-functions-and-operators.md) | +, -, \*, /, [`ABS()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_abs), [`CEIL()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceil), [`CEILING()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceiling), [`FLOOR()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_floor) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [控制流运算](/functions-and-operators/control-flow-functions.md) | [`CASE`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#operator_case), [`IF()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_if), [`IFNULL()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_ifnull) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [JSON 运算](/functions-and-operators/json-functions.md) | [JSON_TYPE(json_val)](https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-type), | +| | [JSON_EXTRACT(json_doc, path\[, path\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-search-functions.html#function_json-extract), | +| | [JSON_UNQUOTE(json_val)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-unquote), | +| | [JSON_OBJECT(key, val\[, key, val\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html#function_json-object), | +| | [JSON_ARRAY(\[val\[, val\] ...\])](https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html#function_json-array), | +| | [JSON_MERGE(json_doc, json_doc\[, json_doc\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-merge), | +| | [JSON_SET(json_doc, path, val\[, path, val\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-set), | +| | [JSON_INSERT(json_doc, path, val\[, path, val\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-insert), | +| | [JSON_REPLACE(json_doc, path, val\[, path, val\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-replace), | +| | [JSON_REMOVE(json_doc, path\[, path\] ...)](https://dev.mysql.com/doc/refman/5.7/en/json-modification-functions.html#function_json-remove) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [日期运算](/functions-and-operators/date-and-time-functions.md) | [`DATE_FORMAT()`](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ### 禁止特定表达式下推 diff --git a/connectors-and-apis.md b/connectors-and-apis.md index 1e092b9d81b9..3c29ed577364 100644 --- a/connectors-and-apis.md +++ b/connectors-and-apis.md @@ -40,40 +40,73 @@ Oracle 官方提供了以下 API,TiDB 可以兼容所有这些 API。 第三方 API 非 Oracle 官方提供,下表列出了常用的第三方 API: -| Environment | API | Type | Notes | -| -------------- | ---------------------------------------- | -------------------------------- | ---------------------------------------- | -| Ada | GNU Ada MySQL Bindings | `libmysqlclient` | See [MySQL Bindings for GNU Ada](http://gnade.sourceforge.net/) | -| C | C API | `libmysqlclient` | See [MySQL C API](https://dev.mysql.com/doc/refman/5.7/en/c-api-info.html). | -| C++ | Connector/C++ | `libmysqlclient` | See [MySQL Connector/C++ Developer Guide](https://dev.mysql.com/doc/connector-cpp/en/). | -| | MySQL++ | `libmysqlclient` | See [MySQL++ Web site](http://tangentsoft.net/mysql++/doc/). | -| | MySQL wrapped | `libmysqlclient` | See [MySQL wrapped](http://www.alhem.net/project/mysql/). | -| Go | go-sql-driver | Native Driver | See [Mysql Go API](https://github.com/go-sql-driver/mysql) | -| Cocoa | MySQL-Cocoa | `libmysqlclient` | Compatible with the Objective-C Cocoa environment. See | -| D | MySQL for D | `libmysqlclient` | See [MySQL for D](https://github.com/mysql-d/mysql-native). | -| Eiffel | Eiffel MySQL | `libmysqlclient` | See [Section 27.14, “MySQL Eiffel Wrapper”](https://dev.mysql.com/doc/refman/5.7/en/apis-eiffel.html). | -| Erlang | `erlang-mysql-driver` | `libmysqlclient` | See [`erlang-mysql-driver`.](http://code.google.com/p/erlang-mysql-driver/) | -| Haskell | Haskell MySQL Bindings | Native Driver | See [Brian O'Sullivan's pure Haskell MySQL bindings](http://www.serpentine.com/blog/software/mysql/). | -| | `hsql-mysql` | `libmysqlclient` | See [MySQL driver for Haskell](http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hsql-mysql-1.7). | -| Java/JDBC | Connector/J | Native Driver | See [MySQL Connector/J 5.1 Developer Guide](https://dev.mysql.com/doc/connector-j/5.1/en/). | -| Lua | LuaSQL | `libmysqlclient` | See [LuaSQL](http://keplerproject.github.io/luasql/manual.html). | -| .NET/Mono | Connector/Net | Native Driver | See [MySQL Connector/Net Developer Guide](https://dev.mysql.com/doc/connector-net/en/). | -| Objective Caml | OBjective Caml MySQL Bindings | `libmysqlclient` | See [MySQL Bindings for Objective Caml](http://raevnos.pennmush.org/code/ocaml-mysql/). | -| Octave | Database bindings for GNU Octave | `libmysqlclient` | See [Database bindings for GNU Octave](http://octave.sourceforge.net/database/index.html). | -| ODBC | Connector/ODBC | `libmysqlclient` | See [MySQL Connector/ODBC Developer Guide](https://dev.mysql.com/doc/connector-odbc/en/). | -| Perl | `DBI`/`DBD::mysql` | `libmysqlclient` | See [Section 27.10, “MySQL Perl API”](https://dev.mysql.com/doc/refman/5.7/en/apis-perl.html). | -| | `Net::MySQL` | Native Driver | See [`Net::MySQL`](http://search.cpan.org/dist/Net-MySQL/MySQL.pm) at CPAN | -| PHP | `mysql`, `ext/mysql`interface (deprecated) | `libmysqlclient` | See [Original MySQL API](https://dev.mysql.com/doc/apis-php/en/apis-php-mysql.html). | -| | `mysqli`, `ext/mysqli`interface | `libmysqlclient` | See [MySQL Improved Extension](https://dev.mysql.com/doc/apis-php/en/apis-php-mysqli.html). | -| | `PDO_MYSQL` | `libmysqlclient` | See [MySQL Functions (PDO_MYSQL)](https://dev.mysql.com/doc/apis-php/en/apis-php-pdo-mysql.html). | -| | PDO mysqlnd | Native Driver | | -| Python | Connector/Python | Native Driver | See [MySQL Connector/Python Developer Guide](https://dev.mysql.com/doc/connector-python/en/). | -| Python | Connector/Python C Extension | `libmysqlclient` | See [MySQL Connector/Python Developer Guide](https://dev.mysql.com/doc/connector-python/en/). | -| | MySQLdb | `libmysqlclient` | See [Section 27.11, “MySQL Python API”](https://dev.mysql.com/doc/refman/5.7/en/apis-python.html). | -| Ruby | MySQL/Ruby | `libmysqlclient` | Uses `libmysqlclient`. See [Section 27.12.1, “The MySQL/Ruby API”](https://dev.mysql.com/doc/refman/5.7/en/apis-ruby-mysqlruby.html). | -| | Ruby/MySQL | Native Driver | See [Section 27.12.2, “The Ruby/MySQL API”](https://dev.mysql.com/doc/refman/5.7/en/apis-ruby-rubymysql.html). | -| Scheme | `Myscsh` | `libmysqlclient` | See [`Myscsh`](https://github.com/aehrisch/myscsh). | -| SPL | `sql_mysql` | `libmysqlclient` | See [`sql_mysql` for SPL](http://www.clifford.at/spl/spldoc/sql_mysql.html). | -| Tcl | MySQLtcl | `libmysqlclient` | See [Section 27.13, “MySQL Tcl API”](https://dev.mysql.com/doc/refman/5.7/en/apis-tcl.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Environment | API | Type | Notes | ++================+============================================+==================+=======================================================================================================================================+ +| Ada | GNU Ada MySQL Bindings | `libmysqlclient` | See [MySQL Bindings for GNU Ada](http://gnade.sourceforge.net/) | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| C | C API | `libmysqlclient` | See [MySQL C API](https://dev.mysql.com/doc/refman/5.7/en/c-api-info.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| C++ | Connector/C++ | `libmysqlclient` | See [MySQL Connector/C++ Developer Guide](https://dev.mysql.com/doc/connector-cpp/en/). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | MySQL++ | `libmysqlclient` | See [MySQL++ Web site](http://tangentsoft.net/mysql++/doc/). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | MySQL wrapped | `libmysqlclient` | See [MySQL wrapped](http://www.alhem.net/project/mysql/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Go | go-sql-driver | Native Driver | See [Mysql Go API](https://github.com/go-sql-driver/mysql) | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Cocoa | MySQL-Cocoa | `libmysqlclient` | Compatible with the Objective-C Cocoa environment. See | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| D | MySQL for D | `libmysqlclient` | See [MySQL for D](https://github.com/mysql-d/mysql-native). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Eiffel | Eiffel MySQL | `libmysqlclient` | See [Section 27.14, “MySQL Eiffel Wrapper”](https://dev.mysql.com/doc/refman/5.7/en/apis-eiffel.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Erlang | `erlang-mysql-driver` | `libmysqlclient` | See [`erlang-mysql-driver`.](http://code.google.com/p/erlang-mysql-driver/) | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Haskell | Haskell MySQL Bindings | Native Driver | See [Brian O'Sullivan's pure Haskell MySQL bindings](http://www.serpentine.com/blog/software/mysql/). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | `hsql-mysql` | `libmysqlclient` | See [MySQL driver for Haskell](http://hackage.haskell.org/cgi-bin/hackage-scripts/package/hsql-mysql-1.7). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Java/JDBC | Connector/J | Native Driver | See [MySQL Connector/J 5.1 Developer Guide](https://dev.mysql.com/doc/connector-j/5.1/en/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Lua | LuaSQL | `libmysqlclient` | See [LuaSQL](http://keplerproject.github.io/luasql/manual.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| .NET/Mono | Connector/Net | Native Driver | See [MySQL Connector/Net Developer Guide](https://dev.mysql.com/doc/connector-net/en/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Objective Caml | OBjective Caml MySQL Bindings | `libmysqlclient` | See [MySQL Bindings for Objective Caml](http://raevnos.pennmush.org/code/ocaml-mysql/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Octave | Database bindings for GNU Octave | `libmysqlclient` | See [Database bindings for GNU Octave](http://octave.sourceforge.net/database/index.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| ODBC | Connector/ODBC | `libmysqlclient` | See [MySQL Connector/ODBC Developer Guide](https://dev.mysql.com/doc/connector-odbc/en/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Perl | `DBI`/`DBD::mysql` | `libmysqlclient` | See [Section 27.10, “MySQL Perl API”](https://dev.mysql.com/doc/refman/5.7/en/apis-perl.html). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | `Net::MySQL` | Native Driver | See [`Net::MySQL`](http://search.cpan.org/dist/Net-MySQL/MySQL.pm) at CPAN | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| PHP | `mysql`, `ext/mysql`interface (deprecated) | `libmysqlclient` | See [Original MySQL API](https://dev.mysql.com/doc/apis-php/en/apis-php-mysql.html). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | `mysqli`, `ext/mysqli`interface | `libmysqlclient` | See [MySQL Improved Extension](https://dev.mysql.com/doc/apis-php/en/apis-php-mysqli.html). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | `PDO_MYSQL` | `libmysqlclient` | See [MySQL Functions (PDO_MYSQL)](https://dev.mysql.com/doc/apis-php/en/apis-php-pdo-mysql.html). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | PDO mysqlnd | Native Driver | | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Python | Connector/Python | Native Driver | See [MySQL Connector/Python Developer Guide](https://dev.mysql.com/doc/connector-python/en/). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Python | Connector/Python C Extension | `libmysqlclient` | See [MySQL Connector/Python Developer Guide](https://dev.mysql.com/doc/connector-python/en/). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | MySQLdb | `libmysqlclient` | See [Section 27.11, “MySQL Python API”](https://dev.mysql.com/doc/refman/5.7/en/apis-python.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Ruby | MySQL/Ruby | `libmysqlclient` | Uses `libmysqlclient`. See [Section 27.12.1, “The MySQL/Ruby API”](https://dev.mysql.com/doc/refman/5.7/en/apis-ruby-mysqlruby.html). | +| +--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| | Ruby/MySQL | Native Driver | See [Section 27.12.2, “The Ruby/MySQL API”](https://dev.mysql.com/doc/refman/5.7/en/apis-ruby-rubymysql.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Scheme | `Myscsh` | `libmysqlclient` | See [`Myscsh`](https://github.com/aehrisch/myscsh). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| SPL | `sql_mysql` | `libmysqlclient` | See [`sql_mysql` for SPL](http://www.clifford.at/spl/spldoc/sql_mysql.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ +| Tcl | MySQLtcl | `libmysqlclient` | See [Section 27.13, “MySQL Tcl API”](https://dev.mysql.com/doc/refman/5.7/en/apis-tcl.html). | ++----------------+--------------------------------------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------+ ## TiDB 支持的连接器版本 diff --git a/dumpling-overview.md b/dumpling-overview.md index 126f0461775b..10767ef703fe 100644 --- a/dumpling-overview.md +++ b/dumpling-overview.md @@ -337,43 +337,88 @@ SET GLOBAL tidb_gc_life_time = '10m'; ## Dumpling 主要选项表 -| 主要选项 | 用途 | 默认值 | -| --------| --- | --- | -| -V 或 --version | 输出 Dumpling 版本并直接退出 | -| -B 或 --database | 导出指定数据库 | -| -T 或 --tables-list | 导出指定数据表 | -| -f 或 --filter | 导出能匹配模式的表,语法可参考 [table-filter](/table-filter.md) | `[\*.\*,!/^(mysql|sys|INFORMATION_SCHEMA|PERFORMANCE_SCHEMA|METRICS_SCHEMA|INSPECTION_SCHEMA)$/.\*]`(导出除系统库外的所有库表) | -| --case-sensitive | table-filter 是否大小写敏感 | false,大小写不敏感 | -| -h 或 --host| 连接的数据库主机的地址 | "127.0.0.1" | -| -t 或 --threads | 备份并发线程数| 4 | -| -r 或 --rows | 将 table 划分成 row 行数据,一般针对大表操作并发生成多个文件。| -| -L 或 --logfile | 日志输出地址,为空时会输出到控制台 | "" | -| --loglevel | 日志级别 {debug,info,warn,error,dpanic,panic,fatal} | "info" | -| --logfmt | 日志输出格式 {text,json} | "text" | -| -d 或 --no-data | 不导出数据,适用于只导出 schema 场景 | -| --no-header | 导出 csv 格式的 table 数据,不生成 header | -| -W 或 --no-views| 不导出 view | true | -| -m 或 --no-schemas | 不导出 schema,只导出数据 | -| -s 或--statement-size | 控制 `INSERT` SQL 语句的大小,单位 bytes | -| -F 或 --filesize | 将 table 数据划分出来的文件大小,需指明单位(如 `128B`, `64KiB`, `32MiB`, `1.5GiB`) | -| --filetype| 导出文件类型(csv/sql) | "sql" | -| -o 或 --output | 导出本地文件路径或[外部存储 URL](/br/backup-and-restore-storages.md) | "./export-${time}" | -| -S 或 --sql | 根据指定的 sql 导出数据,该选项不支持并发导出 | -| --consistency | flush: dump 前用 FTWRL
snapshot: 通过 TSO 来指定 dump 某个快照时间点的 TiDB 数据
lock: 对需要 dump 的所有表执行 `lock tables read` 命令
none: 不加锁 dump,无法保证一致性
auto: 对 MySQL 使用 --consistency flush;对 TiDB 使用 --consistency snapshot | "auto" | -| --snapshot | snapshot tso,只在 consistency=snapshot 下生效 | -| --where | 对备份的数据表通过 where 条件指定范围 | -| -p 或 --password | 连接的数据库主机的密码 | -| -P 或 --port | 连接的数据库主机的端口 | 4000 | -| -u 或 --user | 连接的数据库主机的用户名 | "root" | -| --dump-empty-database | 导出空数据库的建库语句 | true | -| --ca | 用于 TLS 连接的 certificate authority 文件的地址 | -| --cert | 用于 TLS 连接的 client certificate 文件的地址 | -| --key | 用于 TLS 连接的 client private key 文件的地址 | -| --csv-delimiter | csv 文件中字符类型变量的定界符 | '"' | -| --csv-separator | csv 文件中各值的分隔符,如果数据中可能有逗号,建议源文件导出时分隔符使用非常见组合字符| ','| -| --csv-null-value | csv 文件空值的表示 | "\\N" | -| --escape-backslash | 使用反斜杠 (`\`) 来转义导出文件中的特殊字符 | true | -| --output-filename-template | 以 [golang template](https://golang.org/pkg/text/template/#hdr-Arguments) 格式表示的数据文件名格式
支持 `{{.DB}}`、`{{.Table}}`、`{{.Index}}` 三个参数
分别表示数据文件的库名、表名、分块 ID | '{{.DB}}.{{.Table}}.{{.Index}}' | -| --status-addr | Dumpling 的服务地址,包含了 Prometheus 拉取 metrics 信息及 pprof 调试的地址 | ":8281" | -| --tidb-mem-quota-query | 单条 dumpling 命令导出 SQL 语句的内存限制,单位为 byte。对于 v4.0.10 或以上版本,若不设置该参数,默认使用 TiDB 中的 `mem-quota-query` 配置项值作为内存限制值。对于 v4.0.10 以下版本,该参数值默认为 32 GB | 34359738368 | -| --params | 为需导出的数据库连接指定 session 变量,可接受的格式: "character_set_client=latin1,character_set_connection=latin1" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 主要选项 | 用途 | 默认值 | ++============================+===========================================================================================================================================================================================================+===========================================================================================================================================================+ +| -V 或 --version | 输出 Dumpling 版本并直接退出 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -B 或 --database | 导出指定数据库 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -T 或 --tables-list | 导出指定数据表 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -f 或 --filter | 导出能匹配模式的表,语法可参考 [table-filter](/table-filter.md) | `[\*.\*,!/^(mysql|sys|INFORMATION_SCHEMA|PERFORMANCE_SCHEMA|METRICS_SCHEMA|INSPECTION_SCHEMA)$/.\*]`(导出除系统库外的所有库表) | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --case-sensitive | table-filter 是否大小写敏感 | false,大小写不敏感 | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -h 或 --host | 连接的数据库主机的地址 | "127.0.0.1" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -t 或 --threads | 备份并发线程数 | 4 | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -r 或 --rows | 将 table 划分成 row 行数据,一般针对大表操作并发生成多个文件。 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -L 或 --logfile | 日志输出地址,为空时会输出到控制台 | "" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --loglevel | 日志级别 {debug,info,warn,error,dpanic,panic,fatal} | "info" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --logfmt | 日志输出格式 {text,json} | "text" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -d 或 --no-data | 不导出数据,适用于只导出 schema 场景 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --no-header | 导出 csv 格式的 table 数据,不生成 header | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -W 或 --no-views | 不导出 view | true | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -m 或 --no-schemas | 不导出 schema,只导出数据 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -s 或--statement-size | 控制 `INSERT` SQL 语句的大小,单位 bytes | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -F 或 --filesize | 将 table 数据划分出来的文件大小,需指明单位(如 `128B`, `64KiB`, `32MiB`, `1.5GiB`) | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --filetype | 导出文件类型(csv/sql) | "sql" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -o 或 --output | 导出本地文件路径或[外部存储 URL](/br/backup-and-restore-storages.md) | "./export-\${time}" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -S 或 --sql | 根据指定的 sql 导出数据,该选项不支持并发导出 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --consistency | flush: dump 前用 FTWRL | "auto" | +| | snapshot: 通过 TSO 来指定 dump 某个快照时间点的 TiDB 数据 | | +| | lock: 对需要 dump 的所有表执行 `lock tables read` 命令 | | +| | none: 不加锁 dump,无法保证一致性 | | +| | auto: 对 MySQL 使用 --consistency flush;对 TiDB 使用 --consistency snapshot | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --snapshot | snapshot tso,只在 consistency=snapshot 下生效 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --where | 对备份的数据表通过 where 条件指定范围 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -p 或 --password | 连接的数据库主机的密码 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -P 或 --port | 连接的数据库主机的端口 | 4000 | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| -u 或 --user | 连接的数据库主机的用户名 | "root" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --dump-empty-database | 导出空数据库的建库语句 | true | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --ca | 用于 TLS 连接的 certificate authority 文件的地址 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --cert | 用于 TLS 连接的 client certificate 文件的地址 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --key | 用于 TLS 连接的 client private key 文件的地址 | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --csv-delimiter | csv 文件中字符类型变量的定界符 | '"' | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --csv-separator | csv 文件中各值的分隔符,如果数据中可能有逗号,建议源文件导出时分隔符使用非常见组合字符 | ',' | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --csv-null-value | csv 文件空值的表示 | "\\N" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --escape-backslash | 使用反斜杠 (`\`) 来转义导出文件中的特殊字符 | true | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --output-filename-template | 以 [golang template](https://golang.org/pkg/text/template/#hdr-Arguments) 格式表示的数据文件名格式 | '{{.DB}}.{{.Table}}.{{.Index}}' | +| | 支持 `{{.DB}}`、`{{.Table}}`、`{{.Index}}` 三个参数 | | +| | 分别表示数据文件的库名、表名、分块 ID | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --status-addr | Dumpling 的服务地址,包含了 Prometheus 拉取 metrics 信息及 pprof 调试的地址 | ":8281" | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --tidb-mem-quota-query | 单条 dumpling 命令导出 SQL 语句的内存限制,单位为 byte。对于 v4.0.10 或以上版本,若不设置该参数,默认使用 TiDB 中的 `mem-quota-query` 配置项值作为内存限制值。对于 v4.0.10 以下版本,该参数值默认为 32 GB | 34359738368 | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ +| --params | 为需导出的数据库连接指定 session 变量,可接受的格式: "character_set_client=latin1,character_set_connection=latin1" | | ++----------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/ecosystem-tool-user-case.md b/ecosystem-tool-user-case.md index 98a6305f7203..76123bc1975d 100644 --- a/ecosystem-tool-user-case.md +++ b/ecosystem-tool-user-case.md @@ -1,46 +1,46 @@ ---- -title: TiDB 工具适用场景 -summary: 本文档介绍 TiDB 工具的常见适用场景与工具选择。 ---- - -# TiDB 工具适用场景 - -本文档从生态工具的适用场景出发,介绍部分常见场景下的生态工具选择。 - -## 在物理机或虚拟机上部署运维 TiDB - -当需要在物理机或虚拟机上部署运维 TiDB 时,你可以先安装 [TiUP](/tiup/tiup-overview.md),再通过 TiUP 管理 TiDB 的众多组件,如 TiDB、PD、TiKV 等。 - -## 在 Kubernetes 上部署运维 TiDB - -当需要在 Kubernetes 上部署运维 TiDB 时,你可以先创建 Kubernetes 集群,部署[TiDB Operator](https://docs.pingcap.com/zh/tidb-in-kubernetes/stable),然后使用 TiDB Operator 部署运维 TiDB 集群。 - -## 从 CSV 导入数据到 TiDB - -当需要将其他工具导出的格式兼容的 CSV files 导入到 TiDB 时,可使用 [TiDB Lightning](/tidb-lightning/migrate-from-csv-using-tidb-lightning.md)。 - -## 从 MySQL/Aurora 导入全量数据 - -当需要从 MySQL/Aurora 导入全量数据时,可先使用 [Dumpling](/dumpling-overview.md) 将数据导出为 SQL dump files,然后再使用 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 将数据导入到 TiDB 集群。 - -## 从 MySQL/Aurora 迁移数据 - -当既需要从 MySQL/Aurora 导入全量数据,又需要迁移增量数据时,可使用 [TiDB Data Migration (DM)](https://docs.pingcap.com/zh/tidb-data-migration/v2.0/overview) 完成[全量数据和增量数据的迁移](https://docs.pingcap.com/zh/tidb-data-migration/v2.0/migrate-from-mysql-aurora)。 - -如果全量数据量较大(TB 级别),则可先使用 [Dumpling](/dumpling-overview.md) 与 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 完成全量数据的迁移,再使用 DM 完成增量数据的迁移。 - -## TiDB 集群备份与恢复 - -当需要对 TiDB 集群进行备份或在之后对 TiDB 集群进行恢复时,可使用 [BR](/br/backup-and-restore-tool.md)。 - -另外,BR 也可以对 TiDB 的数据进行[增量备份](/br/use-br-command-line-tool.md#增量备份)和[增量恢复](/br/use-br-command-line-tool.md#增量恢复)。 - -## 迁出数据到 TiDB - -当需要将 TiDB 集群的数据迁出到其他 TiDB 集群时,可使用 [Dumpling](/dumpling-overview.md) 从 TiDB 将全量数据导出为 SQL dump files,然后再使用 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 将数据导入到 TiDB。 - -如果还需要执行增量数据的迁移,则可使用 [TiDB Binlog](/tidb-binlog/tidb-binlog-overview.md)。 - -## TiDB 增量数据订阅 - -当需要订阅 TiDB 增量数据的变更时,可使用 [TiDB Binlog](/tidb-binlog/binlog-consumer-client.md)。 +--- +title: TiDB 工具适用场景 +summary: 本文档介绍 TiDB 工具的常见适用场景与工具选择。 +--- + +# TiDB 工具适用场景 + +本文档从生态工具的适用场景出发,介绍部分常见场景下的生态工具选择。 + +## 在物理机或虚拟机上部署运维 TiDB + +当需要在物理机或虚拟机上部署运维 TiDB 时,你可以先安装 [TiUP](/tiup/tiup-overview.md),再通过 TiUP 管理 TiDB 的众多组件,如 TiDB、PD、TiKV 等。 + +## 在 Kubernetes 上部署运维 TiDB + +当需要在 Kubernetes 上部署运维 TiDB 时,你可以先创建 Kubernetes 集群,部署[TiDB Operator](https://docs.pingcap.com/zh/tidb-in-kubernetes/stable),然后使用 TiDB Operator 部署运维 TiDB 集群。 + +## 从 CSV 导入数据到 TiDB + +当需要将其他工具导出的格式兼容的 CSV files 导入到 TiDB 时,可使用 [TiDB Lightning](/tidb-lightning/migrate-from-csv-using-tidb-lightning.md)。 + +## 从 MySQL/Aurora 导入全量数据 + +当需要从 MySQL/Aurora 导入全量数据时,可先使用 [Dumpling](/dumpling-overview.md) 将数据导出为 SQL dump files,然后再使用 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 将数据导入到 TiDB 集群。 + +## 从 MySQL/Aurora 迁移数据 + +当既需要从 MySQL/Aurora 导入全量数据,又需要迁移增量数据时,可使用 [TiDB Data Migration (DM)](https://docs.pingcap.com/zh/tidb-data-migration/v2.0/overview) 完成[全量数据和增量数据的迁移](https://docs.pingcap.com/zh/tidb-data-migration/v2.0/migrate-from-mysql-aurora)。 + +如果全量数据量较大(TB 级别),则可先使用 [Dumpling](/dumpling-overview.md) 与 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 完成全量数据的迁移,再使用 DM 完成增量数据的迁移。 + +## TiDB 集群备份与恢复 + +当需要对 TiDB 集群进行备份或在之后对 TiDB 集群进行恢复时,可使用 [BR](/br/backup-and-restore-tool.md)。 + +另外,BR 也可以对 TiDB 的数据进行[增量备份](/br/use-br-command-line-tool.md#增量备份)和[增量恢复](/br/use-br-command-line-tool.md#增量恢复)。 + +## 迁出数据到 TiDB + +当需要将 TiDB 集群的数据迁出到其他 TiDB 集群时,可使用 [Dumpling](/dumpling-overview.md) 从 TiDB 将全量数据导出为 SQL dump files,然后再使用 [TiDB Lightning](/tidb-lightning/tidb-lightning-overview.md) 将数据导入到 TiDB。 + +如果还需要执行增量数据的迁移,则可使用 [TiDB Binlog](/tidb-binlog/tidb-binlog-overview.md)。 + +## TiDB 增量数据订阅 + +当需要订阅 TiDB 增量数据的变更时,可使用 [TiDB Binlog](/tidb-binlog/binlog-consumer-client.md)。 diff --git a/faq/deploy-and-maintain-faq.md b/faq/deploy-and-maintain-faq.md index f5d38c8c1653..78c010a61d3d 100644 --- a/faq/deploy-and-maintain-faq.md +++ b/faq/deploy-and-maintain-faq.md @@ -1,487 +1,487 @@ ---- -title: 部署运维 FAQ -summary: 介绍 TiDB 集群运维部署的常见问题、原因及解决方法。 ---- - -# 部署运维 FAQ - -本文介绍 TiDB 集群运维部署的常见问题、原因及解决方法。 - -## 环境准备 FAQ - -操作系统版本要求如下表: - -| Linux 操作系统平台 | 版本 | -| :--- | :--- | -| Red Hat Enterprise Linux | 7.3 及以上 | -| CentOS | 7.3 及以上 | -| Oracle Enterprise Linux | 7.3 及以上 | - -### 为什么要在 CentOS 7 上部署 TiDB 集群? - -TiDB 作为一款开源分布式 NewSQL 数据库,可以很好的部署和运行在 Intel 架构服务器环境及主流虚拟化环境,并支持绝大多数的主流硬件网络,作为一款高性能数据库系统,TiDB 支持主流的 Linux 操作系统环境,具体可以参考 TiDB 的[官方部署要求](/hardware-and-software-requirements.md)。 - -其中 TiDB 在 CentOS 7.3 的环境下进行大量的测试,同时也有很多这个操作系统的部署最佳实践,因此,我们推荐客户在部署 TiDB 的时候使用 CentOS 7.3+ 以上的Linux 操作系统。 - -## 硬件要求 FAQ - -TiDB 支持部署和运行在 Intel x86-64 架构的 64 位通用硬件服务器平台。对于开发、测试、及生产环境的服务器硬件配置有以下要求和建议: - -### 开发及测试环境 - -| **组件** | **CPU** | **内存** | **本地存储** | **网络** | **实例数量(最低要求)** | -| --- | --- | --- | --- | --- | --- | -| TiDB | 8核+ | 16 GB+ | SAS, 200 GB+ | 千兆网卡 | 1(可与 PD 同机器) | -| PD | 8核+ | 16 GB+ | SAS, 200 GB+ | 千兆网卡 | 1(可与 TiDB 同机器) | -| TiKV | 8核+ | 32 GB+ | SSD, 200 GB+ | 千兆网卡 | 3 | -| | | | | 服务器总计 | 4 | - -### 生产环境 - -| **组件** | **CPU** | **内存** | **硬盘类型** | **网络** | **实例数量(最低要求)** | -| --- | --- | --- | --- | --- | --- | -| TiDB | 16核+ | 48 GB+ | SAS | 万兆网卡(2块最佳) | 2 | -| PD | 8核+ | 16 GB+ | SSD | 万兆网卡(2块最佳) | 3 | -| TiKV | 16核+ | 48 GB+ | SSD | 万兆网卡(2块最佳) | 3 | -| 监控 | 8核+ | 16 GB+ | SAS | 千兆网卡 | 1 | -| | | | | 服务器总计 | 9 | - -### 两块网卡的目的是?万兆的目的是? - -作为一个分布式集群,TiDB 对时间的要求还是比较高的,尤其是 PD 需要分发唯一的时间戳,如果 PD 时间不统一,如果有 PD 切换,将会等待更长的时间。两块网卡可以做 bond,保证数据传输的稳定,万兆可以保证数据传输的速度,千兆网卡容易出现瓶颈,我们强烈建议使用万兆网卡。 - -### SSD 不做 RAID 是否可行? - -资源可接受的话,我们建议做 RAID 10,如果资源有限,也可以不做 RAID。 - -### TiDB 集群各个组件的配置推荐? - -- TiDB 需要 CPU 和内存比较好的机器,参考官网配置要求,如果后期需要开启 TiDB Binlog,根据业务量的评估和 GC 时间的要求,也需要本地磁盘大一点,不要求 SSD 磁盘; -- PD 里面存了集群元信息,会有频繁的读写请求,对磁盘 I/O 要求相对比较高,磁盘太差会影响整个集群性能,推荐 SSD 磁盘,空间不用太大。另外集群 Region 数量越多对 CPU、内存的要求越高; -- TiKV 对 CPU、内存、磁盘要求都比较高,一定要用 SSD 磁盘。 - -详情可参考 [TiDB 软硬件环境需求](/hardware-and-software-requirements.md)。 - -## 安装部署 FAQ - -如果用于生产环境,推荐使用 TiUP [使用 TiUP 部署](/production-deployment-using-tiup.md) TiDB 集群。 - -### 为什么修改了 TiKV/PD 的 toml 配置文件,却没有生效? - -这种情况一般是因为没有使用 `--config` 参数来指定配置文件(目前只会出现在 binary 部署的场景),TiKV/PD 会按默认值来设置。如果要使用配置文件,请设置 TiKV/PD 的 `--config` 参数。对于 TiKV 组件,修改配置后重启服务即可;对于 PD 组件,只会在第一次启动时读取配置文件,之后可以使用 pd-ctl 的方式来修改配置,详情可参考 [PD 配置参数](/command-line-flags-for-pd-configuration.md)。 - -### TiDB 监控框架 Prometheus + Grafana 监控机器建议单独还是多台部署? - -监控机建议单独部署。建议 CPU 8 core,内存 16 GB 以上,硬盘 500 GB 以上。 - -### 有一部分监控信息显示不出来? - -查看访问监控的机器时间跟集群内机器的时间差,如果比较大,更正时间后即可显示正常。 - -### supervise/svc/svstat 服务具体起什么作用? - -- supervise 守护进程 -- svc 启停服务 -- svstat 查看进程状态 - -### inventory.ini 变量参数解读 - -| **变量** | **含义** | -| --- | --- | -| cluster_name | 集群名称,可调整 | -| tidb_version | TiDB 版本 | -| deployment_method | 部署方式,默认为 binary,可选 docker | -| process_supervision | 进程监管方式,默认为 systemd,可选 supervise | -| timezone | 修改部署目标机器时区,默认为 Asia/Shanghai, 可调整,与set_timezone 变量结合使用 | -| set_timezone | 默认为 True,即修改部署目标机器时区,关闭可修改为 False | -| enable_elk | 目前不支持,请忽略 | -| enable_firewalld | 开启防火墙,默认不开启 | -| enable_ntpd | 检测部署目标机器 NTP 服务,默认为 True,请勿关闭 | -| machine_benchmark | 检测部署目标机器磁盘 IOPS,默认为 True,请勿关闭 | -| set_hostname | 根据 IP 修改部署目标机器主机名,默认为 False | -| enable_binlog | 是否部署 pump 并开启 binlog,默认为 False,依赖 Kafka 集群,参见 zookeeper_addrs 变量 | -| zookeeper_addrs | binlog Kafka 集群的 zookeeper 地址 | -| enable_slow_query_log | TiDB 慢查询日志记录到单独文件({{ deploy_dir }}/log/tidb_slow_query.log),默认为 False,记录到 tidb 日志 | -| deploy_without_tidb | KV 模式,不部署 TiDB 服务,仅部署 PD、TiKV 及监控服务,请将 inventory.ini 文件中 tidb_servers 主机组 IP 设置为空。 | - -### 如何单独记录 TiDB 中的慢查询日志,如何定位慢查询 SQL? - -1)TiDB 中,对慢查询的定义在 TiDB 的配置文件中。`slow-threshold: 300`,这个参数是配置慢查询记录阈值的,单位是 ms。 - -2)如果出现了慢查询,可以从 Grafana 监控定位到出现慢查询的 tidb-server 以及时间点,然后在对应节点查找日志中记录的 SQL 信息。 - -3)除了日志,还可以通过 `admin show slow` 命令查看,详情可参考 [`admin show slow` 命令](/identify-slow-queries.md#admin-show-slow-命令)。 - -### 首次部署 TiDB 集群时,没有配置 tikv 的 Label 信息,在后续如何添加配置 Label? - -TiDB 的 Label 设置是与集群的部署架构相关的,是集群部署中的重要内容,是 PD 进行全局管理和调度的依据。如果集群在初期部署过程中没有设置 Label,需要在后期对部署结构进行调整,就需要手动通过 PD 的管理工具 pd-ctl 来添加 location-labels 信息,例如:`config set location-labels "zone,rack,host"`(根据实际的 label 层级名字配置)。 - -pd-ctl 的使用参考 [PD Control 使用说明](/pd-control.md)。 - -### 为什么测试磁盘的 dd 命令用 `oflag=direct` 这个选项? - -Direct 模式就是把写入请求直接封装成 I/O 指令发到磁盘,这样是为了绕开文件系统的缓存,可以直接测试磁盘的真实的 I/O 读写能力。 - -### 如何用 fio 命令测试 TiKV 实例的磁盘性能? - -- 随机读测试: - - {{< copyable "shell-regular" >}} - - ```bash - ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randread -size=10G -filename=fio_randread_test.txt -name='fio randread test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_result.json - ``` - -- 顺序写和随机读混合测试: - - {{< copyable "shell-regular" >}} - - ```bash - ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randrw -percentage_random=100,0 -size=10G -filename=fio_randread_write_test.txt -name='fio mixed randread and sequential write test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_write_test.json - ``` - -## 集群管理 FAQ - -### 集群日常管理 - -#### TiDB 如何登录? - -和 MySQL 登录方式一样,可以按照下面例子进行登录。 - -`mysql -h 127.0.0.1 -uroot -P4000` - -#### TiDB 如何修改数据库系统变量? - -和 MySQL 一样,TiDB 也分为静态参数和固态参数,静态参数可以直接通过`set global xxx = n`的方式进行修改,不过新参数值只限于该实例生命周期有效。 - -#### TiDB (TiKV) 有哪些数据目录? - -默认在 [`--data-dir`](/command-line-flags-for-tikv-configuration.md#--data-dir) 目录下,其中包括 backup、db、raft、snap 四个目录,分别存储备份、数据、raft 数据及镜像数据。 - -#### TiDB 有哪些系统表? - -和 MySQL 类似,TiDB 中也有系统表,用于存放数据库运行时所需信息,具体信息参考 [TiDB 系统表](/mysql-schema.md)文档。 - -#### TiDB 各节点服务器下是否有日志文件,如何管理? - -默认情况下各节点服务器会在日志中输出标准错误,如果启动的时候通过 `--log-file` 参数指定了日志文件,那么日志会输出到指定的文件中,并且按天做 rotation。 - -#### 如何规范停止 TiDB? - -可以直接 kill 掉所有服务。如果使用 kill 命令,TiDB 的组件会做 graceful 的 shutdown。 - -#### TiDB 里面可以执行 kill 命令吗? - -- 可以 kill DML 语句,首先使用 `show processlist`,找到对应 session 的 id,然后执行 `kill tidb [session id]`。 -- 可以 kill DDL 语句,首先使用 `admin show ddl jobs`,查找需要 kill 的 DDL job ID,然后执行 `admin cancel ddl jobs 'job_id' [, 'job_id'] ...`。具体可以参考 [admin 操作](/sql-statements/sql-statement-admin.md)。 - -#### TiDB 是否支持会话超时? - -TiDB 暂不支持数据库层面的会话超时,目前想要实现超时,在没 LB(Load Balancing)的时候,需要应用侧记录发起的 Session 的 ID,通过应用自定义超时,超时以后需要到发起 Query 的节点上用 `kill tidb [session id]` 来杀掉 SQL。目前建议使用应用程序来实现会话超时,当达到超时时间,应用层就会抛出异常继续执行后续的程序段。 - -#### TiDB 生产环境的版本管理策略是怎么样的?如何尽可能避免频繁升级? - -TiDB 版本目前逐步标准化,每次 Release 都包含详细的 Change log,版本功能[变化详情](https://github.com/pingcap/TiDB/releases),生产环境是否有必要升级取决于业务系统,建议升级之前详细了解前后版本的功能差异。 - -版本号说明参考:Release Version: `v1.0.3-1-ga80e796` - -- `v1.0.3` 表示 GA 标准版 -- `1` 表示该版本 commit 1 次 -- `ga80e796` 代表版本的 `git-hash` - -#### 分不清 TiDB master 版本之间的区别,应该怎么办? - -TiDB 目前社区非常活跃,在 1.0 GA 版本发布后,还在不断的优化和修改 BUG,因此 TiDB 的版本更新周期比较快,会不定期有新版本发布,请关注我们的[新版本发布官方网站](https://pingcap.com/weekly/)。此外 TiDB 安装推荐[使用 TiUP 进行安装](/production-deployment-using-tiup.md)。此外,在 TiDB 1.0 GA 版本后,对 TiDB 的版本号进行了统一管理,TiDB 的版本可以通过以下两种方式进行查看: - -- 通过 `select tidb_version()` 进行查看 -- 通过执行 `tidb-server -V` 进行查看 - -#### 有没有图形化部署 TiDB 的工具? - -暂时没有。 - -#### TiDB 如何进行水平扩展? - -当您的业务不断增长时,数据库可能会面临三方面瓶颈,第一是存储空间,第二是计算资源,第三是读写容量,这时可以对 TiDB 集群做水平扩展。 - -- 如果是存储资源不够,可以通过添加 TiKV Server 节点来解决,新节点启动后,PD 会自动将其他节点的部分数据迁移过去,无需人工介入。 -- 如果是计算资源不够,可以查看 TiDB Server 和 TiKV Server 节点的 CPU 消耗情况,再考虑添加 TiDB Server 节点或者是 TiKV Server 节点来解决,如添加 TiDB Server 节点,将其添加到前端 Load Balancer 配置之中即可。 -- 如果是容量跟不上,一般可以考虑同时增加 TiDB Server 和 TiKV Server 节点。 - -#### Percolator 用了分布式锁,crash 的客户端会保持锁,会造成锁没有 release? - -详细可参考 [Percolator 和 TiDB 事务算法](https://pingcap.com/blog-cn/percolator-and-txn/)。 - -#### TiDB 为什么选用 gRPC 而不选用 Thrift,是因为 Google 在用吗? - -不只是因为 Google 在用,有一些比较好的特性我们需要,比如流控、加密还有 Streaming。 - -#### like(bindo.customers.name, jason%, 92) 这个92代表什么? - -那个是转义字符,默认是 (ASCII 92)。 - -#### 为什么 `information_schema.tables.data_length` 记录的大小和 TiKV 监控面板上的 store size 不一样? - -这是因为两者计算的角度不一样。`information_schema.tables.data_length` 是通过统计信息(平均每行的大小)得到的估算值。TiKV 监控面板上的 store size 是单个 TiKV 实例的数据文件(RocksDB 的 SST 文件)的大小总和。由于多版本和 TiKV 会压缩数据,所以两者显示的大小不一样。 - -#### 为什么事务没有使用异步提交或一阶段提交? - -在以下情况中,即使通过系统变量开启了[异步提交](/system-variables.md#tidb_enable_async_commit-从-v50-版本开始引入)和[一阶段提交](/system-variables.md#tidb_enable_1pc-从-v50-版本开始引入),TiDB 也不会使用这些特性: - -- 如果开启了 TiDB Binlog,受 TiDB Binlog 的实现原理限制,TiDB 不会使用异步提交或一阶段提交特性。 -- TiDB 只在事务写入不超过 256 个键值对,以及所有键值对里键的总大小不超过 4 KB 时,才会使用异步提交或一阶段提交特性。这是因为对于写入量大的事务,异步提交不能明显提升执行性能。 - -### PD 管理 - -#### 访问 PD 报错:TiKV cluster is not bootstrapped - -PD 的大部分 API 需要在初始化 TiKV 集群以后才能使用,如果在部署新集群的时候只启动了 PD,还没有启动 TiKV,这时候访问 PD 就会报这个错误。遇到这个错误应该先把要部署的 TiKV 启动起来,TiKV 会自动完成初始化工作,然后就可以正常访问 PD。 - -#### PD 启动报错:etcd cluster ID mismatch - -PD 启动参数中的 `--initial-cluster` 包含了某个不属于该集群的成员。遇到这个错误时请检查各个成员的所属集群,剔除错误的成员后即可正常启动。 - -#### PD 能容忍的时间同步误差是多少? - -理论上,时间同步误差越小越好。PD 可容忍任意时长的误差,但是,时间同步误差越大意味着 PD 分配的时间戳与真实的物理时间相差越大,这个差距会影响读历史版本等功能。 - -#### Client 连接是如何寻找 PD 的? - -Client 连接只能通过 TiDB 访问集群,TiDB 负责连接 PD 与 TiKV,PD 与 TiKV 对 Client 透明。当 TiDB 连接任意一台 PD 的时候,PD 会告知 TiDB 当前的 leader 是谁,如果此台 PD 不是 leader,TiDB 将会重新连接至 leader PD。 - -#### TiKV 节点 (Store) 各状态 (Up, Disconnect, Offline, Down, Tombstone) 之间的关系是什么? - -使用 `pd-ctl` 可以查看 TiKV 节点的状态信息。如需查看各个状态之间的关系,请参考 [TiKV Store 状态之间的关系](/tidb-scheduling.md#信息收集)。 - -#### PD 参数中 leader-schedule-limit 和 region-schedule-limit 调度有什么区别? - -- leader-schedule-limit 调度是用来均衡不同 TiKV 的 leader 数,影响处理查询的负载。 -- region-schedule-limit 调度是均衡不同 TiKV 的副本数,影响不同节点的数据量。 - -#### 每个 region 的 replica 数量可配置吗?调整的方法是? - -可以,目前只能调整全局的 replica 数量。首次启动时 PD 会读配置文件(conf/pd.yml),使用其中的 max-replicas 配置,之后修改需要使用 pd-ctl 配置命令 `config set max-replicas $num`,配置后可通过 `config show all` 来查看已生效的配置。调整的时候,不会影响业务,会在后台添加,注意总 TiKV 实例数总是要大于等于设置的副本数,例如 3 副本需要至少 3 个 TiKV。增加副本数量之前需要预估额外的存储需求。pd-ctl 的详细用法可参考 [PD Control 使用说明](/pd-control.md)。 - -#### 缺少命令行集群管理工具,整个集群的健康度当前是否正常,不好确认? - -可以通过 pd-ctl 等工具来判断集群大概的状态,详细的集群状态还是需要通过监控来确认。 - -#### 集群下线节点后,怎么删除老集群节点监控信息? - -下线节点一般指 TiKV 节点通过 pd-ctl 或者监控判断节点是否下线完成。节点下线完成后,手动停止下线节点上相关的服务。从 Prometheus 配置文件中删除对应节点的 node_exporter 信息。 - -### TiDB server 管理 - -#### TiDB 的 lease 参数应该如何设置? - -启动 TiDB Server 时,需要通过命令行参数设置 lease 参数(--lease=60),其值会影响 DDL 的速度(只会影响当前执行 DDL 的 session,其他的 session 不会受影响)。在测试阶段,lease 的值可以设为 1s,加快测试进度;在生产环境下,我们推荐这个值设为分钟级(一般可以设为 60),这样可以保证 DDL 操作的安全。 - -#### DDL 在正常情况下的耗时是多少? - -一般情况下处理一个 DDL 操作(之前没有其他 DDL 操作在处理)的耗时基本可以分如下为三种: - -- add index 操作,且此操作对应表数据行数比较少,耗时约为 3s。 -- add index 操作,且此操作对应表数据行数比较多,耗时具体由表中数据行数和当时 QPS 情况定(add index 操作优先级比一般 SQL 低)。 -- 其他 DDL 操作耗时约为 1s。 - -此外,如果接收 DDL 请求的 TiDB 和 DDL owner 所处的 TiDB 是一台,那么上面列举的第一和第三种可能的耗时应该在几十到几百毫秒。 - -#### 为什么有的时候执行 DDL 会很慢? - -可能原因如下: - -- 多个 DDL 语句一起执行的时候,后面的几个 DDL 语句会比较慢。原因是当前 TiDB 集群中 DDL 操作是串行执行的。 -- 在正常集群启动后,第一个 DDL 操作的执行时间可能会比较久,一般在 30s 左右,这个原因是刚启动时 TiDB 在竞选处理 DDL 的 leader。 -- 由于停 TiDB 时不能与 PD 正常通信(包括停电情况)或者用 `kill -9` 指令停 TiDB 导致 TiDB 没有及时从 PD 清理注册数据,那么会影响 TiDB 启动后 10min 内的 DDL 语句处理时间。这段时间内运行 DDL 语句时,每个 DDL 状态变化都需要等待 2 * lease(默认 lease = 45s)。 -- 当集群中某个 TiDB 与 PD 之间发生通信问题,即 TiDB 不能从 PD 及时获取或更新版本信息,那么这时候 DDL 操作的每个状态处理需要等待 2 * lease。 - -#### TiDB 可以使用 S3 作为后端存储吗? - -不可以,目前 TiDB 只支持分布式存储引擎和 GolevelDB/RocksDB/BoltDB 引擎。 - -#### Information_schema 能否支持更多真实信息? - -Information_schema 库里面的表主要是为了兼容 MySQL 而存在,有些第三方软件会查询里面的信息。在目前 TiDB 的实现中,里面大部分只是一些空表。后续随着 TiDB 的升级,会提供更多的参数信息。当前 TiDB 支持的 Information\_schema 请参考 [TiDB 系统数据库说明文档](/information-schema/information-schema.md)。 - -#### TiDB Backoff type 主要原因? - -TiDB-server 与 TiKV-server 随时进行通信,在进行大量数据操作过程中,会出现 `Server is busy` 或者 `backoff.maxsleep 20000ms` 的日志提示信息,这是由于 TiKV-server 在处理过程中系统比较忙而出现的提示信息,通常这时候可以通过系统资源监控到 TiKV 主机系统资源使用率比较高的情况出现。如果这种情况出现,可以根据资源使用情况进行相应的扩容操作。 - -#### TiDB TiClient type 主要原因? - -TiClient Region Error 该指标描述的是在 TiDB-server 作为客户端通过 KV 接口访问 TiKV-server 进行数据操作过程中,TiDB-server 操作 TiKV-server 中的 Region 数据出现的错误类型与 metric 指标,错误类型包括 not_leader、stale_epoch。出现这些错误的情况是当 TiDB-server 根据自己的缓存信息去操作 Region leader 数据的时候,Region leader 发生了迁移或者 TiKV 当前的 Region 信息与 TiDB 缓存的路由信息不一致而出现的错误提示。一般这种情况下,TiDB-server 都会自动重新从 PD 获取最新的路由数据,重做之前的操作。 - -#### TiDB 同时支持的最大并发连接数? - -默认情况下,每个 TiDB 服务器的最大连接数没有限制。如有需要,可以在 `config.toml` 文件中设置 `max-server-connections` 来限制最大连接数。如果并发量过大导致响应时间增加,建议通过添加 TiDB 节点进行扩容。 - -#### 如何查看某张表创建的时间? - -information_schema 库中的 tables 表里的 create_time 即为表的真实创建时间。 - -#### TiDB 的日志中 EXPENSIVE_QUERY 是什么意思? - -TiDB 在执行 SQL 时,预估出来每个 operator 处理了超过 10000 条数据就认为这条 query 是 expensive query。可以通过修改 tidb-server 配置参数来对这个门限值进行调整,调整后需要重新启动 tidb-server。 - -### TiKV 管理 - -#### TiKV 集群副本建议配置数量是多少,是不是最小高可用配置(3个)最好? - -如果是测试环境 3 副本足够;在生产环境中,不可让集群副本数低于 3,需根据架构特点、业务系统及恢复能力的需求,适当增加副本数。值得注意的是,副本升高,性能会有下降,但是安全性更高。 - -#### TiKV 启动报错:cluster ID mismatch - -TiKV 本地存储的 cluster ID 和指定的 PD 的 cluster ID 不一致。在部署新的 PD 集群的时候,PD 会随机生成一个 cluster ID,TiKV 第一次初始化的时候会从 PD 获取 cluster ID 存储在本地,下次启动的时候会检查本地的 cluster ID 与 PD 的 cluster ID 是否一致,如果不一致则会报错并退出。出现这个错误一个常见的原因是,用户原先部署了一个集群,后来把 PD 的数据删除了并且重新部署了新的 PD,但是 TiKV 还是使用旧的数据重启连到新的 PD 上,就会报这个错误。 - -#### TiKV 启动报错:duplicated store address - -启动参数中的地址已经被其他的 TiKV 注册在 PD 集群中了。造成该错误的常见情况:TiKV `--data-dir` 指定的路径下没有数据文件夹(删除或移动后没有更新 --data-dir),用之前参数重新启动该 TiKV。请尝试用 pd-ctl 的 [store delete](https://github.com/pingcap/pd/tree/55db505e8f35e8ab4e00efd202beb27a8ecc40fb/tools/pd-ctl#store-delete--label--weight-store_id----jqquery-string) 功能,删除之前的 store,然后重新启动 TiKV 即可。 - -#### TiKV master 和 slave 用的是一样的压缩算法,为什么效果不一样? - -目前来看 master 有些文件的压缩率会高一些,这个取决于底层数据的分布和 RocksDB 的实现,数据大小偶尔有些波动是正常的,底层存储引擎会根据需要调整数据。 - -#### TiKV block cache 有哪些特性? - -TiKV 使用了 RocksDB 的 Column Family (CF) 特性,KV 数据最终存储在默认 RocksDB 内部的 default、write、lock 3 个 CF 内。 - -- default CF 存储的是真正的数据,与其对应的参数位于 `[rocksdb.defaultcf]` 项中。 -- write CF 存储的是数据的版本信息(MVCC)、索引、小表相关的数据,相关的参数位于 `[rocksdb.writecf]` 项中。 -- lock CF 存储的是锁信息,系统使用默认参数。 -- Raft RocksDB 实例存储 Raft log。default CF 主要存储的是 Raft log,与其对应的参数位于 `[raftdb.defaultcf]` 项中。 -- 所有 CF 共享一个 Block-cache,用于缓存数据块,加速 RocksDB 的读取速度,Block-cache 的大小通过参数 `block-cache-size` 控制,`block-cache-size` 越大,能够缓存的热点数据越多,对读取操作越有利,同时占用的系统内存也会越多。 -- 每个 CF 有各自的 Write-buffer,大小通过 `write-buffer-size` 控制。 - -#### TiKV channel full 是什么原因? - -- Raftstore 线程太忙,或者因 I/O 而卡住。可以看一下 Raftstore 的 CPU 使用情况。 -- TiKV 过忙(CPU、磁盘 I/O 等),请求处理不过来。 - -#### TiKV 频繁切换 Region leader 是什么原因? - -- 网络问题导致节点间通信卡了,查看 Report failures 监控。 -- 原主 Leader 的节点卡了,导致没有及时给 Follower 发送消息。 -- Raftstore 线程卡了。 - -#### 如果一个节点挂了会影响服务吗?影响会持续多久? - -TiDB 使用 Raft 在多个副本之间做数据同步(默认为每个 Region 3 个副本)。当一份备份出现问题时,其他的副本能保证数据的安全。根据 Raft 协议,当某个节点挂掉导致该节点里的 Leader 失效时,在最大 2 * lease time(leasetime 是 10 秒)时间后,通过 Raft 协议会很快将一个另外一个节点里的 Follower 选为新的 Region Leader 来提供服务。 - -#### TiKV 在分别在哪些场景下占用大量 IO、内存、CPU(超过参数配置的多倍)? - -在大量写入、读取的场景中会占用大量的磁盘 IO、内存、CPU。在执行很复杂的查询,比如会产生很大中间结果集的情况下,会消耗很多的内存和 CPU 资源。 - -#### TiKV 是否可以使用 SAS/SATA 盘或者进行 SSD/SAS 混合部署? - -不可以使用,TiDB 在进行 OLTP 场景中,数据访问和操作需要高 IO 磁盘的支持,TiDB 作为强一致的分布式数据库,存在一定的写放大,如副本复制、存储底层 Compaction,因此,TiDB 部署的最佳实践中推荐用户使用 NVMe SSD 磁盘作为数据存储磁盘。另外,TiKV 与 PD 不能混合部署。 - -#### 数据表 Key 的 Range 范围划分是在数据接入之前就已经划分好了吗? - -不是的,这个和 MySQL 分表规则不一样,需要提前设置好,TiKV 是根据 Region 的大小动态分裂的。 - -#### Region 是如何进行分裂的? - -Region 不是前期划分好的,但确实有 Region 分裂机制。当 Region 的大小超过参数 `region-max-size` 或 `region-max-keys` 的值时,就会触发分裂,分裂后的信息会汇报给 PD。 - -#### TiKV 是否有类似 MySQL 的 `innodb_flush_log_trx_commit` 参数,来保证提交数据不丢失? - -是的,TiKV 单机的存储引擎目前使用两个 RocksDB 实例,其中一个存储 raft-log,TiKV 有个 sync-log 参数,在 true 的情况下,每次提交都会强制刷盘到 raft-log,如果发生 crash 后,通过 raft-log 进行 KV 数据的恢复。 - -#### 对 WAL 存储有什么推荐的硬件配置,例如 SSD,RAID 级别,RAID 卡 cache 策略,NUMA 设置,文件系统选择,操作系统的 IO 调度策略等? - -WAL 属于顺序写,目前我们并没有单独对他进行配置,建议 SSD,RAID 如果允许的话,最好是 RAID 10,RAID 卡 cache、操作系统 I/O 调度目前没有针对性的最佳实践,Linux 7 以上默认配置即可,NUMA 没有特别建议,NUMA 内存分配策略可以尝试使用 `interleave = all`,文件系统建议 ext4。 - -#### 在最严格的 `sync-log = true` 数据可用模式下,写入性能如何? - -一般来说,开启 `sync-log` 会让性能损耗 30% 左右。关闭 `sync-log` 时的性能表现,请参见 [TiDB Sysbench 性能测试报告](/benchmark/v3.0-performance-benchmarking-with-sysbench.md)。 - -#### 是否可以利用 TiKV 的 Raft + 多副本达到完全的数据可靠,单机存储引擎是否需要最严格模式? - -通过使用 [Raft 一致性算法](https://raft.github.io/),数据在各 TiKV 节点间复制为多副本,以确保某个节点挂掉时数据的安全性。只有当数据已写入超过 50% 的副本时,应用才返回 ACK(三副本中的两副本)。但理论上两个节点也可能同时发生故障,所以除非是对性能要求高于数据安全的场景,一般都强烈推荐开启 `sync-log`。 - -另外,还有一种 `sync-log` 的替代方案,即在 Raft group 中用五个副本而非三个。这将允许两个副本同时发生故障,而仍然能保证数据安全性。 - -对于单机存储引擎也同样推荐打开 `sync-log` 模式。否则如果节点宕机可能会丢失最后一次写入数据。 - -#### 使用 Raft 协议,数据写入会有多次网络的 roundtrip,实际写入延迟如何? - -理论上,和单机数据库相比,数据写入会多四个网络延迟。 - -#### 有没有类似 MySQL 的 InnoDB Memcached plugin,可以直接使用 KV 接口,可以不需要独立的 Cache? - -TiKV 支持单独进行接口调用,理论上也可以起个实例做为 Cache,但 TiDB 最大的价值是分布式关系型数据库,我们原则上不对 TiKV 单独进行支持。 - -#### Coprocessor 组件的主要作用? - -- 减少 TiDB 与 TiKV 之间的数据传输。 -- 计算下推,充分利用 TiKV 的分布式计算资源。 - -#### IO error: No space left on device While appending to file - -这是磁盘空间不足导致的,需要加节点或者扩大磁盘空间。 - -#### 为什么 TiKV 容易出现 OOM? - -TiKV 的内存占用主要来自于 RocksDB 的 block-cache,默认为系统总内存的 40%。当 TiKV 容易出现 OOM 时,检查 `block-cache-size` 配置是否过高。还需要注意,当单机部署了多个 TiKV 实例时,需要显式地配置该参数,以防止多个实例占用过多系统内存导致 OOM。 - -#### TiDB 数据和 RawKV 数据可存储于同一个 TiKV 集群里吗? - -不可以。TiDB 数据(或使用其他事务 API 生成的数据)依赖于一种特殊的键值格式,和 RawKV API 数据(或其他基于 RawKV 的服务生成的数据)并不兼容。 - -### TiDB 测试 - -#### TiDB Sysbench 基准测试结果如何? - -很多用户在接触 TiDB 都习惯做一个基准测试或者 TiDB 与 MySQL 的对比测试,官方也做了一个类似测试,汇总很多测试结果后,我们发现虽然测试的数据有一定的偏差,但结论或者方向基本一致,由于 TiDB 与 MySQL 由于架构上的差别非常大,很多方面是很难找到一个基准点,所以官方的建议两点: - -- 大家不要用过多精力纠结这类基准测试上,应该更多关注 TiDB 的场景上的区别。 -- 大家可以直接参考 [TiDB Sysbench 性能测试报告](/benchmark/v3.0-performance-benchmarking-with-sysbench.md)。 - -#### TiDB 集群容量 QPS 与节点数之间关系如何,和 MySQL 对比如何? - -- 在 10 节点内,TiDB 写入能力(Insert TPS)和节点数量基本成 40% 线性递增,MySQL 由于是单节点写入,所以不具备写入扩展能力。 -- MySQL 读扩容可以通过添加从库进行扩展,但写流量无法扩展,只能通过分库分表,而分库分表有很多问题,具体参考[方案虽好,成本先行:数据库 Sharding+Proxy 实践解析](http://dbaplus.cn/news-11-1854-1.html)。 -- TiDB 不管是读流量、还是写流量都可以通过添加节点快速方便的进行扩展。 - -#### 我们的 DBA 测试过 MySQL 性能,单台 TiDB 的性能没有 MySQL 性能那么好? - -TiDB 设计的目标就是针对 MySQL 单台容量限制而被迫做的分库分表的场景,或者需要强一致性和完整分布式事务的场景。它的优势是通过尽量下推到存储节点进行并行计算。对于小表(比如千万级以下),不适合 TiDB,因为数据量少,Region 有限,发挥不了并行的优势,最极端的就是计数器表,几行记录高频更新,这几行在 TiDB 里,会变成存储引擎上的几个 KV,然后只落在一个 Region 里,而这个 Region 只落在一个节点上。加上后台强一致性复制的开销,TiDB 引擎到 TiKV 引擎的开销,最后表现出来的就是没有单个 MySQL 好。 - -### TiDB 备份恢复 - -#### TiDB 主要备份方式? - -目前,数据量大时推荐使用 [BR](/br/backup-and-restore-tool.md) 进行备份。其他场景推荐使用 [Dumpling](/dumpling-overview.md) 进行备份。 - -尽管 TiDB 也支持使用 MySQL 官方工具 `mysqldump` 进行数据备份和恢复,但其性能低于 [Dumpling](/dumpling-overview.md),并且 `mysqldump` 备份和恢复大量数据的耗费更长。 - -## 监控 FAQ - -+ Prometheus 监控框架详情可见 [TiDB 监控框架概述](/tidb-monitoring-framework.md)。 -+ 监控指标解读详细参考 [重要监控指标详解](/grafana-overview-dashboard.md)。 - -### 目前的监控使用方式及主要监控指标,有没有更好看的监控? - -TiDB 使用 Prometheus + Grafana 组成 TiDB 数据库系统的监控系统,用户在 Grafana 上通过 dashboard 可以监控到 TiDB 的各类运行指标,包括系统资源的监控指标,包括客户端连接与 SQL 运行的指标,包括内部通信和 Region 调度的指标,通过这些指标,可以让数据库管理员更好的了解到系统的运行状态,运行瓶颈等内容。在监控指标的过程中,我们按照 TiDB 不同的模块,分别列出了各个模块重要的指标项,一般用户只需要关注这些常见的指标项。具体指标请参见[官方文档](/grafana-overview-dashboard.md)。 - -### Prometheus 监控数据默认 15 天自动清除一次,可以自己设定成 2 个月或者手动删除吗? - -可以的,在 Prometheus 启动的机器上,找到启动脚本,然后修改启动参数,然后重启 Prometheus 生效。 - -```config ---storage.tsdb.retention="60d" -``` - -### Region Health 监控项 - -TiDB-2.0 版本中,PD metric 监控页面中,对 Region 健康度进行了监控,其中 Region Health 监控项是对所有 Region 副本状况的一些统计。其中 miss 是缺副本,extra 是多副本。同时也增加了按 Label 统计的隔离级别,level-1 表示这些 Region 的副本在第一级 Label 下是物理隔离的,没有配置 location label 时所有 Region 都在 level-0。 - -### Statement Count 监控项中的 selectsimplefull 是什么意思? - -代表全表扫,但是可能是很小的系统表。 - -### 监控上的 QPS 和 Statement OPS 有什么区别? - -QPS 会统计执行的所有 SQL 命令,包括 use database、load data、begin、commit、set、show、insert、select 等。 - -Statement OPS 只统计 select、update、insert 等业务相关的,所以 Statement OPS 的统计和业务比较相符。 +--- +title: 部署运维 FAQ +summary: 介绍 TiDB 集群运维部署的常见问题、原因及解决方法。 +--- + +# 部署运维 FAQ + +本文介绍 TiDB 集群运维部署的常见问题、原因及解决方法。 + +## 环境准备 FAQ + +操作系统版本要求如下表: + +| Linux 操作系统平台 | 版本 | +| :--- | :--- | +| Red Hat Enterprise Linux | 7.3 及以上 | +| CentOS | 7.3 及以上 | +| Oracle Enterprise Linux | 7.3 及以上 | + +### 为什么要在 CentOS 7 上部署 TiDB 集群? + +TiDB 作为一款开源分布式 NewSQL 数据库,可以很好的部署和运行在 Intel 架构服务器环境及主流虚拟化环境,并支持绝大多数的主流硬件网络,作为一款高性能数据库系统,TiDB 支持主流的 Linux 操作系统环境,具体可以参考 TiDB 的[官方部署要求](/hardware-and-software-requirements.md)。 + +其中 TiDB 在 CentOS 7.3 的环境下进行大量的测试,同时也有很多这个操作系统的部署最佳实践,因此,我们推荐客户在部署 TiDB 的时候使用 CentOS 7.3+ 以上的Linux 操作系统。 + +## 硬件要求 FAQ + +TiDB 支持部署和运行在 Intel x86-64 架构的 64 位通用硬件服务器平台。对于开发、测试、及生产环境的服务器硬件配置有以下要求和建议: + +### 开发及测试环境 + +| **组件** | **CPU** | **内存** | **本地存储** | **网络** | **实例数量(最低要求)** | +| --- | --- | --- | --- | --- | --- | +| TiDB | 8核+ | 16 GB+ | SAS, 200 GB+ | 千兆网卡 | 1(可与 PD 同机器) | +| PD | 8核+ | 16 GB+ | SAS, 200 GB+ | 千兆网卡 | 1(可与 TiDB 同机器) | +| TiKV | 8核+ | 32 GB+ | SSD, 200 GB+ | 千兆网卡 | 3 | +| | | | | 服务器总计 | 4 | + +### 生产环境 + +| **组件** | **CPU** | **内存** | **硬盘类型** | **网络** | **实例数量(最低要求)** | +| --- | --- | --- | --- | --- | --- | +| TiDB | 16核+ | 48 GB+ | SAS | 万兆网卡(2块最佳) | 2 | +| PD | 8核+ | 16 GB+ | SSD | 万兆网卡(2块最佳) | 3 | +| TiKV | 16核+ | 48 GB+ | SSD | 万兆网卡(2块最佳) | 3 | +| 监控 | 8核+ | 16 GB+ | SAS | 千兆网卡 | 1 | +| | | | | 服务器总计 | 9 | + +### 两块网卡的目的是?万兆的目的是? + +作为一个分布式集群,TiDB 对时间的要求还是比较高的,尤其是 PD 需要分发唯一的时间戳,如果 PD 时间不统一,如果有 PD 切换,将会等待更长的时间。两块网卡可以做 bond,保证数据传输的稳定,万兆可以保证数据传输的速度,千兆网卡容易出现瓶颈,我们强烈建议使用万兆网卡。 + +### SSD 不做 RAID 是否可行? + +资源可接受的话,我们建议做 RAID 10,如果资源有限,也可以不做 RAID。 + +### TiDB 集群各个组件的配置推荐? + +- TiDB 需要 CPU 和内存比较好的机器,参考官网配置要求,如果后期需要开启 TiDB Binlog,根据业务量的评估和 GC 时间的要求,也需要本地磁盘大一点,不要求 SSD 磁盘; +- PD 里面存了集群元信息,会有频繁的读写请求,对磁盘 I/O 要求相对比较高,磁盘太差会影响整个集群性能,推荐 SSD 磁盘,空间不用太大。另外集群 Region 数量越多对 CPU、内存的要求越高; +- TiKV 对 CPU、内存、磁盘要求都比较高,一定要用 SSD 磁盘。 + +详情可参考 [TiDB 软硬件环境需求](/hardware-and-software-requirements.md)。 + +## 安装部署 FAQ + +如果用于生产环境,推荐使用 TiUP [使用 TiUP 部署](/production-deployment-using-tiup.md) TiDB 集群。 + +### 为什么修改了 TiKV/PD 的 toml 配置文件,却没有生效? + +这种情况一般是因为没有使用 `--config` 参数来指定配置文件(目前只会出现在 binary 部署的场景),TiKV/PD 会按默认值来设置。如果要使用配置文件,请设置 TiKV/PD 的 `--config` 参数。对于 TiKV 组件,修改配置后重启服务即可;对于 PD 组件,只会在第一次启动时读取配置文件,之后可以使用 pd-ctl 的方式来修改配置,详情可参考 [PD 配置参数](/command-line-flags-for-pd-configuration.md)。 + +### TiDB 监控框架 Prometheus + Grafana 监控机器建议单独还是多台部署? + +监控机建议单独部署。建议 CPU 8 core,内存 16 GB 以上,硬盘 500 GB 以上。 + +### 有一部分监控信息显示不出来? + +查看访问监控的机器时间跟集群内机器的时间差,如果比较大,更正时间后即可显示正常。 + +### supervise/svc/svstat 服务具体起什么作用? + +- supervise 守护进程 +- svc 启停服务 +- svstat 查看进程状态 + +### inventory.ini 变量参数解读 + +| **变量** | **含义** | +| --- | --- | +| cluster_name | 集群名称,可调整 | +| tidb_version | TiDB 版本 | +| deployment_method | 部署方式,默认为 binary,可选 docker | +| process_supervision | 进程监管方式,默认为 systemd,可选 supervise | +| timezone | 修改部署目标机器时区,默认为 Asia/Shanghai, 可调整,与set_timezone 变量结合使用 | +| set_timezone | 默认为 True,即修改部署目标机器时区,关闭可修改为 False | +| enable_elk | 目前不支持,请忽略 | +| enable_firewalld | 开启防火墙,默认不开启 | +| enable_ntpd | 检测部署目标机器 NTP 服务,默认为 True,请勿关闭 | +| machine_benchmark | 检测部署目标机器磁盘 IOPS,默认为 True,请勿关闭 | +| set_hostname | 根据 IP 修改部署目标机器主机名,默认为 False | +| enable_binlog | 是否部署 pump 并开启 binlog,默认为 False,依赖 Kafka 集群,参见 zookeeper_addrs 变量 | +| zookeeper_addrs | binlog Kafka 集群的 zookeeper 地址 | +| enable_slow_query_log | TiDB 慢查询日志记录到单独文件({{ deploy_dir }}/log/tidb_slow_query.log),默认为 False,记录到 tidb 日志 | +| deploy_without_tidb | KV 模式,不部署 TiDB 服务,仅部署 PD、TiKV 及监控服务,请将 inventory.ini 文件中 tidb_servers 主机组 IP 设置为空。 | + +### 如何单独记录 TiDB 中的慢查询日志,如何定位慢查询 SQL? + +1)TiDB 中,对慢查询的定义在 TiDB 的配置文件中。`slow-threshold: 300`,这个参数是配置慢查询记录阈值的,单位是 ms。 + +2)如果出现了慢查询,可以从 Grafana 监控定位到出现慢查询的 tidb-server 以及时间点,然后在对应节点查找日志中记录的 SQL 信息。 + +3)除了日志,还可以通过 `admin show slow` 命令查看,详情可参考 [`admin show slow` 命令](/identify-slow-queries.md#admin-show-slow-命令)。 + +### 首次部署 TiDB 集群时,没有配置 tikv 的 Label 信息,在后续如何添加配置 Label? + +TiDB 的 Label 设置是与集群的部署架构相关的,是集群部署中的重要内容,是 PD 进行全局管理和调度的依据。如果集群在初期部署过程中没有设置 Label,需要在后期对部署结构进行调整,就需要手动通过 PD 的管理工具 pd-ctl 来添加 location-labels 信息,例如:`config set location-labels "zone,rack,host"`(根据实际的 label 层级名字配置)。 + +pd-ctl 的使用参考 [PD Control 使用说明](/pd-control.md)。 + +### 为什么测试磁盘的 dd 命令用 `oflag=direct` 这个选项? + +Direct 模式就是把写入请求直接封装成 I/O 指令发到磁盘,这样是为了绕开文件系统的缓存,可以直接测试磁盘的真实的 I/O 读写能力。 + +### 如何用 fio 命令测试 TiKV 实例的磁盘性能? + +- 随机读测试: + + {{< copyable "shell-regular" >}} + + ```bash + ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randread -size=10G -filename=fio_randread_test.txt -name='fio randread test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_result.json + ``` + +- 顺序写和随机读混合测试: + + {{< copyable "shell-regular" >}} + + ```bash + ./fio -ioengine=psync -bs=32k -fdatasync=1 -thread -rw=randrw -percentage_random=100,0 -size=10G -filename=fio_randread_write_test.txt -name='fio mixed randread and sequential write test' -iodepth=4 -runtime=60 -numjobs=4 -group_reporting --output-format=json --output=fio_randread_write_test.json + ``` + +## 集群管理 FAQ + +### 集群日常管理 + +#### TiDB 如何登录? + +和 MySQL 登录方式一样,可以按照下面例子进行登录。 + +`mysql -h 127.0.0.1 -uroot -P4000` + +#### TiDB 如何修改数据库系统变量? + +和 MySQL 一样,TiDB 也分为静态参数和固态参数,静态参数可以直接通过`set global xxx = n`的方式进行修改,不过新参数值只限于该实例生命周期有效。 + +#### TiDB (TiKV) 有哪些数据目录? + +默认在 [`--data-dir`](/command-line-flags-for-tikv-configuration.md#--data-dir) 目录下,其中包括 backup、db、raft、snap 四个目录,分别存储备份、数据、raft 数据及镜像数据。 + +#### TiDB 有哪些系统表? + +和 MySQL 类似,TiDB 中也有系统表,用于存放数据库运行时所需信息,具体信息参考 [TiDB 系统表](/mysql-schema.md)文档。 + +#### TiDB 各节点服务器下是否有日志文件,如何管理? + +默认情况下各节点服务器会在日志中输出标准错误,如果启动的时候通过 `--log-file` 参数指定了日志文件,那么日志会输出到指定的文件中,并且按天做 rotation。 + +#### 如何规范停止 TiDB? + +可以直接 kill 掉所有服务。如果使用 kill 命令,TiDB 的组件会做 graceful 的 shutdown。 + +#### TiDB 里面可以执行 kill 命令吗? + +- 可以 kill DML 语句,首先使用 `show processlist`,找到对应 session 的 id,然后执行 `kill tidb [session id]`。 +- 可以 kill DDL 语句,首先使用 `admin show ddl jobs`,查找需要 kill 的 DDL job ID,然后执行 `admin cancel ddl jobs 'job_id' [, 'job_id'] ...`。具体可以参考 [admin 操作](/sql-statements/sql-statement-admin.md)。 + +#### TiDB 是否支持会话超时? + +TiDB 暂不支持数据库层面的会话超时,目前想要实现超时,在没 LB(Load Balancing)的时候,需要应用侧记录发起的 Session 的 ID,通过应用自定义超时,超时以后需要到发起 Query 的节点上用 `kill tidb [session id]` 来杀掉 SQL。目前建议使用应用程序来实现会话超时,当达到超时时间,应用层就会抛出异常继续执行后续的程序段。 + +#### TiDB 生产环境的版本管理策略是怎么样的?如何尽可能避免频繁升级? + +TiDB 版本目前逐步标准化,每次 Release 都包含详细的 Change log,版本功能[变化详情](https://github.com/pingcap/TiDB/releases),生产环境是否有必要升级取决于业务系统,建议升级之前详细了解前后版本的功能差异。 + +版本号说明参考:Release Version: `v1.0.3-1-ga80e796` + +- `v1.0.3` 表示 GA 标准版 +- `1` 表示该版本 commit 1 次 +- `ga80e796` 代表版本的 `git-hash` + +#### 分不清 TiDB master 版本之间的区别,应该怎么办? + +TiDB 目前社区非常活跃,在 1.0 GA 版本发布后,还在不断的优化和修改 BUG,因此 TiDB 的版本更新周期比较快,会不定期有新版本发布,请关注我们的[新版本发布官方网站](https://pingcap.com/weekly/)。此外 TiDB 安装推荐[使用 TiUP 进行安装](/production-deployment-using-tiup.md)。此外,在 TiDB 1.0 GA 版本后,对 TiDB 的版本号进行了统一管理,TiDB 的版本可以通过以下两种方式进行查看: + +- 通过 `select tidb_version()` 进行查看 +- 通过执行 `tidb-server -V` 进行查看 + +#### 有没有图形化部署 TiDB 的工具? + +暂时没有。 + +#### TiDB 如何进行水平扩展? + +当您的业务不断增长时,数据库可能会面临三方面瓶颈,第一是存储空间,第二是计算资源,第三是读写容量,这时可以对 TiDB 集群做水平扩展。 + +- 如果是存储资源不够,可以通过添加 TiKV Server 节点来解决,新节点启动后,PD 会自动将其他节点的部分数据迁移过去,无需人工介入。 +- 如果是计算资源不够,可以查看 TiDB Server 和 TiKV Server 节点的 CPU 消耗情况,再考虑添加 TiDB Server 节点或者是 TiKV Server 节点来解决,如添加 TiDB Server 节点,将其添加到前端 Load Balancer 配置之中即可。 +- 如果是容量跟不上,一般可以考虑同时增加 TiDB Server 和 TiKV Server 节点。 + +#### Percolator 用了分布式锁,crash 的客户端会保持锁,会造成锁没有 release? + +详细可参考 [Percolator 和 TiDB 事务算法](https://pingcap.com/blog-cn/percolator-and-txn/)。 + +#### TiDB 为什么选用 gRPC 而不选用 Thrift,是因为 Google 在用吗? + +不只是因为 Google 在用,有一些比较好的特性我们需要,比如流控、加密还有 Streaming。 + +#### like(bindo.customers.name, jason%, 92) 这个92代表什么? + +那个是转义字符,默认是 (ASCII 92)。 + +#### 为什么 `information_schema.tables.data_length` 记录的大小和 TiKV 监控面板上的 store size 不一样? + +这是因为两者计算的角度不一样。`information_schema.tables.data_length` 是通过统计信息(平均每行的大小)得到的估算值。TiKV 监控面板上的 store size 是单个 TiKV 实例的数据文件(RocksDB 的 SST 文件)的大小总和。由于多版本和 TiKV 会压缩数据,所以两者显示的大小不一样。 + +#### 为什么事务没有使用异步提交或一阶段提交? + +在以下情况中,即使通过系统变量开启了[异步提交](/system-variables.md#tidb_enable_async_commit-从-v50-版本开始引入)和[一阶段提交](/system-variables.md#tidb_enable_1pc-从-v50-版本开始引入),TiDB 也不会使用这些特性: + +- 如果开启了 TiDB Binlog,受 TiDB Binlog 的实现原理限制,TiDB 不会使用异步提交或一阶段提交特性。 +- TiDB 只在事务写入不超过 256 个键值对,以及所有键值对里键的总大小不超过 4 KB 时,才会使用异步提交或一阶段提交特性。这是因为对于写入量大的事务,异步提交不能明显提升执行性能。 + +### PD 管理 + +#### 访问 PD 报错:TiKV cluster is not bootstrapped + +PD 的大部分 API 需要在初始化 TiKV 集群以后才能使用,如果在部署新集群的时候只启动了 PD,还没有启动 TiKV,这时候访问 PD 就会报这个错误。遇到这个错误应该先把要部署的 TiKV 启动起来,TiKV 会自动完成初始化工作,然后就可以正常访问 PD。 + +#### PD 启动报错:etcd cluster ID mismatch + +PD 启动参数中的 `--initial-cluster` 包含了某个不属于该集群的成员。遇到这个错误时请检查各个成员的所属集群,剔除错误的成员后即可正常启动。 + +#### PD 能容忍的时间同步误差是多少? + +理论上,时间同步误差越小越好。PD 可容忍任意时长的误差,但是,时间同步误差越大意味着 PD 分配的时间戳与真实的物理时间相差越大,这个差距会影响读历史版本等功能。 + +#### Client 连接是如何寻找 PD 的? + +Client 连接只能通过 TiDB 访问集群,TiDB 负责连接 PD 与 TiKV,PD 与 TiKV 对 Client 透明。当 TiDB 连接任意一台 PD 的时候,PD 会告知 TiDB 当前的 leader 是谁,如果此台 PD 不是 leader,TiDB 将会重新连接至 leader PD。 + +#### TiKV 节点 (Store) 各状态 (Up, Disconnect, Offline, Down, Tombstone) 之间的关系是什么? + +使用 `pd-ctl` 可以查看 TiKV 节点的状态信息。如需查看各个状态之间的关系,请参考 [TiKV Store 状态之间的关系](/tidb-scheduling.md#信息收集)。 + +#### PD 参数中 leader-schedule-limit 和 region-schedule-limit 调度有什么区别? + +- leader-schedule-limit 调度是用来均衡不同 TiKV 的 leader 数,影响处理查询的负载。 +- region-schedule-limit 调度是均衡不同 TiKV 的副本数,影响不同节点的数据量。 + +#### 每个 region 的 replica 数量可配置吗?调整的方法是? + +可以,目前只能调整全局的 replica 数量。首次启动时 PD 会读配置文件(conf/pd.yml),使用其中的 max-replicas 配置,之后修改需要使用 pd-ctl 配置命令 `config set max-replicas $num`,配置后可通过 `config show all` 来查看已生效的配置。调整的时候,不会影响业务,会在后台添加,注意总 TiKV 实例数总是要大于等于设置的副本数,例如 3 副本需要至少 3 个 TiKV。增加副本数量之前需要预估额外的存储需求。pd-ctl 的详细用法可参考 [PD Control 使用说明](/pd-control.md)。 + +#### 缺少命令行集群管理工具,整个集群的健康度当前是否正常,不好确认? + +可以通过 pd-ctl 等工具来判断集群大概的状态,详细的集群状态还是需要通过监控来确认。 + +#### 集群下线节点后,怎么删除老集群节点监控信息? + +下线节点一般指 TiKV 节点通过 pd-ctl 或者监控判断节点是否下线完成。节点下线完成后,手动停止下线节点上相关的服务。从 Prometheus 配置文件中删除对应节点的 node_exporter 信息。 + +### TiDB server 管理 + +#### TiDB 的 lease 参数应该如何设置? + +启动 TiDB Server 时,需要通过命令行参数设置 lease 参数(--lease=60),其值会影响 DDL 的速度(只会影响当前执行 DDL 的 session,其他的 session 不会受影响)。在测试阶段,lease 的值可以设为 1s,加快测试进度;在生产环境下,我们推荐这个值设为分钟级(一般可以设为 60),这样可以保证 DDL 操作的安全。 + +#### DDL 在正常情况下的耗时是多少? + +一般情况下处理一个 DDL 操作(之前没有其他 DDL 操作在处理)的耗时基本可以分如下为三种: + +- add index 操作,且此操作对应表数据行数比较少,耗时约为 3s。 +- add index 操作,且此操作对应表数据行数比较多,耗时具体由表中数据行数和当时 QPS 情况定(add index 操作优先级比一般 SQL 低)。 +- 其他 DDL 操作耗时约为 1s。 + +此外,如果接收 DDL 请求的 TiDB 和 DDL owner 所处的 TiDB 是一台,那么上面列举的第一和第三种可能的耗时应该在几十到几百毫秒。 + +#### 为什么有的时候执行 DDL 会很慢? + +可能原因如下: + +- 多个 DDL 语句一起执行的时候,后面的几个 DDL 语句会比较慢。原因是当前 TiDB 集群中 DDL 操作是串行执行的。 +- 在正常集群启动后,第一个 DDL 操作的执行时间可能会比较久,一般在 30s 左右,这个原因是刚启动时 TiDB 在竞选处理 DDL 的 leader。 +- 由于停 TiDB 时不能与 PD 正常通信(包括停电情况)或者用 `kill -9` 指令停 TiDB 导致 TiDB 没有及时从 PD 清理注册数据,那么会影响 TiDB 启动后 10min 内的 DDL 语句处理时间。这段时间内运行 DDL 语句时,每个 DDL 状态变化都需要等待 2 * lease(默认 lease = 45s)。 +- 当集群中某个 TiDB 与 PD 之间发生通信问题,即 TiDB 不能从 PD 及时获取或更新版本信息,那么这时候 DDL 操作的每个状态处理需要等待 2 * lease。 + +#### TiDB 可以使用 S3 作为后端存储吗? + +不可以,目前 TiDB 只支持分布式存储引擎和 GolevelDB/RocksDB/BoltDB 引擎。 + +#### Information_schema 能否支持更多真实信息? + +Information_schema 库里面的表主要是为了兼容 MySQL 而存在,有些第三方软件会查询里面的信息。在目前 TiDB 的实现中,里面大部分只是一些空表。后续随着 TiDB 的升级,会提供更多的参数信息。当前 TiDB 支持的 Information\_schema 请参考 [TiDB 系统数据库说明文档](/information-schema/information-schema.md)。 + +#### TiDB Backoff type 主要原因? + +TiDB-server 与 TiKV-server 随时进行通信,在进行大量数据操作过程中,会出现 `Server is busy` 或者 `backoff.maxsleep 20000ms` 的日志提示信息,这是由于 TiKV-server 在处理过程中系统比较忙而出现的提示信息,通常这时候可以通过系统资源监控到 TiKV 主机系统资源使用率比较高的情况出现。如果这种情况出现,可以根据资源使用情况进行相应的扩容操作。 + +#### TiDB TiClient type 主要原因? + +TiClient Region Error 该指标描述的是在 TiDB-server 作为客户端通过 KV 接口访问 TiKV-server 进行数据操作过程中,TiDB-server 操作 TiKV-server 中的 Region 数据出现的错误类型与 metric 指标,错误类型包括 not_leader、stale_epoch。出现这些错误的情况是当 TiDB-server 根据自己的缓存信息去操作 Region leader 数据的时候,Region leader 发生了迁移或者 TiKV 当前的 Region 信息与 TiDB 缓存的路由信息不一致而出现的错误提示。一般这种情况下,TiDB-server 都会自动重新从 PD 获取最新的路由数据,重做之前的操作。 + +#### TiDB 同时支持的最大并发连接数? + +默认情况下,每个 TiDB 服务器的最大连接数没有限制。如有需要,可以在 `config.toml` 文件中设置 `max-server-connections` 来限制最大连接数。如果并发量过大导致响应时间增加,建议通过添加 TiDB 节点进行扩容。 + +#### 如何查看某张表创建的时间? + +information_schema 库中的 tables 表里的 create_time 即为表的真实创建时间。 + +#### TiDB 的日志中 EXPENSIVE_QUERY 是什么意思? + +TiDB 在执行 SQL 时,预估出来每个 operator 处理了超过 10000 条数据就认为这条 query 是 expensive query。可以通过修改 tidb-server 配置参数来对这个门限值进行调整,调整后需要重新启动 tidb-server。 + +### TiKV 管理 + +#### TiKV 集群副本建议配置数量是多少,是不是最小高可用配置(3个)最好? + +如果是测试环境 3 副本足够;在生产环境中,不可让集群副本数低于 3,需根据架构特点、业务系统及恢复能力的需求,适当增加副本数。值得注意的是,副本升高,性能会有下降,但是安全性更高。 + +#### TiKV 启动报错:cluster ID mismatch + +TiKV 本地存储的 cluster ID 和指定的 PD 的 cluster ID 不一致。在部署新的 PD 集群的时候,PD 会随机生成一个 cluster ID,TiKV 第一次初始化的时候会从 PD 获取 cluster ID 存储在本地,下次启动的时候会检查本地的 cluster ID 与 PD 的 cluster ID 是否一致,如果不一致则会报错并退出。出现这个错误一个常见的原因是,用户原先部署了一个集群,后来把 PD 的数据删除了并且重新部署了新的 PD,但是 TiKV 还是使用旧的数据重启连到新的 PD 上,就会报这个错误。 + +#### TiKV 启动报错:duplicated store address + +启动参数中的地址已经被其他的 TiKV 注册在 PD 集群中了。造成该错误的常见情况:TiKV `--data-dir` 指定的路径下没有数据文件夹(删除或移动后没有更新 --data-dir),用之前参数重新启动该 TiKV。请尝试用 pd-ctl 的 [store delete](https://github.com/pingcap/pd/tree/55db505e8f35e8ab4e00efd202beb27a8ecc40fb/tools/pd-ctl#store-delete--label--weight-store_id----jqquery-string) 功能,删除之前的 store,然后重新启动 TiKV 即可。 + +#### TiKV master 和 slave 用的是一样的压缩算法,为什么效果不一样? + +目前来看 master 有些文件的压缩率会高一些,这个取决于底层数据的分布和 RocksDB 的实现,数据大小偶尔有些波动是正常的,底层存储引擎会根据需要调整数据。 + +#### TiKV block cache 有哪些特性? + +TiKV 使用了 RocksDB 的 Column Family (CF) 特性,KV 数据最终存储在默认 RocksDB 内部的 default、write、lock 3 个 CF 内。 + +- default CF 存储的是真正的数据,与其对应的参数位于 `[rocksdb.defaultcf]` 项中。 +- write CF 存储的是数据的版本信息(MVCC)、索引、小表相关的数据,相关的参数位于 `[rocksdb.writecf]` 项中。 +- lock CF 存储的是锁信息,系统使用默认参数。 +- Raft RocksDB 实例存储 Raft log。default CF 主要存储的是 Raft log,与其对应的参数位于 `[raftdb.defaultcf]` 项中。 +- 所有 CF 共享一个 Block-cache,用于缓存数据块,加速 RocksDB 的读取速度,Block-cache 的大小通过参数 `block-cache-size` 控制,`block-cache-size` 越大,能够缓存的热点数据越多,对读取操作越有利,同时占用的系统内存也会越多。 +- 每个 CF 有各自的 Write-buffer,大小通过 `write-buffer-size` 控制。 + +#### TiKV channel full 是什么原因? + +- Raftstore 线程太忙,或者因 I/O 而卡住。可以看一下 Raftstore 的 CPU 使用情况。 +- TiKV 过忙(CPU、磁盘 I/O 等),请求处理不过来。 + +#### TiKV 频繁切换 Region leader 是什么原因? + +- 网络问题导致节点间通信卡了,查看 Report failures 监控。 +- 原主 Leader 的节点卡了,导致没有及时给 Follower 发送消息。 +- Raftstore 线程卡了。 + +#### 如果一个节点挂了会影响服务吗?影响会持续多久? + +TiDB 使用 Raft 在多个副本之间做数据同步(默认为每个 Region 3 个副本)。当一份备份出现问题时,其他的副本能保证数据的安全。根据 Raft 协议,当某个节点挂掉导致该节点里的 Leader 失效时,在最大 2 * lease time(leasetime 是 10 秒)时间后,通过 Raft 协议会很快将一个另外一个节点里的 Follower 选为新的 Region Leader 来提供服务。 + +#### TiKV 在分别在哪些场景下占用大量 IO、内存、CPU(超过参数配置的多倍)? + +在大量写入、读取的场景中会占用大量的磁盘 IO、内存、CPU。在执行很复杂的查询,比如会产生很大中间结果集的情况下,会消耗很多的内存和 CPU 资源。 + +#### TiKV 是否可以使用 SAS/SATA 盘或者进行 SSD/SAS 混合部署? + +不可以使用,TiDB 在进行 OLTP 场景中,数据访问和操作需要高 IO 磁盘的支持,TiDB 作为强一致的分布式数据库,存在一定的写放大,如副本复制、存储底层 Compaction,因此,TiDB 部署的最佳实践中推荐用户使用 NVMe SSD 磁盘作为数据存储磁盘。另外,TiKV 与 PD 不能混合部署。 + +#### 数据表 Key 的 Range 范围划分是在数据接入之前就已经划分好了吗? + +不是的,这个和 MySQL 分表规则不一样,需要提前设置好,TiKV 是根据 Region 的大小动态分裂的。 + +#### Region 是如何进行分裂的? + +Region 不是前期划分好的,但确实有 Region 分裂机制。当 Region 的大小超过参数 `region-max-size` 或 `region-max-keys` 的值时,就会触发分裂,分裂后的信息会汇报给 PD。 + +#### TiKV 是否有类似 MySQL 的 `innodb_flush_log_trx_commit` 参数,来保证提交数据不丢失? + +是的,TiKV 单机的存储引擎目前使用两个 RocksDB 实例,其中一个存储 raft-log,TiKV 有个 sync-log 参数,在 true 的情况下,每次提交都会强制刷盘到 raft-log,如果发生 crash 后,通过 raft-log 进行 KV 数据的恢复。 + +#### 对 WAL 存储有什么推荐的硬件配置,例如 SSD,RAID 级别,RAID 卡 cache 策略,NUMA 设置,文件系统选择,操作系统的 IO 调度策略等? + +WAL 属于顺序写,目前我们并没有单独对他进行配置,建议 SSD,RAID 如果允许的话,最好是 RAID 10,RAID 卡 cache、操作系统 I/O 调度目前没有针对性的最佳实践,Linux 7 以上默认配置即可,NUMA 没有特别建议,NUMA 内存分配策略可以尝试使用 `interleave = all`,文件系统建议 ext4。 + +#### 在最严格的 `sync-log = true` 数据可用模式下,写入性能如何? + +一般来说,开启 `sync-log` 会让性能损耗 30% 左右。关闭 `sync-log` 时的性能表现,请参见 [TiDB Sysbench 性能测试报告](/benchmark/v3.0-performance-benchmarking-with-sysbench.md)。 + +#### 是否可以利用 TiKV 的 Raft + 多副本达到完全的数据可靠,单机存储引擎是否需要最严格模式? + +通过使用 [Raft 一致性算法](https://raft.github.io/),数据在各 TiKV 节点间复制为多副本,以确保某个节点挂掉时数据的安全性。只有当数据已写入超过 50% 的副本时,应用才返回 ACK(三副本中的两副本)。但理论上两个节点也可能同时发生故障,所以除非是对性能要求高于数据安全的场景,一般都强烈推荐开启 `sync-log`。 + +另外,还有一种 `sync-log` 的替代方案,即在 Raft group 中用五个副本而非三个。这将允许两个副本同时发生故障,而仍然能保证数据安全性。 + +对于单机存储引擎也同样推荐打开 `sync-log` 模式。否则如果节点宕机可能会丢失最后一次写入数据。 + +#### 使用 Raft 协议,数据写入会有多次网络的 roundtrip,实际写入延迟如何? + +理论上,和单机数据库相比,数据写入会多四个网络延迟。 + +#### 有没有类似 MySQL 的 InnoDB Memcached plugin,可以直接使用 KV 接口,可以不需要独立的 Cache? + +TiKV 支持单独进行接口调用,理论上也可以起个实例做为 Cache,但 TiDB 最大的价值是分布式关系型数据库,我们原则上不对 TiKV 单独进行支持。 + +#### Coprocessor 组件的主要作用? + +- 减少 TiDB 与 TiKV 之间的数据传输。 +- 计算下推,充分利用 TiKV 的分布式计算资源。 + +#### IO error: No space left on device While appending to file + +这是磁盘空间不足导致的,需要加节点或者扩大磁盘空间。 + +#### 为什么 TiKV 容易出现 OOM? + +TiKV 的内存占用主要来自于 RocksDB 的 block-cache,默认为系统总内存的 40%。当 TiKV 容易出现 OOM 时,检查 `block-cache-size` 配置是否过高。还需要注意,当单机部署了多个 TiKV 实例时,需要显式地配置该参数,以防止多个实例占用过多系统内存导致 OOM。 + +#### TiDB 数据和 RawKV 数据可存储于同一个 TiKV 集群里吗? + +不可以。TiDB 数据(或使用其他事务 API 生成的数据)依赖于一种特殊的键值格式,和 RawKV API 数据(或其他基于 RawKV 的服务生成的数据)并不兼容。 + +### TiDB 测试 + +#### TiDB Sysbench 基准测试结果如何? + +很多用户在接触 TiDB 都习惯做一个基准测试或者 TiDB 与 MySQL 的对比测试,官方也做了一个类似测试,汇总很多测试结果后,我们发现虽然测试的数据有一定的偏差,但结论或者方向基本一致,由于 TiDB 与 MySQL 由于架构上的差别非常大,很多方面是很难找到一个基准点,所以官方的建议两点: + +- 大家不要用过多精力纠结这类基准测试上,应该更多关注 TiDB 的场景上的区别。 +- 大家可以直接参考 [TiDB Sysbench 性能测试报告](/benchmark/v3.0-performance-benchmarking-with-sysbench.md)。 + +#### TiDB 集群容量 QPS 与节点数之间关系如何,和 MySQL 对比如何? + +- 在 10 节点内,TiDB 写入能力(Insert TPS)和节点数量基本成 40% 线性递增,MySQL 由于是单节点写入,所以不具备写入扩展能力。 +- MySQL 读扩容可以通过添加从库进行扩展,但写流量无法扩展,只能通过分库分表,而分库分表有很多问题,具体参考[方案虽好,成本先行:数据库 Sharding+Proxy 实践解析](http://dbaplus.cn/news-11-1854-1.html)。 +- TiDB 不管是读流量、还是写流量都可以通过添加节点快速方便的进行扩展。 + +#### 我们的 DBA 测试过 MySQL 性能,单台 TiDB 的性能没有 MySQL 性能那么好? + +TiDB 设计的目标就是针对 MySQL 单台容量限制而被迫做的分库分表的场景,或者需要强一致性和完整分布式事务的场景。它的优势是通过尽量下推到存储节点进行并行计算。对于小表(比如千万级以下),不适合 TiDB,因为数据量少,Region 有限,发挥不了并行的优势,最极端的就是计数器表,几行记录高频更新,这几行在 TiDB 里,会变成存储引擎上的几个 KV,然后只落在一个 Region 里,而这个 Region 只落在一个节点上。加上后台强一致性复制的开销,TiDB 引擎到 TiKV 引擎的开销,最后表现出来的就是没有单个 MySQL 好。 + +### TiDB 备份恢复 + +#### TiDB 主要备份方式? + +目前,数据量大时推荐使用 [BR](/br/backup-and-restore-tool.md) 进行备份。其他场景推荐使用 [Dumpling](/dumpling-overview.md) 进行备份。 + +尽管 TiDB 也支持使用 MySQL 官方工具 `mysqldump` 进行数据备份和恢复,但其性能低于 [Dumpling](/dumpling-overview.md),并且 `mysqldump` 备份和恢复大量数据的耗费更长。 + +## 监控 FAQ + ++ Prometheus 监控框架详情可见 [TiDB 监控框架概述](/tidb-monitoring-framework.md)。 ++ 监控指标解读详细参考 [重要监控指标详解](/grafana-overview-dashboard.md)。 + +### 目前的监控使用方式及主要监控指标,有没有更好看的监控? + +TiDB 使用 Prometheus + Grafana 组成 TiDB 数据库系统的监控系统,用户在 Grafana 上通过 dashboard 可以监控到 TiDB 的各类运行指标,包括系统资源的监控指标,包括客户端连接与 SQL 运行的指标,包括内部通信和 Region 调度的指标,通过这些指标,可以让数据库管理员更好的了解到系统的运行状态,运行瓶颈等内容。在监控指标的过程中,我们按照 TiDB 不同的模块,分别列出了各个模块重要的指标项,一般用户只需要关注这些常见的指标项。具体指标请参见[官方文档](/grafana-overview-dashboard.md)。 + +### Prometheus 监控数据默认 15 天自动清除一次,可以自己设定成 2 个月或者手动删除吗? + +可以的,在 Prometheus 启动的机器上,找到启动脚本,然后修改启动参数,然后重启 Prometheus 生效。 + +```config +--storage.tsdb.retention="60d" +``` + +### Region Health 监控项 + +TiDB-2.0 版本中,PD metric 监控页面中,对 Region 健康度进行了监控,其中 Region Health 监控项是对所有 Region 副本状况的一些统计。其中 miss 是缺副本,extra 是多副本。同时也增加了按 Label 统计的隔离级别,level-1 表示这些 Region 的副本在第一级 Label 下是物理隔离的,没有配置 location label 时所有 Region 都在 level-0。 + +### Statement Count 监控项中的 selectsimplefull 是什么意思? + +代表全表扫,但是可能是很小的系统表。 + +### 监控上的 QPS 和 Statement OPS 有什么区别? + +QPS 会统计执行的所有 SQL 命令,包括 use database、load data、begin、commit、set、show、insert、select 等。 + +Statement OPS 只统计 select、update、insert 等业务相关的,所以 Statement OPS 的统计和业务比较相符。 diff --git a/faq/high-reliability-faq.md b/faq/high-reliability-faq.md index 79b80aee5ad8..74bf9942e768 100644 --- a/faq/high-reliability-faq.md +++ b/faq/high-reliability-faq.md @@ -1,35 +1,35 @@ ---- -title: 高可靠常见问题 -summary: 介绍高可靠相关的常见问题。 ---- - -# 高可靠常见问题 - -本文档介绍高可靠相关的常见问题。 - -## 我们的安全漏洞扫描工具对 MySQL version 有要求,TiDB 是否支持修改 server 版本号呢? - -TiDB 在 v3.0.8 后支持修改 server 版本号,可以通过配置文件中的 [`server-version`](/tidb-configuration-file.md#server-version) 配置项进行修改。在使用 TiUP 部署集群时,可以通过 `tiup cluster edit-config ` 修改配置文件中以下部分来设置合适的版本号: - -``` -server_configs: - tidb: - server-version: 'YOUR_VERSION_STRING' -``` - -并通过 `tiup cluster reload -R tidb` 命令使得以上修改生效,以避免出现安全漏洞扫描不通过的问题。 - -## TiDB 支持哪些认证协议,过程是怎样的? - -这一层跟 MySQL 一样,走的 SASL 认证协议,用于用户登录认证,对密码的处理流程。 - -客户端连接 TiDB 的时候,走的是 challenge-response(挑战-应答)的认证模式,过程如下: - -1. 客户端连接服务器; -2. 服务器发送随机字符串 `challenge` 给客户端; -3. 客户端发送 `username` + `response` 给服务器; -4. 服务器验证 `response`。 - -## 如何修改用户名密码和权限? - -TiDB 作为分布式数据库,在 TiDB 中修改用户密码建议使用 `set password for 'root'@'%' = '0101001';` 或 `alter` 方法,不推荐使用 `update mysql.user` 的方法进行,这种方法可能会造成其它节点刷新不及时的情况。修改权限也一样,都建议采用官方的标准语法。详情可参考 [TiDB 用户账户管理](/user-account-management.md)。 +--- +title: 高可靠常见问题 +summary: 介绍高可靠相关的常见问题。 +--- + +# 高可靠常见问题 + +本文档介绍高可靠相关的常见问题。 + +## 我们的安全漏洞扫描工具对 MySQL version 有要求,TiDB 是否支持修改 server 版本号呢? + +TiDB 在 v3.0.8 后支持修改 server 版本号,可以通过配置文件中的 [`server-version`](/tidb-configuration-file.md#server-version) 配置项进行修改。在使用 TiUP 部署集群时,可以通过 `tiup cluster edit-config ` 修改配置文件中以下部分来设置合适的版本号: + +``` +server_configs: + tidb: + server-version: 'YOUR_VERSION_STRING' +``` + +并通过 `tiup cluster reload -R tidb` 命令使得以上修改生效,以避免出现安全漏洞扫描不通过的问题。 + +## TiDB 支持哪些认证协议,过程是怎样的? + +这一层跟 MySQL 一样,走的 SASL 认证协议,用于用户登录认证,对密码的处理流程。 + +客户端连接 TiDB 的时候,走的是 challenge-response(挑战-应答)的认证模式,过程如下: + +1. 客户端连接服务器; +2. 服务器发送随机字符串 `challenge` 给客户端; +3. 客户端发送 `username` + `response` 给服务器; +4. 服务器验证 `response`。 + +## 如何修改用户名密码和权限? + +TiDB 作为分布式数据库,在 TiDB 中修改用户密码建议使用 `set password for 'root'@'%' = '0101001';` 或 `alter` 方法,不推荐使用 `update mysql.user` 的方法进行,这种方法可能会造成其它节点刷新不及时的情况。修改权限也一样,都建议采用官方的标准语法。详情可参考 [TiDB 用户账户管理](/user-account-management.md)。 diff --git a/faq/sql-faq.md b/faq/sql-faq.md index 042c984c14a2..c8f028f852ca 100644 --- a/faq/sql-faq.md +++ b/faq/sql-faq.md @@ -1,283 +1,283 @@ ---- -title: SQL 操作常见问题 -summary: 介绍 SQL 操作相关的常见问题。 ---- - -# SQL 操作常见问题 - -本文档介绍 TiDB 中常见的 SQL 操作问题。 - -## TiDB 对哪些 MySQL variables 兼容? - -详细可参考[系统变量](/system-variables.md)。 - -## 省略 `ORDER BY` 条件时 TiDB 中返回结果的顺序与 MySQL 中的不一致 - -这不是 bug。返回结果的顺序视不同情况而定,不保证顺序统一。 - -MySQL 中,返回结果的顺序可能较为固定,因为查询是通过单线程执行的。但升级到新版本后,查询计划也可能变化。无论是否期待返回结果有序,都推荐使用 `ORDER BY` 条件。 - -[ISO/IEC 9075:1992, Database Language SQL- July 30, 1992](http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt) 对此有如下表述: - -> If an `` is not specified, then the table specified by the `` is T and the ordering of rows in T is implementation-dependent.(如果未指定 ``,通过 `` 指定的表为 T,那么 T 表中的行顺序视执行情况而定。) - -以下两条查询的结果都是合法的: - -```sql -> select * from t; -+------+------+ -| a | b | -+------+------+ -| 1 | 1 | -| 2 | 2 | -+------+------+ -2 rows in set (0.00 sec) -``` - -```sql -> select * from t; -- 不确定返回结果的顺序 -+------+------+ -| a | b | -+------+------+ -| 2 | 2 | -| 1 | 1 | -+------+------+ -2 rows in set (0.00 sec) -``` - -如果 `ORDER BY` 中使用的列不是唯一列,该语句就不确定返回结果的顺序。以下示例中,`a` 列有重复值,因此只有 `ORDER BY a, b` 能确定返回结果的顺序。 - -```sql -> select * from t order by a; -+------+------+ -| a | b | -+------+------+ -| 1 | 1 | -| 2 | 1 | -| 2 | 2 | -+------+------+ -3 rows in set (0.00 sec) -``` - -```sql -> select * from t order by a; -- 能确定 a 列的顺序,不确定 b 列的顺序 -+------+------+ -| a | b | -+------+------+ -| 1 | 1 | -| 2 | 2 | -| 2 | 1 | -+------+------+ -3 rows in set (0.00 sec) -``` - -## TiDB 是否支持 `SELECT FOR UPDATE`? - -支持。当 TiDB 使用悲观锁(自 TiDB v3.0 起默认使用)时,TiDB 中 `SELECT FOR UPDATE` 的行为与 MySQL 中的基本一致。 - -当 TiDB 使用乐观锁时,`SELECT FOR UPDATE` 不会在事务启动时对数据加锁,而是在提交事务时检查冲突。如果检查出冲突,会回滚待提交的事务。 - -## TiDB 的 codec 能保证 UTF8 的字符串是 memcomparable 的吗?我们的 key 需要支持 UTF8,有什么编码建议吗? - -TiDB 字符集默认就是 UTF8 而且目前只支持 UTF8,字符串就是 memcomparable 格式的。 - -## 一个事务中的语句数量最大是多少? - -一个事务中的语句数量,默认限制最大为 5000 条。 - -## TiDB 中,为什么出现后插入数据的自增 ID 反而小? - -TiDB 的自增 ID (`AUTO_INCREMENT`) 只保证自增且唯一,并不保证连续分配。TiDB 目前采用批量分配的方式,所以如果在多台 TiDB 上同时插入数据,分配的自增 ID 会不连续。当多个线程并发往不同的 TiDB-server 插入数据的时候,有可能会出现后插入的数据自增 ID 小的情况。此外,TiDB 允许给整型类型的字段指定 AUTO_INCREMENT,且一个表只允许一个属性为 `AUTO_INCREMENT` 的字段。详情可参考 [CREATE TABLE 语法](/mysql-compatibility.md#自增-id)。 - -## 如何在 TiDB 中修改 `sql_mode`? - -TiDB 支持在会话或全局作用域上修改 [`sql_mode`](/system-variables.md#sql_mode) 系统变量。对全局作用域变量的更改将作用于集群中的其它服务器,并且重启后更改依然有效。因此,你无需在每台 TiDB 服务器上都更改 `sql_mode` 的值。 - -## 用 Sqoop 批量写入 TiDB 数据,虽然配置了 `--batch` 选项,但还是会遇到 `java.sql.BatchUpdateExecption:statement count 5001 exceeds the transaction limitation` 的错误,该如何解决? - -- 在 Sqoop 中,`--batch` 是指每个批次提交 100 条 statement,但是默认每个 statement 包含 100 条 SQL 语句,所以此时 100 * 100 = 10000 条 SQL 语句,超出了 TiDB 的事务限制 5000 条,可以增加选项 `-Dsqoop.export.records.per.statement=10` 来解决这个问题,完整的用法如下: - - {{< copyable "shell-regular" >}} - - ```bash - sqoop export \ - -Dsqoop.export.records.per.statement=10 \ - --connect jdbc:mysql://mysql.example.com/sqoop \ - --username sqoop ${user} \ - --password ${passwd} \ - --table ${tab_name} \ - --export-dir ${dir} \ - --batch - ``` - -- 也可以选择增大 TiDB 的单个事物语句数量限制,不过此操作会导致内存增加。 - -## TiDB 有像 Oracle 那样的 Flashback Query 功能么,DDL 支持么? - -有,也支持 DDL。详细参考 [TiDB 历史数据回溯](/read-historical-data.md)。 - -## TiDB 中删除数据后会立即释放空间吗? - -DELETE,TRUNCATE 和 DROP 都不会立即释放空间。对于 TRUNCATE 和 DROP 操作,在达到 TiDB 的 GC (garbage collection) 时间后(默认 10 分钟),TiDB 的 GC 机制会删除数据并释放空间。对于 DELETE 操作 TiDB 的 GC 机制会删除数据,但不会释放空间,而是当后续数据写入 RocksDB 且进行 compact 时对空间重新利用。 - -## TiDB 是否支持 `REPLACE INTO` 语法? - -支持,例外是当前 `LOAD DATA` 不支持 `REPLACE INTO` 语法。 - -## 数据删除后查询速度为何会变慢? - -大量删除数据后,会有很多无用的 key 存在,影响查询效率。可以尝试开启 [Region Merge](/best-practices/massive-regions-best-practices.md#方法五开启-region-merge) 功能,具体看参考[最佳实践](https://pingcap.com/blog-cn/tidb-best-practice/)中的删除数据部分。 - -## 对数据做删除操作之后,空间回收比较慢,如何处理? - -TiDB 采用了多版本并发控制 (MVCC),为了使并发事务能查看到早期版本的数据,删除数据不会立即回收空间,而是推迟一段时间后再进行垃圾回收 (GC)。你可以通过修改系统变量 [`tidb_gc_life_time`](/system-variables.md#tidb_gc_life_time-从-v50-版本开始引入)的值(默认值为 `10m0s`)配置历史数据的保留时限。 - -## `SHOW PROCESSLIST` 是否显示系统进程号? - -TiDB 的 `SHOW PROCESSLIST` 与 MySQL 的 `SHOW PROCESSLIST` 显示内容基本一样,不会显示系统进程号,而 ID 表示当前的 session ID。其中 TiDB 的 `show processlist` 和 MySQL 的 `show processlist` 区别如下: - -+ 由于 TiDB 是分布式数据库,TiDB server 实例是无状态的 SQL 解析和执行引擎(详情可参考 [TiDB 整体架构](/tidb-architecture.md)),用户使用 MySQL 客户端登录的是哪个 TiDB server ,`show processlist` 就会显示当前连接的这个 TiDB server 中执行的 session 列表,不是整个集群中运行的全部 session 列表;而 MySQL 是单机数据库,`show processlist` 列出的是当前整个 MySQL 数据库的全部执行 SQL 列表。 - -+ 在查询执行期间,TiDB 中的 `State` 列不会持续更新。由于 TiDB 支持并行查询,每个语句可能同时处于多个状态,因此很难显示为某一种状态。 - -## 在 TiDB 中如何控制或改变 SQL 提交的执行优先级? - -TiDB 支持改变 [per-session](/system-variables.md#tidb_force_priority)、[全局](/tidb-configuration-file.md#force-priority)或单个语句的优先级。优先级包括: - -- HIGH_PRIORITY:该语句为高优先级语句,TiDB 在执行阶段会优先处理这条语句 -- LOW_PRIORITY:该语句为低优先级语句,TiDB 在执行阶段会降低这条语句的优先级 - -以上两种参数可以结合 TiDB 的 DML 语言进行使用,使用方法举例如下: - -1. 通过在数据库中写 SQL 的方式来调整优先级: - - {{< copyable "sql" >}} - - ```sql - select HIGH_PRIORITY | LOW_PRIORITY count(*) from table_name; - insert HIGH_PRIORITY | LOW_PRIORITY into table_name insert_values; - delete HIGH_PRIORITY | LOW_PRIORITY from table_name; - update HIGH_PRIORITY | LOW_PRIORITY table_reference set assignment_list where where_condition; - replace HIGH_PRIORITY | LOW_PRIORITY into table_name; - ``` - -2. 全表扫会自动调整为低优先级,analyze 也是默认低优先级。 - -## 在 TiDB 中 auto analyze 的触发策略是怎样的? - -触发策略:新表达到 1000 条,并且在 1 分钟内没有写入,会自动触发。 - -当表的(修改数/当前总行数)大于 `tidb_auto_analyze_ratio` 的时候,会自动触发 `analyze` 语句。`tidb_auto_analyze_ratio` 的默认值为 0.5,即默认开启此功能。为了保险起见,在开启此功能的时候,保证了其最小值为 0.3。但是不能大于等于 `pseudo-estimate-ratio`(默认值为 0.8),否则会有一段时间使用 pseudo 统计信息,建议设置值为 0.5。 - -## 可以使用 Hints 控制优化器行为吗? - -在 TiDB 中,你可以用多种方法控制查询优化器的默认行为,包括使用 [Optimizer Hints](/optimizer-hints.md) 和 [SQL 执行计划管理 (SPM)](/sql-plan-management.md)。基本用法同 MySQL 中的一致,还包含若干 TiDB 特有的用法,示例如下:`select column_name from table_name use index(index_name)where where_condition;` - -## 触发 Information schema is changed 错误的原因? - -TiDB 在执行 SQL 语句时,会使用当时的 `schema` 来处理该 SQL 语句,而且 TiDB 支持在线异步变更 DDL。那么,在执行 DML 的时候可能有 DDL 语句也在执行,而你需要确保每个 SQL 语句在同一个 `schema` 上执行。所以当执行 DML 时,遇到正在执行中的 DDL 操作就可能会报 `Information schema is changed` 的错误。为了避免太多的 DML 语句报错,已做了一些优化。 - -现在会报此错的可能原因如下(只有第一个报错原因与表有关): - -- 执行的 DML 语句中涉及的表和集群中正在执行的 DDL 的表有相同的,那么这个 DML 语句就会报此错。 -- 这个 DML 执行时间很久,而这段时间内执行了很多 DDL 语句,导致中间 `schema` 版本变更次数超过 1024 (此为默认值,可以通过 `tidb_max_delta_schema_count` 变量修改)。 -- 接受 DML 请求的 TiDB 长时间不能加载到 `schema information`(TiDB 与 PD 或 TiKV 之间的网络连接故障等会导致此问题),而这段时间内执行了很多 DDL 语句,导致中间 `schema` 版本变更次数超过 100。 -- TiDB 重启后执行第一个 DDL 操作前,执行 DML 操作,并且在执行过程中遇到了第 1 个 DDL 操作(即在执行第 1 个 DDL 操作前,启动该 DML 对应的事务,且在该 DDL 变更第一个 `schema` 版本后,提交该 DML 对应的事务),那么这个 DML 会报此错。 - -> **注意:** -> -> + 目前 TiDB 未缓存所有的 `schema` 版本信息。 -> + 对于每个 DDL 操作,`schema` 版本变更的数量与对应 `schema state` 变更的次数一致。 -> + 不同的 DDL 操作版本变更次数不一样。例如,`create table` 操作会有 1 次 `schema` 版本变更;`add column` 操作有 4 次 `schema` 版本变更。 - -## 触发 Information schema is out of date 错误的原因? - -当执行 DML 时,TiDB 超过一个 DDL lease 时间(默认 45s)没能加载到最新的 schema 就可能会报 `Information schema is out of date` 的错误。遇到此错的可能原因如下: - -- 执行此 DML 的 TiDB 被 kill 后准备退出,且此 DML 对应的事务执行时间超过一个 DDL lease,在事务提交时会报这个错误。 -- TiDB 在执行此 DML 时,有一段时间内连不上 PD 或者 TiKV,导致 TiDB 超过一个 DDL lease 时间没有 load schema,或者导致 TiDB 断开与 PD 之间带 keep alive 设置的连接。 - -## 高并发情况下执行 DDL 时报错的原因? - -高并发情况下执行 DDL(比如批量建表)时,极少部分 DDL 可能会由于并发执行时 key 冲突而执行失败。 - -并发执行 DDL 时,建议将 DDL 数量保持在 20 以下,否则你需要在应用端重试失败的 DDL 语句。 - -## SQL 优化 - -### TiDB 执行计划解读 - -详细解读[理解 TiDB 执行计划](/explain-overview.md)。 - -### 统计信息收集 - -详细解读[统计信息](/statistics.md)。 - -### Count 如何加速? - -Count 就是暴力扫表,提高并发度能显著的提升速度,修改并发度可以参考 `tidb_distsql_scan_concurrency` 变量,但是也要看 CPU 和 I/O 资源。TiDB 每次查询都要访问 TiKV,在数据量小的情况下,MySQL 都在内存里,TiDB 还需要进行一次网络访问。 - -提升建议: - -- 建议提升硬件配置,可以参考[部署建议](/hardware-and-software-requirements.md)。 -- 提升并发度,默认是 10,可以提升到 50 试试,但是一般提升在 2-4 倍之间。 -- 测试大数据量的 count。 -- 调优 TiKV 配置,可以参考[性能调优](/tune-tikv-memory-performance.md)。 -- 可以参考[下推计算结果缓存](/coprocessor-cache.md)。 - -### 查看当前 DDL 的进度? - -通过 `admin show ddl` 查看当前 job 进度。操作如下: - -{{< copyable "sql" >}} - -```sql -admin show ddl; -``` - -```sql -*************************** 1. row *************************** - SCHEMA_VER: 140 - OWNER: 1a1c4174-0fcd-4ba0-add9-12d08c4077dc -RUNNING_JOBS: ID:121, Type:add index, State:running, SchemaState:write reorganization, SchemaID:1, TableID:118, RowCount:77312, ArgLen:0, start time: 2018-12-05 16:26:10.652 +0800 CST, Err:, ErrCount:0, SnapshotVersion:404749908941733890 - SELF_ID: 1a1c4174-0fcd-4ba0-add9-12d08c4077dc -``` - -从上面操作结果可知,当前正在处理的是 `add index` 操作。且从 `RUNNING_JOBS` 列的 `RowCount` 字段可以知道当前 `add index` 操作已经添加了 77312 行索引。 - -### 如何查看 DDL job? - -可以使用 `admin show ddl` 语句查看正在运行的 DDL 作业。 - -- `admin show ddl jobs`:用于查看当前 DDL 作业队列中的所有结果(包括正在运行以及等待运行的任务)以及已执行完成的 DDL 作业队列中的最近十条结果。 -- `admin show ddl job queries 'job_id' [, 'job_id'] ...`:用于显示 `job_id` 对应的 DDL 任务的原始 SQL 语句。此 `job_id` 只搜索正在执行中的任务以及 DDL 历史作业队列中的最近十条结果。 - -### TiDB 是否支持基于 COST 的优化(CBO),如果支持,实现到什么程度? - -是的,TiDB 使用的是基于成本的优化器 (CBO),会对代价模型、统计信息持续优化。除此之外,TiDB 还支持 hash join、soft-merge join 等 join 算法。 - -### 如何确定某张表是否需要做 analyze ? - -可以通过 `show stats_healthy` 来查看 Healthy 字段,一般小于等于 60 的表需要做 analyze。 - -### SQL 的执行计划展开成了树,ID 的序号有什么规律吗?这棵树的执行顺序会是怎么样的? - -ID 没什么规律,只要是唯一就行,不过生成的时候,是有一个计数器,生成一个 plan 就加一,执行的顺序和序号无关,整个执行计划是一颗树,执行时从根节点开始,不断地向上返回数据。执行计划的理解,请参考[理解 TiDB 执行计划](/explain-overview.md)。 - -### TiDB 执行计划中,task cop 在一个 root 下,这个是并行的么? - -目前 TiDB 的计算任务隶属于两种不同的 task:cop task 和 root task。cop task 是指被下推到 KV 端分布式执行的计算任务,root task 是指在 TiDB 端单点执行的计算任务。一般来讲 root task 的输入数据是来自于 cop task 的;但是 root task 在处理数据的时候,TiKV 上的 cop task 也可以同时处理数据,等待 TiDB 的 root task 拉取,所以从这个观点上来看,他们是并行的;但是存在数据上下游关系;在执行的过程中,某些时间段其实也是并行的,第一个 cop task 在处理 [100, 200] 的数据,第二个 cop task 在处理 [1, 100] 的数据。执行计划的理解,请参考[理解 TiDB 执行计划](/explain-overview.md)。 - -## 数据库优化 - -### TiDB 参数及调整 - -详情参考 [TiDB 配置参数](/command-line-flags-for-tidb-configuration.md)。 - -### 如何打散热点 - -TiDB 中以 Region 分片来管理数据库,通常来讲,TiDB 的热点指的是 Region 的读写访问热点。而 TiDB 中对于非整数主键或没有主键的表,可以通过设置 `SHARD_ROW_ID_BITS` 来适度分解 Region 分片,以达到打散 Region 热点的效果。详情可参考官网 [SHARD_ROW_ID_BITS](/shard-row-id-bits.md) 中的介绍。 - -### TiKV 性能参数调优 - -详情参考 [TiKV 性能参数调优](/tune-tikv-memory-performance.md)。 +--- +title: SQL 操作常见问题 +summary: 介绍 SQL 操作相关的常见问题。 +--- + +# SQL 操作常见问题 + +本文档介绍 TiDB 中常见的 SQL 操作问题。 + +## TiDB 对哪些 MySQL variables 兼容? + +详细可参考[系统变量](/system-variables.md)。 + +## 省略 `ORDER BY` 条件时 TiDB 中返回结果的顺序与 MySQL 中的不一致 + +这不是 bug。返回结果的顺序视不同情况而定,不保证顺序统一。 + +MySQL 中,返回结果的顺序可能较为固定,因为查询是通过单线程执行的。但升级到新版本后,查询计划也可能变化。无论是否期待返回结果有序,都推荐使用 `ORDER BY` 条件。 + +[ISO/IEC 9075:1992, Database Language SQL- July 30, 1992](http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt) 对此有如下表述: + +> If an `` is not specified, then the table specified by the `` is T and the ordering of rows in T is implementation-dependent.(如果未指定 ``,通过 `` 指定的表为 T,那么 T 表中的行顺序视执行情况而定。) + +以下两条查询的结果都是合法的: + +```sql +> select * from t; ++------+------+ +| a | b | ++------+------+ +| 1 | 1 | +| 2 | 2 | ++------+------+ +2 rows in set (0.00 sec) +``` + +```sql +> select * from t; -- 不确定返回结果的顺序 ++------+------+ +| a | b | ++------+------+ +| 2 | 2 | +| 1 | 1 | ++------+------+ +2 rows in set (0.00 sec) +``` + +如果 `ORDER BY` 中使用的列不是唯一列,该语句就不确定返回结果的顺序。以下示例中,`a` 列有重复值,因此只有 `ORDER BY a, b` 能确定返回结果的顺序。 + +```sql +> select * from t order by a; ++------+------+ +| a | b | ++------+------+ +| 1 | 1 | +| 2 | 1 | +| 2 | 2 | ++------+------+ +3 rows in set (0.00 sec) +``` + +```sql +> select * from t order by a; -- 能确定 a 列的顺序,不确定 b 列的顺序 ++------+------+ +| a | b | ++------+------+ +| 1 | 1 | +| 2 | 2 | +| 2 | 1 | ++------+------+ +3 rows in set (0.00 sec) +``` + +## TiDB 是否支持 `SELECT FOR UPDATE`? + +支持。当 TiDB 使用悲观锁(自 TiDB v3.0 起默认使用)时,TiDB 中 `SELECT FOR UPDATE` 的行为与 MySQL 中的基本一致。 + +当 TiDB 使用乐观锁时,`SELECT FOR UPDATE` 不会在事务启动时对数据加锁,而是在提交事务时检查冲突。如果检查出冲突,会回滚待提交的事务。 + +## TiDB 的 codec 能保证 UTF8 的字符串是 memcomparable 的吗?我们的 key 需要支持 UTF8,有什么编码建议吗? + +TiDB 字符集默认就是 UTF8 而且目前只支持 UTF8,字符串就是 memcomparable 格式的。 + +## 一个事务中的语句数量最大是多少? + +一个事务中的语句数量,默认限制最大为 5000 条。 + +## TiDB 中,为什么出现后插入数据的自增 ID 反而小? + +TiDB 的自增 ID (`AUTO_INCREMENT`) 只保证自增且唯一,并不保证连续分配。TiDB 目前采用批量分配的方式,所以如果在多台 TiDB 上同时插入数据,分配的自增 ID 会不连续。当多个线程并发往不同的 TiDB-server 插入数据的时候,有可能会出现后插入的数据自增 ID 小的情况。此外,TiDB 允许给整型类型的字段指定 AUTO_INCREMENT,且一个表只允许一个属性为 `AUTO_INCREMENT` 的字段。详情可参考 [CREATE TABLE 语法](/mysql-compatibility.md#自增-id)。 + +## 如何在 TiDB 中修改 `sql_mode`? + +TiDB 支持在会话或全局作用域上修改 [`sql_mode`](/system-variables.md#sql_mode) 系统变量。对全局作用域变量的更改将作用于集群中的其它服务器,并且重启后更改依然有效。因此,你无需在每台 TiDB 服务器上都更改 `sql_mode` 的值。 + +## 用 Sqoop 批量写入 TiDB 数据,虽然配置了 `--batch` 选项,但还是会遇到 `java.sql.BatchUpdateExecption:statement count 5001 exceeds the transaction limitation` 的错误,该如何解决? + +- 在 Sqoop 中,`--batch` 是指每个批次提交 100 条 statement,但是默认每个 statement 包含 100 条 SQL 语句,所以此时 100 * 100 = 10000 条 SQL 语句,超出了 TiDB 的事务限制 5000 条,可以增加选项 `-Dsqoop.export.records.per.statement=10` 来解决这个问题,完整的用法如下: + + {{< copyable "shell-regular" >}} + + ```bash + sqoop export \ + -Dsqoop.export.records.per.statement=10 \ + --connect jdbc:mysql://mysql.example.com/sqoop \ + --username sqoop ${user} \ + --password ${passwd} \ + --table ${tab_name} \ + --export-dir ${dir} \ + --batch + ``` + +- 也可以选择增大 TiDB 的单个事物语句数量限制,不过此操作会导致内存增加。 + +## TiDB 有像 Oracle 那样的 Flashback Query 功能么,DDL 支持么? + +有,也支持 DDL。详细参考 [TiDB 历史数据回溯](/read-historical-data.md)。 + +## TiDB 中删除数据后会立即释放空间吗? + +DELETE,TRUNCATE 和 DROP 都不会立即释放空间。对于 TRUNCATE 和 DROP 操作,在达到 TiDB 的 GC (garbage collection) 时间后(默认 10 分钟),TiDB 的 GC 机制会删除数据并释放空间。对于 DELETE 操作 TiDB 的 GC 机制会删除数据,但不会释放空间,而是当后续数据写入 RocksDB 且进行 compact 时对空间重新利用。 + +## TiDB 是否支持 `REPLACE INTO` 语法? + +支持,例外是当前 `LOAD DATA` 不支持 `REPLACE INTO` 语法。 + +## 数据删除后查询速度为何会变慢? + +大量删除数据后,会有很多无用的 key 存在,影响查询效率。可以尝试开启 [Region Merge](/best-practices/massive-regions-best-practices.md#方法五开启-region-merge) 功能,具体看参考[最佳实践](https://pingcap.com/blog-cn/tidb-best-practice/)中的删除数据部分。 + +## 对数据做删除操作之后,空间回收比较慢,如何处理? + +TiDB 采用了多版本并发控制 (MVCC),为了使并发事务能查看到早期版本的数据,删除数据不会立即回收空间,而是推迟一段时间后再进行垃圾回收 (GC)。你可以通过修改系统变量 [`tidb_gc_life_time`](/system-variables.md#tidb_gc_life_time-从-v50-版本开始引入)的值(默认值为 `10m0s`)配置历史数据的保留时限。 + +## `SHOW PROCESSLIST` 是否显示系统进程号? + +TiDB 的 `SHOW PROCESSLIST` 与 MySQL 的 `SHOW PROCESSLIST` 显示内容基本一样,不会显示系统进程号,而 ID 表示当前的 session ID。其中 TiDB 的 `show processlist` 和 MySQL 的 `show processlist` 区别如下: + ++ 由于 TiDB 是分布式数据库,TiDB server 实例是无状态的 SQL 解析和执行引擎(详情可参考 [TiDB 整体架构](/tidb-architecture.md)),用户使用 MySQL 客户端登录的是哪个 TiDB server ,`show processlist` 就会显示当前连接的这个 TiDB server 中执行的 session 列表,不是整个集群中运行的全部 session 列表;而 MySQL 是单机数据库,`show processlist` 列出的是当前整个 MySQL 数据库的全部执行 SQL 列表。 + ++ 在查询执行期间,TiDB 中的 `State` 列不会持续更新。由于 TiDB 支持并行查询,每个语句可能同时处于多个状态,因此很难显示为某一种状态。 + +## 在 TiDB 中如何控制或改变 SQL 提交的执行优先级? + +TiDB 支持改变 [per-session](/system-variables.md#tidb_force_priority)、[全局](/tidb-configuration-file.md#force-priority)或单个语句的优先级。优先级包括: + +- HIGH_PRIORITY:该语句为高优先级语句,TiDB 在执行阶段会优先处理这条语句 +- LOW_PRIORITY:该语句为低优先级语句,TiDB 在执行阶段会降低这条语句的优先级 + +以上两种参数可以结合 TiDB 的 DML 语言进行使用,使用方法举例如下: + +1. 通过在数据库中写 SQL 的方式来调整优先级: + + {{< copyable "sql" >}} + + ```sql + select HIGH_PRIORITY | LOW_PRIORITY count(*) from table_name; + insert HIGH_PRIORITY | LOW_PRIORITY into table_name insert_values; + delete HIGH_PRIORITY | LOW_PRIORITY from table_name; + update HIGH_PRIORITY | LOW_PRIORITY table_reference set assignment_list where where_condition; + replace HIGH_PRIORITY | LOW_PRIORITY into table_name; + ``` + +2. 全表扫会自动调整为低优先级,analyze 也是默认低优先级。 + +## 在 TiDB 中 auto analyze 的触发策略是怎样的? + +触发策略:新表达到 1000 条,并且在 1 分钟内没有写入,会自动触发。 + +当表的(修改数/当前总行数)大于 `tidb_auto_analyze_ratio` 的时候,会自动触发 `analyze` 语句。`tidb_auto_analyze_ratio` 的默认值为 0.5,即默认开启此功能。为了保险起见,在开启此功能的时候,保证了其最小值为 0.3。但是不能大于等于 `pseudo-estimate-ratio`(默认值为 0.8),否则会有一段时间使用 pseudo 统计信息,建议设置值为 0.5。 + +## 可以使用 Hints 控制优化器行为吗? + +在 TiDB 中,你可以用多种方法控制查询优化器的默认行为,包括使用 [Optimizer Hints](/optimizer-hints.md) 和 [SQL 执行计划管理 (SPM)](/sql-plan-management.md)。基本用法同 MySQL 中的一致,还包含若干 TiDB 特有的用法,示例如下:`select column_name from table_name use index(index_name)where where_condition;` + +## 触发 Information schema is changed 错误的原因? + +TiDB 在执行 SQL 语句时,会使用当时的 `schema` 来处理该 SQL 语句,而且 TiDB 支持在线异步变更 DDL。那么,在执行 DML 的时候可能有 DDL 语句也在执行,而你需要确保每个 SQL 语句在同一个 `schema` 上执行。所以当执行 DML 时,遇到正在执行中的 DDL 操作就可能会报 `Information schema is changed` 的错误。为了避免太多的 DML 语句报错,已做了一些优化。 + +现在会报此错的可能原因如下(只有第一个报错原因与表有关): + +- 执行的 DML 语句中涉及的表和集群中正在执行的 DDL 的表有相同的,那么这个 DML 语句就会报此错。 +- 这个 DML 执行时间很久,而这段时间内执行了很多 DDL 语句,导致中间 `schema` 版本变更次数超过 1024 (此为默认值,可以通过 `tidb_max_delta_schema_count` 变量修改)。 +- 接受 DML 请求的 TiDB 长时间不能加载到 `schema information`(TiDB 与 PD 或 TiKV 之间的网络连接故障等会导致此问题),而这段时间内执行了很多 DDL 语句,导致中间 `schema` 版本变更次数超过 100。 +- TiDB 重启后执行第一个 DDL 操作前,执行 DML 操作,并且在执行过程中遇到了第 1 个 DDL 操作(即在执行第 1 个 DDL 操作前,启动该 DML 对应的事务,且在该 DDL 变更第一个 `schema` 版本后,提交该 DML 对应的事务),那么这个 DML 会报此错。 + +> **注意:** +> +> + 目前 TiDB 未缓存所有的 `schema` 版本信息。 +> + 对于每个 DDL 操作,`schema` 版本变更的数量与对应 `schema state` 变更的次数一致。 +> + 不同的 DDL 操作版本变更次数不一样。例如,`create table` 操作会有 1 次 `schema` 版本变更;`add column` 操作有 4 次 `schema` 版本变更。 + +## 触发 Information schema is out of date 错误的原因? + +当执行 DML 时,TiDB 超过一个 DDL lease 时间(默认 45s)没能加载到最新的 schema 就可能会报 `Information schema is out of date` 的错误。遇到此错的可能原因如下: + +- 执行此 DML 的 TiDB 被 kill 后准备退出,且此 DML 对应的事务执行时间超过一个 DDL lease,在事务提交时会报这个错误。 +- TiDB 在执行此 DML 时,有一段时间内连不上 PD 或者 TiKV,导致 TiDB 超过一个 DDL lease 时间没有 load schema,或者导致 TiDB 断开与 PD 之间带 keep alive 设置的连接。 + +## 高并发情况下执行 DDL 时报错的原因? + +高并发情况下执行 DDL(比如批量建表)时,极少部分 DDL 可能会由于并发执行时 key 冲突而执行失败。 + +并发执行 DDL 时,建议将 DDL 数量保持在 20 以下,否则你需要在应用端重试失败的 DDL 语句。 + +## SQL 优化 + +### TiDB 执行计划解读 + +详细解读[理解 TiDB 执行计划](/explain-overview.md)。 + +### 统计信息收集 + +详细解读[统计信息](/statistics.md)。 + +### Count 如何加速? + +Count 就是暴力扫表,提高并发度能显著的提升速度,修改并发度可以参考 `tidb_distsql_scan_concurrency` 变量,但是也要看 CPU 和 I/O 资源。TiDB 每次查询都要访问 TiKV,在数据量小的情况下,MySQL 都在内存里,TiDB 还需要进行一次网络访问。 + +提升建议: + +- 建议提升硬件配置,可以参考[部署建议](/hardware-and-software-requirements.md)。 +- 提升并发度,默认是 10,可以提升到 50 试试,但是一般提升在 2-4 倍之间。 +- 测试大数据量的 count。 +- 调优 TiKV 配置,可以参考[性能调优](/tune-tikv-memory-performance.md)。 +- 可以参考[下推计算结果缓存](/coprocessor-cache.md)。 + +### 查看当前 DDL 的进度? + +通过 `admin show ddl` 查看当前 job 进度。操作如下: + +{{< copyable "sql" >}} + +```sql +admin show ddl; +``` + +```sql +*************************** 1. row *************************** + SCHEMA_VER: 140 + OWNER: 1a1c4174-0fcd-4ba0-add9-12d08c4077dc +RUNNING_JOBS: ID:121, Type:add index, State:running, SchemaState:write reorganization, SchemaID:1, TableID:118, RowCount:77312, ArgLen:0, start time: 2018-12-05 16:26:10.652 +0800 CST, Err:, ErrCount:0, SnapshotVersion:404749908941733890 + SELF_ID: 1a1c4174-0fcd-4ba0-add9-12d08c4077dc +``` + +从上面操作结果可知,当前正在处理的是 `add index` 操作。且从 `RUNNING_JOBS` 列的 `RowCount` 字段可以知道当前 `add index` 操作已经添加了 77312 行索引。 + +### 如何查看 DDL job? + +可以使用 `admin show ddl` 语句查看正在运行的 DDL 作业。 + +- `admin show ddl jobs`:用于查看当前 DDL 作业队列中的所有结果(包括正在运行以及等待运行的任务)以及已执行完成的 DDL 作业队列中的最近十条结果。 +- `admin show ddl job queries 'job_id' [, 'job_id'] ...`:用于显示 `job_id` 对应的 DDL 任务的原始 SQL 语句。此 `job_id` 只搜索正在执行中的任务以及 DDL 历史作业队列中的最近十条结果。 + +### TiDB 是否支持基于 COST 的优化(CBO),如果支持,实现到什么程度? + +是的,TiDB 使用的是基于成本的优化器 (CBO),会对代价模型、统计信息持续优化。除此之外,TiDB 还支持 hash join、soft-merge join 等 join 算法。 + +### 如何确定某张表是否需要做 analyze ? + +可以通过 `show stats_healthy` 来查看 Healthy 字段,一般小于等于 60 的表需要做 analyze。 + +### SQL 的执行计划展开成了树,ID 的序号有什么规律吗?这棵树的执行顺序会是怎么样的? + +ID 没什么规律,只要是唯一就行,不过生成的时候,是有一个计数器,生成一个 plan 就加一,执行的顺序和序号无关,整个执行计划是一颗树,执行时从根节点开始,不断地向上返回数据。执行计划的理解,请参考[理解 TiDB 执行计划](/explain-overview.md)。 + +### TiDB 执行计划中,task cop 在一个 root 下,这个是并行的么? + +目前 TiDB 的计算任务隶属于两种不同的 task:cop task 和 root task。cop task 是指被下推到 KV 端分布式执行的计算任务,root task 是指在 TiDB 端单点执行的计算任务。一般来讲 root task 的输入数据是来自于 cop task 的;但是 root task 在处理数据的时候,TiKV 上的 cop task 也可以同时处理数据,等待 TiDB 的 root task 拉取,所以从这个观点上来看,他们是并行的;但是存在数据上下游关系;在执行的过程中,某些时间段其实也是并行的,第一个 cop task 在处理 [100, 200] 的数据,第二个 cop task 在处理 [1, 100] 的数据。执行计划的理解,请参考[理解 TiDB 执行计划](/explain-overview.md)。 + +## 数据库优化 + +### TiDB 参数及调整 + +详情参考 [TiDB 配置参数](/command-line-flags-for-tidb-configuration.md)。 + +### 如何打散热点 + +TiDB 中以 Region 分片来管理数据库,通常来讲,TiDB 的热点指的是 Region 的读写访问热点。而 TiDB 中对于非整数主键或没有主键的表,可以通过设置 `SHARD_ROW_ID_BITS` 来适度分解 Region 分片,以达到打散 Region 热点的效果。详情可参考官网 [SHARD_ROW_ID_BITS](/shard-row-id-bits.md) 中的介绍。 + +### TiKV 性能参数调优 + +详情参考 [TiKV 性能参数调优](/tune-tikv-memory-performance.md)。 diff --git a/functions-and-operators/expressions-pushed-down.md b/functions-and-operators/expressions-pushed-down.md index b89e10fccbdf..0e1570f36521 100644 --- a/functions-and-operators/expressions-pushed-down.md +++ b/functions-and-operators/expressions-pushed-down.md @@ -9,14 +9,29 @@ summary: TiDB 中下推到 TiKV 的表达式列表及相关设置。 ## 已支持下推的表达式列表 -| 表达式分类 | 具体操作 | -| :-------------- | :------------------------------------- | -| [逻辑运算](/functions-and-operators/operators.md#逻辑操作符) | AND (&&), OR (||), NOT (!) | -| [比较运算](/functions-and-operators/operators.md#比较方法和操作符) | <, <=, =, != (`<>`), >, >=, [`<=>`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_equal-to), [`IN()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in), IS NULL, LIKE, IS TRUE, IS FALSE, [`COALESCE()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce) | -| [数值运算](/functions-and-operators/numeric-functions-and-operators.md) | +, -, *, /, [`ABS()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_abs), [`CEIL()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceil), [`CEILING()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceiling), [`FLOOR()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_floor) | -| [控制流运算](/functions-and-operators/control-flow-functions.md) | [`CASE`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#operator_case), [`IF()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_if), [`IFNULL()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_ifnull) | -| [JSON运算](/functions-and-operators/json-functions.md) | [JSON_TYPE(json_val)][json_type],
[JSON_EXTRACT(json_doc, path[, path] ...)][json_extract],
[JSON_OBJECT(key, val[, key, val] ...)][json_object],
[JSON_ARRAY([val[, val] ...])][json_array],
[JSON_MERGE(json_doc, json_doc[, json_doc] ...)][json_merge],
[JSON_SET(json_doc, path, val[, path, val] ...)][json_set],
[JSON_INSERT(json_doc, path, val[, path, val] ...)][json_insert],
[JSON_REPLACE(json_doc, path, val[, path, val] ...)][json_replace],
[JSON_REMOVE(json_doc, path[, path] ...)][json_remove] | -| [日期运算](/functions-and-operators/date-and-time-functions.md) | [`DATE_FORMAT()`](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 表达式分类 | 具体操作 | ++:========================================================================+:==============================================================================================================================================================================================================================================================================================================================================================================================================+ +| [逻辑运算](/functions-and-operators/operators.md#逻辑操作符) | AND (&&), OR (||), NOT (!) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [比较运算](/functions-and-operators/operators.md#比较方法和操作符) | \<, \<=, =, != (`<>`), \>, \>=, [`<=>`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_equal-to), [`IN()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_in), IS NULL, LIKE, IS TRUE, IS FALSE, [`COALESCE()`](https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [数值运算](/functions-and-operators/numeric-functions-and-operators.md) | +, -, \*, /, [`ABS()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_abs), [`CEIL()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceil), [`CEILING()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_ceiling), [`FLOOR()`](https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_floor) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [控制流运算](/functions-and-operators/control-flow-functions.md) | [`CASE`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#operator_case), [`IF()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_if), [`IFNULL()`](https://dev.mysql.com/doc/refman/5.7/en/flow-control-functions.html#function_ifnull) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [JSON运算](/functions-and-operators/json-functions.md) | \[JSON_TYPE(json_val)\]\[json_type\], | +| | \[JSON_EXTRACT(json_doc, path\[, path\] ...)\]\[json_extract\], | +| | \[JSON_OBJECT(key, val\[, key, val\] ...)\]\[json_object\], | +| | \[JSON_ARRAY(\[val\[, val\] ...\])\]\[json_array\], | +| | \[JSON_MERGE(json_doc, json_doc\[, json_doc\] ...)\]\[json_merge\], | +| | \[JSON_SET(json_doc, path, val\[, path, val\] ...)\]\[json_set\], | +| | \[JSON_INSERT(json_doc, path, val\[, path, val\] ...)\]\[json_insert\], | +| | \[JSON_REPLACE(json_doc, path, val\[, path, val\] ...)\]\[json_replace\], | +| | \[JSON_REMOVE(json_doc, path\[, path\] ...)\]\[json_remove\] | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| [日期运算](/functions-and-operators/date-and-time-functions.md) | [`DATE_FORMAT()`](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format) | ++-------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ## 禁止特定表达式下推 diff --git a/geo-distributed-deployment-topology.md b/geo-distributed-deployment-topology.md index d847fa98d427..465a37d83a41 100644 --- a/geo-distributed-deployment-topology.md +++ b/geo-distributed-deployment-topology.md @@ -9,12 +9,27 @@ summary: 介绍跨数据中心部署 TiDB 集群的拓扑结构。 ## 拓扑信息 -|实例 | 个数 | 物理机配置 | BJ IP | SH IP |配置 | -| :-- | :-- | :-- | :-- | :-- | :-- | -| TiDB |5 | 16 VCore 32GB * 1 | 10.0.1.1
10.0.1.2
10.0.1.3
10.0.1.4 | 10.0.1.5 | 默认端口
全局目录配置 | -| PD | 5 | 4 VCore 8GB * 1 |10.0.1.6
10.0.1.7
10.0.1.8
10.0.1.9 | 10.0.1.10 | 默认端口
全局目录配置 | -| TiKV | 5 | 16 VCore 32GB 2TB (nvme ssd) * 1 | 10.0.1.11
10.0.1.12
10.0.1.13
10.0.1.14 | 10.0.1.15 | 默认端口
全局目录配置 | -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.16 || 默认端口
全局目录配置 | ++----------------------+-----------+-----------------------------------+-----------+-----------+--------------+ +| 实例 | 个数 | 物理机配置 | BJ IP | SH IP | 配置 | ++:=====================+:==========+:==================================+:==========+:==========+:=============+ +| TiDB | 5 | 16 VCore 32GB \* 1 | 10.0.1.1 | 10.0.1.5 | 默认端口 | +| | | | 10.0.1.2 | | 全局目录配置 | +| | | | 10.0.1.3 | | | +| | | | 10.0.1.4 | | | ++----------------------+-----------+-----------------------------------+-----------+-----------+--------------+ +| PD | 5 | 4 VCore 8GB \* 1 | 10.0.1.6 | 10.0.1.10 | 默认端口 | +| | | | 10.0.1.7 | | 全局目录配置 | +| | | | 10.0.1.8 | | | +| | | | 10.0.1.9 | | | ++----------------------+-----------+-----------------------------------+-----------+-----------+--------------+ +| TiKV | 5 | 16 VCore 32GB 2TB (nvme ssd) \* 1 | 10.0.1.11 | 10.0.1.15 | 默认端口 | +| | | | 10.0.1.12 | | 全局目录配置 | +| | | | 10.0.1.13 | | | +| | | | 10.0.1.14 | | | ++----------------------+-----------+-----------------------------------+-----------+-----------+--------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.16 | | 默认端口 | +| | | | | | 全局目录配置 | ++----------------------+-----------+-----------------------------------+-----------+-----------+--------------+ ### 拓扑模版 diff --git a/hybrid-deployment-topology.md b/hybrid-deployment-topology.md index 9ef7bfda70fe..18dd4aaadaf8 100644 --- a/hybrid-deployment-topology.md +++ b/hybrid-deployment-topology.md @@ -9,12 +9,24 @@ summary: 介绍混合部署 TiDB 集群的拓扑结构。 ## 拓扑信息 -| 实例 | 个数 | 物理机配置 | IP | 配置 | -| :-- | :-- | :-- | :-- | :-- | -| TiDB | 6 | 32 VCore 64GB | 10.0.1.1
10.0.1.2
10.0.1.3 | 配置 numa 绑核操作 | -| PD | 3 | 16 VCore 32 GB | 10.0.1.4
10.0.1.5
10.0.1.6 | 配置 location_lables 参数 | -| TiKV | 6 | 32 VCore 64GB | 10.0.1.7
10.0.1.8
10.0.1.9 | 1. 区分实例级别的 port、status_port;
2. 配置全局参数 readpool、storage 以及 raftstore;
3. 配置实例级别 host 维度的 labels;
4. 配置 numa 绑核操作| -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.10 | 默认配置 | ++----------------------+-------------+------------------------------+-------------+----------------------------------------------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:=====================+:============+:=============================+:============+:===================================================+ +| TiDB | 6 | 32 VCore 64GB | 10.0.1.1 | 配置 numa 绑核操作 | +| | | | 10.0.1.2 | | +| | | | 10.0.1.3 | | ++----------------------+-------------+------------------------------+-------------+----------------------------------------------------+ +| PD | 3 | 16 VCore 32 GB | 10.0.1.4 | 配置 location_lables 参数 | +| | | | 10.0.1.5 | | +| | | | 10.0.1.6 | | ++----------------------+-------------+------------------------------+-------------+----------------------------------------------------+ +| TiKV | 6 | 32 VCore 64GB | 10.0.1.7 | 1\. 区分实例级别的 port、status_port; | +| | | | 10.0.1.8 | 2. 配置全局参数 readpool、storage 以及 raftstore; | +| | | | 10.0.1.9 | 3. 配置实例级别 host 维度的 labels; | +| | | | | 4. 配置 numa 绑核操作 | ++----------------------+-------------+------------------------------+-------------+----------------------------------------------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.10 | 默认配置 | ++----------------------+-------------+------------------------------+-------------+----------------------------------------------------+ ### 拓扑模版 diff --git a/minimal-deployment-topology.md b/minimal-deployment-topology.md index 0753b329b92b..01da14bbf542 100644 --- a/minimal-deployment-topology.md +++ b/minimal-deployment-topology.md @@ -9,12 +9,24 @@ summary: 介绍 TiDB 集群的最小拓扑。 ## 拓扑信息 -|实例 | 个数 | 物理机配置 | IP |配置 | -| :-- | :-- | :-- | :-- | :-- | -| TiDB |3 | 16 VCore 32GB * 1 | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口
全局目录配置 | -| PD | 3 | 4 VCore 8GB * 1 |10.0.1.4
10.0.1.5
10.0.1.6 | 默认端口
全局目录配置 | -| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) * 1 | 10.0.1.7
10.0.1.8
10.0.1.9 | 默认端口
全局目录配置 | -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.11 | 默认端口
全局目录配置 | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:=====================+:============+:==================================+:============+:=============+ +| TiDB | 3 | 16 VCore 32GB \* 1 | 10.0.1.1 | 默认端口 | +| | | | 10.0.1.2 | 全局目录配置 | +| | | | 10.0.1.3 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| PD | 3 | 4 VCore 8GB \* 1 | 10.0.1.4 | 默认端口 | +| | | | 10.0.1.5 | 全局目录配置 | +| | | | 10.0.1.6 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) \* 1 | 10.0.1.7 | 默认端口 | +| | | | 10.0.1.8 | 全局目录配置 | +| | | | 10.0.1.9 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.11 | 默认端口 | +| | | | | 全局目录配置 | ++----------------------+-------------+-----------------------------------+-------------+--------------+ ### 拓扑模版 diff --git a/multi-data-centers-in-one-city-deployment.md b/multi-data-centers-in-one-city-deployment.md index 2e6df03263c9..1cc5f8ee84fd 100644 --- a/multi-data-centers-in-one-city-deployment.md +++ b/multi-data-centers-in-one-city-deployment.md @@ -1,154 +1,154 @@ ---- -title: 同城多数据中心部署 TiDB -summary: 本文档介绍同城多数据中心部署 TiDB 方案。 ---- - -# 同城多数据中心部署 TiDB - -作为 NewSQL 数据库,TiDB 兼顾了传统关系型数据库的优秀特性、NoSQL 数据库可扩展性以及跨数据中心场景下的高可用。本文档旨在介绍同城多数据中心部署 TiDB 方案。 - -## 了解 Raft 协议 - -Raft 是一种分布式一致性算法,在 TiDB 集群的多种组件中,PD 和 TiKV 都通过 Raft 实现了数据的容灾。Raft 的灾难恢复能力通过如下机制实现: - -- Raft 成员的本质是日志复制和状态机。Raft 成员之间通过复制日志来实现数据同步;Raft 成员在不同条件下切换自己的成员状态,其目标是选出 leader 以提供对外服务。 -- Raft 是一个表决系统,它遵循多数派协议,在一个 Raft Group 中,某成员获得大多数投票,它的成员状态就会转变为 leader。也就是说,当一个 Raft Group 还保有大多数节点 (majority) 时,它就能够选出 leader 以提供对外服务。 - -遵循 Raft 可靠性的特点,放到现实场景中: - -- 想克服任意 1 台服务器 (host) 的故障,应至少提供 3 台服务器。 -- 想克服任意 1 个机柜 (rack) 的故障,应至少提供 3 个机柜。 -- 想克服任意 1 个数据中心(dc,又称机房)的故障,应至少提供 3 个数据中心。 -- 想应对任意 1 个城市的灾难场景,应至少规划 3 个城市用于部署。 - -可见,原生 Raft 协议对于偶数副本的支持并不是很友好,考虑跨城网络延迟影响,或许同城三数据中心是最适合部署 Raft 的高可用及容灾方案。 - -## 同城三数据中心方案 - -同城三数据中心方案,即同城存有三个机房部署 TiDB 集群,同城三数据中心间的数据同步通过集群自身内部(Raft 协议)完成。同城三数据中心可同时对外进行读写服务,任意中心发生故障不影响数据一致性。 - -### 简易架构图 - -集群 TiDB、TiKV 和 PD 组件分别分布在 3 个不同的数据中心,这是最常规且高可用性最高的方案。 - -![三中心部署](/media/deploy-3dc.png) - -**优点:** - -- 所有数据的副本分布在三个数据中心,具备高可用和容灾能力 -- 任何一个数据中心失效后,不会产生任何数据丢失 (RPO = 0) -- 任何一个数据中心失效后,其他两个数据中心会自动发起 leader election,并在合理长的时间内(通常情况 20s 以内)自动恢复服务 - -![三中心部署容灾](/media/deploy-3dc-dr.png) - -**缺点:** - -性能受网络延迟影响。具体影响如下: - -- 对于写入的场景,所有写入的数据需要同步复制到至少 2 个数据中心,由于 TiDB 写入过程使用两阶段提交,故写入延迟至少需要 2 倍数据中心间的延迟。 -- 对于读请求来说,如果数据 leader 与发起读取的 TiDB 节点不在同一个数据中心,也会受网络延迟影响。 -- TiDB 中的每个事务都需要向 PD leader 获取 TSO,当 TiDB 与 PD leader 不在同一个数据中心时,它上面运行的事务也会因此受网络延迟影响,每个有写入的事务会获取两次 TSO。 - -### 架构优化图 - -如果不需要每个数据中心同时对外提供服务,可以将业务流量全部派发到一个数据中心,并通过调度策略把 Region leader 和 PD leader 都迁移到同一个数据中心。这样一来,不管是从 PD 获取 TSO,还是读取 Region,都不会受数据中心间网络的影响。当该数据中心失效后,PD leader 和 Region leader 会自动在其它数据中心选出,只需要把业务流量转移至其他存活的数据中心即可。 - -![三中心部署读性能优化](/media/deploy-3dc-optimize.png) - -**优点:** - -集群 TSO 获取能力以及读取性能有所提升。具体调度策略设置模板参照如下: - -```shell --- 其他机房统一驱逐 leader 到业务流量机房 -config set label-property reject-leader LabelName labelValue - --- 迁移 PD leader 并设置优先级 -member leader transfer pdName1 -member leader_priority pdName1 5 -member leader_priority pdName2 4 -member leader_priority pdName3 3 -``` - -**缺点:** - -- 写入场景仍受数据中心网络延迟影响,这是因为遵循 Raft 多数派协议,所有写入的数据需要同步复制到至少 2 个数据中心 -- TiDB Server 数据中心级别单点 -- 业务流量纯走单数据中心,性能受限于单数据中心网络带宽压力 -- TSO 获取能力以及读取性能受限于业务流量数据中心集群 PD、TiKV 组件是否正常,否则仍受跨数据中心网络交互影响 - -### 样例部署图 - -#### 样例拓扑架构 - -下面假设某城存有 IDC1、IDC2、IDC3 三机房,机房 IDC 中存有两套机架,每个机架存有三台服务器,不考虑混布以及单台机器多实例部署下,同城三数据中心架构集群(3 副本)部署参考如下: - -![同城三数据中心集群部署](/media/multi-data-centers-in-one-city-deployment-sample.png) - -#### TiKV Labels 简介 - -TiKV 是一个 Multi-Raft 系统,其数据按 Region(默认 96M)切分,每个 Region 的 3 个副本构成了一个 Raft Group。假设一个 3 副本 TiDB 集群,由于 Region 的副本数与 TiKV 实例数量无关,则一个 Region 的 3 个副本只会被调度到其中 3 个 TiKV 实例上,也就是说即使集群扩容 N 个 TiKV 实例,其本质仍是一个 3 副本集群。 - -由于 3 副本的 Raft Group 只能容忍 1 副本故障,当集群被扩容到 N 个 TiKV 实例时,这个集群依然只能容忍一个 TiKV 实例的故障。2 个 TiKV 实例的故障可能会导致某些 Region 丢失多个副本,整个集群的数据也不再完整,访问到这些 Region 上的数据的 SQL 请求将会失败。而 N 个 TiKV 实例中同时有两个发生故障的概率是远远高于 3 个 TiKV 中同时有两个发生故障的概率的,也就是说 Multi-Raft 系统集群扩容 TiKV 实例越多,其可用性是逐渐降低的。 - -正因为 Multi-Raft TiKV 系统局限性, Labels 标签应运而出,其主要用于描述 TiKV 的位置信息。Label 信息随着部署或滚动更新操作刷新到 TiKV 的启动配置文件中,启动后的 TiKV 会将自己最新的 Label 信息上报给 PD,PD 根据用户登记的 Label 名称(也就是 Label 元信息),结合 TiKV 的拓扑进行 Region 副本的最优调度,从而提高系统可用性。 - -#### TiKV Labels 样例规划 - -针对 TiKV Labels 标签,你需要根据已有的物理资源、容灾能力容忍度等方面进行设计与规划,进而提升系统的可用性和容灾能力。并根据已规划的拓扑架构,在集群初始化配置文件中进行配置(此处省略其他非重点项): - -```ini -server_configs: - pd: - replication.location-labels: ["zone","dc","rack","host"] - -tikv_servers: - - host: 10.63.10.30 - config: - server.labels: { zone: "z1", dc: "d1", rack: "r1", host: "30" } - - host: 10.63.10.31 - config: - server.labels: { zone: "z1", dc: "d1", rack: "r1", host: "31" } - - host: 10.63.10.32 - config: - server.labels: { zone: "z1", dc: "d1", rack: "r2", host: "32" } - - host: 10.63.10.33 - config: - server.labels: { zone: "z1", dc: "d1", rack: "r2", host: "33" } - - - host: 10.63.10.34 - config: - server.labels: { zone: "z2", dc: "d1", rack: "r1", host: "34" } - - host: 10.63.10.35 - config: - server.labels: { zone: "z2", dc: "d1", rack: "r1", host: "35" } - - host: 10.63.10.36 - config: - server.labels: { zone: "z2", dc: "d1", rack: "r2", host: "36" } - - host: 10.63.10.37 - config: - server.labels: { zone: "z2", dc: "d1", rack: "r2", host: "37" } - - - host: 10.63.10.38 - config: - server.labels: { zone: "z3", dc: "d1", rack: "r1", host: "38" } - - host: 10.63.10.39 - config: - server.labels: { zone: "z3", dc: "d1", rack: "r1", host: "39" } - - host: 10.63.10.40 - config: - server.labels: { zone: "z3", dc: "d1", rack: "r2", host: "40" } - - host: 10.63.10.41 - config: - server.labels: { zone: "z3", dc: "d1", rack: "r2", host: "41" } -``` - -本例中,zone 表示逻辑可用区层级,用于控制副本的隔离(当前集群 3 副本)。 - -不直接采用 dc,rack,host 三层 Label 结构的原因是考虑到将来可能发生 dc(数据中心)的扩容,假设新扩容的 dc 编号是 d2,d3,d4,则只需在对应可用区下扩容 dc;rack 扩容只需在对应 dc 下扩容即可。 - -如果直接采用 dc,rack,host 三层 Label 结构,那么扩容 dc 操作可能需重打 Label,TiKV 数据整体需要 Rebalance。 - -### 高可用和容灾分析 - -同城多数据中心方案提供的保障是,任意一个数据中心故障时,集群能自动恢复服务,不需要人工介入,并能保证数据一致性。注意,各种调度策略都是用于帮助性能优化的,当发生故障时,调度机制总是第一优先考虑可用性而不是性能。 +--- +title: 同城多数据中心部署 TiDB +summary: 本文档介绍同城多数据中心部署 TiDB 方案。 +--- + +# 同城多数据中心部署 TiDB + +作为 NewSQL 数据库,TiDB 兼顾了传统关系型数据库的优秀特性、NoSQL 数据库可扩展性以及跨数据中心场景下的高可用。本文档旨在介绍同城多数据中心部署 TiDB 方案。 + +## 了解 Raft 协议 + +Raft 是一种分布式一致性算法,在 TiDB 集群的多种组件中,PD 和 TiKV 都通过 Raft 实现了数据的容灾。Raft 的灾难恢复能力通过如下机制实现: + +- Raft 成员的本质是日志复制和状态机。Raft 成员之间通过复制日志来实现数据同步;Raft 成员在不同条件下切换自己的成员状态,其目标是选出 leader 以提供对外服务。 +- Raft 是一个表决系统,它遵循多数派协议,在一个 Raft Group 中,某成员获得大多数投票,它的成员状态就会转变为 leader。也就是说,当一个 Raft Group 还保有大多数节点 (majority) 时,它就能够选出 leader 以提供对外服务。 + +遵循 Raft 可靠性的特点,放到现实场景中: + +- 想克服任意 1 台服务器 (host) 的故障,应至少提供 3 台服务器。 +- 想克服任意 1 个机柜 (rack) 的故障,应至少提供 3 个机柜。 +- 想克服任意 1 个数据中心(dc,又称机房)的故障,应至少提供 3 个数据中心。 +- 想应对任意 1 个城市的灾难场景,应至少规划 3 个城市用于部署。 + +可见,原生 Raft 协议对于偶数副本的支持并不是很友好,考虑跨城网络延迟影响,或许同城三数据中心是最适合部署 Raft 的高可用及容灾方案。 + +## 同城三数据中心方案 + +同城三数据中心方案,即同城存有三个机房部署 TiDB 集群,同城三数据中心间的数据同步通过集群自身内部(Raft 协议)完成。同城三数据中心可同时对外进行读写服务,任意中心发生故障不影响数据一致性。 + +### 简易架构图 + +集群 TiDB、TiKV 和 PD 组件分别分布在 3 个不同的数据中心,这是最常规且高可用性最高的方案。 + +![三中心部署](/media/deploy-3dc.png) + +**优点:** + +- 所有数据的副本分布在三个数据中心,具备高可用和容灾能力 +- 任何一个数据中心失效后,不会产生任何数据丢失 (RPO = 0) +- 任何一个数据中心失效后,其他两个数据中心会自动发起 leader election,并在合理长的时间内(通常情况 20s 以内)自动恢复服务 + +![三中心部署容灾](/media/deploy-3dc-dr.png) + +**缺点:** + +性能受网络延迟影响。具体影响如下: + +- 对于写入的场景,所有写入的数据需要同步复制到至少 2 个数据中心,由于 TiDB 写入过程使用两阶段提交,故写入延迟至少需要 2 倍数据中心间的延迟。 +- 对于读请求来说,如果数据 leader 与发起读取的 TiDB 节点不在同一个数据中心,也会受网络延迟影响。 +- TiDB 中的每个事务都需要向 PD leader 获取 TSO,当 TiDB 与 PD leader 不在同一个数据中心时,它上面运行的事务也会因此受网络延迟影响,每个有写入的事务会获取两次 TSO。 + +### 架构优化图 + +如果不需要每个数据中心同时对外提供服务,可以将业务流量全部派发到一个数据中心,并通过调度策略把 Region leader 和 PD leader 都迁移到同一个数据中心。这样一来,不管是从 PD 获取 TSO,还是读取 Region,都不会受数据中心间网络的影响。当该数据中心失效后,PD leader 和 Region leader 会自动在其它数据中心选出,只需要把业务流量转移至其他存活的数据中心即可。 + +![三中心部署读性能优化](/media/deploy-3dc-optimize.png) + +**优点:** + +集群 TSO 获取能力以及读取性能有所提升。具体调度策略设置模板参照如下: + +```shell +-- 其他机房统一驱逐 leader 到业务流量机房 +config set label-property reject-leader LabelName labelValue + +-- 迁移 PD leader 并设置优先级 +member leader transfer pdName1 +member leader_priority pdName1 5 +member leader_priority pdName2 4 +member leader_priority pdName3 3 +``` + +**缺点:** + +- 写入场景仍受数据中心网络延迟影响,这是因为遵循 Raft 多数派协议,所有写入的数据需要同步复制到至少 2 个数据中心 +- TiDB Server 数据中心级别单点 +- 业务流量纯走单数据中心,性能受限于单数据中心网络带宽压力 +- TSO 获取能力以及读取性能受限于业务流量数据中心集群 PD、TiKV 组件是否正常,否则仍受跨数据中心网络交互影响 + +### 样例部署图 + +#### 样例拓扑架构 + +下面假设某城存有 IDC1、IDC2、IDC3 三机房,机房 IDC 中存有两套机架,每个机架存有三台服务器,不考虑混布以及单台机器多实例部署下,同城三数据中心架构集群(3 副本)部署参考如下: + +![同城三数据中心集群部署](/media/multi-data-centers-in-one-city-deployment-sample.png) + +#### TiKV Labels 简介 + +TiKV 是一个 Multi-Raft 系统,其数据按 Region(默认 96M)切分,每个 Region 的 3 个副本构成了一个 Raft Group。假设一个 3 副本 TiDB 集群,由于 Region 的副本数与 TiKV 实例数量无关,则一个 Region 的 3 个副本只会被调度到其中 3 个 TiKV 实例上,也就是说即使集群扩容 N 个 TiKV 实例,其本质仍是一个 3 副本集群。 + +由于 3 副本的 Raft Group 只能容忍 1 副本故障,当集群被扩容到 N 个 TiKV 实例时,这个集群依然只能容忍一个 TiKV 实例的故障。2 个 TiKV 实例的故障可能会导致某些 Region 丢失多个副本,整个集群的数据也不再完整,访问到这些 Region 上的数据的 SQL 请求将会失败。而 N 个 TiKV 实例中同时有两个发生故障的概率是远远高于 3 个 TiKV 中同时有两个发生故障的概率的,也就是说 Multi-Raft 系统集群扩容 TiKV 实例越多,其可用性是逐渐降低的。 + +正因为 Multi-Raft TiKV 系统局限性, Labels 标签应运而出,其主要用于描述 TiKV 的位置信息。Label 信息随着部署或滚动更新操作刷新到 TiKV 的启动配置文件中,启动后的 TiKV 会将自己最新的 Label 信息上报给 PD,PD 根据用户登记的 Label 名称(也就是 Label 元信息),结合 TiKV 的拓扑进行 Region 副本的最优调度,从而提高系统可用性。 + +#### TiKV Labels 样例规划 + +针对 TiKV Labels 标签,你需要根据已有的物理资源、容灾能力容忍度等方面进行设计与规划,进而提升系统的可用性和容灾能力。并根据已规划的拓扑架构,在集群初始化配置文件中进行配置(此处省略其他非重点项): + +```ini +server_configs: + pd: + replication.location-labels: ["zone","dc","rack","host"] + +tikv_servers: + - host: 10.63.10.30 + config: + server.labels: { zone: "z1", dc: "d1", rack: "r1", host: "30" } + - host: 10.63.10.31 + config: + server.labels: { zone: "z1", dc: "d1", rack: "r1", host: "31" } + - host: 10.63.10.32 + config: + server.labels: { zone: "z1", dc: "d1", rack: "r2", host: "32" } + - host: 10.63.10.33 + config: + server.labels: { zone: "z1", dc: "d1", rack: "r2", host: "33" } + + - host: 10.63.10.34 + config: + server.labels: { zone: "z2", dc: "d1", rack: "r1", host: "34" } + - host: 10.63.10.35 + config: + server.labels: { zone: "z2", dc: "d1", rack: "r1", host: "35" } + - host: 10.63.10.36 + config: + server.labels: { zone: "z2", dc: "d1", rack: "r2", host: "36" } + - host: 10.63.10.37 + config: + server.labels: { zone: "z2", dc: "d1", rack: "r2", host: "37" } + + - host: 10.63.10.38 + config: + server.labels: { zone: "z3", dc: "d1", rack: "r1", host: "38" } + - host: 10.63.10.39 + config: + server.labels: { zone: "z3", dc: "d1", rack: "r1", host: "39" } + - host: 10.63.10.40 + config: + server.labels: { zone: "z3", dc: "d1", rack: "r2", host: "40" } + - host: 10.63.10.41 + config: + server.labels: { zone: "z3", dc: "d1", rack: "r2", host: "41" } +``` + +本例中,zone 表示逻辑可用区层级,用于控制副本的隔离(当前集群 3 副本)。 + +不直接采用 dc,rack,host 三层 Label 结构的原因是考虑到将来可能发生 dc(数据中心)的扩容,假设新扩容的 dc 编号是 d2,d3,d4,则只需在对应可用区下扩容 dc;rack 扩容只需在对应 dc 下扩容即可。 + +如果直接采用 dc,rack,host 三层 Label 结构,那么扩容 dc 操作可能需重打 Label,TiKV 数据整体需要 Rebalance。 + +### 高可用和容灾分析 + +同城多数据中心方案提供的保障是,任意一个数据中心故障时,集群能自动恢复服务,不需要人工介入,并能保证数据一致性。注意,各种调度策略都是用于帮助性能优化的,当发生故障时,调度机制总是第一优先考虑可用性而不是性能。 diff --git a/post-installation-check.md b/post-installation-check.md index 2bc7c63d8721..ece91da6043c 100644 --- a/post-installation-check.md +++ b/post-installation-check.md @@ -1,212 +1,212 @@ ---- -title: 验证集群运行状态 -summary: 介绍如何验证集群运行状态。 ---- - -# 验证集群运行状态 - -在部署完一套 TiDB 集群后,需要检查集群是否正常运行。本文介绍如何通过 TiUP 命令、[TiDB Dashboard](/dashboard/dashboard-intro.md) 和 Grafana 检查集群状态,以及如何登录 TiDB 数据库执行简单的 SQL 操作。 - -## 通过 TiUP 检查集群状态 - -检查集群状态的命令是 `tiup cluster display `,例如: - -{{< copyable "shell-regular" >}} - -```shell -tiup cluster display tidb-test -``` - -预期结果输出:各节点 Status 状态信息为 `Up` 说明集群状态正常。 - -## 通过 TiDB Dashboard 和 Grafana 检查集群状态 - -本节介绍如何通过 [TiDB Dashboard](/dashboard/dashboard-intro.md) 和 Grafana 检查集群状态。 - -### 查看 TiDB Dashboard 检查 TiDB 集群状态 - -1. 通过 `{pd-ip}:{pd-port}/dashboard` 登录 TiDB Dashboard,登录用户和口令为 TiDB 数据库 `root` 用户和口令。如果你修改过数据库的 `root` 密码,则以修改后的密码为准,默认密码为空。 - - ![TiDB-Dashboard](/media/tiup/tidb-dashboard.png) - -2. 主页面显示 TiDB 集群中节点信息 - - ![TiDB-Dashboard-status](/media/tiup/tidb-dashboard-status.png) - -### 查看 Grafana 监控 Overview 页面检查 TiDB 集群状态 - -- 通过 `{Grafana-ip}:3000` 登录 Grafana 监控,默认用户名及密码为 `admin`/`admin`。 - -- 点击 **Overview** 监控页面检查 TiDB 端口和负载监控信息。 - - ![Grafana-overview](/media/tiup/grafana-overview.png) - -## 登录数据库执行简单 DML/DDL 操作和查询 SQL 语句 - -> **注意:** -> -> 登录数据库前,你需要安装 MySQL 客户端。 - -执行以下命令登录数据库: - -{{< copyable "shell-regular" >}} - -```shell -mysql -u root -h ${tidb_server_host_IP_address} -P 4000 -``` - -其中, `${tidb_server_host_IP_address}` 是在[初始化集群拓扑文件](/production-deployment-using-tiup.md#第-3-步初始化集群拓扑文件)时为 `tidb_servers` 配置的 IP 地址之一,例如 `10.0.1.7`。 - -输出下列信息表示登录成功: - -```sql -Welcome to the MySQL monitor. Commands end with ; or \g. -Your MySQL connection id is 3 -Server version: 5.7.25-TiDB-v5.0.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 5.7 compatible - -Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. - -Oracle is a registered trademark of Oracle Corporation and/or its -affiliates. Other names may be trademarks of their respective -owners. - -Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. -``` - -### 数据库操作 - -+ 检查 TiDB 版本 - - {{< copyable "sql" >}} - - ```sql - select tidb_version()\G - ``` - - 预期结果输出: - - ```sql - *************************** 1. row *************************** - tidb_version(): Release Version: v5.0.0 - Edition: Community - Git Commit Hash: 689a6b6439ae7835947fcaccf329a3fc303986cb - Git Branch: HEAD - UTC Build Time: 2020-05-28 11:09:45 - GoVersion: go1.13.4 - Race Enabled: false - TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306 - Check Table Before Drop: false - 1 row in set (0.00 sec) - ``` - -+ 创建 PingCAP database - - {{< copyable "sql" >}} - - ```sql - create database pingcap; - ``` - - ```sql - Query OK, 0 rows affected (0.10 sec) - ``` - - {{< copyable "sql" >}} - - ```sql - use pingcap; - ``` - - 预期输出 - - ```sql - Database changed - ``` - -+ 创建 `tab_tidb` 表 - - {{< copyable "sql" >}} - - ```sql - CREATE TABLE `tab_tidb` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `name` varchar(20) NOT NULL DEFAULT '', - `age` int(11) NOT NULL DEFAULT 0, - `version` varchar(20) NOT NULL DEFAULT '', - PRIMARY KEY (`id`), - KEY `idx_age` (`age`)); - ``` - - 预期输出 - - ```sql - Query OK, 0 rows affected (0.11 sec) - ``` - -+ 插入数据 - - {{< copyable "sql" >}} - - ```sql - insert into `tab_tidb` values (1,'TiDB',5,'TiDB-v5.0.0'); - ``` - - 预期输出 - - ```sql - Query OK, 1 row affected (0.03 sec) - ``` - -+ 查看 `tab_tidb` 结果 - - {{< copyable "sql" >}} - - ```sql - select * from tab_tidb; - ``` - - 预期输出 - - ```sql - +----+------+-----+-------------+ - | id | name | age | version | - +----+------+-----+-------------+ - | 1 | TiDB | 5 | TiDB-v5.0.0 | - +----+------+-----+-------------+ - 1 row in set (0.00 sec) - ``` - -+ 查看 TiKV store 状态、`store_id`、存储情况以及启动时间 - - {{< copyable "sql" >}} - - ```sql - select STORE_ID,ADDRESS,STORE_STATE,STORE_STATE_NAME,CAPACITY,AVAILABLE,UPTIME from INFORMATION_SCHEMA.TIKV_STORE_STATUS; - ``` - - 预期输出 - - ```sql - +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ - | STORE_ID | ADDRESS | STORE_STATE | STORE_STATE_NAME | CAPACITY | AVAILABLE | UPTIME | - +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ - | 1 | 10.0.1.1:20160 | 0 | Up | 49.98GiB | 46.3GiB | 5h21m52.474864026s | - | 4 | 10.0.1.2:20160 | 0 | Up | 49.98GiB | 46.32GiB | 5h21m52.522669177s | - | 5 | 10.0.1.3:20160 | 0 | Up | 49.98GiB | 45.44GiB | 5h21m52.713660541s | - +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ - 3 rows in set (0.00 sec) - ``` - -+ 退出 - - {{< copyable "sql" >}} - - ```sql - exit - ``` - - 预期输出 - - ```sql - Bye - ``` +--- +title: 验证集群运行状态 +summary: 介绍如何验证集群运行状态。 +--- + +# 验证集群运行状态 + +在部署完一套 TiDB 集群后,需要检查集群是否正常运行。本文介绍如何通过 TiUP 命令、[TiDB Dashboard](/dashboard/dashboard-intro.md) 和 Grafana 检查集群状态,以及如何登录 TiDB 数据库执行简单的 SQL 操作。 + +## 通过 TiUP 检查集群状态 + +检查集群状态的命令是 `tiup cluster display `,例如: + +{{< copyable "shell-regular" >}} + +```shell +tiup cluster display tidb-test +``` + +预期结果输出:各节点 Status 状态信息为 `Up` 说明集群状态正常。 + +## 通过 TiDB Dashboard 和 Grafana 检查集群状态 + +本节介绍如何通过 [TiDB Dashboard](/dashboard/dashboard-intro.md) 和 Grafana 检查集群状态。 + +### 查看 TiDB Dashboard 检查 TiDB 集群状态 + +1. 通过 `{pd-ip}:{pd-port}/dashboard` 登录 TiDB Dashboard,登录用户和口令为 TiDB 数据库 `root` 用户和口令。如果你修改过数据库的 `root` 密码,则以修改后的密码为准,默认密码为空。 + + ![TiDB-Dashboard](/media/tiup/tidb-dashboard.png) + +2. 主页面显示 TiDB 集群中节点信息 + + ![TiDB-Dashboard-status](/media/tiup/tidb-dashboard-status.png) + +### 查看 Grafana 监控 Overview 页面检查 TiDB 集群状态 + +- 通过 `{Grafana-ip}:3000` 登录 Grafana 监控,默认用户名及密码为 `admin`/`admin`。 + +- 点击 **Overview** 监控页面检查 TiDB 端口和负载监控信息。 + + ![Grafana-overview](/media/tiup/grafana-overview.png) + +## 登录数据库执行简单 DML/DDL 操作和查询 SQL 语句 + +> **注意:** +> +> 登录数据库前,你需要安装 MySQL 客户端。 + +执行以下命令登录数据库: + +{{< copyable "shell-regular" >}} + +```shell +mysql -u root -h ${tidb_server_host_IP_address} -P 4000 +``` + +其中, `${tidb_server_host_IP_address}` 是在[初始化集群拓扑文件](/production-deployment-using-tiup.md#第-3-步初始化集群拓扑文件)时为 `tidb_servers` 配置的 IP 地址之一,例如 `10.0.1.7`。 + +输出下列信息表示登录成功: + +```sql +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 3 +Server version: 5.7.25-TiDB-v5.0.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 5.7 compatible + +Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + +Oracle is a registered trademark of Oracle Corporation and/or its +affiliates. Other names may be trademarks of their respective +owners. + +Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. +``` + +### 数据库操作 + ++ 检查 TiDB 版本 + + {{< copyable "sql" >}} + + ```sql + select tidb_version()\G + ``` + + 预期结果输出: + + ```sql + *************************** 1. row *************************** + tidb_version(): Release Version: v5.0.0 + Edition: Community + Git Commit Hash: 689a6b6439ae7835947fcaccf329a3fc303986cb + Git Branch: HEAD + UTC Build Time: 2020-05-28 11:09:45 + GoVersion: go1.13.4 + Race Enabled: false + TiKV Min Version: v3.0.0-60965b006877ca7234adaced7890d7b029ed1306 + Check Table Before Drop: false + 1 row in set (0.00 sec) + ``` + ++ 创建 PingCAP database + + {{< copyable "sql" >}} + + ```sql + create database pingcap; + ``` + + ```sql + Query OK, 0 rows affected (0.10 sec) + ``` + + {{< copyable "sql" >}} + + ```sql + use pingcap; + ``` + + 预期输出 + + ```sql + Database changed + ``` + ++ 创建 `tab_tidb` 表 + + {{< copyable "sql" >}} + + ```sql + CREATE TABLE `tab_tidb` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(20) NOT NULL DEFAULT '', + `age` int(11) NOT NULL DEFAULT 0, + `version` varchar(20) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `idx_age` (`age`)); + ``` + + 预期输出 + + ```sql + Query OK, 0 rows affected (0.11 sec) + ``` + ++ 插入数据 + + {{< copyable "sql" >}} + + ```sql + insert into `tab_tidb` values (1,'TiDB',5,'TiDB-v5.0.0'); + ``` + + 预期输出 + + ```sql + Query OK, 1 row affected (0.03 sec) + ``` + ++ 查看 `tab_tidb` 结果 + + {{< copyable "sql" >}} + + ```sql + select * from tab_tidb; + ``` + + 预期输出 + + ```sql + +----+------+-----+-------------+ + | id | name | age | version | + +----+------+-----+-------------+ + | 1 | TiDB | 5 | TiDB-v5.0.0 | + +----+------+-----+-------------+ + 1 row in set (0.00 sec) + ``` + ++ 查看 TiKV store 状态、`store_id`、存储情况以及启动时间 + + {{< copyable "sql" >}} + + ```sql + select STORE_ID,ADDRESS,STORE_STATE,STORE_STATE_NAME,CAPACITY,AVAILABLE,UPTIME from INFORMATION_SCHEMA.TIKV_STORE_STATUS; + ``` + + 预期输出 + + ```sql + +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ + | STORE_ID | ADDRESS | STORE_STATE | STORE_STATE_NAME | CAPACITY | AVAILABLE | UPTIME | + +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ + | 1 | 10.0.1.1:20160 | 0 | Up | 49.98GiB | 46.3GiB | 5h21m52.474864026s | + | 4 | 10.0.1.2:20160 | 0 | Up | 49.98GiB | 46.32GiB | 5h21m52.522669177s | + | 5 | 10.0.1.3:20160 | 0 | Up | 49.98GiB | 45.44GiB | 5h21m52.713660541s | + +----------+--------------------+-------------+------------------+----------+-----------+--------------------+ + 3 rows in set (0.00 sec) + ``` + ++ 退出 + + {{< copyable "sql" >}} + + ```sql + exit + ``` + + 预期输出 + + ```sql + Bye + ``` diff --git a/quick-start-with-tidb.md b/quick-start-with-tidb.md index 414d3edfec38..17acb0f4f79f 100644 --- a/quick-start-with-tidb.md +++ b/quick-start-with-tidb.md @@ -245,13 +245,25 @@ TiDB 是一个分布式系统。最基础的 TiDB 测试集群通常由 2 个 Ti > > 下表中拓扑实例的 IP 为示例 IP。在实际部署时,请替换为实际的 IP。 -| 实例 | 个数 | IP | 配置 | -|:-- | :-- | :-- | :-- | -| TiKV | 3 | 10.0.1.1
10.0.1.1
10.0.1.1 | 避免端口和目录冲突 | -| TiDB | 1 | 10.0.1.1 | 默认端口
全局目录配置 | -| PD | 1 | 10.0.1.1 | 默认端口
全局目录配置 | -| TiFlash | 1 | 10.0.1.1 | 默认端口
全局目录配置 | -| Monitor | 1 | 10.0.1.1 | 默认端口
全局目录配置 | ++-----------------+-----------------+-----------------+--------------------+ +| 实例 | 个数 | IP | 配置 | ++:================+:================+:================+:===================+ +| TiKV | 3 | 10.0.1.1 | 避免端口和目录冲突 | +| | | 10.0.1.1 | | +| | | 10.0.1.1 | | ++-----------------+-----------------+-----------------+--------------------+ +| TiDB | 1 | 10.0.1.1 | 默认端口 | +| | | | 全局目录配置 | ++-----------------+-----------------+-----------------+--------------------+ +| PD | 1 | 10.0.1.1 | 默认端口 | +| | | | 全局目录配置 | ++-----------------+-----------------+-----------------+--------------------+ +| TiFlash | 1 | 10.0.1.1 | 默认端口 | +| | | | 全局目录配置 | ++-----------------+-----------------+-----------------+--------------------+ +| Monitor | 1 | 10.0.1.1 | 默认端口 | +| | | | 全局目录配置 | ++-----------------+-----------------+-----------------+--------------------+ 部署主机软件和环境要求: diff --git a/sql-mode.md b/sql-mode.md index 0d2ee5b4fdea..f290ee9812a8 100644 --- a/sql-mode.md +++ b/sql-mode.md @@ -18,36 +18,69 @@ Modes 是用逗号 (',') 间隔开的一系列不同的模式。使用 `SELECT @ ## SQL mode 列表,如下 -| 名称 | 含义 | -| --- | --- | -| PIPES_AS_CONCAT | 将 "\|\|" 视为字符串连接操作符(+)(同CONCAT()),而不视为OR(支持) | -| ANSI_QUOTES | 将 `"` 视为识别符,如果启用 ANSI_QUOTES,只单引号内的会被认为是 String Literals,双引号被解释为识别符,因此不能用双引号来引用字符串(支持)| -| IGNORE_SPACE | 若开启该模式,系统忽略空格。例如:“user” 和 “user “ 是相同的(支持)| -| ONLY_FULL_GROUP_BY | 如果 GROUP BY 出现的列并没有在 SELECT,HAVING,ORDER BY 中出现,此 SQL 不合法,因为不在 GROUP BY 中的列被查询展示出来不符合正常现象(支持) | -| NO_UNSIGNED_SUBTRACTION | 在减运算中,如果某个操作数没有符号,不要将结果标记为UNSIGNED(支持)| -| NO_DIR_IN_CREATE | 创建表时,忽视所有 INDEX DIRECTORY 和 DATA DIRECTORY 指令,该选项仅对从复制服务器有用 (仅语法支持)| -| NO_KEY_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持)| -| NO_FIELD_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持)| -| NO_TABLE_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持)| -| NO_AUTO_VALUE_ON_ZERO | 若启用该模式,在AUTO_INCREMENT列的处理传入的值是 0 或者具体数值时系统直接将该值写入此列,传入 NULL 时系统自动生成下一个序列号(支持)| -| NO_BACKSLASH_ESCAPES | 若启用该模式,`\` 反斜杠符号仅代表它自己(支持)| -| STRICT_TRANS_TABLES | 对于事务存储引擎启用严格模式,insert非法值之后,回滚整条语句(支持)| -| STRICT_ALL_TABLES | 对于事务型表,写入非法值之后,回滚整个事务语句(支持)| -| NO_ZERO_IN_DATE | 在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入'0000-00-00'。在非严格模式,可以接受该日期,但会生成警告(支持) -| NO_ZERO_DATE | 在严格模式,不要将 '0000-00-00'做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告(支持)| -| ALLOW_INVALID_DATES | 不检查全部日期的合法性,仅检查月份值在 1 到 12 及 日期值在 1 到 31 之间,仅适用于 DATE 和 DATATIME 列,TIMESTAMP 列需要全部检查其合法性(支持)| -| ERROR_FOR_DIVISION_BY_ZERO | 若启用该模式,在 INSERT 或 UPDATE 过程中,被除数为 0 值时,系统产生错误
若未启用该模式,被除数为 0 值时,系统产生警告,并用 NULL 代替(支持) | -| NO_AUTO_CREATE_USER | 防止 GRANT 自动创建新用户,但指定密码除外(支持)| -| HIGH_NOT_PRECEDENCE | NOT 操作符的优先级是表达式。例如: NOT a BETWEEN b AND c 被解释为 NOT (a BETWEEN b AND c)。在部份旧版本MySQL中, 表达式被解释为(NOT a) BETWEEN b AND c (支持) | -| NO_ENGINE_SUBSTITUTION | 如果需要的存储引擎被禁用或未编译,可以防止自动替换存储引擎(仅语法支持)| -| PAD_CHAR_TO_FULL_LENGTH | 若启用该模式,系统对于 CHAR 类型不会截断尾部空格(仅语法支持。该模式[在 MySQL 8.0 中已废弃](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_pad_char_to_full_length)。)| -| REAL_AS_FLOAT | 将 REAL 视为 FLOAT 的同义词,而不是 DOUBLE 的同义词(支持)| -| POSTGRESQL | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS(仅语法支持)| -| MSSQL | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、 NO_FIELD_OPTIONS(仅语法支持)| -| DB2 | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS(仅语法支持)| -| MAXDB | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER(支持)| -| MySQL323 | 等同于 NO_FIELD_OPTIONS、HIGH_NOT_PRECEDENCE(仅语法支持)| -| MYSQL40 | 等同于 NO_FIELD_OPTIONS、HIGH_NOT_PRECEDENCE(仅语法支持)| -| ANSI | 等同于 REAL_AS_FLOAT、PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE(仅语法支持)| -| TRADITIONAL | 等同于 STRICT_TRANS_TABLES、STRICT_ALL_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、ERROR_FOR_DIVISION_BY_ZERO、NO_AUTO_CREATE_USER(仅语法支持)| -| ORACLE | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER(仅语法支持)| ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| 名称 | 含义 | ++===================================+========================================================================================================================================================================================+ +| PIPES_AS_CONCAT | 将 "||" 视为字符串连接操作符(+)(同CONCAT()),而不视为OR(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ANSI_QUOTES | 将 `"` 视为识别符,如果启用 ANSI_QUOTES,只单引号内的会被认为是 String Literals,双引号被解释为识别符,因此不能用双引号来引用字符串(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| IGNORE_SPACE | 若开启该模式,系统忽略空格。例如:“user” 和 “user “ 是相同的(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ONLY_FULL_GROUP_BY | 如果 GROUP BY 出现的列并没有在 SELECT,HAVING,ORDER BY 中出现,此 SQL 不合法,因为不在 GROUP BY 中的列被查询展示出来不符合正常现象(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_UNSIGNED_SUBTRACTION | 在减运算中,如果某个操作数没有符号,不要将结果标记为UNSIGNED(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_DIR_IN_CREATE | 创建表时,忽视所有 INDEX DIRECTORY 和 DATA DIRECTORY 指令,该选项仅对从复制服务器有用 (仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_KEY_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_FIELD_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_TABLE_OPTIONS | 使用 SHOW CREATE TABLE 时不会输出 MySQL 特有的语法部分,如 ENGINE ,使用 mysqldump 跨DB种类迁移的时需要考虑此选项(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_AUTO_VALUE_ON_ZERO | 若启用该模式,在AUTO_INCREMENT列的处理传入的值是 0 或者具体数值时系统直接将该值写入此列,传入 NULL 时系统自动生成下一个序列号(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_BACKSLASH_ESCAPES | 若启用该模式,`\` 反斜杠符号仅代表它自己(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| STRICT_TRANS_TABLES | 对于事务存储引擎启用严格模式,insert非法值之后,回滚整条语句(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| STRICT_ALL_TABLES | 对于事务型表,写入非法值之后,回滚整个事务语句(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_ZERO_IN_DATE | 在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入'0000-00-00'。在非严格模式,可以接受该日期,但会生成警告(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_ZERO_DATE | 在严格模式,不要将 '0000-00-00'做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ALLOW_INVALID_DATES | 不检查全部日期的合法性,仅检查月份值在 1 到 12 及 日期值在 1 到 31 之间,仅适用于 DATE 和 DATATIME 列,TIMESTAMP 列需要全部检查其合法性(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ERROR_FOR_DIVISION_BY_ZERO | 若启用该模式,在 INSERT 或 UPDATE 过程中,被除数为 0 值时,系统产生错误 | +| | 若未启用该模式,被除数为 0 值时,系统产生警告,并用 NULL 代替(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_AUTO_CREATE_USER | 防止 GRANT 自动创建新用户,但指定密码除外(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| HIGH_NOT_PRECEDENCE | NOT 操作符的优先级是表达式。例如: NOT a BETWEEN b AND c 被解释为 NOT (a BETWEEN b AND c)。在部份旧版本MySQL中, 表达式被解释为(NOT a) BETWEEN b AND c (支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| NO_ENGINE_SUBSTITUTION | 如果需要的存储引擎被禁用或未编译,可以防止自动替换存储引擎(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| PAD_CHAR_TO_FULL_LENGTH | 若启用该模式,系统对于 CHAR 类型不会截断尾部空格(仅语法支持。该模式[在 MySQL 8.0 中已废弃](https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_pad_char_to_full_length)。) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| REAL_AS_FLOAT | 将 REAL 视为 FLOAT 的同义词,而不是 DOUBLE 的同义词(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| POSTGRESQL | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| MSSQL | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、 NO_FIELD_OPTIONS(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| DB2 | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| MAXDB | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER(支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| MySQL323 | 等同于 NO_FIELD_OPTIONS、HIGH_NOT_PRECEDENCE(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| MYSQL40 | 等同于 NO_FIELD_OPTIONS、HIGH_NOT_PRECEDENCE(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ANSI | 等同于 REAL_AS_FLOAT、PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| TRADITIONAL | 等同于 STRICT_TRANS_TABLES、STRICT_ALL_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、ERROR_FOR_DIVISION_BY_ZERO、NO_AUTO_CREATE_USER(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ORACLE | 等同于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER(仅语法支持) | ++-----------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/sql-statements/sql-statement-admin.md b/sql-statements/sql-statement-admin.md index 04ba1ac6aada..ec06129eebb5 100644 --- a/sql-statements/sql-statement-admin.md +++ b/sql-statements/sql-statement-admin.md @@ -8,12 +8,17 @@ title: ADMIN ## ADMIN 与 DDL 相关的扩展语句 -| 语句 | 功能描述 | -|------------------------------------------------------------------------------------------|-----------------------------| -| [`ADMIN CANCEL DDL JOBS`](/sql-statements/sql-statement-admin-cancel-ddl.md) | 取消当前正在运行的 DDL 作业 | -| [`ADMIN CHECKSUM TABLE`](/sql-statements/sql-statement-admin-checksum-table.md) | 计算表中所有行和索引的 CRC64 校验和 | -| [ADMIN CHECK [TABLE\|INDEX]](/sql-statements/sql-statement-admin-check-table-index.md) | 校验表中数据和对应索引的一致性 | -| [ADMIN SHOW DDL [JOBS\|QUERIES]](/sql-statements/sql-statement-admin-show-ddl.md) | 显示有关当前正在运行或最近完成的 DDL 作业的详细信息| ++-----------------------------------------------------------------------------------------+-----------------------------------------------------+ +| 语句 | 功能描述 | ++=========================================================================================+=====================================================+ +| [`ADMIN CANCEL DDL JOBS`](/sql-statements/sql-statement-admin-cancel-ddl.md) | 取消当前正在运行的 DDL 作业 | ++-----------------------------------------------------------------------------------------+-----------------------------------------------------+ +| [`ADMIN CHECKSUM TABLE`](/sql-statements/sql-statement-admin-checksum-table.md) | 计算表中所有行和索引的 CRC64 校验和 | ++-----------------------------------------------------------------------------------------+-----------------------------------------------------+ +| [`ADMIN CHECK [TABLE|INDEX]`](/sql-statements/sql-statement-admin-check-table-index.md) | 校验表中数据和对应索引的一致性 | ++-----------------------------------------------------------------------------------------+-----------------------------------------------------+ +| [`ADMIN SHOW DDL [JOBS|QUERIES]`](/sql-statements/sql-statement-admin-show-ddl.md) | 显示有关当前正在运行或最近完成的 DDL 作业的详细信息 | ++-----------------------------------------------------------------------------------------+-----------------------------------------------------+ ## `admin reload` 语句 diff --git a/ticdc-deployment-topology.md b/ticdc-deployment-topology.md index dfb573f55c24..0adc5d03331d 100644 --- a/ticdc-deployment-topology.md +++ b/ticdc-deployment-topology.md @@ -13,13 +13,28 @@ summary: 介绍 TiCDC 部署 TiDB 集群的拓扑结构。 ## 拓扑信息 -|实例 | 个数 | 物理机配置 | IP |配置 | -| :-- | :-- | :-- | :-- | :-- | -| TiDB |3 | 16 VCore 32GB * 1 | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口
全局目录配置 | -| PD | 3 | 4 VCore 8GB * 1 |10.0.1.4
10.0.1.5
10.0.1.6 | 默认端口
全局目录配置 | -| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) * 1 | 10.0.1.7
10.0.1.8
10.0.1.9 | 默认端口
全局目录配置 | -| CDC | 3 | 8 VCore 16GB * 1 | 10.0.1.11
10.0.1.12
10.0.1.13 | 默认端口
全局目录配置 | -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.11 | 默认端口
全局目录配置 | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:=====================+:============+:==================================+:============+:=============+ +| TiDB | 3 | 16 VCore 32GB \* 1 | 10.0.1.1 | 默认端口 | +| | | | 10.0.1.2 | 全局目录配置 | +| | | | 10.0.1.3 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| PD | 3 | 4 VCore 8GB \* 1 | 10.0.1.4 | 默认端口 | +| | | | 10.0.1.5 | 全局目录配置 | +| | | | 10.0.1.6 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) \* 1 | 10.0.1.7 | 默认端口 | +| | | | 10.0.1.8 | 全局目录配置 | +| | | | 10.0.1.9 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| CDC | 3 | 8 VCore 16GB \* 1 | 10.0.1.11 | 默认端口 | +| | | | 10.0.1.12 | 全局目录配置 | +| | | | 10.0.1.13 | | ++----------------------+-------------+-----------------------------------+-------------+--------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.11 | 默认端口 | +| | | | | 全局目录配置 | ++----------------------+-------------+-----------------------------------+-------------+--------------+ ### 拓扑模版 diff --git a/ticdc/ticdc-overview.md b/ticdc/ticdc-overview.md index 8d8a6604195f..feb508295105 100644 --- a/ticdc/ticdc-overview.md +++ b/ticdc/ticdc-overview.md @@ -127,8 +127,16 @@ TiCDC Open Protocol 是一种行级别的数据变更通知协议,为监控、 `sort-dir` 配置项用于给 TiCDC 内部的排序器指定临时文件目录,其作用在各版本有过如下兼容性更改: -| 版本 | `sort-engine` 的使用 | 说明 | 使用建议 | -| :--- | :--- | :-- | :-- | -| v4.0.11 及之前的 v4.0 版本,v5.0.0-rc | 作为 changefeed 配置项,给 `file` sorter 和 `unified` Sorter 指定临时文件目录 | 在这些版本中,`file` sorter 和 `unified` sorter **均不是**正式功能 (GA),不推荐在生产环境中使用。

如果有多个 changefeed 被配置使用了 `unified` 作为 `sort-engine`,那么实际使用的临时文件目录可能是任何一个 changefeed 的 `sort-dir` 配置,且每个 TiCDC 节点上使用的目录可能不一致。 | 不推荐在生产环境中使用 Unified Sorter | -| v4.0.12,v4.0.13,v5.0.0 及 v5.0.1 | 作为 changefeed 配置项或 `cdc server` 配置项 | 在默认情况下 changefeed 的 `sort-dir` 配置不会生效,而 `cdc server` 的 `sort-dir` 配置默认为 `/tmp/cdc_sort`。建议生产环境下仅配置 `cdc server` 的相关配置。

如果你使用 TiUP 部署 TiCDC,建议升级到最新的 TiUP 版本并在 TiCDC server 配置中设置 `sorter.sort-dir` 一项。

在 v4.0.13、v5.0.0 和 v5.0.1 中 unified sorter 是默认开启的,如果要将集群升级至这些版本,请确保 TiCDC server 配置中的 `sorter.sort-dir` 已经被正确配置。| 需要通过 `cdc server` 命令行参数(或 TiUP)配置 `sort-dir` | -| v4.0.14 及之后的 v4.0 版本,v5.0.3 及之后的 v5.0 版本,更新的版本 | `sort-dir` 被弃用,建议配置 `data-dir` | `data-dir` 可以通过最新版本的 TiUP 进行配置。这些版本中 unified sorter 是默认开启的,升级时请确保 `data-dir` 已经被正确配置,否则将默认使用 `/tmp/cdc_data`。

如果该目录所在设备空间不足,有可能出现硬盘空间不足的问题。之前配置的 changefeed 的 `sort-dir` 配置将会失效。| 需要通过 `cdc server` 命令行参数(或 TiUP)配置 `data-dir` | ++-------------------------------------------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+ +| 版本 | `sort-engine` 的使用 | 说明 | 使用建议 | ++:==================================================================+:==============================================================================+:===================================================================================================================================================================================+:===========================================================+ +| v4.0.11 及之前的 v4.0 版本,v5.0.0-rc | 作为 changefeed 配置项,给 `file` sorter 和 `unified` Sorter 指定临时文件目录 | 在这些版本中,`file` sorter 和 `unified` sorter **均不是**正式功能 (GA),不推荐在生产环境中使用。 | 不推荐在生产环境中使用 Unified Sorter | +| | | 如果有多个 changefeed 被配置使用了 `unified` 作为 `sort-engine`,那么实际使用的临时文件目录可能是任何一个 changefeed 的 `sort-dir` 配置,且每个 TiCDC 节点上使用的目录可能不一致。 | | ++-------------------------------------------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+ +| v4.0.12,v4.0.13,v5.0.0 及 v5.0.1 | 作为 changefeed 配置项或 `cdc server` 配置项 | 在默认情况下 changefeed 的 `sort-dir` 配置不会生效,而 `cdc server` 的 `sort-dir` 配置默认为 `/tmp/cdc_sort`。建议生产环境下仅配置 `cdc server` 的相关配置。 | 需要通过 `cdc server` 命令行参数(或 TiUP)配置 `sort-dir` | +| | | 如果你使用 TiUP 部署 TiCDC,建议升级到最新的 TiUP 版本并在 TiCDC server 配置中设置 `sorter.sort-dir` 一项。 | | +| | | 在 v4.0.13、v5.0.0 和 v5.0.1 中 unified sorter 是默认开启的,如果要将集群升级至这些版本,请确保 TiCDC server 配置中的 `sorter.sort-dir` 已经被正确配置。 | | ++-------------------------------------------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+ +| v4.0.14 及之后的 v4.0 版本,v5.0.3 及之后的 v5.0 版本,更新的版本 | `sort-dir` 被弃用,建议配置 `data-dir` | `data-dir` 可以通过最新版本的 TiUP 进行配置。这些版本中 unified sorter 是默认开启的,升级时请确保 `data-dir` 已经被正确配置,否则将默认使用 `/tmp/cdc_data`。 | 需要通过 `cdc server` 命令行参数(或 TiUP)配置 `data-dir` | +| | | 如果该目录所在设备空间不足,有可能出现硬盘空间不足的问题。之前配置的 changefeed 的 `sort-dir` 配置将会失效。 | | ++-------------------------------------------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+ diff --git a/tidb-binlog-deployment-topology.md b/tidb-binlog-deployment-topology.md index 14e7faac147d..8b588b2d9c80 100644 --- a/tidb-binlog-deployment-topology.md +++ b/tidb-binlog-deployment-topology.md @@ -9,13 +9,29 @@ summary: 介绍如何在部署最小拓扑集群的基础上,同时部署 TiDB ## 拓扑信息 -| 实例 |个数| 物理机配置 | IP | 配置 | -| :-- | :-- | :-- | :-- | :-- | -|TiDB | 3 | 16 VCore 32 GB | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口配置;
开启 enable_binlog;
开启 ignore-error | -| PD | 3 | 4 VCore 8 GB | 10.0.1.4
10.0.1.5
10.0.1.6 | 默认端口配置 | -| TiKV | 3 | 16 VCore 32 GB | 10.0.1.7
10.0.1.8
10.0.1.9 | 默认端口配置 | -| Pump| 3 |8 VCore 16GB |10.0.1.1
10.0.1.7
10.0.1.8 | 默认端口配置;
设置 GC 时间 7 天 | -| Drainer | 1 | 8 VCore 16GB | 10.0.1.12 | 默认端口配置;
设置默认初始化 commitTS -1 为最近的时间戳
配置下游目标 TiDB 10.0.1.12:4000 | ++-------------+-------------+----------------+-------------+-------------------------------------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:============+:============+:===============+:============+:==========================================+ +| TiDB | 3 | 16 VCore 32 GB | 10.0.1.1 | 默认端口配置; | +| | | | 10.0.1.2 | 开启 enable_binlog; | +| | | | 10.0.1.3 | 开启 ignore-error | ++-------------+-------------+----------------+-------------+-------------------------------------------+ +| PD | 3 | 4 VCore 8 GB | 10.0.1.4 | 默认端口配置 | +| | | | 10.0.1.5 | | +| | | | 10.0.1.6 | | ++-------------+-------------+----------------+-------------+-------------------------------------------+ +| TiKV | 3 | 16 VCore 32 GB | 10.0.1.7 | 默认端口配置 | +| | | | 10.0.1.8 | | +| | | | 10.0.1.9 | | ++-------------+-------------+----------------+-------------+-------------------------------------------+ +| Pump | 3 | 8 VCore 16GB | 10.0.1.1 | 默认端口配置; | +| | | | 10.0.1.7 | 设置 GC 时间 7 天 | +| | | | 10.0.1.8 | | ++-------------+-------------+----------------+-------------+-------------------------------------------+ +| Drainer | 1 | 8 VCore 16GB | 10.0.1.12 | 默认端口配置; | +| | | | | 设置默认初始化 commitTS -1 为最近的时间戳 | +| | | | | 配置下游目标 TiDB 10.0.1.12:4000 | ++-------------+-------------+----------------+-------------+-------------------------------------------+ ### 拓扑模版 diff --git a/tidb-lightning/tidb-lightning-backends.md b/tidb-lightning/tidb-lightning-backends.md index 459b293a6aeb..df4d3c1aee2d 100644 --- a/tidb-lightning/tidb-lightning-backends.md +++ b/tidb-lightning/tidb-lightning-backends.md @@ -256,140 +256,64 @@ on-duplicate = "replace" # 或者 “error”、“ignore” 当需要将数据导入到 TiDB 集群时,TiDB Lightning TiDB-backend 可以完全取代 [Loader](https://docs.pingcap.com/zh/tidb/v4.0/loader-overview)。下表说明了如何将 Loader 的配置迁移到 [TiDB Lightning 配置](/tidb-lightning/tidb-lightning-configuration.md)中: - - - - - - - - - - - -
LoaderTiDB Lightning
- -```toml -# 日志级别 -log-level = "info" -# 日志的输出目录 -log-file = "loader.log" -# Prometheus -status-addr = ":8272" -# 线程数 -pool-size = 16 -``` - - - -```toml -[lightning] -# 日志级别 -level = "info" -# 日志的输出目录。如果未指定该位置目录,默认为执行命令的所在目录。 -file = "tidb-lightning.log" -# Prometheus -pprof-port = 8289 -# 并发度 (最好使用默认设置) -#region-concurrency = 16 -``` - -
- -```toml -# 断点数据库名 -checkpoint-schema = "tidb_loader" -``` - - - -```toml -[checkpoint] -# 断点存储 -enable = true -schema = "tidb_lightning_checkpoint" -# 断点默认存储在本地的文件系统,这样更高效。但你也可以 -# 选择将断点存储在目标数据库中,设置如下: -# driver = "mysql" -``` - -
- -```toml -``` - - - -```toml -[tikv-importer] -# 使用 TiDB-backend -backend = "tidb" -``` - -
- -```toml -# 数据源目录 -dir = "/data/export/" -``` - - - -```toml -[mydumper] -# 数据源目录 -data-source-dir = "/data/export" -``` - -
- -```toml -[db] -# TiDB 连接参数 -host = "127.0.0.1" -port = 4000 -user = "root" -password = "" -#sql-mode = "" -``` - - - -```toml -[tidb] -# TiDB 连接参数 -host = "127.0.0.1" -port = 4000 -# 在 TiDB-backend 模式下,该参数为可选参数 -# status-port = 10080 -user = "root" -password = "" -#sql-mode = "" -``` - -
- -```toml -# [[route-rules]] -# Table routes -# schema-pattern = "shard_db_*" -# table-pattern = "shard_table_*" -# target-schema = "shard_db" -# target-table = "shard_table" -``` - - - -```toml -# [[routes]] -# Table routes -# schema-pattern = "shard_db_*" -# table-pattern = "shard_table_*" -# target-schema = "shard_db" -# target-table = "shard_table" -``` - -
++-----------------------------------+--------------------------------------------------------------------+ +| Loader | TiDB Lightning | ++===================================+====================================================================+ +| ``` toml | ``` toml | +| # 日志级别 | [lightning] | +| log-level = "info" | # 日志级别 | +| # 日志的输出目录 | level = "info" | +| log-file = "loader.log" | # 日志的输出目录。如果未指定该位置目录,默认为执行命令的所在目录。 | +| # Prometheus | file = "tidb-lightning.log" | +| status-addr = ":8272" | # Prometheus | +| # 线程数 | pprof-port = 8289 | +| pool-size = 16 | # 并发度 (最好使用默认设置) | +| ``` | #region-concurrency = 16 | +| | ``` | ++-----------------------------------+--------------------------------------------------------------------+ +| ``` toml | ``` toml | +| # 断点数据库名 | [checkpoint] | +| checkpoint-schema = "tidb_loader" | # 断点存储 | +| ``` | enable = true | +| | schema = "tidb_lightning_checkpoint" | +| | # 断点默认存储在本地的文件系统,这样更高效。但你也可以 | +| | # 选择将断点存储在目标数据库中,设置如下: | +| | # driver = "mysql" | +| | ``` | ++-----------------------------------+--------------------------------------------------------------------+ +| ``` toml | ``` toml | +| ``` | [tikv-importer] | +| | # 使用 TiDB-backend | +| | backend = "tidb" | +| | ``` | ++-----------------------------------+--------------------------------------------------------------------+ +| ``` toml | ``` toml | +| # 数据源目录 | [mydumper] | +| dir = "/data/export/" | # 数据源目录 | +| ``` | data-source-dir = "/data/export" | +| | ``` | ++-----------------------------------+--------------------------------------------------------------------+ +| ``` toml | ``` toml | +| [db] | [tidb] | +| # TiDB 连接参数 | # TiDB 连接参数 | +| host = "127.0.0.1" | host = "127.0.0.1" | +| port = 4000 | port = 4000 | +| user = "root" | # 在 TiDB-backend 模式下,该参数为可选参数 | +| password = "" | # status-port = 10080 | +| #sql-mode = "" | user = "root" | +| ``` | password = "" | +| | #sql-mode = "" | +| | ``` | ++-----------------------------------+--------------------------------------------------------------------+ +| ``` toml | ``` toml | +| # [[route-rules]] | # [[routes]] | +| # Table routes | # Table routes | +| # schema-pattern = "shard_db_*" | # schema-pattern = "shard_db_*" | +| # table-pattern = "shard_table_*" | # table-pattern = "shard_table_*" | +| # target-schema = "shard_db" | # target-schema = "shard_db" | +| # target-table = "shard_table" | # target-table = "shard_table" | +| ``` | ``` | ++-----------------------------------+--------------------------------------------------------------------+ ## TiDB Lightning Importer-backend diff --git a/tidb-limitations.md b/tidb-limitations.md index 1d8832f8a646..0a6c974f36d5 100644 --- a/tidb-limitations.md +++ b/tidb-limitations.md @@ -1,69 +1,69 @@ ---- -title: TiDB 使用限制 ---- - -# 使用限制 - -本文会将详细描述 TiDB 中常见的使用限制,包括:标识符长度,最大支持的数据库、表、索引、分区表、序列等的个数。 - -## 标识符长度限制 - -| 标识符类型 | 最大长度(字符)| -|:---------|:--------------| -| Database | 64 | -| Table | 64 | -| Column | 64 | -| Index | 64 | -| View | 64 | -| Sequence | 64 | - -## Databases、Tables、Views、Connections 总个数限制 - -| 标识符类型 | 最大个数 | -|:----------|:----------| -| Databases | unlimited | -| Tables | unlimited | -| Views | unlimited | -| Connections| unlimited| - -## 单个 Database 的限制 - -| 类型 | 最大限制 | -|:----------|:----------| -| Tables |unlimited | - -## 单个 Table 的限制 - -| 类型 | 最大限制(默认值) | -|:----------|:------------------------------| -| Columns | 默认为 1017,最大可调至 4096 | -| Indexs | 默认为 64,最大可调至 512 | -| Rows | 无限制 | -| Size | 无限制 | -| Partitions| 8192 | - -* Columns 的最大限制可通过 [`table-column-count-limit`](/tidb-configuration-file.md#table-column-count-limit-从-v50-版本开始引入) 修改。 -* Indexs 的最大限制可通过 [`index-limit`](/tidb-configuration-file.md#index-limit-从-v50-版本开始引入) 修改。 - -## 单行的限制 - -| 类型 | 最大限制 | -|:----------|:----------| -| Size | 默认为 6MB,可通过 [`txn-entry-size-limit`](/tidb-configuration-file.md#txn-entry-size-limit-从-v50-版本开始引入) 配置项调整 | - -## 字符串类型限制 - -| 类型 | 最大限制 | -|:----------|:----------| -| CHAR | 256 字符 | -| BINARY | 256 字节 | -| VARBINARY | 65535 字节 | -| VARCHAR | 16383 字符 | -| TEXT | 6MB 字节 | -| BLOB | 6MB 字节 | - -## SQL Statements 的限制 - -| 类型 | 最大限制 | -|:----------|:----------| -| 单个事务最大语句数 | 在使用乐观事务并开启事务重试的情况下,默认限制 5000,可通过 [`stmt-count-limit`](/tidb-configuration-file.md#stmt-count-limit) 调整 | +--- +title: TiDB 使用限制 +--- + +# 使用限制 + +本文会将详细描述 TiDB 中常见的使用限制,包括:标识符长度,最大支持的数据库、表、索引、分区表、序列等的个数。 + +## 标识符长度限制 + +| 标识符类型 | 最大长度(字符)| +|:---------|:--------------| +| Database | 64 | +| Table | 64 | +| Column | 64 | +| Index | 64 | +| View | 64 | +| Sequence | 64 | + +## Databases、Tables、Views、Connections 总个数限制 + +| 标识符类型 | 最大个数 | +|:----------|:----------| +| Databases | unlimited | +| Tables | unlimited | +| Views | unlimited | +| Connections| unlimited| + +## 单个 Database 的限制 + +| 类型 | 最大限制 | +|:----------|:----------| +| Tables |unlimited | + +## 单个 Table 的限制 + +| 类型 | 最大限制(默认值) | +|:----------|:------------------------------| +| Columns | 默认为 1017,最大可调至 4096 | +| Indexs | 默认为 64,最大可调至 512 | +| Rows | 无限制 | +| Size | 无限制 | +| Partitions| 8192 | + +* Columns 的最大限制可通过 [`table-column-count-limit`](/tidb-configuration-file.md#table-column-count-limit-从-v50-版本开始引入) 修改。 +* Indexs 的最大限制可通过 [`index-limit`](/tidb-configuration-file.md#index-limit-从-v50-版本开始引入) 修改。 + +## 单行的限制 + +| 类型 | 最大限制 | +|:----------|:----------| +| Size | 默认为 6MB,可通过 [`txn-entry-size-limit`](/tidb-configuration-file.md#txn-entry-size-limit-从-v50-版本开始引入) 配置项调整 | + +## 字符串类型限制 + +| 类型 | 最大限制 | +|:----------|:----------| +| CHAR | 256 字符 | +| BINARY | 256 字节 | +| VARBINARY | 65535 字节 | +| VARCHAR | 16383 字符 | +| TEXT | 6MB 字节 | +| BLOB | 6MB 字节 | + +## SQL Statements 的限制 + +| 类型 | 最大限制 | +|:----------|:----------| +| 单个事务最大语句数 | 在使用乐观事务并开启事务重试的情况下,默认限制 5000,可通过 [`stmt-count-limit`](/tidb-configuration-file.md#stmt-count-limit) 调整 | diff --git a/tiflash-deployment-topology.md b/tiflash-deployment-topology.md index cbd469428178..8ba4c585e767 100644 --- a/tiflash-deployment-topology.md +++ b/tiflash-deployment-topology.md @@ -9,13 +9,27 @@ summary: 了解在部署最小拓扑集群的基础上,部署 TiFlash 的拓 ## 拓扑信息 -|实例 | 个数 | 物理机配置 | IP |配置 | -| :-- | :-- | :-- | :-- | :-- | -| TiDB |3 | 16 VCore 32GB * 1 | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口
全局目录配置 | -| PD | 3 | 4 VCore 8GB * 1 |10.0.1.4
10.0.1.5
10.0.1.6 | 默认端口
全局目录配置 | -| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) * 1 | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口
全局目录配置 | -| TiFlash | 1 | 32 VCore 64 GB 2TB (nvme ssd) * 1 | 10.0.1.10 | 默认端口
全局目录配置 | -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.10 | 默认端口
全局目录配置 | ++----------------------+-------------+------------------------------------+-------------+--------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:=====================+:============+:===================================+:============+:=============+ +| TiDB | 3 | 16 VCore 32GB \* 1 | 10.0.1.1 | 默认端口 | +| | | | 10.0.1.2 | 全局目录配置 | +| | | | 10.0.1.3 | | ++----------------------+-------------+------------------------------------+-------------+--------------+ +| PD | 3 | 4 VCore 8GB \* 1 | 10.0.1.4 | 默认端口 | +| | | | 10.0.1.5 | 全局目录配置 | +| | | | 10.0.1.6 | | ++----------------------+-------------+------------------------------------+-------------+--------------+ +| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) \* 1 | 10.0.1.1 | 默认端口 | +| | | | 10.0.1.2 | 全局目录配置 | +| | | | 10.0.1.3 | | ++----------------------+-------------+------------------------------------+-------------+--------------+ +| TiFlash | 1 | 32 VCore 64 GB 2TB (nvme ssd) \* 1 | 10.0.1.10 | 默认端口 | +| | | | | 全局目录配置 | ++----------------------+-------------+------------------------------------+-------------+--------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.10 | 默认端口 | +| | | | | 全局目录配置 | ++----------------------+-------------+------------------------------------+-------------+--------------+ ### 拓扑模版 diff --git a/tispark-deployment-topology.md b/tispark-deployment-topology.md index b0bffbade80f..83355fe8c3cb 100644 --- a/tispark-deployment-topology.md +++ b/tispark-deployment-topology.md @@ -15,13 +15,28 @@ summary: 介绍 TiUP 部署包含 TiSpark 组件的 TiDB 集群的拓扑结构 ## 拓扑信息 -|实例 | 个数 | 物理机配置 | IP |配置 | -| :-- | :-- | :-- | :-- | :-- | -| TiDB |3 | 16 VCore 32GB * 1 | 10.0.1.1
10.0.1.2
10.0.1.3 | 默认端口
全局目录配置 | -| PD | 3 | 4 VCore 8GB * 1 |10.0.1.4
10.0.1.5
10.0.1.6 | 默认端口
全局目录配置 | -| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) * 1 | 10.0.1.7
10.0.1.8
10.0.1.9 | 默认端口
全局目录配置 | -| TiSpark | 3 | 8 VCore 16GB * 1 | 10.0.1.21 (master)
10.0.1.22 (worker)
10.0.1.23 (worker) | 默认端口
全局目录配置 | -| Monitoring & Grafana | 1 | 4 VCore 8GB * 1 500GB (ssd) | 10.0.1.11 | 默认端口
全局目录配置 | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ +| 实例 | 个数 | 物理机配置 | IP | 配置 | ++:=====================+:============+:==================================+:===================+:=============+ +| TiDB | 3 | 16 VCore 32GB \* 1 | 10.0.1.1 | 默认端口 | +| | | | 10.0.1.2 | 全局目录配置 | +| | | | 10.0.1.3 | | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ +| PD | 3 | 4 VCore 8GB \* 1 | 10.0.1.4 | 默认端口 | +| | | | 10.0.1.5 | 全局目录配置 | +| | | | 10.0.1.6 | | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ +| TiKV | 3 | 16 VCore 32GB 2TB (nvme ssd) \* 1 | 10.0.1.7 | 默认端口 | +| | | | 10.0.1.8 | 全局目录配置 | +| | | | 10.0.1.9 | | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ +| TiSpark | 3 | 8 VCore 16GB \* 1 | 10.0.1.21 (master) | 默认端口 | +| | | | 10.0.1.22 (worker) | 全局目录配置 | +| | | | 10.0.1.23 (worker) | | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ +| Monitoring & Grafana | 1 | 4 VCore 8GB \* 1 500GB (ssd) | 10.0.1.11 | 默认端口 | +| | | | | 全局目录配置 | ++----------------------+-------------+-----------------------------------+--------------------+--------------+ ### 拓扑模版 diff --git a/troubleshoot-cpu-issues.md b/troubleshoot-cpu-issues.md index a7ba2e0159f5..03117c7915c5 100644 --- a/troubleshoot-cpu-issues.md +++ b/troubleshoot-cpu-issues.md @@ -1,139 +1,139 @@ ---- -title: 读写延迟增加 -summary: 介绍读写延时增加、抖动时的排查思路,可能的原因和解决方法。 ---- - -# 读写延迟增加 - -本文档介绍读写延迟增加、抖动时的排查思路,可能的原因和解决方法。 - -## 常见原因 - -### TiDB 执行计划不对导致延迟增高 - -查询语句的执行计划不稳定,偶尔执行计划选择错误的索引,导致查询延迟增加。 - -**现象:** - -* 如果慢日志中输出了执行计划,可以直接查看执行计划。用 `select tidb_decode_plan('xxx...')` 语句可以解析出具体的执行计划。 -* 监控中的 key 扫描异常升高;慢日志中 SQL 执行时间 `Scan Keys` 数目较大。 -* SQL 执行时间相比于其他数据库(例如 MySQL)有较大差距。可以对比其他数据库执行计划,例如 `Join Order` 是否不同。 - -**可能的原因:** - -* 统计信息不准确 - -**解决方案:** - -* 更新统计信息 - * 手动 `analyze table`,配合 crontab 定期 `analyze`,维持统计信息准确度。 - * 自动 `auto analyze`,调低 `analyze ratio` 阈值,提高收集频次,并设置运行时间窗口。示例如下: - * `set global tidb_auto_analyze_ratio=0.2;` - * `set global tidb_auto_analyze_start_time='00:00 +0800';` - * `set global tidb_auto_analyze_end_time='06:00 +0800';` -* 绑定执行计划 - * 修改业务 SQL,使用 `use index` 固定使用列上的索引。 - * 3.0 版本下,业务可以不用修改 SQL,使用 `create global binding` 创建 `force index` 的绑定 SQL。 - * 4.0 版本支持 SQL Plan Management,可以避免因执行计划不稳定导致的性能下降。 - -### PD 异常 - -**现象:** - -监控中 PD TSO 的 **wait duration** 异常升高。**wait duration** 代表从开始等待 PD 返回,到等待结束的时间。 - -**可能的原因:** - -* 磁盘问题。PD 所在的节点 I/O 被占满,排查是否有其他 I/O 高的组件与 PD 混合部署以及磁盘的健康情况,可通过监控 Grafana -> **disk performance** -> **latency** 和 **load** 等指标进行验证,必要时可以使用 fio 工具对盘进行检测,见案例 [case-292](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case292.md)。 - -* PD 之间的网络问题。PD 日志中有 `"lost the TCP streaming connection"`,排查 PD 之间网络是否有问题,可通过监控 Grafana -> **PD** -> **etcd** 的 **round trip** 来验证,见案例 [case-177](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case177.md)。 - -* 系统负载高,日志中能看到 `"server is likely overloaded"`,见案例 [case-214](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case214.md)。 - -* 选举不出 leader。PD 日志中有 `"lease is not expired"`,见 issues [https://github.com/etcd-io/etcd/issues/10355](https://github.com/etcd-io/etcd/issues/10355)。v3.0.x 版本和 v2.1.19 版本已解决该问题,见案例 [case-875](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case875.md)。 - -* 选举慢。Region 加载时间长,从 PD 日志中 `grep "regions cost"`(例如日志中可能是 `load 460927 regions cost 11.77099s`), 如果出现秒级,则说明较慢,v3.0 版本可开启 Region Storage(设置 `use-region-storage` 为 `true`),该特性能极大缩短加载 Region 的时间,见案例 [case-429](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case429.md)。 - -* TiDB 与 PD 之间的网络问题,应排查网络相关情况。通过监控 Grafana -> **blackbox_exporter** -> **ping latency** 确定 TiDB 到 PD leader 的网络是否正常。 - -* PD 报 `FATAL` 错误,日志中有 `"range failed to find revision pair"`。v3.0.8 中已经解决问题,见 PR [https://github.com/pingcap/pd/pull/2040](https://github.com/pingcap/pd/pull/2040)。详情请参考案例 [case-947](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case947.md)。 - -* 使用 `/api/v1/regions` 接口时 Region 数量过多可能会导致 PD OOM。已于 v3.0.8 版本修复,见 [https://github.com/pingcap/pd/pull/1986](https://github.com/pingcap/pd/pull/1986)。 - -* 滚动升级的时候 PD OOM,gRPC 消息大小没限制,监控可看到 TCP InSegs 较大,已于 v3.0.6 版本修复,见 [https://github.com/pingcap/pd/pull/1952](https://github.com/pingcap/pd/pull/1952)。 - -* PD panic。请[提交 bug](https://github.com/tikv/pd/issues/new?labels=kind/bug&template=bug-report.md)。 - -* 其他原因,通过 `curl http://127.0.0.1:2379/debug/pprof/goroutine?debug=2` 抓取 goroutine,并[提交 bug](https://github.com/pingcap/pd/issues/new?labels=kind%2Fbug&template=bug-report.md)。 - -### TiKV 异常 - -**现象:** - -监控中 **KV Cmd Duration** 异常升高。KV Cmd Duration 是 TiDB 发送请求给 TiKV 到收到回复的延迟。 - -**可能的原因:** - -* 查看 gRPC duration。gRPC duration 是请求在 TiKV 端的总耗时。通过对比 TiKV 的 gRPC duration 以及 TiDB 中的 KV duration 可以发现潜在的网络问题。比如 gRPC duration 很短但是 TiDB 的 KV duration 显示很长,说明 TiDB 和 TiKV 之间网络延迟可能很高,或者 TiDB 和 TiKV 之间的网卡带宽被占满。 - -* TiKV 重启了导致重新选举 - * TiKV panic 之后又被 `systemd` 重新拉起正常运行,可以通过查看 TiKV 的日志来确认是否有 `panic`,这种情况属于非预期,需要报 bug。 - * 被第三者 `stop`/`kill`,被 `systemd` 重新拉起。查看 `dmesg` 和 `TiKV log` 确认原因。 - * TiKV 发生 OOM 导致重启了。 - * 动态调整 `THP` 导致 hung 住,见案例 [case-500](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case500.md)。 - -* 查看监控:Grafana -> **TiKV-details** -> **errors** 面板 `server is busy` 看到 TiKV RocksDB 出现 write stall 导致发生重新选举。 - -* TiKV 发生网络隔离导致重新选举。 - -* `block-cache` 配置太大导致 OOM,在监控 Grafana -> **TiKV-details** 选中对应的 instance 之后查看 RocksDB 的 `block cache size` 监控来确认是否是该问题。同时请检查 `[storage.block-cache] capacity = # "1GB"` 参数是否设置合理,默认情况下 TiKV 的 `block-cache` 设置为机器总内存的 `45%`。在容器化部署时需要显式指定该参数,因为 TiKV 获取的是物理机的内存,可能会超出单个 container 的内存限制。 - -* Coprocessor 收到大量大查询,返回的数据量太大,gRPC 发送速度跟不上 Coprocessor 向客户端输出数据的速度导致 OOM。可以通过检查监控:Grafana -> **TiKV-details** -> **coprocessor overview** 的 `response size` 是否超过 `network outbound` 流量来确认是否属于这种情况。 - -### TiKV 单线程瓶颈 - -TiKV 中存在一些单线程线程,可能会成为瓶颈。 - -* 单个 TiKV Region 过多导致单个 gRPC 线程成为瓶颈(查看 Grafana -> TiKV-details -> `Thread CPU/gRPC CPU Per Thread` 监控),v3.x 以上版本可以开启 Hibernate Region 特性来解决,见案例 [case-612](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case612.md)。 -* v3.0 之前版本 Raftstore 单线程或者 Apply 单线程到达瓶颈(Grafana -> TiKV-details -> `Thread CPU/raft store CPU 和 Async apply CPU` 超过 `80%`),可以选择扩容 TiKV(v2.x 版本)实例或者升级到多线程模型的 v3.x 版本。 - -### CPU Load 升高 - -**现象:** - -CPU 资源使用到达瓶颈 - -**可能的原因:** - -* 热点问题。 -* 整体负载高,排查 TiDB 的 slow query 和 expensive query。对运行的 query 进行优化,如果缺索引就加索引,如果可以批量执行就批量执行。另一个方案是对集群进行扩容。 - -## 其它原因 - -### 集群维护 - -通常大多数的线上集群有 3 或 5 个 PD 节点,如果维护的主机上有 PD 组件,需要具体考虑节点是 leader 还是 follower,关闭 follower 对集群运行没有任何影响,关闭 leader 需要先切换,并在切换时有 3 秒左右的性能抖动。 - -### 少数派副本离线 - -TiDB 集群默认配置为 3 副本,每一个 Region 都会在集群中保存 3 份,它们之间通过 Raft 协议来选举 Leader 并同步数据。Raft 协议可以保证在数量小于副本数(注意:不是节点数)一半的节点挂掉或者隔离的情况下,仍然能够提供服务,并且不丢失任何数据。对于 3 副本集群,挂掉一个节点可能会导致性能抖动,可用性和正确性理论上不会受影响。 - -### 新增索引 - -由于创建索引在扫表回填索引的时候会消耗大量资源,甚至与一些频繁更新的字段会发生冲突导致正常业务受到影响。大表创建索引的过程往往会持续很长时间,所以要尽可能地平衡执行时间和集群性能之间的关系,比如选择非高频更新时间段。 - -**参数调整:** - -目前主要使用 `tidb_ddl_reorg_worker_cnt` 和 `tidb_ddl_reorg_batch_size` 这两个参数来动态调整索引创建速度,通常来说它们的值越小对系统影响越小,但是执行时间越长。 - -一般情况下,先将值保持为默认的 `4` 和 `256` ,观察集群资源使用情况和响应速度,再逐渐调大 `tidb_ddl_reorg_worker_cnt` 参数来增加并发,观察监控如果系统没有发生明显的抖动,再逐渐调大 `tidb_ddl_reorg_batch_size` 参数,但如果索引涉及的列更新很频繁的话就会造成大量冲突造成失败重试。 - -另外还可以通过调整参数 `tidb_ddl_reorg_priority` 为 `PRIORITY_HIGH` 来让创建索引的任务保持高优先级来提升速度,但在通用 OLTP 系统上,一般建议保持默认。 - -### GC 压力大 - -TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧的数据时,旧的数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。GC 的任务便是清理不再需要的旧数据。 - -* Resolve Locks 阶段在 TiKV 一侧会产生大量的 scan_lock 请求,可以在 gRPC 相关的 metrics 中观察到。`scan_lock` 请求会对全部的 Region 调用。 -* Delete Ranges 阶段会往 TiKV 发送少量的 `unsafe_destroy_range` 请求,也可能没有。可以在 gRPC 相关的 metrics 中和 GC 分类下的 GC tasks 中观察到。 -* Do GC 阶段,默认每台 TiKV 会自动扫描本机上的 leader Region 并对每一个 leader 进行 GC,这一活动可以在 GC 分类下的 GC tasks 中观察到。 +--- +title: 读写延迟增加 +summary: 介绍读写延时增加、抖动时的排查思路,可能的原因和解决方法。 +--- + +# 读写延迟增加 + +本文档介绍读写延迟增加、抖动时的排查思路,可能的原因和解决方法。 + +## 常见原因 + +### TiDB 执行计划不对导致延迟增高 + +查询语句的执行计划不稳定,偶尔执行计划选择错误的索引,导致查询延迟增加。 + +**现象:** + +* 如果慢日志中输出了执行计划,可以直接查看执行计划。用 `select tidb_decode_plan('xxx...')` 语句可以解析出具体的执行计划。 +* 监控中的 key 扫描异常升高;慢日志中 SQL 执行时间 `Scan Keys` 数目较大。 +* SQL 执行时间相比于其他数据库(例如 MySQL)有较大差距。可以对比其他数据库执行计划,例如 `Join Order` 是否不同。 + +**可能的原因:** + +* 统计信息不准确 + +**解决方案:** + +* 更新统计信息 + * 手动 `analyze table`,配合 crontab 定期 `analyze`,维持统计信息准确度。 + * 自动 `auto analyze`,调低 `analyze ratio` 阈值,提高收集频次,并设置运行时间窗口。示例如下: + * `set global tidb_auto_analyze_ratio=0.2;` + * `set global tidb_auto_analyze_start_time='00:00 +0800';` + * `set global tidb_auto_analyze_end_time='06:00 +0800';` +* 绑定执行计划 + * 修改业务 SQL,使用 `use index` 固定使用列上的索引。 + * 3.0 版本下,业务可以不用修改 SQL,使用 `create global binding` 创建 `force index` 的绑定 SQL。 + * 4.0 版本支持 SQL Plan Management,可以避免因执行计划不稳定导致的性能下降。 + +### PD 异常 + +**现象:** + +监控中 PD TSO 的 **wait duration** 异常升高。**wait duration** 代表从开始等待 PD 返回,到等待结束的时间。 + +**可能的原因:** + +* 磁盘问题。PD 所在的节点 I/O 被占满,排查是否有其他 I/O 高的组件与 PD 混合部署以及磁盘的健康情况,可通过监控 Grafana -> **disk performance** -> **latency** 和 **load** 等指标进行验证,必要时可以使用 fio 工具对盘进行检测,见案例 [case-292](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case292.md)。 + +* PD 之间的网络问题。PD 日志中有 `"lost the TCP streaming connection"`,排查 PD 之间网络是否有问题,可通过监控 Grafana -> **PD** -> **etcd** 的 **round trip** 来验证,见案例 [case-177](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case177.md)。 + +* 系统负载高,日志中能看到 `"server is likely overloaded"`,见案例 [case-214](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case214.md)。 + +* 选举不出 leader。PD 日志中有 `"lease is not expired"`,见 issues [https://github.com/etcd-io/etcd/issues/10355](https://github.com/etcd-io/etcd/issues/10355)。v3.0.x 版本和 v2.1.19 版本已解决该问题,见案例 [case-875](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case875.md)。 + +* 选举慢。Region 加载时间长,从 PD 日志中 `grep "regions cost"`(例如日志中可能是 `load 460927 regions cost 11.77099s`), 如果出现秒级,则说明较慢,v3.0 版本可开启 Region Storage(设置 `use-region-storage` 为 `true`),该特性能极大缩短加载 Region 的时间,见案例 [case-429](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case429.md)。 + +* TiDB 与 PD 之间的网络问题,应排查网络相关情况。通过监控 Grafana -> **blackbox_exporter** -> **ping latency** 确定 TiDB 到 PD leader 的网络是否正常。 + +* PD 报 `FATAL` 错误,日志中有 `"range failed to find revision pair"`。v3.0.8 中已经解决问题,见 PR [https://github.com/pingcap/pd/pull/2040](https://github.com/pingcap/pd/pull/2040)。详情请参考案例 [case-947](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case947.md)。 + +* 使用 `/api/v1/regions` 接口时 Region 数量过多可能会导致 PD OOM。已于 v3.0.8 版本修复,见 [https://github.com/pingcap/pd/pull/1986](https://github.com/pingcap/pd/pull/1986)。 + +* 滚动升级的时候 PD OOM,gRPC 消息大小没限制,监控可看到 TCP InSegs 较大,已于 v3.0.6 版本修复,见 [https://github.com/pingcap/pd/pull/1952](https://github.com/pingcap/pd/pull/1952)。 + +* PD panic。请[提交 bug](https://github.com/tikv/pd/issues/new?labels=kind/bug&template=bug-report.md)。 + +* 其他原因,通过 `curl http://127.0.0.1:2379/debug/pprof/goroutine?debug=2` 抓取 goroutine,并[提交 bug](https://github.com/pingcap/pd/issues/new?labels=kind%2Fbug&template=bug-report.md)。 + +### TiKV 异常 + +**现象:** + +监控中 **KV Cmd Duration** 异常升高。KV Cmd Duration 是 TiDB 发送请求给 TiKV 到收到回复的延迟。 + +**可能的原因:** + +* 查看 gRPC duration。gRPC duration 是请求在 TiKV 端的总耗时。通过对比 TiKV 的 gRPC duration 以及 TiDB 中的 KV duration 可以发现潜在的网络问题。比如 gRPC duration 很短但是 TiDB 的 KV duration 显示很长,说明 TiDB 和 TiKV 之间网络延迟可能很高,或者 TiDB 和 TiKV 之间的网卡带宽被占满。 + +* TiKV 重启了导致重新选举 + * TiKV panic 之后又被 `systemd` 重新拉起正常运行,可以通过查看 TiKV 的日志来确认是否有 `panic`,这种情况属于非预期,需要报 bug。 + * 被第三者 `stop`/`kill`,被 `systemd` 重新拉起。查看 `dmesg` 和 `TiKV log` 确认原因。 + * TiKV 发生 OOM 导致重启了。 + * 动态调整 `THP` 导致 hung 住,见案例 [case-500](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case500.md)。 + +* 查看监控:Grafana -> **TiKV-details** -> **errors** 面板 `server is busy` 看到 TiKV RocksDB 出现 write stall 导致发生重新选举。 + +* TiKV 发生网络隔离导致重新选举。 + +* `block-cache` 配置太大导致 OOM,在监控 Grafana -> **TiKV-details** 选中对应的 instance 之后查看 RocksDB 的 `block cache size` 监控来确认是否是该问题。同时请检查 `[storage.block-cache] capacity = # "1GB"` 参数是否设置合理,默认情况下 TiKV 的 `block-cache` 设置为机器总内存的 `45%`。在容器化部署时需要显式指定该参数,因为 TiKV 获取的是物理机的内存,可能会超出单个 container 的内存限制。 + +* Coprocessor 收到大量大查询,返回的数据量太大,gRPC 发送速度跟不上 Coprocessor 向客户端输出数据的速度导致 OOM。可以通过检查监控:Grafana -> **TiKV-details** -> **coprocessor overview** 的 `response size` 是否超过 `network outbound` 流量来确认是否属于这种情况。 + +### TiKV 单线程瓶颈 + +TiKV 中存在一些单线程线程,可能会成为瓶颈。 + +* 单个 TiKV Region 过多导致单个 gRPC 线程成为瓶颈(查看 Grafana -> TiKV-details -> `Thread CPU/gRPC CPU Per Thread` 监控),v3.x 以上版本可以开启 Hibernate Region 特性来解决,见案例 [case-612](https://github.com/pingcap/tidb-map/blob/master/maps/diagnose-case-study/case612.md)。 +* v3.0 之前版本 Raftstore 单线程或者 Apply 单线程到达瓶颈(Grafana -> TiKV-details -> `Thread CPU/raft store CPU 和 Async apply CPU` 超过 `80%`),可以选择扩容 TiKV(v2.x 版本)实例或者升级到多线程模型的 v3.x 版本。 + +### CPU Load 升高 + +**现象:** + +CPU 资源使用到达瓶颈 + +**可能的原因:** + +* 热点问题。 +* 整体负载高,排查 TiDB 的 slow query 和 expensive query。对运行的 query 进行优化,如果缺索引就加索引,如果可以批量执行就批量执行。另一个方案是对集群进行扩容。 + +## 其它原因 + +### 集群维护 + +通常大多数的线上集群有 3 或 5 个 PD 节点,如果维护的主机上有 PD 组件,需要具体考虑节点是 leader 还是 follower,关闭 follower 对集群运行没有任何影响,关闭 leader 需要先切换,并在切换时有 3 秒左右的性能抖动。 + +### 少数派副本离线 + +TiDB 集群默认配置为 3 副本,每一个 Region 都会在集群中保存 3 份,它们之间通过 Raft 协议来选举 Leader 并同步数据。Raft 协议可以保证在数量小于副本数(注意:不是节点数)一半的节点挂掉或者隔离的情况下,仍然能够提供服务,并且不丢失任何数据。对于 3 副本集群,挂掉一个节点可能会导致性能抖动,可用性和正确性理论上不会受影响。 + +### 新增索引 + +由于创建索引在扫表回填索引的时候会消耗大量资源,甚至与一些频繁更新的字段会发生冲突导致正常业务受到影响。大表创建索引的过程往往会持续很长时间,所以要尽可能地平衡执行时间和集群性能之间的关系,比如选择非高频更新时间段。 + +**参数调整:** + +目前主要使用 `tidb_ddl_reorg_worker_cnt` 和 `tidb_ddl_reorg_batch_size` 这两个参数来动态调整索引创建速度,通常来说它们的值越小对系统影响越小,但是执行时间越长。 + +一般情况下,先将值保持为默认的 `4` 和 `256` ,观察集群资源使用情况和响应速度,再逐渐调大 `tidb_ddl_reorg_worker_cnt` 参数来增加并发,观察监控如果系统没有发生明显的抖动,再逐渐调大 `tidb_ddl_reorg_batch_size` 参数,但如果索引涉及的列更新很频繁的话就会造成大量冲突造成失败重试。 + +另外还可以通过调整参数 `tidb_ddl_reorg_priority` 为 `PRIORITY_HIGH` 来让创建索引的任务保持高优先级来提升速度,但在通用 OLTP 系统上,一般建议保持默认。 + +### GC 压力大 + +TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧的数据时,旧的数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。GC 的任务便是清理不再需要的旧数据。 + +* Resolve Locks 阶段在 TiKV 一侧会产生大量的 scan_lock 请求,可以在 gRPC 相关的 metrics 中观察到。`scan_lock` 请求会对全部的 Region 调用。 +* Delete Ranges 阶段会往 TiKV 发送少量的 `unsafe_destroy_range` 请求,也可能没有。可以在 gRPC 相关的 metrics 中和 GC 分类下的 GC tasks 中观察到。 +* Do GC 阶段,默认每台 TiKV 会自动扫描本机上的 leader Region 并对每一个 leader 进行 GC,这一活动可以在 GC 分类下的 GC tasks 中观察到。 diff --git a/troubleshoot-hot-spot-issues.md b/troubleshoot-hot-spot-issues.md index 5d26ab3fe011..237883bdf4ab 100644 --- a/troubleshoot-hot-spot-issues.md +++ b/troubleshoot-hot-spot-issues.md @@ -1,170 +1,170 @@ ---- -title: TiDB 热点问题处理 ---- - -# TiDB 热点问题处理 - -本文介绍如何定位和解决读写热点问题。 - -TiDB 作为分布式数据库,内建负载均衡机制,尽可能将业务负载均匀地分布到不同计算或存储节点上,更好地利用上整体系统资源。然而,机制不是万能的,在一些场景下仍会有部分业务负载不能被很好地分散,影响性能,形成单点的过高负载,也称为热点。 - -TiDB 提供了完整的方案用于排查、解决或规避这类热点。通过均衡负载热点,可以提升整体性能,包括提高 QPS 和降低延迟等。 - -## 常见热点场景 - -### TiDB 编码规则回顾 - -TiDB 对每个表分配一个 TableID,每一个索引都会分配一个 IndexID,每一行分配一个 RowID(默认情况下,如果表使用整数型的 Primary Key,那么会用 Primary Key 的值当做 RowID)。其中 TableID 在整个集群内唯一,IndexID/RowID 在表内唯一,这些 ID 都是 int64 类型。 - -每行数据按照如下规则进行编码成 Key-Value pair: - -```text -Key: tablePrefix{tableID}_recordPrefixSep{rowID} -Value: [col1, col2, col3, col4] -``` - -其中 Key 的 `tablePrefix` 和 `recordPrefixSep` 都是特定的字符串常量,用于在 KV 空间内区分其他数据。 - -对于 Index 数据,会按照如下规则编码成 Key-Value pair: - -```text -Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue -Value: rowID -``` - -Index 数据还需要考虑 Unique Index 和非 Unique Index 两种情况,对于 Unique Index,可以按照上述编码规则。但是对于非 Unique Index,通过这种编码并不能构造出唯一的 Key,因为同一个 Index 的 `tablePrefix{tableID}_indexPrefixSep{indexID}` 都一样,可能有多行数据的 `ColumnsValue` 是一样的,所以对于非 Unique Index 的编码做了一点调整: - -```text -Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_rowID -Value: null -``` - -### 表热点 - -从 TiDB 编码规则可知,同一个表的数据会在以表 ID 开头为前缀的一个 range 中,数据的顺序按照 RowID 的值顺序排列。在表 insert 的过程中如果 RowID 的值是递增的,则插入的行只能在末端追加。当 Region 达到一定的大小之后会进行分裂,分裂之后还是只能在 range 范围的末端追加,永远只能在一个 Region 上进行 insert 操作,形成热点。 - -常见的 increment 类型自增主键就是顺序递增的,默认情况下,在主键为整数型时,会用主键值当做 RowID ,此时 RowID 为顺序递增,在大量 insert 时形成表的写入热点。 - -同时,TiDB 中 RowID 默认也按照自增的方式顺序递增,主键不为整数类型时,同样会遇到写入热点的问题。 - -### 索引热点 - -索引热点与表热点类似,常见的热点场景出现在时间顺序单调递增的字段,或者插入大量重复值的场景。 - -## 确定存在热点问题 - -性能问题不一定是热点造成的,也可能存在多个因素共同影响,在排查前需要先确认是否与热点相关。 - -- 判断写热点依据:打开监控面板 TiKV-Trouble-Shooting 中 Hot Write 面板(如下图所示),观察 Raftstore CPU 监控是否存在个别 TiKV 节点的指标明显高于其他节点的现象。 - -- 判断读热点依据:打开监控面板 TIKV-Details 中 Thread_CPU,查看 coprocessor cpu 有没有明显的某个 TiKV 特别高。 - -## 使用 TiDB Dashboard 定位热点表 - -[TiDB Dashboard](/dashboard/dashboard-intro.md) 中的[流量可视化](/dashboard/dashboard-key-visualizer.md)功能可帮助用户缩小热点排查范围到表级别。以下是流量可视化功能展示的一个热力图样例,该图横坐标是时间,纵坐标是各个表和索引,颜色越亮代表其流量越大。可在工具栏中切换显示读或写流量。 - -![Dashboard 示例1](/media/troubleshoot-hot-spot-issues-1.png) - -当图中写入流量图出现以下明亮斜线(斜向上或斜向下)时,由于写入只出现在末端,随着表 Region 数量变多,呈现出阶梯状。此时说明该表构成了写入热点: - -![Dashboard 示例2](/media/troubleshoot-hot-spot-issues-2.png) - -对于读热点,在热力图中一般表现为一条明亮的横线,通常是有大量访问的小表,如下图所示: - -![Dashboard 示例3](/media/troubleshoot-hot-spot-issues-3.png) - -将鼠标移到亮色块上,即可看到是什么表或索引具有大流量,如下图所示: - -![Dashboard 示例4](/media/troubleshoot-hot-spot-issues-4.png) - -## 使用 SHARD_ROW_ID_BITS 处理热点表 - -对于主键非整数或没有主键的表或者是联合主键,TiDB 会使用一个隐式的自增 RowID,大量 INSERT 时会把数据集中写入单个 Region,造成写入热点。 - -通过设置 SHARD_ROW_ID_BITS,可以把 RowID 打散写入多个不同的 Region,缓解写入热点问题。但是设置的过大会造成 RPC 请求数放大,增加 CPU 和网络开销。 - -``` -SHARD_ROW_ID_BITS = 4 表示 16 个分片\ -SHARD_ROW_ID_BITS = 6 表示 64 个分片\ -SHARD_ROW_ID_BITS = 0 表示默认值 1 个分片 -``` - -语句示例: - -```sql -CREATE TABLE:CREATE TABLE t (c int) SHARD_ROW_ID_BITS = 4; -ALTER TABLE:ALTER TABLE t SHARD_ROW_ID_BITS = 4; -``` - -`SHARD_ROW_ID_BITS` 的值可以动态修改,每次修改之后,只对新写入的数据生效。 - -对于含有 `CLUSTERED` 主键的表,TiDB 会使用表的主键作为 RowID,因为 `SHARD_ROW_ID_BITS` 会改变 RowID 生成规则,所以此时无法使用 `SHARD_ROW_ID_BITS` 选项。而对于使用 `NONCLUSTERED` 主键的表,TiDB 会使用自动分配的 64 位整数作为 RowID,此时也可以使用 `SHARD_ROW_ID_BITS` 特性。要了解关于 `CLUSTERED` 主键的详细信息,请参考[聚簇索引](/clustered-indexes.md)。 - -以下是两张无主键情况下使用 `SHARD_ROW_ID_BITS` 打散热点后的流量图,第一张展示了打散前的情况,第二张展示了打散后的情况。 - -![Dashboard 示例5](/media/troubleshoot-hot-spot-issues-5.png) - -![Dashboard 示例6](/media/troubleshoot-hot-spot-issues-6.png) - -从流量图可见,设置 `SHARD_ROW_ID_BITS` 后,流量热点由之前的只在一个 Region 上变得很分散。 - -## 使用 AUTO_RANDOM 处理自增主键热点表 - -使用 `AUTO_RANDOM` 处理自增主键热点表,适用于代替自增主键,解决自增主键带来的写入热点。 - -使用该功能后,将由 TiDB 生成随机分布且空间耗尽前不重复的主键,达到离散写入、打散写入热点的目的。 - -注意 TiDB 生成的主键不再是自增的主键,可使用 `LAST_INSERT_ID()` 获取上次分配的主键值。 - -将建表语句中的 `AUTO_INCREMENT` 改为 `AUTO_RANDOM` 即可使用该功能,适用于主键只需要保证唯一,不包含业务意义的场景。示例如下: - -{{< copyable "sql" >}} - -```sql -CREATE TABLE t (a BIGINT PRIMARY KEY AUTO_RANDOM, b varchar(255)); -INSERT INTO t (b) VALUES ("foo"); -SELECT * FROM t; -``` - -```sql -+------------+---+ -| a | b | -+------------+---+ -| 1073741825 | b | -+------------+---+ -``` - -{{< copyable "sql" >}} - -```sql -SELECT LAST_INSERT_ID(); -``` - -```sql -+------------------+ -| LAST_INSERT_ID() | -+------------------+ -| 1073741825 | -+------------------+ -``` - -以下是将 `AUTO_INCREMENT` 表改为 `AUTO_RANDOM` 打散热点后的流量图,第一张是 `AUTO_INCREMENT`,第二张是 `AUTO_RANDOM`。 - -![Dashboard 示例7](/media/troubleshoot-hot-spot-issues-7.png) - -![Dashboard 示例8](/media/troubleshoot-hot-spot-issues-8.png) - -由流量图可见,使用 `AUTO_RANDOM` 代替 `AUTO_INCREMENT` 能很好地打散热点。 - -更详细的说明参见 [`AUTO_RANDOM`](/auto-random.md) 文档。 - -## 小表热点的优化 - -TiDB 的 Coprocessor Cache 功能支持下推计算结果缓存。开启该功能后,将在 TiDB 实例侧缓存下推给 TiKV 计算的结果,对于小表读热点能起到比较好的效果。 - -更详细的说明参见[下推计算结果缓存](/coprocessor-cache.md#配置)文档。 - -**其他相关资料**: - -+ [TiDB 高并发写入场景最佳实践](/best-practices/high-concurrency-best-practices.md) -+ [Split Region 使用文档](/sql-statements/sql-statement-split-region.md) +--- +title: TiDB 热点问题处理 +--- + +# TiDB 热点问题处理 + +本文介绍如何定位和解决读写热点问题。 + +TiDB 作为分布式数据库,内建负载均衡机制,尽可能将业务负载均匀地分布到不同计算或存储节点上,更好地利用上整体系统资源。然而,机制不是万能的,在一些场景下仍会有部分业务负载不能被很好地分散,影响性能,形成单点的过高负载,也称为热点。 + +TiDB 提供了完整的方案用于排查、解决或规避这类热点。通过均衡负载热点,可以提升整体性能,包括提高 QPS 和降低延迟等。 + +## 常见热点场景 + +### TiDB 编码规则回顾 + +TiDB 对每个表分配一个 TableID,每一个索引都会分配一个 IndexID,每一行分配一个 RowID(默认情况下,如果表使用整数型的 Primary Key,那么会用 Primary Key 的值当做 RowID)。其中 TableID 在整个集群内唯一,IndexID/RowID 在表内唯一,这些 ID 都是 int64 类型。 + +每行数据按照如下规则进行编码成 Key-Value pair: + +```text +Key: tablePrefix{tableID}_recordPrefixSep{rowID} +Value: [col1, col2, col3, col4] +``` + +其中 Key 的 `tablePrefix` 和 `recordPrefixSep` 都是特定的字符串常量,用于在 KV 空间内区分其他数据。 + +对于 Index 数据,会按照如下规则编码成 Key-Value pair: + +```text +Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue +Value: rowID +``` + +Index 数据还需要考虑 Unique Index 和非 Unique Index 两种情况,对于 Unique Index,可以按照上述编码规则。但是对于非 Unique Index,通过这种编码并不能构造出唯一的 Key,因为同一个 Index 的 `tablePrefix{tableID}_indexPrefixSep{indexID}` 都一样,可能有多行数据的 `ColumnsValue` 是一样的,所以对于非 Unique Index 的编码做了一点调整: + +```text +Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_rowID +Value: null +``` + +### 表热点 + +从 TiDB 编码规则可知,同一个表的数据会在以表 ID 开头为前缀的一个 range 中,数据的顺序按照 RowID 的值顺序排列。在表 insert 的过程中如果 RowID 的值是递增的,则插入的行只能在末端追加。当 Region 达到一定的大小之后会进行分裂,分裂之后还是只能在 range 范围的末端追加,永远只能在一个 Region 上进行 insert 操作,形成热点。 + +常见的 increment 类型自增主键就是顺序递增的,默认情况下,在主键为整数型时,会用主键值当做 RowID ,此时 RowID 为顺序递增,在大量 insert 时形成表的写入热点。 + +同时,TiDB 中 RowID 默认也按照自增的方式顺序递增,主键不为整数类型时,同样会遇到写入热点的问题。 + +### 索引热点 + +索引热点与表热点类似,常见的热点场景出现在时间顺序单调递增的字段,或者插入大量重复值的场景。 + +## 确定存在热点问题 + +性能问题不一定是热点造成的,也可能存在多个因素共同影响,在排查前需要先确认是否与热点相关。 + +- 判断写热点依据:打开监控面板 TiKV-Trouble-Shooting 中 Hot Write 面板(如下图所示),观察 Raftstore CPU 监控是否存在个别 TiKV 节点的指标明显高于其他节点的现象。 + +- 判断读热点依据:打开监控面板 TIKV-Details 中 Thread_CPU,查看 coprocessor cpu 有没有明显的某个 TiKV 特别高。 + +## 使用 TiDB Dashboard 定位热点表 + +[TiDB Dashboard](/dashboard/dashboard-intro.md) 中的[流量可视化](/dashboard/dashboard-key-visualizer.md)功能可帮助用户缩小热点排查范围到表级别。以下是流量可视化功能展示的一个热力图样例,该图横坐标是时间,纵坐标是各个表和索引,颜色越亮代表其流量越大。可在工具栏中切换显示读或写流量。 + +![Dashboard 示例1](/media/troubleshoot-hot-spot-issues-1.png) + +当图中写入流量图出现以下明亮斜线(斜向上或斜向下)时,由于写入只出现在末端,随着表 Region 数量变多,呈现出阶梯状。此时说明该表构成了写入热点: + +![Dashboard 示例2](/media/troubleshoot-hot-spot-issues-2.png) + +对于读热点,在热力图中一般表现为一条明亮的横线,通常是有大量访问的小表,如下图所示: + +![Dashboard 示例3](/media/troubleshoot-hot-spot-issues-3.png) + +将鼠标移到亮色块上,即可看到是什么表或索引具有大流量,如下图所示: + +![Dashboard 示例4](/media/troubleshoot-hot-spot-issues-4.png) + +## 使用 SHARD_ROW_ID_BITS 处理热点表 + +对于主键非整数或没有主键的表或者是联合主键,TiDB 会使用一个隐式的自增 RowID,大量 INSERT 时会把数据集中写入单个 Region,造成写入热点。 + +通过设置 SHARD_ROW_ID_BITS,可以把 RowID 打散写入多个不同的 Region,缓解写入热点问题。但是设置的过大会造成 RPC 请求数放大,增加 CPU 和网络开销。 + +``` +SHARD_ROW_ID_BITS = 4 表示 16 个分片\ +SHARD_ROW_ID_BITS = 6 表示 64 个分片\ +SHARD_ROW_ID_BITS = 0 表示默认值 1 个分片 +``` + +语句示例: + +```sql +CREATE TABLE:CREATE TABLE t (c int) SHARD_ROW_ID_BITS = 4; +ALTER TABLE:ALTER TABLE t SHARD_ROW_ID_BITS = 4; +``` + +`SHARD_ROW_ID_BITS` 的值可以动态修改,每次修改之后,只对新写入的数据生效。 + +对于含有 `CLUSTERED` 主键的表,TiDB 会使用表的主键作为 RowID,因为 `SHARD_ROW_ID_BITS` 会改变 RowID 生成规则,所以此时无法使用 `SHARD_ROW_ID_BITS` 选项。而对于使用 `NONCLUSTERED` 主键的表,TiDB 会使用自动分配的 64 位整数作为 RowID,此时也可以使用 `SHARD_ROW_ID_BITS` 特性。要了解关于 `CLUSTERED` 主键的详细信息,请参考[聚簇索引](/clustered-indexes.md)。 + +以下是两张无主键情况下使用 `SHARD_ROW_ID_BITS` 打散热点后的流量图,第一张展示了打散前的情况,第二张展示了打散后的情况。 + +![Dashboard 示例5](/media/troubleshoot-hot-spot-issues-5.png) + +![Dashboard 示例6](/media/troubleshoot-hot-spot-issues-6.png) + +从流量图可见,设置 `SHARD_ROW_ID_BITS` 后,流量热点由之前的只在一个 Region 上变得很分散。 + +## 使用 AUTO_RANDOM 处理自增主键热点表 + +使用 `AUTO_RANDOM` 处理自增主键热点表,适用于代替自增主键,解决自增主键带来的写入热点。 + +使用该功能后,将由 TiDB 生成随机分布且空间耗尽前不重复的主键,达到离散写入、打散写入热点的目的。 + +注意 TiDB 生成的主键不再是自增的主键,可使用 `LAST_INSERT_ID()` 获取上次分配的主键值。 + +将建表语句中的 `AUTO_INCREMENT` 改为 `AUTO_RANDOM` 即可使用该功能,适用于主键只需要保证唯一,不包含业务意义的场景。示例如下: + +{{< copyable "sql" >}} + +```sql +CREATE TABLE t (a BIGINT PRIMARY KEY AUTO_RANDOM, b varchar(255)); +INSERT INTO t (b) VALUES ("foo"); +SELECT * FROM t; +``` + +```sql ++------------+---+ +| a | b | ++------------+---+ +| 1073741825 | b | ++------------+---+ +``` + +{{< copyable "sql" >}} + +```sql +SELECT LAST_INSERT_ID(); +``` + +```sql ++------------------+ +| LAST_INSERT_ID() | ++------------------+ +| 1073741825 | ++------------------+ +``` + +以下是将 `AUTO_INCREMENT` 表改为 `AUTO_RANDOM` 打散热点后的流量图,第一张是 `AUTO_INCREMENT`,第二张是 `AUTO_RANDOM`。 + +![Dashboard 示例7](/media/troubleshoot-hot-spot-issues-7.png) + +![Dashboard 示例8](/media/troubleshoot-hot-spot-issues-8.png) + +由流量图可见,使用 `AUTO_RANDOM` 代替 `AUTO_INCREMENT` 能很好地打散热点。 + +更详细的说明参见 [`AUTO_RANDOM`](/auto-random.md) 文档。 + +## 小表热点的优化 + +TiDB 的 Coprocessor Cache 功能支持下推计算结果缓存。开启该功能后,将在 TiDB 实例侧缓存下推给 TiKV 计算的结果,对于小表读热点能起到比较好的效果。 + +更详细的说明参见[下推计算结果缓存](/coprocessor-cache.md#配置)文档。 + +**其他相关资料**: + ++ [TiDB 高并发写入场景最佳实践](/best-practices/high-concurrency-best-practices.md) ++ [Split Region 使用文档](/sql-statements/sql-statement-split-region.md) diff --git a/troubleshoot-lock-conflicts.md b/troubleshoot-lock-conflicts.md index d65373973318..f50546f26724 100644 --- a/troubleshoot-lock-conflicts.md +++ b/troubleshoot-lock-conflicts.md @@ -1,384 +1,384 @@ ---- -title: TiDB 锁冲突问题处理 -summary: 了解 TiDB 锁冲突问题以及处理方式。 ---- - -# TiDB 锁冲突问题处理 - -TiDB 支持完整的分布式事务,自 v3.0 版本起,提供乐观事务与悲观事务两种事务模型。本文介绍在使用乐观事务或者悲观事务的过程中常见的锁冲突问题以及解决思路。 - -## 乐观锁 - -TiDB 中事务使用两阶段提交,分为 Prewrite 和 Commit 两个阶段,示意图如下。 - -![TiDB 中乐观事务的两阶段提交](/media/troubleshooting-lock-pic-01.png) - -相关细节本节不再赘述,详情可阅读 [Percolator 和 TiDB 事务算法](https://pingcap.com/blog-cn/percolator-and-txn/)。 - -### Prewrite 阶段 - -在两阶段提交的 Prewrite 阶段,TiDB 会对目标 key 分别上 primary lock 和 secondary lock。在冲突严重的场景中,会出现写冲突 (write conflict)、keyislocked 等报错。具体而言,这个阶段可能会遇到的锁相关的报错信息如下。 - -#### 读写冲突 - -在 TiDB 中,读取数据时,会获取一个包含当前物理时间且全局唯一递增的时间戳作为当前事务的 start_ts。事务在读取时,需要读到目标 key 的 commit_ts 小于这个事务的 start_ts 的最新的数据版本。当读取时发现目标 key 上存在 lock 时,因为无法知道上锁的那个事务是在 Commit 阶段还是 Prewrite 阶段,所以就会出现读写冲突的情况,如下图: - -![读写冲突](/media/troubleshooting-lock-pic-04.png) - -分析: - -Txn0 完成了 Prewrite,在 Commit 的过程中 Txn1 对该 key 发起了读请求,Txn1 需要读取 start_ts > commit_ts 最近的 key 的版本。此时,Txn1 的 `start_ts > Txn0` 的 lock_ts,需要读取的 key 上的锁信息仍未清理,故无法判断 Txn0 是否提交成功,因此 Txn1 与 Txn0 出现读写冲突。 - -你可以通过如下两种途径来检测当前环境中是否存在读写冲突: - -1. TiDB 监控及日志 - - * 通过 TiDB Grafana 监控分析: - - 观察 KV Errors 下 Lock Resolve OPS 面板中的 not_expired/resolve 监控项以及 KV Backoff OPS 面板中的 txnLockFast 监控项,如果有较为明显的上升趋势,那么可能是当前的环境中出现了大量的读写冲突。其中,not_expired 是指对应的锁还没有超时,resolve 是指尝试清锁的操作,txnLockFast 代表出现了读写冲突。 - - ![KV-backoff-txnLockFast-optimistic](/media/troubleshooting-lock-pic-09.png) - ![KV-Errors-resolve-optimistic](/media/troubleshooting-lock-pic-08.png) - - * 通过 TiDB 日志分析: - - 在 TiDB 的日志中可以看到下列信息: - - ```log - [INFO] [coprocessor.go:743] ["[TIME_COP_PROCESS] resp_time:406.038899ms txnStartTS:416643508703592451 region_id:8297 store_addr:10.8.1.208:20160 backoff_ms:255 backoff_types:[txnLockFast,txnLockFast] kv_process_ms:333 scan_total_write:0 scan_processed_write:0 scan_total_data:0 scan_processed_data:0 scan_total_lock:0 scan_processed_lock:0"] - ``` - - * txnStartTS:发起读请求的事务的 start_ts,如上面示例中的 416643508703592451 - * backoff_types:读写发生了冲突,并且读请求进行了 backoff 重试,重试的类型为 txnLockFast - * backoff_ms:读请求 backoff 重试的耗时,单位为 ms ,如上面示例中的 255 - * region_id:读请求访问的目标 region 的 id - -2. 通过 TiKV 日志分析: - - 在 TiKV 的日志可以看到下列信息: - - ```log - [ERROR] [endpoint.rs:454] [error-response] [err=""locked primary_lock:7480000000000004D35F6980000000000000010380000000004C788E0380000000004C0748 lock_version: 411402933858205712 key: 7480000000000004D35F7280000000004C0748 lock_ttl: 3008 txn_size: 1""] - ``` - - 这段报错信息表示出现了读写冲突,当读数据时发现 key 有锁阻碍读,这个锁包括未提交的乐观锁和未提交的 prewrite 后的悲观锁。 - - * primary_lock:锁对应事务的 primary lock。 - * lock_version:锁对应事务的 start_ts。 - * key:表示被锁的 key。 - * lock_ttl: 锁的 TTL。 - * txn_size:锁所在事务在其 Region 的 key 数量,指导清锁方式。 - -处理建议: - -* 在遇到读写冲突时会有 backoff 自动重试机制,如上述示例中 Txn1 会进行 backoff 重试,单次初始 100 ms,单次最大 3000 ms,总共最大 20000 ms - -* 可以使用 TiDB Control 的子命令 [decoder](/tidb-control.md#decoder-命令) 来查看指定 key 对应的行的 table id 以及 rowid: - - ```sh - ./tidb-ctl decoder -f table_row -k "t\x00\x00\x00\x00\x00\x00\x00\x1c_r\x00\x00\x00\x00\x00\x00\x00\xfa" - - table_id: -9223372036854775780 - row_id: -9223372036854775558 - ``` - -#### KeyIsLocked 错误 - -事务在 Prewrite 阶段的第一步就会检查是否有写写冲突,第二步会检查目标 key 是否已经被另一个事务上锁。当检测到该 key 被 lock 后,会在 TiKV 端报出 KeyIsLocked。目前该报错信息没有打印到 TiDB 以及 TiKV 的日志中。与读写冲突一样,在出现 KeyIsLocked 时,后台会自动进行 backoff 重试。 - -你可以通过 TiDB Grafana 监控检测 KeyIsLocked 错误: - -观察 KV Errors 下 Lock Resolve OPS 面板中的 resolve 监控项以及 KV Backoff OPS 面板中的 txnLock 监控项,会有比较明显的上升趋势,其中 resolve 是指尝试清锁的操作,txnLock 代表出现了写冲突。 - -![KV-backoff-txnLockFast-optimistic-01](/media/troubleshooting-lock-pic-07.png) -![KV-Errors-resolve-optimistic-01](/media/troubleshooting-lock-pic-08.png) - -处理建议: - -* 监控中出现少量 txnLock,无需过多关注。后台会自动进行 backoff 重试,单次初始 200 ms,单次最大 3000 ms。 -* 如果出现大量的 txnLock,需要从业务的角度评估下冲突的原因。 -* 使用悲观锁模式。 - -### Commit 阶段 - -当 Prewrite 全部完成时,客户端便会取得 commit_ts,然后继续两阶段提交的第二阶段。这里需要注意的是,由于 primary key 是否提交成功标志着整个事务是否提交成功,因而客户端需要在单独 commit primary key 之后再继续 commit 其余的 key。 - -#### 锁被清除 (LockNotFound) 错误 - -TxnLockNotFound 错误是由于事务提交的慢了,超过了 TTL 的时间。当要提交时,发现被其他事务给 Rollback 掉了。在开启 TiDB [自动重试事务](/system-variables.md#tidb_retry_limit)的情况下,会自动在后台进行事务重试(注意显示和隐式事务的差别)。 - -你可以通过如下两种途径来查看 LockNotFound 报错信息: - -1. 查看 TiDB 日志 - - 如果出现了 TxnLockNotFound 的报错,会在 TiDB 的日志中看到下面的信息: - - ```log - [WARN] [session.go:446] ["commit failed"] [conn=149370] ["finished txn"="Txn{state=invalid}"] [error="[kv:6]Error: KV error safe to retry tikv restarts txn: Txn(Mvcc(TxnLockNotFound{ start_ts: 412720515987275779, commit_ts: 412720519984971777, key: [116, 128, 0, 0, 0, 0, 1, 111, 16, 95, 114, 128, 0, 0, 0, 0, 0, 0, 2] })) [try again later]"] - ``` - - * start_ts:出现 TxnLockNotFound 报错的事务的 start_ts,如上例中的 412720515987275779 - * commit_ts:出现 TxnLockNotFound 报错的事务的 commit_ts,如上例中的 412720519984971777 - -2. 查看 TiKV 日志 - - 如果出现了 TxnLockNotFound 的报错,在 TiKV 的日志中同样可以看到相应的报错信息: - - ```log - Error: KV error safe to retry restarts txn: Txn(Mvcc(TxnLockNotFound)) [ERROR [Kv.rs:708] ["KvService::batch_raft send response fail"] [err=RemoteStoped] - ``` - -处理建议: - -* 通过检查 start_ts 和 commit_ts 之间的提交间隔,可以确认是否超过了默认的 TTL 的时间。 - - 查看提交间隔: - - ```shell - ./pd-ctl tso [start_ts] - ./pd-ctl tso [commit_ts] - ``` - -* 建议检查下是否是因为写入性能的缓慢导致事务提交的效率差,进而出现了锁被清除的情况。 - -* 在关闭 TiDB 事务重试的情况下,需要在应用端捕获异常,并进行重试。 - -## 悲观锁 - -在 v3.0.8 之前,TiDB 默认使用的乐观事务模式会导致事务提交时因为冲突而失败。为了保证事务的成功率,需要修改应用程序,加上重试的逻辑。悲观事务模式可以避免这个问题,应用程序无需添加重试逻辑,就可以正常执行。 - -TiDB 悲观锁复用了乐观锁的两阶段提交逻辑,重点在 DML 执行时做了改造。 - -![TiDB 悲观事务的提交逻辑](/media/troubleshooting-lock-pic-05.png) - -在两阶段提交之前增加了 Acquire Pessimistic Lock 阶段,简要步骤如下。 - -1. (同乐观锁)TiDB 收到来自客户端的 begin 请求,获取当前版本号作为本事务的 StartTS。 -2. TiDB 收到来自客户端的更新数据的请求:TiDB 向 TiKV 发起加悲观锁请求,该锁持久化到 TiKV。 -3. (同乐观锁)客户端发起 commit,TiDB 开始执行与乐观锁一样的两阶段提交。 - -![TiDB 中的悲观事务](/media/troubleshooting-lock-pic-06.png) - -相关细节本节不再赘述,详情可阅读 [TiDB 悲观锁实现原理](https://asktug.com/t/topic/33550)。 - -### Prewrite 阶段 - -在悲观锁模式下,在事务的提交阶段沿用的仍然是乐观锁模式,所以在 Prewrite 阶段乐观锁遇到的锁相关的一些报错,在悲观锁模式同样会遇到。 - -#### 读写冲突 - -报错信息以及处理建议同乐观锁模式。 - -### Commit 阶段 - -在乐观模型下,会出现 TxnLockNotFound 错误,而在悲观锁模型下,不会出现这个问题。同样的,悲观锁也有一个 TTL 的时间。txn heartbeat 会自动的更新事务的 TTL,以确保第二个事务不会将第一个事务的锁清掉。 - -### 其他锁相关错误 - -#### pessimistic lock retry limit reached - -在冲突非常严重的场景下,或者当发生 write conflict 时,乐观事务会直接终止,而悲观事务会尝试用最新数据重试该语句直到没有 write conflict。因为 TiDB 的加锁操作是一个写入操作,且操作过程是先读后写,需要 2 次 RPC。如果在这中间发生了 write conflict,那么会重试。每次重试都会打印日志,不用特别关注。重试次数由 [pessimistic-txn.max-retry-count](/tidb-configuration-file.md#max-retry-count) 定义。 - -可通过查看 TiDB 日志查看报错信息: - -悲观事务模式下,如果发生 write conflict,并且重试的次数达到了上限,那么在 TiDB 的日志中会出现含有下述关键字的报错信息。如下: - -```log -err="pessimistic lock retry limit reached" -``` - -处理建议: - -* 如果上述报错出现的比较频繁,建议从业务的角度进行调整。 - -#### Lock wait timeout exceeded - -在悲观锁模式下,事务之间出现会等锁的情况。等锁的超时时间由 TiDB 的 [innodb_lock_wait_timeout](/system-variables.md#innodb_lock_wait_timeout) 参数来定义,这个是 SQL 语句层面的最大允许等锁时间,即一个 SQL 语句期望加锁,但锁一直获取不到,超过这个时间,TiDB 不会再尝试加锁,会向客户端返回相应的报错信息。 - -可通过查看 TiDB 日志查看报错信息: - -当出现等锁超时的情况时,会向客户端返回下述报错信息: - -```log -ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction -``` - -处理建议: - -* 如果出现的次数非常频繁,建议从业务逻辑的角度来进行调整。 - -#### TTL manager has timed out - -除了有不能超出 GC 时间的限制外,悲观锁的 TTL 有上限,默认为 1 小时,所以执行时间超过 1 小时的悲观事务有可能提交失败。这个超时时间由 TiDB 参数 [performance.max-txn-ttl](https://github.com/pingcap/tidb/blob/master/config/config.toml.example) 指定。 - -可通过查看 TiDB 日志查看报错信息: - -当悲观锁的事务执行时间超过 TTL 时,会出现下述报错: - -```log -TTL manager has timed out, pessimistic locks may expire, please commit or rollback this transaction -``` - -处理建议: - -* 当遇到该报错时,建议确认下业务逻辑是否可以进行优化,如将大事务拆分为小事务。在未使用[大事务](/tidb-configuration-file.md#txn-total-size-limit)的前提下,大事务可能会触发 TiDB 的事务限制。 - -* 可适当调整相关参数,使其符合事务要求。 - -#### Deadlock found when trying to get lock - -死锁是指两个或两个以上的事务在执行过程中,由于竞争资源而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,将永远在互相等待。此时,需要终止其中一个事务使其能够继续推进下去。 - -TiDB 在使用悲观锁的情况下,多个事务之间出现了死锁,必定有一个事务 abort 来解开死锁。在客户端层面行为和 MySQL 一致,在客户端返回表示死锁的 Error 1213。如下: - -```log -[err="[executor:1213]Deadlock found when trying to get lock; try restarting transaction"] -``` - -处理建议: - -* 如果难以确认产生死锁的原因,对于 v5.1 及以后的版本,建议尝试查询 `INFORMATION_SCHEMA.DEADLOCKS` 或 `INFORMATION_SCHEMA.CLUSTER_DEADLOCKS` 系统表来获取死锁的等待链信息。详情请参考[死锁错误](#死锁错误)小节和 [`DEADLOCKS` 表](/information-schema/information-schema-deadlocks.md)文档。 -* 如果出现非常频繁,需要调整业务代码来降低死锁发生概率。 - -### 使用 Lock View 排查悲观锁相关的问题 - -自 v5.1 版本起,TiDB 支持 Lock View 功能。该功能在 `information_schema` 中内置了若干系统表,用于提供更多关于悲观锁的锁冲突和锁等待的信息。关于这些表的详细说明,请参考相关系统表的文档: - -* [`TIDB_TRX` 与 `CLUSTER_TIDB_TRX`](/information-schema/information-schema-tidb-trx.md):提供当前 TiDB 节点上或整个集群上所有运行中的事务的信息,包括事务是否处于等锁状态、等锁时间和事务曾经执行过的语句的 Digest 等信息。 -* [`DATA_LOCK_WAITS`](/information-schema/information-schema-data-lock-waits.md):提供关于 TiKV 内的悲观锁等锁信息,包括阻塞和被阻塞的事务的 `start_ts`、被阻塞的 SQL 语句的 Digest 和发生等待的 key。 -* [`DEADLOCKS` 与 `CLUSTER_DEADLOCKS`](/information-schema/information-schema-deadlocks.md):提供当前 TiDB 节点上或整个集群上最近发生过的若干次死锁的相关信息,包括死锁环中事务之间的等待关系、事务当前正在执行的语句的 Digest 和发生等待的 key。 - -> **警告:** -> -> 该功能目前为实验性功能,相关表结构的定义和行为在未来版本可能有较大改动。 - -以下为排查部分问题的示例。 - -#### 死锁错误 - -要获取最近发生的死锁错误的信息,可查询 `DEADLOCKS` 或 `CLUSTER_DEADLOCKS` 表,示例如下: - -{{< copyable "sql" >}} - -```sql -select * from information_schema.deadlocks; -``` - -```sql -+-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ -| DEADLOCK_ID | OCCUR_TIME | RETRYABLE | TRY_LOCK_TRX_ID | CURRENT_SQL_DIGEST | KEY | TRX_HOLDING_LOCK | -+-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ -| 1 | 2021-06-04 08:22:38.765699 | 0 | 425405959304904707 | 22230766411edb40f27a68dadefc63c6c6970d5827f1e5e22fc97be2c4d8350d | 7480000000000000385F728000000000000002 | 425405959304904708 | -| 1 | 2021-06-04 08:22:38.765699 | 0 | 425405959304904708 | 22230766411edb40f27a68dadefc63c6c6970d5827f1e5e22fc97be2c4d8350d | 7480000000000000385F728000000000000001 | 425405959304904707 | -+-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ -``` - -查询结果会显示死锁错误中多个事务之间的等待关系和各个事务当前正在执行的 SQL 语句的 Digest,以及发生冲突的 key。 - -你可以从 `STATEMENTS_SUMMARY` 或 `STATEMENTS_SUMMARY_HISTORY` 表中获取最近一段时间内,执行过的 SQL 语句的 Digest 所对应的归一化的 SQL 语句的文本(详见 [`STATEMENTS_SUMMARY` 和 `STATEMENTS_SUMMARY_HISTORY` 表](/statement-summary-tables.md))。你也可将获取到的结果直接与 `DEADLOCKS` 表进行 join 操作。注意:`STATEMENTS_SUMMARY` 中可能不包含所有 SQL 语句的信息,所以以下例子中使用了 left join: - -{{< copyable "sql" >}} - -```sql -select l.deadlock_id, l.occur_time, l.try_lock_trx_id, l.trx_holding_lock, s.digest_text from information_schema.deadlocks as l left join information_schema.statements_summary as s on l.current_sql_digest = s.digest; -``` - -```sql -+-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ -| deadlock_id | occur_time | try_lock_trx_id | trx_holding_lock | digest_text | -+-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ -| 1 | 2021-06-04 08:22:38.765699 | 425405959304904707 | 425405959304904708 | update `t` set `v` = ? where `id` = ? ; | -| 1 | 2021-06-04 08:22:38.765699 | 425405959304904708 | 425405959304904707 | update `t` set `v` = ? where `id` = ? ; | -+-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ -``` - -#### 少数热点 key 造成锁排队 - -`DATA_LOCK_WAITS` 系统表提供 TiKV 节点上的等锁情况。查询该表时,TiDB 将自动从所有 TiKV 节点上获取当前时刻的等锁信息。当少数热点 key 频繁被上锁并阻塞较多事务时,你可以查询 `DATA_LOCK_WAITS` 表并按 key 对结果进行聚合,以尝试找出经常发生问题的 key: - -{{< copyable "sql" >}} - -```sql -select `key`, count(*) as `count` from information_schema.data_lock_waits group by `key` order by `count` desc; -``` - -```sql -+----------------------------------------+-------+ -| key | count | -+----------------------------------------+-------+ -| 7480000000000000415f728000000000000001 | 2 | -| 7480000000000000415f728000000000000002 | 1 | -+----------------------------------------+-------+ -``` - -为避免偶然性,你可考虑进行多次查询。 - -如果已知频繁出问题的 key,可尝试从 `TIDB_TRX` 或 `CLUSTER_TIDB_TRX` 表中获取试图上锁该 key 的事务的信息。 - -需要注意 `TIDB_TRX` 和 `CLUSTER_TIDB_TRX` 表所展示的信息也是对其进行查询的时刻正在运行的事务的信息,并不展示已经结束的事务。如果并发的事务数量很大,该查询的结果集也可能很大,可以考虑添加 limit 子句,或用 where 子句筛选出等锁时间较长的事务。需要注意,对 Lock View 中的多张表进行 join 时,不同表之间的数据并不保证在同一时刻获取,因而不同表中的信息可能并不同步。 - -{{< copyable "sql" >}} - -```sql -select trx.* from information_schema.data_lock_waits as l left join information_schema.tidb_trx as trx on l.trx_id = trx.id where l.key = "7480000000000000415f728000000000000001"\G -``` - -```sql -*************************** 1. row *************************** - ID: 425496938634543111 - START_TIME: 2021-06-08 08:46:48.341000 -CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 - STATE: LockWaiting -WAITING_START_TIME: 2021-06-08 08:46:48.388024 - MEM_BUFFER_KEYS: 1 - MEM_BUFFER_BYTES: 19 - SESSION_ID: 87 - USER: root - DB: test - ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 -ba07e3cc34b6b3be7b7c2de7fe9, a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9] -*************************** 2. row *************************** - ID: 425496940994101249 - START_TIME: 2021-06-08 08:46:57.342000 -CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 - STATE: LockWaiting -WAITING_START_TIME: 2021-06-08 08:46:57.590060 - MEM_BUFFER_KEYS: 0 - MEM_BUFFER_BYTES: 0 - SESSION_ID: 85 - USER: root - DB: test - ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 -ba07e3cc34b6b3be7b7c2de7fe9] -2 rows in set (0.00 sec) -``` - -#### 事务被长时间阻塞 - -如果已知一个事务被另一事务(或多个事务)阻塞,且已知当前事务的 `start_ts`(即事务 ID),则可使用如下方式获取导致该事务阻塞的事务的信息。注意对 Lock View 中的多张表进行 join 时,不同表之间的数据并不保证在同一时刻获取,因而可能不同表中的信息可能并不同步。 - -{{< copyable "sql" >}} - -```sql -select l.key, trx.* from information_schema.data_lock_waits as l join information_schema.tidb_trx as trx on l.current_holding_trx_id = trx.id where l.trx_id = 425497223886536705\G -``` - -```sql -*************************** 1. row *************************** - key: 7480000000000000475f728000000000000002 - ID: 425497219115778059 - START_TIME: 2021-06-08 09:04:38.292000 -CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 - STATE: LockWaiting -WAITING_START_TIME: 2021-06-08 09:04:38.336264 - MEM_BUFFER_KEYS: 1 - MEM_BUFFER_BYTES: 19 - SESSION_ID: 97 - USER: root - DB: test - ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 -ba07e3cc34b6b3be7b7c2de7fe9, a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9] -1 row in set (0.01 sec) -``` - -如果当前事务的 `start_ts` 未知,可以尝试从 `TIDB_TRX` / `CLUSTER_TIDB_TRX` 表或者 [`PROCESSLIST` / `CLUSTER_PROCESSLIST`](/information-schema/information-schema-processlist.md) 表中的信息进行判断。 +--- +title: TiDB 锁冲突问题处理 +summary: 了解 TiDB 锁冲突问题以及处理方式。 +--- + +# TiDB 锁冲突问题处理 + +TiDB 支持完整的分布式事务,自 v3.0 版本起,提供乐观事务与悲观事务两种事务模型。本文介绍在使用乐观事务或者悲观事务的过程中常见的锁冲突问题以及解决思路。 + +## 乐观锁 + +TiDB 中事务使用两阶段提交,分为 Prewrite 和 Commit 两个阶段,示意图如下。 + +![TiDB 中乐观事务的两阶段提交](/media/troubleshooting-lock-pic-01.png) + +相关细节本节不再赘述,详情可阅读 [Percolator 和 TiDB 事务算法](https://pingcap.com/blog-cn/percolator-and-txn/)。 + +### Prewrite 阶段 + +在两阶段提交的 Prewrite 阶段,TiDB 会对目标 key 分别上 primary lock 和 secondary lock。在冲突严重的场景中,会出现写冲突 (write conflict)、keyislocked 等报错。具体而言,这个阶段可能会遇到的锁相关的报错信息如下。 + +#### 读写冲突 + +在 TiDB 中,读取数据时,会获取一个包含当前物理时间且全局唯一递增的时间戳作为当前事务的 start_ts。事务在读取时,需要读到目标 key 的 commit_ts 小于这个事务的 start_ts 的最新的数据版本。当读取时发现目标 key 上存在 lock 时,因为无法知道上锁的那个事务是在 Commit 阶段还是 Prewrite 阶段,所以就会出现读写冲突的情况,如下图: + +![读写冲突](/media/troubleshooting-lock-pic-04.png) + +分析: + +Txn0 完成了 Prewrite,在 Commit 的过程中 Txn1 对该 key 发起了读请求,Txn1 需要读取 start_ts > commit_ts 最近的 key 的版本。此时,Txn1 的 `start_ts > Txn0` 的 lock_ts,需要读取的 key 上的锁信息仍未清理,故无法判断 Txn0 是否提交成功,因此 Txn1 与 Txn0 出现读写冲突。 + +你可以通过如下两种途径来检测当前环境中是否存在读写冲突: + +1. TiDB 监控及日志 + + * 通过 TiDB Grafana 监控分析: + + 观察 KV Errors 下 Lock Resolve OPS 面板中的 not_expired/resolve 监控项以及 KV Backoff OPS 面板中的 txnLockFast 监控项,如果有较为明显的上升趋势,那么可能是当前的环境中出现了大量的读写冲突。其中,not_expired 是指对应的锁还没有超时,resolve 是指尝试清锁的操作,txnLockFast 代表出现了读写冲突。 + + ![KV-backoff-txnLockFast-optimistic](/media/troubleshooting-lock-pic-09.png) + ![KV-Errors-resolve-optimistic](/media/troubleshooting-lock-pic-08.png) + + * 通过 TiDB 日志分析: + + 在 TiDB 的日志中可以看到下列信息: + + ```log + [INFO] [coprocessor.go:743] ["[TIME_COP_PROCESS] resp_time:406.038899ms txnStartTS:416643508703592451 region_id:8297 store_addr:10.8.1.208:20160 backoff_ms:255 backoff_types:[txnLockFast,txnLockFast] kv_process_ms:333 scan_total_write:0 scan_processed_write:0 scan_total_data:0 scan_processed_data:0 scan_total_lock:0 scan_processed_lock:0"] + ``` + + * txnStartTS:发起读请求的事务的 start_ts,如上面示例中的 416643508703592451 + * backoff_types:读写发生了冲突,并且读请求进行了 backoff 重试,重试的类型为 txnLockFast + * backoff_ms:读请求 backoff 重试的耗时,单位为 ms ,如上面示例中的 255 + * region_id:读请求访问的目标 region 的 id + +2. 通过 TiKV 日志分析: + + 在 TiKV 的日志可以看到下列信息: + + ```log + [ERROR] [endpoint.rs:454] [error-response] [err=""locked primary_lock:7480000000000004D35F6980000000000000010380000000004C788E0380000000004C0748 lock_version: 411402933858205712 key: 7480000000000004D35F7280000000004C0748 lock_ttl: 3008 txn_size: 1""] + ``` + + 这段报错信息表示出现了读写冲突,当读数据时发现 key 有锁阻碍读,这个锁包括未提交的乐观锁和未提交的 prewrite 后的悲观锁。 + + * primary_lock:锁对应事务的 primary lock。 + * lock_version:锁对应事务的 start_ts。 + * key:表示被锁的 key。 + * lock_ttl: 锁的 TTL。 + * txn_size:锁所在事务在其 Region 的 key 数量,指导清锁方式。 + +处理建议: + +* 在遇到读写冲突时会有 backoff 自动重试机制,如上述示例中 Txn1 会进行 backoff 重试,单次初始 100 ms,单次最大 3000 ms,总共最大 20000 ms + +* 可以使用 TiDB Control 的子命令 [decoder](/tidb-control.md#decoder-命令) 来查看指定 key 对应的行的 table id 以及 rowid: + + ```sh + ./tidb-ctl decoder -f table_row -k "t\x00\x00\x00\x00\x00\x00\x00\x1c_r\x00\x00\x00\x00\x00\x00\x00\xfa" + + table_id: -9223372036854775780 + row_id: -9223372036854775558 + ``` + +#### KeyIsLocked 错误 + +事务在 Prewrite 阶段的第一步就会检查是否有写写冲突,第二步会检查目标 key 是否已经被另一个事务上锁。当检测到该 key 被 lock 后,会在 TiKV 端报出 KeyIsLocked。目前该报错信息没有打印到 TiDB 以及 TiKV 的日志中。与读写冲突一样,在出现 KeyIsLocked 时,后台会自动进行 backoff 重试。 + +你可以通过 TiDB Grafana 监控检测 KeyIsLocked 错误: + +观察 KV Errors 下 Lock Resolve OPS 面板中的 resolve 监控项以及 KV Backoff OPS 面板中的 txnLock 监控项,会有比较明显的上升趋势,其中 resolve 是指尝试清锁的操作,txnLock 代表出现了写冲突。 + +![KV-backoff-txnLockFast-optimistic-01](/media/troubleshooting-lock-pic-07.png) +![KV-Errors-resolve-optimistic-01](/media/troubleshooting-lock-pic-08.png) + +处理建议: + +* 监控中出现少量 txnLock,无需过多关注。后台会自动进行 backoff 重试,单次初始 200 ms,单次最大 3000 ms。 +* 如果出现大量的 txnLock,需要从业务的角度评估下冲突的原因。 +* 使用悲观锁模式。 + +### Commit 阶段 + +当 Prewrite 全部完成时,客户端便会取得 commit_ts,然后继续两阶段提交的第二阶段。这里需要注意的是,由于 primary key 是否提交成功标志着整个事务是否提交成功,因而客户端需要在单独 commit primary key 之后再继续 commit 其余的 key。 + +#### 锁被清除 (LockNotFound) 错误 + +TxnLockNotFound 错误是由于事务提交的慢了,超过了 TTL 的时间。当要提交时,发现被其他事务给 Rollback 掉了。在开启 TiDB [自动重试事务](/system-variables.md#tidb_retry_limit)的情况下,会自动在后台进行事务重试(注意显示和隐式事务的差别)。 + +你可以通过如下两种途径来查看 LockNotFound 报错信息: + +1. 查看 TiDB 日志 + + 如果出现了 TxnLockNotFound 的报错,会在 TiDB 的日志中看到下面的信息: + + ```log + [WARN] [session.go:446] ["commit failed"] [conn=149370] ["finished txn"="Txn{state=invalid}"] [error="[kv:6]Error: KV error safe to retry tikv restarts txn: Txn(Mvcc(TxnLockNotFound{ start_ts: 412720515987275779, commit_ts: 412720519984971777, key: [116, 128, 0, 0, 0, 0, 1, 111, 16, 95, 114, 128, 0, 0, 0, 0, 0, 0, 2] })) [try again later]"] + ``` + + * start_ts:出现 TxnLockNotFound 报错的事务的 start_ts,如上例中的 412720515987275779 + * commit_ts:出现 TxnLockNotFound 报错的事务的 commit_ts,如上例中的 412720519984971777 + +2. 查看 TiKV 日志 + + 如果出现了 TxnLockNotFound 的报错,在 TiKV 的日志中同样可以看到相应的报错信息: + + ```log + Error: KV error safe to retry restarts txn: Txn(Mvcc(TxnLockNotFound)) [ERROR [Kv.rs:708] ["KvService::batch_raft send response fail"] [err=RemoteStoped] + ``` + +处理建议: + +* 通过检查 start_ts 和 commit_ts 之间的提交间隔,可以确认是否超过了默认的 TTL 的时间。 + + 查看提交间隔: + + ```shell + ./pd-ctl tso [start_ts] + ./pd-ctl tso [commit_ts] + ``` + +* 建议检查下是否是因为写入性能的缓慢导致事务提交的效率差,进而出现了锁被清除的情况。 + +* 在关闭 TiDB 事务重试的情况下,需要在应用端捕获异常,并进行重试。 + +## 悲观锁 + +在 v3.0.8 之前,TiDB 默认使用的乐观事务模式会导致事务提交时因为冲突而失败。为了保证事务的成功率,需要修改应用程序,加上重试的逻辑。悲观事务模式可以避免这个问题,应用程序无需添加重试逻辑,就可以正常执行。 + +TiDB 悲观锁复用了乐观锁的两阶段提交逻辑,重点在 DML 执行时做了改造。 + +![TiDB 悲观事务的提交逻辑](/media/troubleshooting-lock-pic-05.png) + +在两阶段提交之前增加了 Acquire Pessimistic Lock 阶段,简要步骤如下。 + +1. (同乐观锁)TiDB 收到来自客户端的 begin 请求,获取当前版本号作为本事务的 StartTS。 +2. TiDB 收到来自客户端的更新数据的请求:TiDB 向 TiKV 发起加悲观锁请求,该锁持久化到 TiKV。 +3. (同乐观锁)客户端发起 commit,TiDB 开始执行与乐观锁一样的两阶段提交。 + +![TiDB 中的悲观事务](/media/troubleshooting-lock-pic-06.png) + +相关细节本节不再赘述,详情可阅读 [TiDB 悲观锁实现原理](https://asktug.com/t/topic/33550)。 + +### Prewrite 阶段 + +在悲观锁模式下,在事务的提交阶段沿用的仍然是乐观锁模式,所以在 Prewrite 阶段乐观锁遇到的锁相关的一些报错,在悲观锁模式同样会遇到。 + +#### 读写冲突 + +报错信息以及处理建议同乐观锁模式。 + +### Commit 阶段 + +在乐观模型下,会出现 TxnLockNotFound 错误,而在悲观锁模型下,不会出现这个问题。同样的,悲观锁也有一个 TTL 的时间。txn heartbeat 会自动的更新事务的 TTL,以确保第二个事务不会将第一个事务的锁清掉。 + +### 其他锁相关错误 + +#### pessimistic lock retry limit reached + +在冲突非常严重的场景下,或者当发生 write conflict 时,乐观事务会直接终止,而悲观事务会尝试用最新数据重试该语句直到没有 write conflict。因为 TiDB 的加锁操作是一个写入操作,且操作过程是先读后写,需要 2 次 RPC。如果在这中间发生了 write conflict,那么会重试。每次重试都会打印日志,不用特别关注。重试次数由 [pessimistic-txn.max-retry-count](/tidb-configuration-file.md#max-retry-count) 定义。 + +可通过查看 TiDB 日志查看报错信息: + +悲观事务模式下,如果发生 write conflict,并且重试的次数达到了上限,那么在 TiDB 的日志中会出现含有下述关键字的报错信息。如下: + +```log +err="pessimistic lock retry limit reached" +``` + +处理建议: + +* 如果上述报错出现的比较频繁,建议从业务的角度进行调整。 + +#### Lock wait timeout exceeded + +在悲观锁模式下,事务之间出现会等锁的情况。等锁的超时时间由 TiDB 的 [innodb_lock_wait_timeout](/system-variables.md#innodb_lock_wait_timeout) 参数来定义,这个是 SQL 语句层面的最大允许等锁时间,即一个 SQL 语句期望加锁,但锁一直获取不到,超过这个时间,TiDB 不会再尝试加锁,会向客户端返回相应的报错信息。 + +可通过查看 TiDB 日志查看报错信息: + +当出现等锁超时的情况时,会向客户端返回下述报错信息: + +```log +ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction +``` + +处理建议: + +* 如果出现的次数非常频繁,建议从业务逻辑的角度来进行调整。 + +#### TTL manager has timed out + +除了有不能超出 GC 时间的限制外,悲观锁的 TTL 有上限,默认为 1 小时,所以执行时间超过 1 小时的悲观事务有可能提交失败。这个超时时间由 TiDB 参数 [performance.max-txn-ttl](https://github.com/pingcap/tidb/blob/master/config/config.toml.example) 指定。 + +可通过查看 TiDB 日志查看报错信息: + +当悲观锁的事务执行时间超过 TTL 时,会出现下述报错: + +```log +TTL manager has timed out, pessimistic locks may expire, please commit or rollback this transaction +``` + +处理建议: + +* 当遇到该报错时,建议确认下业务逻辑是否可以进行优化,如将大事务拆分为小事务。在未使用[大事务](/tidb-configuration-file.md#txn-total-size-limit)的前提下,大事务可能会触发 TiDB 的事务限制。 + +* 可适当调整相关参数,使其符合事务要求。 + +#### Deadlock found when trying to get lock + +死锁是指两个或两个以上的事务在执行过程中,由于竞争资源而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去,将永远在互相等待。此时,需要终止其中一个事务使其能够继续推进下去。 + +TiDB 在使用悲观锁的情况下,多个事务之间出现了死锁,必定有一个事务 abort 来解开死锁。在客户端层面行为和 MySQL 一致,在客户端返回表示死锁的 Error 1213。如下: + +```log +[err="[executor:1213]Deadlock found when trying to get lock; try restarting transaction"] +``` + +处理建议: + +* 如果难以确认产生死锁的原因,对于 v5.1 及以后的版本,建议尝试查询 `INFORMATION_SCHEMA.DEADLOCKS` 或 `INFORMATION_SCHEMA.CLUSTER_DEADLOCKS` 系统表来获取死锁的等待链信息。详情请参考[死锁错误](#死锁错误)小节和 [`DEADLOCKS` 表](/information-schema/information-schema-deadlocks.md)文档。 +* 如果出现非常频繁,需要调整业务代码来降低死锁发生概率。 + +### 使用 Lock View 排查悲观锁相关的问题 + +自 v5.1 版本起,TiDB 支持 Lock View 功能。该功能在 `information_schema` 中内置了若干系统表,用于提供更多关于悲观锁的锁冲突和锁等待的信息。关于这些表的详细说明,请参考相关系统表的文档: + +* [`TIDB_TRX` 与 `CLUSTER_TIDB_TRX`](/information-schema/information-schema-tidb-trx.md):提供当前 TiDB 节点上或整个集群上所有运行中的事务的信息,包括事务是否处于等锁状态、等锁时间和事务曾经执行过的语句的 Digest 等信息。 +* [`DATA_LOCK_WAITS`](/information-schema/information-schema-data-lock-waits.md):提供关于 TiKV 内的悲观锁等锁信息,包括阻塞和被阻塞的事务的 `start_ts`、被阻塞的 SQL 语句的 Digest 和发生等待的 key。 +* [`DEADLOCKS` 与 `CLUSTER_DEADLOCKS`](/information-schema/information-schema-deadlocks.md):提供当前 TiDB 节点上或整个集群上最近发生过的若干次死锁的相关信息,包括死锁环中事务之间的等待关系、事务当前正在执行的语句的 Digest 和发生等待的 key。 + +> **警告:** +> +> 该功能目前为实验性功能,相关表结构的定义和行为在未来版本可能有较大改动。 + +以下为排查部分问题的示例。 + +#### 死锁错误 + +要获取最近发生的死锁错误的信息,可查询 `DEADLOCKS` 或 `CLUSTER_DEADLOCKS` 表,示例如下: + +{{< copyable "sql" >}} + +```sql +select * from information_schema.deadlocks; +``` + +```sql ++-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ +| DEADLOCK_ID | OCCUR_TIME | RETRYABLE | TRY_LOCK_TRX_ID | CURRENT_SQL_DIGEST | KEY | TRX_HOLDING_LOCK | ++-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ +| 1 | 2021-06-04 08:22:38.765699 | 0 | 425405959304904707 | 22230766411edb40f27a68dadefc63c6c6970d5827f1e5e22fc97be2c4d8350d | 7480000000000000385F728000000000000002 | 425405959304904708 | +| 1 | 2021-06-04 08:22:38.765699 | 0 | 425405959304904708 | 22230766411edb40f27a68dadefc63c6c6970d5827f1e5e22fc97be2c4d8350d | 7480000000000000385F728000000000000001 | 425405959304904707 | ++-------------+----------------------------+-----------+--------------------+------------------------------------------------------------------+----------------------------------------+--------------------+ +``` + +查询结果会显示死锁错误中多个事务之间的等待关系和各个事务当前正在执行的 SQL 语句的 Digest,以及发生冲突的 key。 + +你可以从 `STATEMENTS_SUMMARY` 或 `STATEMENTS_SUMMARY_HISTORY` 表中获取最近一段时间内,执行过的 SQL 语句的 Digest 所对应的归一化的 SQL 语句的文本(详见 [`STATEMENTS_SUMMARY` 和 `STATEMENTS_SUMMARY_HISTORY` 表](/statement-summary-tables.md))。你也可将获取到的结果直接与 `DEADLOCKS` 表进行 join 操作。注意:`STATEMENTS_SUMMARY` 中可能不包含所有 SQL 语句的信息,所以以下例子中使用了 left join: + +{{< copyable "sql" >}} + +```sql +select l.deadlock_id, l.occur_time, l.try_lock_trx_id, l.trx_holding_lock, s.digest_text from information_schema.deadlocks as l left join information_schema.statements_summary as s on l.current_sql_digest = s.digest; +``` + +```sql ++-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ +| deadlock_id | occur_time | try_lock_trx_id | trx_holding_lock | digest_text | ++-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ +| 1 | 2021-06-04 08:22:38.765699 | 425405959304904707 | 425405959304904708 | update `t` set `v` = ? where `id` = ? ; | +| 1 | 2021-06-04 08:22:38.765699 | 425405959304904708 | 425405959304904707 | update `t` set `v` = ? where `id` = ? ; | ++-------------+----------------------------+--------------------+--------------------+-----------------------------------------+ +``` + +#### 少数热点 key 造成锁排队 + +`DATA_LOCK_WAITS` 系统表提供 TiKV 节点上的等锁情况。查询该表时,TiDB 将自动从所有 TiKV 节点上获取当前时刻的等锁信息。当少数热点 key 频繁被上锁并阻塞较多事务时,你可以查询 `DATA_LOCK_WAITS` 表并按 key 对结果进行聚合,以尝试找出经常发生问题的 key: + +{{< copyable "sql" >}} + +```sql +select `key`, count(*) as `count` from information_schema.data_lock_waits group by `key` order by `count` desc; +``` + +```sql ++----------------------------------------+-------+ +| key | count | ++----------------------------------------+-------+ +| 7480000000000000415f728000000000000001 | 2 | +| 7480000000000000415f728000000000000002 | 1 | ++----------------------------------------+-------+ +``` + +为避免偶然性,你可考虑进行多次查询。 + +如果已知频繁出问题的 key,可尝试从 `TIDB_TRX` 或 `CLUSTER_TIDB_TRX` 表中获取试图上锁该 key 的事务的信息。 + +需要注意 `TIDB_TRX` 和 `CLUSTER_TIDB_TRX` 表所展示的信息也是对其进行查询的时刻正在运行的事务的信息,并不展示已经结束的事务。如果并发的事务数量很大,该查询的结果集也可能很大,可以考虑添加 limit 子句,或用 where 子句筛选出等锁时间较长的事务。需要注意,对 Lock View 中的多张表进行 join 时,不同表之间的数据并不保证在同一时刻获取,因而不同表中的信息可能并不同步。 + +{{< copyable "sql" >}} + +```sql +select trx.* from information_schema.data_lock_waits as l left join information_schema.tidb_trx as trx on l.trx_id = trx.id where l.key = "7480000000000000415f728000000000000001"\G +``` + +```sql +*************************** 1. row *************************** + ID: 425496938634543111 + START_TIME: 2021-06-08 08:46:48.341000 +CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 + STATE: LockWaiting +WAITING_START_TIME: 2021-06-08 08:46:48.388024 + MEM_BUFFER_KEYS: 1 + MEM_BUFFER_BYTES: 19 + SESSION_ID: 87 + USER: root + DB: test + ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 +ba07e3cc34b6b3be7b7c2de7fe9, a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9] +*************************** 2. row *************************** + ID: 425496940994101249 + START_TIME: 2021-06-08 08:46:57.342000 +CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 + STATE: LockWaiting +WAITING_START_TIME: 2021-06-08 08:46:57.590060 + MEM_BUFFER_KEYS: 0 + MEM_BUFFER_BYTES: 0 + SESSION_ID: 85 + USER: root + DB: test + ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 +ba07e3cc34b6b3be7b7c2de7fe9] +2 rows in set (0.00 sec) +``` + +#### 事务被长时间阻塞 + +如果已知一个事务被另一事务(或多个事务)阻塞,且已知当前事务的 `start_ts`(即事务 ID),则可使用如下方式获取导致该事务阻塞的事务的信息。注意对 Lock View 中的多张表进行 join 时,不同表之间的数据并不保证在同一时刻获取,因而可能不同表中的信息可能并不同步。 + +{{< copyable "sql" >}} + +```sql +select l.key, trx.* from information_schema.data_lock_waits as l join information_schema.tidb_trx as trx on l.current_holding_trx_id = trx.id where l.trx_id = 425497223886536705\G +``` + +```sql +*************************** 1. row *************************** + key: 7480000000000000475f728000000000000002 + ID: 425497219115778059 + START_TIME: 2021-06-08 09:04:38.292000 +CURRENT_SQL_DIGEST: a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9 + STATE: LockWaiting +WAITING_START_TIME: 2021-06-08 09:04:38.336264 + MEM_BUFFER_KEYS: 1 + MEM_BUFFER_BYTES: 19 + SESSION_ID: 97 + USER: root + DB: test + ALL_SQL_DIGESTS: [0fdc781f19da1c6078c9de7eadef8a307889c001e05f107847bee4cfc8f3cdf3, a4e28cc182bdd18288e2a34180499b9404cd0 +ba07e3cc34b6b3be7b7c2de7fe9, a4e28cc182bdd18288e2a34180499b9404cd0ba07e3cc34b6b3be7b7c2de7fe9] +1 row in set (0.01 sec) +``` + +如果当前事务的 `start_ts` 未知,可以尝试从 `TIDB_TRX` / `CLUSTER_TIDB_TRX` 表或者 [`PROCESSLIST` / `CLUSTER_PROCESSLIST`](/information-schema/information-schema-processlist.md) 表中的信息进行判断。