{
    "componentChunkName": "component---src-templates-blog-blog-detail-tsx",
    "path": "/blog/tidb-as-mysql-slave",
    "result": {"pageContext":{"blog":{"id":"Blogs_112","title":"TiDB 作为 MySQL Slave 实现实时数据同步","tags":["MySQL","TiDB Binlog","工具","主从","数据同步"],"category":{"name":"产品技术解读"},"summary":"由于 TiDB 本身兼容绝大多数的 MySQL 语法，所以对于绝大多数业务来说，最安全的切换数据库方式就是将 TiDB 作为现有数据库的从库接在主 MySQL 库的后方，这样对业务方实现完全没有侵入性下使用 TiDB 对现有的业务进行备份，应对未来数据量或者并发量增长带来的单点故障风险，如需上线 TiDB，也只需要简单的将业务的主 MySQL 地址指向 TiDB 即可。","body":">*由于 TiDB 版本迭代，本文部分内容已不适用于最新的、广泛使用的版本，所以在阅读本文时请注意：*\n>\n>*1. 关于文中「使用 checker 进行 Schema 检查」：我们不再推荐使用 checker 工具进行同步前检查，推荐使用 [TiDB Data Migration (DM)](https://pingcap.com/docs-cn/v3.0/reference/tools/data-migration/overview/) 进行数据同步及同步前检查。*\n>\n>*2. 关于文中「一个无法迁移的 table 例子」：TiDB DDL 兼容性在持续提升，大家可以持续关注 [官方文档](https://pingcap.com/docs-cn/v3.0/releases/rn/) 中 TiDB 版本的更新。*\n>\n>*——2019 年 10 月 11 日*\n\n由于 TiDB 本身兼容绝大多数的 MySQL 语法，所以对于绝大多数业务来说，最安全的切换数据库方式就是将 TiDB 作为现有数据库的从库接在主 MySQL 库的后方，这样对业务方实现完全没有侵入性下使用 TiDB 对现有的业务进行备份，应对未来数据量或者并发量增长带来的单点故障风险，如需上线 TiDB，也只需要简单的将业务的主 MySQL 地址指向 TiDB 即可。\n\n下面我们详细介绍了如何将 MySQL 的数据迁移到 TiDB，并将 TiDB 作为 MySQL 的 Slave 进行数据同步。\n\n这里我们假定 MySQL 以及 TiDB 服务信息如下:\n\n```\n+------------------+-------------+----------------------------------------+\n| Name             | Address     | Port   |   User    | Password          |\n+------------------+-------------+----------------------------------------+\n| MySQL            |  127.0.0.1  | 3306   |   root    |                   |\n| TiDB             |  127.0.0.1  | 4000   |   root    |                   |\n+------------------+-------------+--------+-----------+-------------------+\n```\n\n## 使用 checker 进行 Schema 检查\n\n在迁移之前，我们可以使用 TiDB 的 `checker` 工具，checker 是我们开发的一个小工具，用于检测目标 MySQL 库中的表的表结构是否支持无缝的迁移到 TiDB，TiDB 支持绝大多数的 MySQL 常用的原生数据类型，所以大多数情况 checker 的返回应该是 ok。如果 check 某个 table schema 失败，表明 TiDB 当前并不支持，我们不能对该 table 里面的数据进行迁移。`checker` 包含在 TiDB 工具集里面，我们可以直接下载。\n\n### 下载 TiDB 工具集\n\n#### Linux\n\n```bash\n# 下载 tool 压缩包\nwget http://download.pingcap.org/tidb-tools-latest-linux-amd64.tar.gz\nwget http://download.pingcap.org/tidb-tools-latest-linux-amd64.sha256\n\n\n# 检查文件完整性，返回 ok 则正确\nsha256sum -c tidb-tools-latest-linux-amd64.sha256 \n# 解开压缩包\ntar -xzf tidb-tools-latest-linux-amd64.tar.gz\ncd tidb-tools-latest-linux-amd64\n```\n\n### 使用 `checker` 检查的一个示范\n\n+ 在 MySQL 的 test database 里面创建几张表，并插入数据:\n\n```bash\nUSE test;\nCREATE TABLE t1 (id INT, age INT, PRIMARY KEY(id)) ENGINE=InnoDB;\nCREATE TABLE t2 (id INT, name VARCHAR(256), PRIMARY KEY(id)) ENGINE=InnoDB;\n\n\nINSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3);\nINSERT INTO t2 VALUES (1, \"a\"), (2, \"b\"), (3, \"c\");\n```\n\n+ 使用 `checker` 检查 test database 里面所有的 table\n\n```bash\n./bin/checker -host 127.0.0.1 -port 3306 -user root test\n2016/10/27 13:11:49 checker.go:48: [info] Checking database test\n2016/10/27 13:11:49 main.go:37: [info] Database DSN: root:@tcp(127.0.0.1:3306)/test?charset=utf8\n2016/10/27 13:11:49 checker.go:63: [info] Checking table t1\n2016/10/27 13:11:49 checker.go:69: [info] Check table t1 succ\n2016/10/27 13:11:49 checker.go:63: [info] Checking table t2\n2016/10/27 13:11:49 checker.go:69: [info] Check table t2 succ\n```\n\n+ 使用 `checker` 检查 test database 里面某一个 table\n\n这里，假设我们只需要迁移 table `t1`。\n\n```bash\n./bin/checker -host 127.0.0.1 -port 3306 -user root test t1\n2016/10/27 13:13:56 checker.go:48: [info] Checking database test\n2016/10/27 13:13:56 main.go:37: [info] Database DSN: root:@tcp(127.0.0.1:3306)/test?charset=utf8\n2016/10/27 13:13:56 checker.go:63: [info] Checking table t1\n2016/10/27 13:13:56 checker.go:69: [info] Check table t1 succ\nCheck database succ!\n```\n\n### 一个无法迁移的 table 例子\n\n我们在 MySQL 里面创建如下表：\n\n```bash\nCREATE TABLE t_error (\n  c timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1;\n```\n\n使用 `checker` 进行检查，会报错，表明我们没法迁移 `t_error` 这张表。\n\n```bash\n./bin/checker -host 127.0.0.1 -port 3306 -user root test t_error\n2016/10/27 13:19:28 checker.go:48: [info] Checking database test\n2016/10/27 13:19:28 main.go:37: [info] Database DSN: root:@tcp(127.0.0.1:3306)/test?charset=utf8\n2016/10/27 13:19:28 checker.go:63: [info] Checking table t_error\n2016/10/27 13:19:28 checker.go:67: [error] Check table t_error failed with err: line 1 column 56 near \") ON UPDATE CURRENT_TIMESTAMP(3)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1\"\ngithub.com/pingcap/tidb/parser/yy_parser.go:111:\ngithub.com/pingcap/tidb/parser/yy_parser.go:124:\n/home/jenkins/workspace/WORKFLOW_TOOLS_BUILDING/go/src/github.com/pingcap/tidb-tools/checker/checker.go:122:  parse CREATE TABLE `t_error` (\n  `c` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)\n) ENGINE=InnoDB DEFAULT CHARSET=latin1 error\n/home/jenkins/workspace/WORKFLOW_TOOLS_BUILDING/go/src/github.com/pingcap/tidb-tools/checker/checker.go:114:\n2016/10/27 13:19:28 main.go:68: [error] Check database test with 1 errors and 0 warnings.\n```\n\n## 使用 `mydumper`/`myloader` 全量导入数据\n\n我们使用 `mydumper` 从 MySQL 导出数据，然后用 `myloader` 将其导入到 TiDB 里面。\n\n**注意，虽然我们也支持使用 MySQL 官方的  `mysqldump` 工具来进行数据的迁移工作，但相比于 `mydumper`/`myloader`，性能会慢很多，对于大量数据的迁移会花费很多时间，这里我们并不推荐。**\n\n`mydumper`/`myloader` 是一个更强大的数据迁移工具，具体可以参考 [https://github.com/maxbube/mydumper](https://github.com/maxbube/mydumper)。\n\n### 下载 Binary\n\n#### Linux\n\n```bash\n# 下载 mydumper 压缩包\nwget http://download.pingcap.org/mydumper-linux-amd64.tar.gz\nwget http://download.pingcap.org/mydumper-linux-amd64.sha256\n\n# 检查文件完整性，返回 ok 则正确\nsha256sum -c mydumper-linux-amd64.sha256 \n# 解开压缩包\ntar -xzf mydumper-linux-amd64.tar.gz\ncd mydumper-linux-amd64\n```\n\n### 从 MySQL 导出数据\n\n我们使用 `mydumper` 从 MySQL 导出数据，如下:\n\n```bash\n./bin/mydumper -h 127.0.0.1 -P 3306 -u root -t 16 -F 128 -B test -T t1,t2 -o ./var/test\n```\n\n上面，我们使用 `-B test` 表明是对 `test` 这个 database 操作，然后用 `-T t1,t2` 表明只导出 `t1`，`t2` 两张表。\n\n`-t 16` 表明使用 16 个线程去导出数据。`-F 128` 是将实际的 table 切分成多大的 chunk，这里就是 128MB 一个 chunk。\n\n**注意：在阿里云一些需要 `super privilege` 的云上面，`mydumper` 需要加上 `--no-locks` 参数，否则会提示没有权限操作。**\n\n### 给 TiDB 导入数据\n\n我们使用 `myloader` 将之前导出的数据导入到 TiDB。\n\n```bash\n./bin/myloader -h 127.0.0.1 -P 4000 -u root -t 16 -q 1 -d ./var/test\n```\n\n这里 `-q 1` 表明每个事务包含多少个 query，默认是 1000，我们这里使用 1 就可以了。\n\n导入成功之后，我们可以用 MySQL 官方客户端进入 TiDB，查看:\n\n```bash\nmysql -h127.0.0.1 -P4000 -uroot\n\nmysql> show tables;\n+----------------+\n| Tables_in_test |\n+----------------+\n| t1             |\n| t2             |\n+----------------+\n\nmysql> select * from t1;\n+----+------+\n| id | age  |\n+----+------+\n|  1 |    1 |\n|  2 |    2 |\n|  3 |    3 |\n+----+------+\n\nmysql> select * from t2;\n+----+------+\n| id | name |\n+----+------+\n|  1 | a    |\n|  2 | b    |\n|  3 | c    |\n+----+------+\n```\n\n## 使用 `syncer` 增量导入数据实现数据和 MySQL 实时同步\n\n上面我们介绍了如何使用 `mydumper`/`myloader` 将 MySQL 的数据全量导入到 TiDB，但如果后续 MySQL 的数据有更新，我们仍然希望快速导入，使用全量的方式就不合适了。\n\nTiDB 提供 `syncer` 工具能方便的将 MySQL 的数据增量的导入到 TiDB 里面。\n\n`syncer` 也属于 TiDB 工具集，如何获取可以参考 [下载 TiDB 工具集](#下载-tidb-工具集)。\n\n假设我们之前已经使用 `mydumper`/`myloader` 导入了 `t1` 和 `t2` 两张表的一些数据，现在我们希望这两张表的任何更新，都是实时的同步到 TiDB 上面。\n\n### MySQL 开启 binlog\n\n在使用 `syncer` 之前，我们必须保证：\n\n+ MySQL 开启 binlog 功能，参考 [Setting the Replication Master Configuration](http://dev.mysql.com/doc/refman/5.7/en/replication-howto-masterbaseconfig.html)\n+ Binlog 格式必须使用 `row` format，这也是 MySQL 5.7 之后推荐的 binlog 格式，可以使用如下语句打开:\n\n  ```bash\n    SET GLOBAL binlog_format = ROW;\n  ```\n\n### 获取同步 position\n\n我们通过 `show master status` 得到当前 binlog 的 position，`syncer` 的初始同步位置就是从这个地方开始。\n\n```bash\nshow master status;\n+------------------+----------+--------------+------------------+-------------------+\n| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |\n+------------------+----------+--------------+------------------+-------------------+\n| mysql-bin.000003 |     1280 |              |                  |                   |\n+------------------+----------+--------------+------------------+-------------------+\n```\n\n我们将 position 相关的信息保存到一个 `syncer.meta` 文件里面，用于 `syncer` 的同步:\n\n```bash\n# cat syncer.meta\nbinlog-name = \"mysql-bin.000003\"\nbinlog-pos = 1280\n```\n\n注意：`syncer.meta` 只需要第一次使用的时候配置，后续 `syncer` 同步新的 binlog 之后会自动将其更新到最新的 position。\n\n### 启动 `syncer`\n\n`syncer` 的配置文件 `config.toml`:\n\n```toml\nlog-level = \"info\"\n\nserver-id = 101\n\n# meta 文件地址\nmeta = \"./syncer.meta\"\nworker-count = 1\nbatch = 1\n\npprof-addr = \":10081\"\n\n[from]\nhost = \"127.0.0.1\"\nuser = \"root\"\npassword = \"\"\nport = 3306\n\n[to]\nhost = \"127.0.0.1\"\nuser = \"root\"\npassword = \"\"\nport = 4000\n```\n\n启动 `syncer`:\n\n```bash\n./bin/syncer -config config.toml\n\n2016/10/27 15:22:01 binlogsyncer.go:226: [info] begin to sync binlog from position (mysql-bin.000003, 1280)\n2016/10/27 15:22:01 binlogsyncer.go:130: [info] register slave for master server 127.0.0.1:3306\n2016/10/27 15:22:01 binlogsyncer.go:552: [info] rotate to (mysql-bin.000003, 1280)\n2016/10/27 15:22:01 syncer.go:549: [info] rotate binlog to (mysql-bin.000003, 1280)\n```\n\n## 在 MySQL 插入新的数据\n\n```bash\nINSERT INTO t1 VALUES (4, 4), (5, 5);\n```\n\n登录到 TiDB 查看：\n\n```bash\nmysql -h127.0.0.1 -P4000 -uroot -p\nmysql> select * from t1;\n+----+------+\n| id | age  |\n+----+------+\n|  1 |    1 |\n|  2 |    2 |\n|  3 |    3 |\n|  4 |    4 |\n|  5 |    5 |\n+----+------+\n```\n\n`syncer` 每隔 30s 会输出当前的同步统计，如下\n\n```bash\n2016/10/27 15:22:31 syncer.go:668: [info] [syncer]total events = 1, insert = 1, update = 0, delete = 0, total tps = 0, recent tps = 0, binlog name = mysql-bin.000003, binlog pos = 1280.\n2016/10/27 15:23:01 syncer.go:668: [info] [syncer]total events = 2, insert = 2, update = 0, delete = 0, total tps = 0, recent tps = 0, binlog name = mysql-bin.000003, binlog pos = 1538.\n```\n\n可以看到，使用 `syncer`，我们就能自动的将 MySQL 的更新同步到 TiDB。","date":"2016-11-03","author":"黄东旭","fillInMethod":"writeDirectly","customUrl":"tidb-as-mysql-slave","file":null,"relatedBlogs":[]}}},
    "staticQueryHashes": ["1327623483","1820662718","3081853212","3430003955","3649515864","4265596160","63159454"]}