- 关于 TiDB
- 快速上手
- 应用开发
- 概览
- 快速开始
- 示例程序
- 连接到 TiDB
- 数据库模式设计
- 数据写入
- 数据读取
- 事务
- 优化 SQL 性能
- 故障诊断
- 引用文档
- 云原生开发环境
- 部署标准集群
- 数据迁移
- 运维操作
- 监控与告警
- 故障诊断
- 性能调优
- 优化手册
- 配置调优
- SQL 性能调优
- SQL 性能调优概览
- 理解 TiDB 执行计划
- SQL 优化流程
- 控制执行计划
- 教程
- 同城多中心部署
- 两地三中心部署
- 同城两中心部署
- 读取历史数据
- 使用 Stale Read 功能读取历史数据(推荐)
- 使用系统变量
tidb_snapshot
读取历史数据
- 最佳实践
- Placement Rules 使用文档
- Load Base Split 使用文档
- Store Limit 使用文档
- TiDB 工具
- 功能概览
- 适用场景
- 工具下载
- TiUP
- 文档地图
- 概览
- 术语及核心概念
- TiUP 组件管理
- FAQ
- 故障排查
- TiUP 命令参考手册
- 命令概览
- TiUP 命令
- TiUP Cluster 命令
- TiUP Cluster 命令概览
- tiup cluster audit
- tiup cluster check
- tiup cluster clean
- tiup cluster deploy
- tiup cluster destroy
- tiup cluster disable
- tiup cluster display
- tiup cluster edit-config
- tiup cluster enable
- tiup cluster help
- tiup cluster import
- tiup cluster list
- tiup cluster patch
- tiup cluster prune
- tiup cluster reload
- tiup cluster rename
- tiup cluster replay
- tiup cluster restart
- tiup cluster scale-in
- tiup cluster scale-out
- tiup cluster start
- tiup cluster stop
- tiup cluster template
- tiup cluster upgrade
- TiUP DM 命令
- TiUP DM 命令概览
- tiup dm audit
- tiup dm deploy
- tiup dm destroy
- tiup dm disable
- tiup dm display
- tiup dm edit-config
- tiup dm enable
- tiup dm help
- tiup dm import
- tiup dm list
- tiup dm patch
- tiup dm prune
- tiup dm reload
- tiup dm replay
- tiup dm restart
- tiup dm scale-in
- tiup dm scale-out
- tiup dm start
- tiup dm stop
- tiup dm template
- tiup dm upgrade
- TiDB 集群拓扑文件配置
- DM 集群拓扑文件配置
- TiUP 镜像参考指南
- TiUP 组件文档
- PingCAP Clinic 诊断服务 (Technical Preview)
- TiDB Operator
- Dumpling
- TiDB Lightning
- TiDB Data Migration
- 关于 Data Migration
- 架构简介
- 快速开始
- 部署 DM 集群
- 入门指南
- 进阶教程
- 运维管理
- 参考手册
- 使用示例
- 异常解决
- 版本发布历史
- Backup & Restore (BR)
- TiDB Binlog
- TiCDC
- TiUniManager
- sync-diff-inspector
- TiSpark
- 参考指南
- 架构
- 监控指标
- 安全加固
- 权限
- SQL
- SQL 语言结构和语法
- SQL 语句
ADD COLUMN
ADD INDEX
ADMIN
ADMIN CANCEL DDL
ADMIN CHECKSUM TABLE
ADMIN CHECK [TABLE|INDEX]
ADMIN SHOW DDL [JOBS|QUERIES]
ADMIN SHOW TELEMETRY
ALTER DATABASE
ALTER INDEX
ALTER INSTANCE
ALTER PLACEMENT POLICY
ALTER TABLE
ALTER TABLE COMPACT
ALTER USER
ANALYZE TABLE
BACKUP
BATCH
BEGIN
CHANGE COLUMN
CHANGE DRAINER
CHANGE PUMP
COMMIT
CREATE [GLOBAL|SESSION] BINDING
CREATE DATABASE
CREATE INDEX
CREATE PLACEMENT POLICY
CREATE ROLE
CREATE SEQUENCE
CREATE TABLE LIKE
CREATE TABLE
CREATE USER
CREATE VIEW
DEALLOCATE
DELETE
DESC
DESCRIBE
DO
DROP [GLOBAL|SESSION] BINDING
DROP COLUMN
DROP DATABASE
DROP INDEX
DROP PLACEMENT POLICY
DROP ROLE
DROP SEQUENCE
DROP STATS
DROP TABLE
DROP USER
DROP VIEW
EXECUTE
EXPLAIN ANALYZE
EXPLAIN
FLASHBACK TABLE
FLUSH PRIVILEGES
FLUSH STATUS
FLUSH TABLES
GRANT <privileges>
GRANT <role>
INSERT
KILL [TIDB]
LOAD DATA
LOAD STATS
MODIFY COLUMN
PREPARE
RECOVER TABLE
RENAME INDEX
RENAME TABLE
REPLACE
RESTORE
REVOKE <privileges>
REVOKE <role>
ROLLBACK
SELECT
SET DEFAULT ROLE
SET [NAMES|CHARACTER SET]
SET PASSWORD
SET ROLE
SET TRANSACTION
SET [GLOBAL|SESSION] <variable>
SHOW [BACKUPS|RESTORES]
SHOW ANALYZE STATUS
SHOW [GLOBAL|SESSION] BINDINGS
SHOW BUILTINS
SHOW CHARACTER SET
SHOW COLLATION
SHOW [FULL] COLUMNS FROM
SHOW CONFIG
SHOW CREATE PLACEMENT POLICY
SHOW CREATE SEQUENCE
SHOW CREATE TABLE
SHOW CREATE USER
SHOW DATABASES
SHOW DRAINER STATUS
SHOW ENGINES
SHOW ERRORS
SHOW [FULL] FIELDS FROM
SHOW GRANTS
SHOW INDEX [FROM|IN]
SHOW INDEXES [FROM|IN]
SHOW KEYS [FROM|IN]
SHOW MASTER STATUS
SHOW PLACEMENT
SHOW PLACEMENT FOR
SHOW PLACEMENT LABELS
SHOW PLUGINS
SHOW PRIVILEGES
SHOW [FULL] PROCESSSLIST
SHOW PROFILES
SHOW PUMP STATUS
SHOW SCHEMAS
SHOW STATS_HEALTHY
SHOW STATS_HISTOGRAMS
SHOW STATS_META
SHOW STATUS
SHOW TABLE NEXT_ROW_ID
SHOW TABLE REGIONS
SHOW TABLE STATUS
SHOW [FULL] TABLES
SHOW [GLOBAL|SESSION] VARIABLES
SHOW WARNINGS
SHUTDOWN
SPLIT REGION
START TRANSACTION
TABLE
TRACE
TRUNCATE
UPDATE
USE
WITH
- 数据类型
- 函数与操作符
- 聚簇索引
- 约束
- 生成列
- SQL 模式
- 表属性
- 事务
- 视图
- 分区表
- 临时表
- 缓存表
- 字符集和排序
- Placement Rules in SQL
- 系统表
mysql
- INFORMATION_SCHEMA
- Overview
ANALYZE_STATUS
CLIENT_ERRORS_SUMMARY_BY_HOST
CLIENT_ERRORS_SUMMARY_BY_USER
CLIENT_ERRORS_SUMMARY_GLOBAL
CHARACTER_SETS
CLUSTER_CONFIG
CLUSTER_HARDWARE
CLUSTER_INFO
CLUSTER_LOAD
CLUSTER_LOG
CLUSTER_SYSTEMINFO
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
DATA_LOCK_WAITS
DDL_JOBS
DEADLOCKS
ENGINES
INSPECTION_RESULT
INSPECTION_RULES
INSPECTION_SUMMARY
KEY_COLUMN_USAGE
METRICS_SUMMARY
METRICS_TABLES
PARTITIONS
PLACEMENT_POLICIES
PROCESSLIST
REFERENTIAL_CONSTRAINTS
SCHEMATA
SEQUENCES
SESSION_VARIABLES
SLOW_QUERY
STATISTICS
TABLES
TABLE_CONSTRAINTS
TABLE_STORAGE_STATS
TIDB_HOT_REGIONS
TIDB_HOT_REGIONS_HISTORY
TIDB_INDEXES
TIDB_SERVERS_INFO
TIDB_TRX
TIFLASH_REPLICA
TIKV_REGION_PEERS
TIKV_REGION_STATUS
TIKV_STORE_STATUS
USER_PRIVILEGES
VIEWS
METRICS_SCHEMA
- UI
- CLI
- 命令行参数
- 配置文件参数
- 系统变量
- 存储引擎
- 遥测
- 错误码
- 通过拓扑 label 进行副本调度
- 常见问题解答 (FAQ)
- 版本发布历史
- 术语表
DM 安全模式
安全模式 (safe mode) 是 DM 在进行增量同步时候的一种运行模式,在安全模式中,DM 增量同步组件在同步 binlog event 时,将把所有 INSERT
和 UPDATE
操作强制进行改写后再在下游执行。
安全模式的目的是在增量同步过程中,同一条 binlog event 能够在下游被重复同步且保证幂等性,从而确保增量同步能够“安全”进行。
DM 从 checkpoint 恢复数据同步任务后,可能重复执行某些 binlog 事件而导致下述问题:
- 在进行增量同步过程中,执行 DML 的操作和写 checkpoint 的操作并不是同步的;写 checkpoint 的操作和写下游数据的操作也并不能保证原子性。因此,当 DM 异常退出时,checkpoint 可能只记录到退出时刻之前的一个恢复点。
- 当 DM 重启同步任务,并从 checkpoint 重新开始增量数据同步时,checkpoint 之后的部分数据可能已经在异常退出前被处理过了,从而导致部分 SQL 语句重复执行。
- 如果重复执行
INSERT
操作,会导致主键或唯一索引冲突,引发同步中断;如果重复执行UPDATE
操作,会导致不能根据筛选条件找到之前对应的更新记录。
在安全模式下,通过改写 SQL 语句,DM 可以解决上述问题。
安全模式原理
安全模式通过 SQL 语句改写来保证 binlog event 的幂等性。具体来说,在安全模式下:
INSERT
语句会被改写成REPLACE
语句。UPDATE
语句会被分析,得到该语句涉及的行的主键或唯一索引的值,然后改写成DELETE
+REPLACE
语句 :先根据主键或唯一索引的定位删除对应的行,然后使用REPLACE
语句插入一条最新值的行记录。
REPLACE
操作是 MySQL 特有的数据插入语法。使用 REPLACE
语法插入数据时,如果新插入的数据和现有数据存在主键或唯一约束冲突,MySQL 会删除所有冲突的记录,然后再执行插入记录操作,相当于“强制插入”的操作。具体请参考 MySQL 官方文档的 REPLACE
语句相关介绍 。
比如,假设一张表 dummydb.dummytbl
的主键是 id
,在这张表中重复执行下面的 SQL 语句:
INSERT INTO dummydb.dummytbl (id, int_value, str_value) VALUES (123, 999, 'abc');
UPDATE dummydb.dummytbl SET int_value = 888999 WHERE int_value = 999; # 假设没有其他 int_value = 999的数据
UPDATE dummydb.dummytbl SET id = 999 WHERE id = 888; # 更新主键操作
启用安全模式后,上述 SQL 语句再次在下游执行时,会被改写为下面的 SQL 语句:
REPLACE INTO dummydb.dummytbl (id, int_value, str_value) VALUES (123, 999, 'abc');
DELETE FROM dummydb.dummytbl WHERE id = 123;
REPLACE INTO dummydb.dummytbl (id, int_value, str_value) VALUES (123, 888999, 'abc');
DELETE FROM dummydb.dummytbl WHERE id = 888;
REPLACE INTO dummydb.dummytbl (id, int_value, str_value) VALUES (999, 888888, 'abc888');
上述语句中,UPDATE
被改写为 DELETE
+ REPLACE
,而不是 DELETE
+ INSERT
。如果这里使用 INSERT
,那么 id = 999
的记录在已经存在的情况下重复插入时,数据库会报错主键冲突。因此这里使用了 REPLACE
,新的记录会替换之前已经插入的记录。
通过这样的语句改写,在进行重复的插入或更新操作时,DM 都会使用执行该操作后的行数据来覆盖之前已经存在的行数据。这样保证了插入和更新操作的可重复执行。
开启安全模式
自动开启
当 DM 从 checkpoint 恢复增量同步任务(例如 worker 重启,网络中断重连等)时,会自动开启一段时间的安全模式。开启安全模式的逻辑,与 checkpoint 中的 safemode_exit_point
信息相关。当一个增量同步任务异常暂停时,DM 会先尝试将内存中所有的 DML 全部同步到下游,然后记录当前内存中从上游拉取到的最新的 binlog 位置点,记作 safemode_exit_point
,把 safemode_exit_point
保存在异常暂停之前最后一个 checkpoint 当中。
当 DM 从 checkpoint 恢复增量同步任务时,会根据下面的判断逻辑来开启安全模式:
如果 checkpoint 信息里面额外记录了
safemode_exit_point
,说明该增量同步任务属于异常暂停。如果 DM 在恢复该增量同步任务时,发现待恢复的 checkpoint 的 binlog 位置点在safemode_exit_point
之前,那么说明从这个 checkpoint 到safemode_exit_point
之间的 binlog 可能已经在下游被执行;恢复该任务后,部分 binlog 会被重复执行。因此,DM 判断在这段 binlog 范围内需要开启安全模式。在当前的 binlog 位置点超过了safemode_exit_point
后,未手动开启 safe-mode 的情况下,DM 将关闭安全模式。如果 checkpoint 信息里没有
safemode_exit_point
信息,则有两种情况:这是一个全新的任务,或该任务是正常暂停。
该任务异常暂停时,记录
safemode_exit_point
失败;或是 DM 进程异常退出。第二种情况下,DM 并不知道哪些 checkpoint 之后的 binlog 已经被执行过。为了保险起见,只要没有在 checkpoint 找到
safemode_exit_point
信息,DM 就会在前 2 个 checkpoint 间隔中都开启安全模式,确保这段时间里的 binlog 被重复执行不会引发问题。默认的 checkpoint 间隔是 30 秒,也就是说,一个正常的增量同步任务开始时,前 2 * 30 = 60 秒会自动强制开启安全模式。你可以配置 syncer 的配置项
checkpoint-flush-interval
来调整 checkpoint 的间隔,从而影响增量同步任务开始时安全模式的持续时间。一般情况下不建议调整,如有需要,应优先使用手动开启。
手动开启
你可以通过调整增量任务配置文件中 syncer 的配置项 safe-mode
,控制是否需要全程开启安全模式。safe-mode
是布尔类型参数,默认为 false
。如果设置为 true
,则表示在增量同步的全过程中都会开启安全模式。例如,下面是一个开启了安全模式的任务配置示例:
syncers: # sync 处理单元的运行配置参数
global: # 配置名称
# 其他配置省略
safe-mode: true # 增量同步任务全程开启"安全模式"
# 其他配置省略
# ----------- 实例配置 -----------
mysql-instances:
-
source-id: "mysql-replica-01"
# 其他配置省略
syncer-config-name: "global" # syncers 配置的名称
注意事项
如果你出于安全考虑,希望全程开启安全模式,你需要注意以下事项:
- 安全模式对于增量同步有额外开销。频繁的
DELETE
+REPLACE
操作会带来主键或唯一索引的频繁变化,带来了比单纯UPDATE
语句更大的性能开销。 - 安全模式会强制替换主键相同的记录,可能导致下游的记录数据丢失。如果由于上游分片表合并导入下游的配置不当,可能导致在上游合并导入到下游数据库的时候,出现大量的主键或唯一索引冲突。如果此时全程开启安全模式,会导致下游大量的数据丢失,并且可能无法从任务中看到有任何的异常,导致大量数据不一致。
- 安全模式依赖于通过主键或唯一索引来判断冲突。如果下游数据库对应的表没有主键或唯一索引,会导致
REPLACE
语句无法实现替换插入的目的。这种情况下,即使开启了安全模式,DM 将INSERT
语句改写成REPLACE
并执行后,依旧会向下游插入重复记录。
因此,如果上游存在主键重复的数据,业务可以接受丢失重复数据和性能损失,你可以开启安全模式来忽略数据重复错误。