同步功能在MySQL 3.23.15就开始引进了,它可以把一个MySQL服务器上的数据复制到另一个服务器上去。本章描述了MySQL的各种复制特性。介绍了同步的概念,如何设置同步服务器,以及可用服务器的参照。还提供了一系列的常见问题及其答案,疑难解答。
"14.6 Replication Statements"中介绍了同步相关的SQL语句语法。
我们建议经常访问"http://www.mysql.com"经常阅读本章的最新内容。同步功能一直在改进,我们经常把这部分的手册更新到当前的最新内容。
MySQL 3.23.15及更新的版本支持单向同步。一个服务器作为master(主服务器),一个或者多个服务器作为slave(从服务器)。master服务器把更新的内容写到二进制日志(binary log或binlog)中,并且维护了一个索引文件来记录日志循环的情况。这些日志中的更新部分会被发送到slave服务器。一个slave连接到master之后,它通知master最后一次成功增量更新的日志位置。slave会找出所有从那个时刻开始的更新操作,然后阻塞并等待master发送新的更新操作。
如果想要做一个同步服务器链的话,slave同时也可以作为master。
注意,启用同步后,所有要同步的更新操作都必须在master上执行。否则,必须注意不要造成用户在master上的更新和在slave上的更新引起冲突。
SELECT 查询就可以在slave上执行以减少master的负载。更新数据的语句则要放在mater上执行以保持master和slave的同步。当非更新操作占多数时,负载均衡就很有效了,不过这只是普通情况而言。 MySQL同步机制基于master把所有对数据库的更新操作(更新、删除 等)都记录在二进制日志里。因此,想要启用同步机制,在master就必须启用二进制日志。详情请看"5.9.4 The Binary Log"。
每个slave接受来自master上在二进制日志中记录的更新操作,因此在slave上执行了这个操作的一个拷贝。
应该非常重要地意识到,二进制日志只是从启用二进制日志开始的时刻才记录更新操作的。所有的slave必须在启用二进制日志时把master上已经存在的数据拷贝过来。如果运行同步时slave上的数据和master上启用二进制日志时的数据不一致的话,那么slave同步就会失败。
把master上的数据拷贝过来的方法之一实在slave上执行 LOAD DATA FROM MASTER 语句。不过要注意,LOAD DATA FROM MASTER 是从MySQL 4.0.0之后才开始可以用的,而且只支持master上的 MyISAM 类型表。同样地,这个操作需要一个全局的读锁,这样的话传送日志到slave的时候在master上就不会有更新操作了。当实现了锁自由表热备份时(在MySQL 5.0中),全局读锁就没必要了。
由于有这些限制,因此我们建议只在master上相关数据比较小的时候才执行 LOAD DATA FROM MASTER 语句,或者在master上允许一个长时间的读锁。由于每个系统之间 LOAD DATA FROM MASTER 的速度各不一样,一个比较好的衡量规则是每秒能拷贝1MB数据。这只是的粗略的估计,不过master和slave都是奔腾700MHz的机器且用100MBit/s网络连接时就能达到这个速度了。
slave上已经完整拷贝master数据后,就可以连接到master上然后等待处理更新了。如果master当机或者slave连接断开,slave会定期尝试连接到master上直到能重连并且等待更新。重试的时间间隔由 --master-connect-retry 选项来控制,它的默认值是60秒。
每个slave都记录了它关闭时的日志位置。msater是不知道有多少个slave连接上来或者哪个slave从什么时候开始更新。
MySQL同步功能由3个线程(master上1个,slave上2个)来实现。执行 START SLAVE 语句后,slave就创建一个I/O线程。I/O线程连接到master上,并请求master发送二进制日志中的语句。master创建一个线程来把日志的内容发送到slave上。这个线程在master上执行 SHOW PROCESSLIST 语句后的结果中的 Binlog Dump 线程便是。slave上的I/O线程读取master的 Binlog Dump 线程发送的语句,并且把它们拷贝到其数据目录下的中继日志(relay logs)中。第三个是SQL线程,salve用它来读取中继日志,然后执行它们来更新数据。
如上所述,每个mster/slave上都有3个线程。每个master上有多个线程,它为每个slave连接都创建一个线程,每个slave只有I/O和SQL线程。
在MySQL 4.0.2以前,同步只需2个线程(master和slave各一个)。slave上的I/O和SQL线程合并成一个了,它不使用中继日志。
slave上使用2个线程的优点是,把读日志和执行分开成2个独立的任务。执行任务如果慢的话,读日志任务不会跟着慢下来。例如,如果slave停止了一段时间,那么I/O线程可以在slave启动后很快地从master上读取全部日志,尽管SQL线程可能落后I/O线程好几的小时。如果slave在SQL线程没全部执行完就停止了,不过I/O线程却已经把所有的更新日志都读取并且保存在本地的中继日志中了,因此在slave再次启动后就会继续执行它们了。这就允许在master上清除二进制日志,因为slave已经无需去master读取更新日志了。
执行 SHOW PROCESSLIST 语句就会告诉我们所关心的master和slave上发生的情况。
下例说明了 SHOW PROCESSLIST 结果中的3个线程是什么样的。这是在MySQL 4.0.15及更新上执行 SHOW PROCESSLIST 的结果,State 字段的内容已经比旧版本显示的更有意义了。
SHOW PROCESSLIST 的结果如下: mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 2
User: root
Host: localhost:32931
db: NULL
Command: Binlog Dump
Time: 94
State: Has sent all binlog to slave; waiting for binlog to
be updated
Info: NULL
在这里,线程2是为一个slave连接创建的。结果表明所有未完成的更新日志已经都发送到slave了,master正等待新的更新日志发生。
在slave上,SHOW PROCESSLIST 的结果如下:
mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 10
User: system user
Host:
db: NULL
Command: Connect
Time: 11
State: Waiting for master to send event
Info: NULL
*************************** 2. row ***************************
Id: 11
User: system user
Host:
db: NULL
Command: Connect
Time: 11
State: Has read all relay log; waiting for the slave I/O
thread to update it
Info: NULL
这表明线程10是I/O线程,它正连接到master上;线程11是SQL线程,它执行中继日志中的更新操作。现在,这2个线程都处于空闲状态,正等待新的更新日志。
注意,Time 字段的值告诉我们slave上的日志比master晚了多久。详情请看"6.9 Replication FAQ"。
以下列出了master的 Binlog Dump 线程 State 字段中最常见的几种状态。如果在master上没有 Binlog Dump 线程,那么同步就没有在运行。也就是说,没有slave连接上来。
Sending binlog event to slaveFinished reading one binlog; switching to next binlogHas sent all binlog to slave; waiting for binlog to be updatedWaiting to finalize termination以下列出了slave的I/O线程 State 字段中最常见的几种状态。从MySQL 4.1.1开始,这个状态在执行 SHOW SLAVE STATUS 语句结果的 Slave_IO_State 字段也会出现。这意味着可以只执行 SHOW SLAVE STATUS 语句就能了解到更多的信息。
Connecting to masterChecking master versionRegistering slave on masterRequesting binlog dumpWaiting to reconnect after a failed binlog dump request--master-connect-retry 选项来指定。Reconnecting after a failed binlog dump requestWaiting for master to send eventslave_read_timeout 秒,就会发生超时。这时,它就会考虑断开连接,然后尝试重连。Queueing master event to the relay logWaiting to reconnect after a failed master event readmaster-connect-retry 秒。 Reconnecting after a failed master event readWaiting for master to send event。 Waiting for the slave SQL thread to free enough relay log spacerelay_log_space_limit 的值非零,中继日志的大小总和超过这个值了。I/O线程等待SQL线程先处理中继日志然后删除它们以释放足够的空间。 Waiting for slave mutex on exit以下列出了slave的SQL线程 State 字段中最常见的几种状态:
Reading event from the relay logHas read all relay log; waiting for the slave I/O thread to update itWaiting for slave mutex on exitSQL线程的 State 字段有时候也可能是一个SQL语句。这意味着它从中继日志中读取到一个事件了,从中提取出SQL语句,并执行它。
默认地,中继日志的名字格式为 `host_name-relay-bin.nnn`,host_name 是服务器的主机名,nnn 是序号。中继日志是根据顺序的序号来创建的,从 000001 (MySQL 4.0 及更旧是 001)开始。slave上用一个索引文件来跟踪当前正在使用的中继日志。默认的中继日志索引文件名是 `host_name-relay-bin.index`。默认地,这个文件位于slave的数据文件目录下。默认文件名可以根据的系统选项 --relay-log 和 --relay-log-index 来替换。详情请看"6.8 Replication Startup Options"。
中继日志和二进制日志的格式一样,因此也可以用 mysqlbinlog 来读取。当SQL线程读取完中继日志中的全部时间后就不再需要它了,会自动删除它。中继日志没有显式的删除机制,因为SQL线程会关注这个。不过,从MySQL 4.0.14开始,执行 FLUSH LOGS 的话就会循环中继日志,会让SQL线程删除它们。
FLUSH LOGS 语句或运行 mysqladmin flush-logs 命令(从 MySQL 4.0.14开始才会创建新中继日志)。max_relay_log_size, 如果 max_relay_log_size > 0 的话max_binlog_size, 如果 max_relay_log_size = 0 或 MySQL 低于 4.0.14slave会在数据文件目录下创建两个额外的文件。它们是状态文件,名字默认为 `master.info` and `relay-log.info`。它们的内容跟执行 SHOW SLAVE STATUS 语句的结果类似。详情请看"14.6.2 SQL Statements for Controlling Slave Servers"。由于是磁盘上的文件,它们在slave关闭后还会留着。下一次slave启动时,就会读取这两个文件来判断从master读取到二进制日志的什么位置了,处理中继日志到什么位置了。
SHOW SLAVE STATUS 语句结果中相对应的字段值一样,如下:| Line | Description |
| 1 | Master_Log_File |
| 2 | Read_Master_Log_Pos |
| 3 | Master_Host |
| 4 | Master_User |
| 5 | Password (not shown by SHOW SLAVE STATUS) |
| 6 | Master_Port |
| 7 | Connect_Retry |
从MySQL 4.1开始,文件内容还包括了SSL选项:
| Line | Description |
| 1 | Number of lines in the file |
| 2 | Master_Log_File |
| 3 | Read_Master_Log_Pos |
| 4 | Master_Host |
| 5 | Master_User |
| 6 | Password (not shown by SHOW SLAVE STATUS) |
| 7 | Master_Port |
| 8 | Connect_Retry |
| 9 | Master_SSL_Allowed |
| 10 | Master_SSL_CA_File |
| 11 | Master_SSL_CA_Path |
| 12 | Master_SSL_Cert |
| 13 | Master_SSL_Cipher |
| 14 | Master_SSL_Key |
`relay-log.info` 文件由SQL线程来更新。文件的内容和执行 SHOW SLAVE STATUS 语句结果中相对应的字段值一样:
| Line | Description |
| 1 | Relay_Log_File |
| 2 | Relay_Log_Pos |
| 3 | Relay_Master_Log_File |
| 4 | Exec_Master_Log_Pos |
备份slave数据时,要把这两个文件也备份起来,和中继日志一道。想要恢复slave时就用得到它们了。如果丢失了中继日志,但是 `relay-log.info` 文件还存在,那么就可以判断出SQL线程执行了多少master二进制日志。然后执行 CHANGE MASTER TO 语句,带上 MASTER_LOG_FILE 和 MASTER_LOG_POS 选项告诉slave要从master的二进制日志哪个位置重新读取。当然了,这要求master上相关的二进制日志都还留着。
如果slav打算同步 LOAD DATA INFILE 语句,那么也要备份对应目录下的任何 `SQL_LOAD-*` 文件。这可以在 LOAD DATA INFILE 被中断后继续保持同步。这个目录由 --slave-load-tmpdir 选项来指定。默认地,如果没有指定的话,它的值就是变量 tmpdir 的值。
以下描述了如何快速设置MySQL同步服务器。假设你打算同步全部的数据库,并且之前没有设置过。需要关闭master服务器以完成全部的步骤。
本章描述的过程可以用于一个slave的情况,也可以用于多个slave的情况。
这只是一个最直接设置slave的办法,并不是只有一个。例如,已经有了master的数据快照(snapshot),master已经设置了服务器编号ID(server_id)并且启用了二进制日志,这就无需关闭master或者阻止在master上更新数据了。详情请看"6.9 Replication FAQ"。
想要完全掌握MySQL同步设置,最好把本章全部读完,并且测试在"14.6.1 SQL Statements for Controlling Master Servers"和"14.6.2 SQL Statements for Controlling Slave Servers"中提到的全部语句。而且要熟悉各种同步设置选项,详情请看"6.8 Replication Startup Options"。
注意,这个过程以及后面一些同步SQL语句需要有 SUPER 权限。MySQL 4.0.2以前,则是 PROCESS 权限。
REPLICATION SLAVE 权限。如果这个帐户只用于同步(推荐这么做),那就没必要授予其他权限了。设定你的域是 mydomain.com,想要授权一个帐户 repl 使用密码 slavepass,允许它可以在域里的任何主机连接到master上。用 GRANT 语句来创建帐户: mysql> GRANT REPLICATION SLAVE ON *.*
-> TO 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass';
在MySQL 4.0.2以前,用 FILE 权限来代替 REPLICATION SLAVE:
mysql> GRANT FILE ON *.*
-> TO 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass';
如果打算在slave上执行 LOAD TABLE FROM MASTER 或 LOAD DATA FROM MASTER 语句,那么必须给该帐户授予附加权限:
SUPER 和 RELOAD 权限。 SELECT 权限。在master上任何没有 SELECT 权限的表都会被 LOAD DATA FROM MASTER 略过。MyISAM 表,执行 FLUSH TABLES WITH READ LOCK 语句刷新所有表并且阻止其他写入: mysql> FLUSH TABLES WITH READ LOCK;
不要退出执行 FLUSH TABLES 语句的客户端,以保持读锁有效(如果退出了,读锁就释放了)。然后从master上取得数据快照。比较简单的办法就是把数据目录打包压缩。例如,Unix上的 tar, PowerArchiver, WinRAR, WinZip,或Windows上的类似程序。想要用 tar 来创建一个压缩包,包括所有的数据库,只需执行以下命令(把目录改成你的真实路径):
shell> tar -cvf /tmp/mysql-snapshot.tar .
如果只想打包一个数据库 this_db,只需执行命令:
shell> tar -cvf /tmp/mysql-snapshot.tar ./this_db
然后把这个文件拷贝到slave的 `/tmp` 目录下。在slave上,执行以下命令解开压缩包(把目录改成你的真实路径):
shell> tar -xvf /tmp/mysql-snapshot.tar
可能不需要同步 mysql 数据库,因为在slave上的权限表和master不一样。这时,解开压缩包的时候要排除它。同时在压缩包中也不要包含任何日志文件,或者 `master.info~ 或 `relay-log.info` 文件。当在master上的 FLUSH TABLES WITH READ LOCK 语句还生效时,在master上读取当前二进制文件的文件名及偏移位置:
mysql > SHOW MASTER STATUS; +---------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +---------------+----------+--------------+------------------+ | mysql-bin.003 | 73 | test | manual,mysql | +---------------+----------+--------------+------------------+
File 字段显示了二进制日志文件名,Position 字段显示了日志偏移位置。在这个例子中,日志文件是 mysql-bin.003,偏移位置是 73。记下这些值,在后面设置slave的时候就需要用到它们了。它们表示了slave要从master的哪个偏移位置开始处理更新操作。取得快照和记录下日志名及偏移位置后,就可以让master释放读锁了:
mysql> UNLOCK TABLES;
如果用到 InnoDB 表,那么最好使用 InnoDB Hot Backup 工具。它无需在master上请求任何锁就能做到快照的一致性,并且在后面中在slave上要用到的快照中已经记录了日志文件名以及偏移位置。InnoDB Hot Backup 是费免费(商业的)的附加工具,它没有包含在MySQL发布包中。访问 InnoDB Hot Backup 的主页 http://www.innodb.com/manual.php 查看更多详细信息。除去 InnoDB Hot Backup 的另一个最快的办法就是关闭master服务器,拷贝 InnoDB 数据文件,日志文件,以及表结构定义文件(`.frm` 文件)。想要记录当前日志文件及偏移位置,需要在master关闭前执行如下可语句:
mysql> FLUSH TABLES WITH READ LOCK; mysql> SHOW MASTER STATUS;
尽快记下 SHOW MASTER STATUS 显示结果中的日志文件及偏移位置。然后,在不解锁的情况下关闭master,确保master上的快照和记录的结果一致:
shell> mysqladmin -u root shutdown
还有一个方法可以同时用于 MyISAM 和 InnoDB 表,这就是在master上作SQL转储而无需如上所述备份二进制日志。在master上运行 mysqldump --master-data 命令,然后把结果文件转储到slave上。不过,这比拷贝二进制日志慢点。如果master在以前没有启用 --log-bin 选项,那么执行 SHOW MASTER STATUS 语句的结果中的文件名及偏移位置值为空了,那么后面在slave上指定的参数值就是空字符串('') 和 4了。
[mysqld] 区间有 log-bin 选项。这个区间还必须有 server-id=master_id 选项,的值必须是 1 到 2^32-1 之间的正整数。例如: [mysqld] log-bin server-id=1
如果这些配置选项不存在,那么就加上并且重启master。
[mysqld] server-id=slave_id
slave_id 的值和 master_id 类似,是 1 到 2^32-1 之间的正整数。另外,这个ID必须不能和master的ID一样。例如:
[mysqld] server-id=2
如果有多个slave,那么每个slave都必须要有一个唯一的 server-id,它的值不能和master以及其其他slave的值一样。可以把 server-id 想象成为IP地址:这些ID标识了整个同步组合中的每个服务器。如果没有指定 server-id 的值,如果也没定义 master-host,那么它的值就为1,否则为2。注意,如果没有设定 server-id,那么master就会拒绝所有的slave连接,同时slave也会拒绝连接到master上。因此,省略配置 server-id 只对备份二进制日志有利。
mysqldump 备份的,那么可以直接启动salve(直接跳到下一步)。--skip-slave-start 选项使之不会立刻去连接master。最好也使用 --log-warnings 选项(从 MySQL 4.0.19 和 4.1.2 开始就是默认启用了)来启动slave,以知道发生问题时的更详细的信息(例如,网络或者连接问题)。从开始MySQL 4.0.21 和 4.1.3,异常中止的连接不再记录到错误日志中,除非 --log-warnings 选项的值大于1。mysqldump 备份数据的话,把文件导入slave中: shell> mysql -u root -p < dump_file.sql
mysql> CHANGE MASTER TO
-> MASTER_HOST='master_host_name',
-> MASTER_USER='replication_user_name',
-> MASTER_PASSWORD='replication_password',
-> MASTER_LOG_FILE='recorded_log_file_name',
-> MASTER_LOG_POS=recorded_log_position;
下表列出了各个选项字符串的最大长度:
MASTER_HOST | 60 |
MASTER_USER | 16 |
MASTER_PASSWORD | 32 |
MASTER_LOG_FILE | 255 |
mysql> START SLAVE;
做完上述过程后,slave应该会连接到master上并且捕获所有从取得快照后的更新操作。
如果忘了设置master的 server-id 值,那么slave就不能连接到master上。
如果忘了设置master的 server-id 值,那么在错误日志中就会记录如下内容:
Warning: You should set server-id to a non-0 value if master_host is set; we force server id to 2, but this MySQL server will not act as a slave.
如果因为其他原因不能同步的话,错误信息也会记录在slave的日志上。
一旦slave开始同步了,就能在数据文件目录下找到2个文件 `master.info` 和`relay-log.info`。slave利用这2个文件来跟踪处理了多少master的二进制日志。不要删除或者修改这2个文件,除非知道怎么改。尽管如此,我们更推荐用 CHANGE MASTER TO 语句来做。
注意:`master.info` 中的内容覆盖了部分命令行中指定的或 `my.cnf` 的选项。详情请看"6.8 Replication Startup Options"。
只要有了master的数据快照,就可以按照上述几个步骤配置其它slave了。无需再次取得master的数据快照,每个slave都可以用这一份快照来做。
最早的二进制格式是在MySQL 3.23中开发出来的。在MySQL 4.0中改进了,MySQL 5.0又改进了。在配置同步时需要升级服务器的话,它们之间的因果关系在"6.6 Upgrading a Replication Setup"中描述了。
如果只关心同步,任何MySQL 4.1.x版本和MySQL 4.0.x是一样的,因为它们都使用相同格式的二进制日志。所以,这些版本是互相兼容的,它们之间可以无缝地运行同步。一个例外的情况是,MySQL 4.0.0到4.0.2由于开发的较早,无法和后来的版本互相兼容,所以不要使用它们(它们是4.0版本的alpha系列。它们之间的兼容性在发布包的手册中均有相关文档)。
下表展示了不同版本的MySQL之间的master/slave同步兼容性。
| Master | Master | Master | ||
| 3.23.33 and up | 4.0.3 and up or any 4.1.x | 5.0.0 | ||
| Slave | 3.23.33 and up | yes | no | no |
| Slave | 4.0.3 and up | yes | yes | no |
| Slave | 5.0.0 | yes | yes | yes |
一个通常的规则是,我们建议使用最近的MySQL版本,因为同步兼容性一直在改善。我们也建议master和slave都使用同一个版本。
如果升级服务器时涉及到配置同步,升级设置的步骤跟当前版本以及升级后的版本不同而异。
本节适用于从MySQL 3.23升级到4.0或者4.1的情况。4.0的服务器必须是4.0.3或者更新,"6.5 Replication Compatibility Between MySQL Versions"中提到了。
把master从MySQL 3.23升级到4.0或4.1时,首先要确认这个master的所有slave都已经是4.0或4.1了,否则的话,要先升级slave:挨个关闭,升级,重启,重启同步等。
通过以下步骤可以实现安全地升级,假定master要升级到3.23,而slave已经是4.0或4.1了。注意,master升级后,不要重启使用任何旧的二进制日志的同步,因为它会干扰到4.0或4.1 slave的同步。
FLUSH TABLES WITH READ LOCK 语句,阻止所有的更新。 SHOW MASTER STATUS 语句取得二进制日志以及偏移位置。然后,再slave用这些值执行 SELECT
MASTER_POS_WAIT() 语句,它会阻止slave上的同步且返回它已经同步的偏移位置。然后在slave上执行 STOP SLAVE 语句。 SHOW MASTER STATUS 语句来取得这些信息。然后在每个slave上都执行如下语句: mysql> CHANGE MASTER TO MASTER_LOG_FILE='binary_log_name',
-> MASTER_LOG_POS=4;
mysql> START SLAVE;本节适用于从MySQL 3.23,4.0或4.1升级到5.0的情况。4.0的服务器必须是4.0.3或者更新,"6.5 Replication Compatibility Between MySQL Versions"中提到了。
首先,注意到MySQL 5.0还是alpha发布系列。它在各方面都比旧版本好(更容易升级一些同步中重要的会话变量,例如 sql_mode;详情请看"C.1.3 Changes in release 5.0.0 (22 Dec 2003: Alpha")。不过,它还没经过广泛测试。由于是alpha版本,我们不建议用于任何生产环境(现在已经可以用于生产了,译者注)。
把master从MySQL 3.23,4.0或4.1升级到5.0.0时,首先要确认这个master的所有slave都已经是5.0.0了,否则的话,要先升级slave:挨个关闭,升级,重启,重启同步等。5.0.0的slave可以读取升级前写入的执行语句的中继日志。升级完后的slave创建的中继日志就是5.0格式了。
当所有的slave都升级完了,关闭master,升级到5.0.0,然后重启。5.0.0的master也可以读取旧格式的二进制日志。slave能识别旧的格式并且合理处理它们。master上新建的二进制日志都是5.0.0格式的。slave也能识别这格式。
换言之,在升级到5.0.0时无需特殊的规定,除非在升级master到5.0.0之前slave必须使用旧版本的。注意,把5.0.0降级到旧版本中不能自动地做了:必须确保所有的5.0.0格式二进制日志和中继日志都已经处理完了,然后才能把它们删除完成降级。
以下列出了同步支持什么,不支持什么。附加的 InnoDB 特殊相关的信息以及同步请看"16.7.5 InnoDB and MySQL Replication"。
AUTO_INCREMENT, LAST_INSERT_ID(), 和 TIMESTAMP 的值都能被正常同步。 USER(), UUID(), 和 LOAD_FILE() 函数都完完全全地同步到slave,因此可能不大可靠。MySQL 4.1.1以前的版本中的 CONNECTION_ID() 函数也是如此。从MySQL 4.1.1及更高以后,新的 PASSWORD() 函数可以正常同步,当然了,slave必须是4.1.1或更高或者不同步它。如果有旧版本的slave必须要同步 PASSWORD() 函数,那么master启动时必须增加 --old-password 选项,这样在master上就用旧的方法来实现 PASSWORD() 了(注意,MySQL 4.1.0的 PASSWORD() 函数实现跟其他的版本都不同,最好不要同步4.1.0)。 FOREIGN_KEY_CHECKS 变量。从5.0.0开始同步 sql_mode, UNIQUE_CHECKS,和 SQL_AUTO_IS_NULL 变量。 SQL_SELECT_LIMIT 和 table_type 变量目前还不能被同步。 --default-character-set, --default-collation 都是相关的全局变量)。否则,slave上可能会出现键重复(duplicate-key)的错误,因为用master的字符集认为该键可能是唯一的,但是用slave的字符集则未必然。 SET NAMES, SET CHARACTER SET 等语句),因为这些对字符集的修改在slave不能识别。如果master是4.1.3或者更新,slave也是这样的话,那么会话字符集就可以随便修改了(执行 NAMES, CHARACTER SET, COLLATION_CLIENT, COLLATION_SERVER 等),并且这些修改都会被记录到二进制日志中,然后同步到slave上,它就知道怎么做了。该会话还会阻止试图修改这些全局变量的操作;就如前面所说,master和slave必须使用同样的全局字符集。 collation_server 不一样字符集的数据库,那么就要设计 CREATE TABLE 语句使得数据表不隐式地使用该数据库的默认字符集,因为这目前还是一个bug(Bug #2326);一个变通的办法是在 CREATE TABLE 语句中显式地声明数据表的字符集以及校验字符集。 InnoDB 表当成 MyISAM 表。不过,slave在一个 BEGIN/COMMIT 区块中停止的话就有问题了,因为slave会从 BEGIN 重新开始。这个问题已经放到TODO中,很快会被修复。 @var_name)的情况下,在MySQL 3.23和4.0不能被正确同步。在 4.1 这已经修复了。注意,从MySQL 5.0开始,用户变量就不区分大小写了。在做MySQL 5.0和旧版本间的同步需要考虑到这个问题。 CREATE TABLE 语句如果包括了 DATA DIRECTORY或 INDEX DIRECTORY 子句,那么它也会应用于slave上。如果slave上不存在对应的目录或者没有权限时便出现问题。从MySQL 4.0.15开始,有个 sql_mode 选项叫 NO_DIR_IN_CREATE。如果slave的SQL模式包含这个选项,那么它在同步 CREATE TABLE 语句前会忽略前面提到的2个子句。结果就是 MyISAM 的数据和索引文件都只能放在该表的数据库目录下。 FLUSH, ANALYZE TABLE, OPTIMIZE TABLE,和 REPAIR TABLE 语句没有写入到二进制日志中,因此也不会同步到slave上。这通常不会引发问题,因为它们并没有修改数据。不过在特定情况下可能导致问题。如果同步 mysql 数据库下的权限表,在更新时不是用 GRANT 语句,那么必须在slave上执行那么必须在slave上执行 FLUSH PRIVILEGES 语句才能使之生效。同样地,如果还有一个 MyISAM 表是 MERGE 表的一部分,那么必须在slave上手工执行 FLUSH TABLES 语句。从MySQL 4.1.1开始,这些语句都写入二进制日志了(除非指定选项 NO_WRITE_TO_BINLOG 或它的同名选项 LOCAL)。一些例外的情况是 FLUSH LOGS, FLUSH SLAVE, 和 FLUSH TABLES WITH READ LOCK (它们中的任何一个同步到slave的话都可能导致问题)。例子可见"14.5.4.2 FLUSH Syntax"。 SELECT 查询发送到不同的slave上达到负载均衡。 MEMORY (HEAP) 表都清空了。从MySQL 4.0.18开始,master用以下方式同步它们:一旦master开始使用一个 MEMORY 表,它会在用完这些表之后在二进制日志中写入一个 DELETE FROM 语句告诉slave把它们删除。详情请看"15.3 The MEMORY (HEAP) Storage Engine"。 STOP SLAVE 语句。 SHOW STATUS 语句检查变量 Slave_open_temp_tables 的值。 mysqladmin shutdown 命令关闭slave。 START SLAVE. START SLAVE 语句重启slave线程。 --log-slave-updates 选项,那么就可以安全地连接到各个服务器上。注意,很多语句可能在这种设置环境下不能正常工作,除非程序中已经特别注意避免这种更新时潜在的问题了,因为可能在不同服务器上不同的顺序上发生更新问题。这意味着可以设定像下面的循环: A -> B -> C -> A
服务器ID都已经编码到二进制日志中了,因此服务器A知道那些自己创建的日志,从而不会去执行它们(除非在服务器A上启动时增加 --replicate-same-server-id 选项,这个选项只在很少的设置时有意义)。因此,这就不会存在无限循环了。不过这个循环只有在更新表时没有发生冲突才不会发生问题。换言之,如果在A和C中同时插入一条记录,那么可能在A中不可能插入数据,因为它的键可能跟C的键冲突了。同样地,也不能在两个服务器上更新同一条记录,除非2次更新操作间有足够的时间间隔。
START SLAVE 语句重启它。 --master-connect-retry 选项来修改)。slave也会处理网络断开的情况。不过,slave会在 slave_net_timeout 秒之后还没接收到来自master的数据才会处理网络断开情况的。如果断开时间不长,可以减少 slave_net_timeout 的值。详情请看"5.2.3 Server System Variables"。 InnoDB 表,使用 --innodb-safe-binlog 选项在master上就能避免这个问题。详情请看"5.9.4 The Binary Log"。 MyISAM 表的非事务本质,就可能发生一个语句只更新了部分表就返回错误代码的情况。例如,一个多重插入语句中,有一条记录违反了约束键规则,一个更新语句在更新了一些记录后辈杀掉了。如果在master上发生这种情况了,那么slave线程会推出,等待数据库管理员决定要怎么做,除非这个错误代码是合法的并且这个语句的执行结果也是一样的错误代码。并没有关于错误代码是否合法的详细描述,一些错误代码可以用 --slave-skip-errors 选项屏蔽掉。这个选项从MySQL 3.23.47开始就可以用了。 BEGIN/COMMIT 段内更新数据表了,如果在非事务表提交之前有其他线程更新它了,那么这个更新操作就不会正确地同步到二进制日志中。这是因为只有整个事务成功提交了才会写到二进制日志中。 COMMIT 后才写入,ROLLBACK 的话就不写入了。因此在一些事务中更新事务表或非事务表时就需要考虑这个情况了(不只是同步时会碰到这个问题,想要把二进制日志作为备份时也一样)。在MySQL 4.0.15中,我们已经修改了更新事务和非事务表混合的情况下的日志记录行为,它解决了这个问题(对于二进制日志来说顺序地记录语句是比较不错的做法,所有必须的语句都会写进去,ROLLBACK 也一样)。当第二个连接更新非事务表而第一个连接的事务还没结束时,就会有同样的问题了;仍会发记录语句顺序发生错误的问题,因为第二个连接会在更新完成后立刻写入到日志中。 LOAD DATA INFILE 时,SHOW SLAVE STATUS 中的 Exec_Master_Log_Pos 和 Relay_Log_Space 字段的值就不正确了。Exec_Master_Log_Pos 值不正确的话在重启slave之后会导致问题;因此最好在重启前修改一下这个值,只需在master上运行 FLUSH LOGS。这个bug在MySQL 5.0.0的slave中已经解决了。下表列出了MySQL 3.23同步时会发生的问题,它们在MySQL 4.0已经解决了:
LOAD DATA INFILE 能正确处理,只要那个数据文件在更新开始时仍然存在于master上。 LOAD DATA LOCAL INFILE 不再像以前3.23那样被略过了。 RAND() 更新同步不正常。因此在使用 RAND() 更新时采用 RAND(some_non_rand_expr) 格式。例如,可以用 UNIX_TIMESTAMP() 作为 RAND() 的参数。不管是master还是slave,都要设定 server-id 选项来确定使它们都有各自唯一的同步ID。必须选择 1 到 2^32-1 之间的正整数。例如: server-id=3。
关于master服务器上可用于控制二进制日志记录的选项详细描述请看"5.9.4 The Binary Log"。
下表描述了slave同步可用的选项,可以在命令行或者配置文件中设定它们。
--master-host--master-user--master-password--master-port--master-connect-retry从MySQL 4.1.1开始,一下选项也按照上述方式特殊处理:
--master-ssl--master-ssl-ca--master-ssl-capath--master-ssl-cert--master-ssl-cipher--master-ssl-key在MySQL 4.1.1中, `master.info` 文件的格式改变了以包含相应的SSL选项。另外,MySQL 4.1.1文件格式还包括了在第一行中的文件总行数。如果从旧版本升级到4.1.1,那么服务器启动时会自动升级 `master.info` 为新的格式。不过,如果是从4.1.1降级到旧版本,就需要在系统第一次启动时手工删除文件的第一行。注意,这种情况下,被降级的服务器就不能再使用SSL选项连接到master了。
slave启动时如果不存在 `master.info` 文件,它就使用在命令行或者配置文件中指定的参数值来启动。这在每次第一次启动slave服务器时都是这样,或者执行 RESET SLAVE 语句关闭且重启slave之后。
slave启动时如果存在 `master.info` 文件,那么它就略过这些选项,而是直接读取 `master.info` 文件中的值。
如果重启salve时使用的选项值和 `master.info` 中的不一样,那么这个新的值不会生效,因为slave服务器还是照样只读取 `master.info` 文件。想要使用不同的选项值,可以在删除 `master.info` 后重启slave或者使用 CHANGE MASTER TO 语句(推荐)重置选项值。
假定在 `my.cnf` 设定了以下选项值:
[mysqld] master-host=some_host
第一次启动slave的时候,它从 `my.cnf` 中读取选项值,然后再把它们保存在 `master.info` 中。下次重启slave时,它就只读取 `master.info` 的内容而略过 `my.cnf` 中的选项值了。企图修改 `my.cnf` 来改变同步选项是不可行的,不过可以通过执行 CHANGE MASTER TO 语句来实现:
由于服务器认为 `master.info` 的优先级比配置文件高,因此建议根本不要在启动时附加同步选项,而只用CHANGE MASTER TO 语句。详情请看"14.6.2.1 CHANGE MASTER TO Syntax"。
下例显示了一些配置slave的扩展选项:
[mysqld] server-id=2 master-host=db-master.mycompany.com master-port=3306 master-user=pertinax master-password=freitag master-connect-retry=60 report-host=db-slave.mycompany.com
下列所述启动选项都是用来控制同步的:它们中的大部分都可以在运行时用 CHANGE MASTER TO 语句来改变。其他的,例如 --replicate-*,只能在salve启动时指定。我们打算在将来解决这个问题。
--log-slave-updates--log-bin 选项才能启用二进制日志。在使用同步链机制时,就需要使用 --log-slave-updates 选项。例如,可能需要设置如下同步关系: A -> B -> C
在这里,A当作B的master,B当作C的master。B同时是slave又是master,在A和B上都需要启用 --log-bin 选项,并且在B上还需要启用 --log-slave-updates 选项。
--log-warnings--skip-log-warnings 就可以禁用它。从MySQL 4.0.21和MySQL 4.1.3开始,除非这个选项的值大于1,否则放弃的连接不再记录在错误日志中。这个选项不只是用于限制同步,它产生的警告跨越了大部分操作。 --master-connect-retry=seconds--master-host=host--master-info-file=file_name--master-password=password--master-port=port_numberconfigure 选项参数,那么就是3306。 --master-ssl,--master-ssl-ca=file_name,--master-ssl-capath=directory_name,
--master-ssl-cert=file_name,--master-ssl-cipher=cipher_list,--master-ssl-key=file_name--ssl, --ssl-ca, --ssl-capath, --ssl-cert, --ssl-cipher, --ssl-key 选项。如果存在的话,`master.info` 文件中选项值优先被读取。这些选项是从MySQL 4.1.1之后开始可以用的。 --master-user=usernameREPLICATION SLAVE 权限(在MySQL 4.0.2以前,则是 FILE 权限)。如果存在的话,`master.info` 文件中选项值优先被读取。如果没有设置,就当作是 test。 --max-relay-log-size=#--read-onlySUPER 权限用户之外的都不能更新数据。这能确保slave不会接受来自其他客户端的更新。这个选项是从MySQL 4.0.14开始有的。 --relay-log=file_namehost_name-relay-bin.nnn,host_name 是slave服务器的主机名,nnn 是指中继日志的顺序号。可以用这个选项创建不依赖主机名的中继日志,或者在中继日志越来越大(不想降低 max_relay_log_size 的值)且需要将它们放在非数据文件目录下,或者想使用磁盘间的负载均衡来提高速度等情况。 --relay-log-index=file_namehost_name-relay-bin.index,host_name 是slave服务器主机名。 --relay-log-info-file=file_name--relay-log-purge={0|1}SET GLOBAL relay_log_purge 来动态更改。这个选项从MySQL 4.1.1开始可以用。 --relay-log-space-limit=#--relay-log-space-limit 的值小于2倍 --max-relay-log-size (如果 --max-relay-log-size 的值为0,则是 --max-binlog-size ) 的值。在这种情况下,由于已经超过 --relay-log-space-limit 了,I/O线程需要等待更多的剩余空间,但是SQL线程没有可以删除的中继日志来满足I/O线程的需求。这就会强制I/O线程暂时忽略 --relay-log-space-limit 限制。 --replicate-do-db=db_nameUSE 选中的)的语句。想要指定更多的数据库,只需多次使用该选项,每次指定一个数据库。注意,类似 UPDATE some_db.some_table SET foo='bar' 这样的跨库操作语句以及没有选中数据库的操作都不会被同步。如果必须使用跨库操作,要确保使用MySQL 3.23.28或更高,并且使用 --replicate-wild-do-table=db_name.% 选项。请仔细阅读最后面的注意事项。 --replicate-do-db=sales 选项,并且在master上执行下列语句,那么这个 UPDATE 语句不会被同步: USE prices; UPDATE sales.january SET amount=amount+1000;
如果需要同步跨库操作,只需使用 --replicate-wild-do-table=db_name.% 选项。这个"只检查缺省数据库"特性的主要原因是因为想要单从一个语句中判断是否要被同步比较困难(例如,使用多表 DELETE 或者 UPDATE,这就跨库了)。不过想要检查是否是缺省数据库却很快。
--replicate-do-table=db_name.tbl_name--replicate-do-db 选项相反。请仔细阅读最后面的注意事项。 --replicate-ignore-db=db_nameUSE 选中的)的语句。想要指定更多的数据库,只需多次使用该选项,每次指定一个数据库。如果有跨库操作且希望这些操作要被同步就不要使用该选项。请仔细阅读最后面的注意事项。 --replicate-ignore-db=sales 选项,并且在master上执行下列语句,那么这个 UPDATE 语句不会被同步: USE prices; UPDATE sales.january SET amount=amount+1000;
想要让跨库操作能正常同步,只需使用 --replicate-wild-ignore-table=db_name.% 选项。
--replicate-ignore-table=db_name.tbl_name--replicate-ignore-db 选项相反。请仔细阅读最后面的注意事项。 --replicate-wild-do-table=db_name.tbl_nameLIKE 模式一样。想要指定更多的数据表,只需多次使用该选项,每次指定一个数据表。请仔细阅读最后面的注意事项。 --replicate-wild-do-table=foo%.bar% 会同步所有以 foo 开头的数据库下的以 bar 开头的数据表上的更新操作。 %,则匹配所有的表名,且应用到数据库级语句(CREATE DATABASE, DROP DATABASE,和 ALTER DATABASE)。例如,使用 --replicate-wild-do-table=foo%.% 选项的话,所有匹配 foo% 模式的数据库级操作都会被同步。 my_own%db 数据库下的所有表,但是不想同步 my1ownAABCdb 数据库下的表,就需要转义字符 `_`: --replicate-wild-do-table=my\_own\%db。如果是在命令行中使用这个选项,就可能需要两个反斜杠来转义,这依赖于命令行解释器。例如,在 bash shell下,就需要输入: --replicate-wild-do-table=my\\_own\\%db。 --replicate-wild-ignore-table=db_name.tbl_name--replicate-wild-ignore-table=foo%.bar% 就不会同步所有以 foo 开头的数据库下的以 bar 开头的数据表上的更新操作。 --replicate-wild-ignore-table 选项的具体描述。模式中包含原义通配符的规则和 --replicate-wild-ignore-table 选项一样。 --replicate-rewrite-db=from_name->to_nameUSE 选中的)转换成 to_name 。只有涉及到数据表的语句(不包括类似 CREATE DATABASE, DROP DATABASE,和 ALTER DATABASE)才会被同步,并且只针对master上缺省数据库为 from_name 的情况。这个选项不支持跨库操作。注意,数据库名字转换先于 --replicate-* 规则之前测试。如果是在命令行中使用这个选项,需要把 `>' 字符用引号引用起来。例如: shell> mysqld --replicate-rewrite-db="olddb->newdb"
--replicate-same-server-id--log-slave-updates 选项,则不能设置为1。注意,从MySQL 4.1开始,slave的I/O线程默认不把包含slave的服务器编号的二进制日志写到中继日志中(相对4.0这能节省磁盘使用)。因此想要在4.1中使用 --replicate-same-server-id 选项,在slave读取自己的更新事件让SQL线程来执行之前要确保启动slave时使用该选项。 --report-host=hostSHOW SLAVE HOSTS 语句时就会显示出来了。如果不想slave注册到master就无需设置这个选项。注意,这在slave连接到master之后,只根据这个配置master还不能直接从TCP/IP套接字中读取slave的IP地址。因为存在 NAT 或者其他路由机制,这个IP信息还不足以在master或者其他主机上连接到slave上。这个选项是从MySQL 4.0.0开始可以用的。 --report-port=port_number--skip-slave-startSTART SLAVE 语句来启动slave线程。 --slave_compressed_protocol={0|1}--slave-load-tmpdir=file_nametmpdir 的值一样。slave的SQL线程同步 LOAD DATA INFILE 语句时,它从中继日志中提取出要被加载的文件放到临时文件中,然后把它们加载到slave中。如果在master上加载的文件很大,那么slave上的临时文件也会很大。因此建议在slave上指定这个选项时把它放在剩余空间比较多的文件系统上。这是,最好也指定 --relay-log 到那个文件系统中,因为中继日志可能也会很大。--slave-load-tmpdir 必须指向基于磁盘的文件系统,而不能是基于内存的文件系统:slave可能会在机器重启后同步 LOAD DATA INFILE 语句时需要用到这个临时文件。这个目录同样不能在会被操作系统的启动进程清除的目录下。 --slave-net-timeout=seconds--master-connect-retry 控制。 --slave-skip-errors= [err_code1,err_code2,... | all]SHOW SLAVE STATUS 语句的结果中就能看到错误代码了。服务器错误代码详情请看"22 Error Handling in MySQL"。 all,它能忽略所有的错误信息,不管什么情况都继续保持同步。不消说,如果使用这个选项值,就不可能期待有数据的完整性了。这种情况下,就不能抱怨slave的数据无论在哪个地方都和master不接近了。已经警告过你了。例如: --slave-skip-errors=1062,1053 --slave-skip-errors=all
--replicate-* 模式根据以下规则来决定一个语句是要执行还是被忽略:是否有 --replicate-do-db 或 --replicate-ignore-db 规则?
--binlog-do-db 和 --binlog-ignore-db 选项(详情请看"5.9.4 The Binary Log")。测试结果?是否有 --replicate-*-table 规则?
INSERT INTO sales SELECT * FROM prices:中只有 sales 和规则比较)。如果有好几个表一起被更新(多表语句),第一个匹配的表(匹配 `do` 或 `ignore`)胜出。也就是说,只有第一个表和规则作比较。然后,如果还没有产生任何决定,就比较第二个表,以此类推。是否有 --replicate-do-table 规则?
是否有 --replicate-ignore-table 规则?
是否有 --replicate-wild-do-table 规则?
是否有 --replicate-wild-ignore-table 规则?
没有匹配 --replicate-*-table 规则。是否有其他表匹配这些规则?
--replicate-do-table 或 --replicate-wild-do-table 规则?问: master还在运行中,如何在不停止它的情况下配置slave?
答: 需要设计几个选项参数。如果已经有了master的备份并且记录了数据快照二进制日志文件名以及偏移位置(运行 SHOW MASTER STATUS 查看结果),执行以下步骤:
mysql> CHANGE MASTER TO
-> MASTER_HOST='master_host_name',
-> MASTER_USER='master_user_name',
-> MASTER_PASSWORD='master_pass',
-> MASTER_LOG_FILE='recorded_log_file_name',
-> MASTER_LOG_POS=recorded_log_position; START SLAVE 语句。mysql> FLUSH TABLES WITH READ LOCK;
shell> tar zcf /tmp/backup.tar.gz /var/lib/mysql
mysql> SHOW MASTER STATUS;
mysql> UNLOCK TABLES;
上述步骤的另一个办法是创建master的SQL转储文件。只需在master上执行 mysqldump --master-data 命令,然后将导出来的SQL转储文件载入slave。不过,这么做会制作二进制数据快照的方式慢一点。
无论使用上述两种方法的哪种,最后都能创建master的数据快照然后记录二进制日志文件名以及偏移位置。可以在好几的其他的slave上使用同一个备份的二进制数据快照。得到master的快照后,只要master的二进制日志完好无损,接着就能开始设置slave了。两个确实需要等待的时间长度被以下两个方面所限制:在master上磁盘空间保存二进制日志,以及slave从master抓取更新事件。
也可以使用 LOAD DATA FROM MASTER。这个语句可以很方便地在slave上取得数据快照并且能立刻调整二进制日志文件名以及偏移位置。在将来,我们推荐用 LOAD DATA FROM MASTER 来设置slave。警告,它只能用于 MyISAM 表,并且可能会保持一个较长时间的读锁。由于它还没达到所期望的高效率,因此如果数据表很大,最好还是在执行完 FLUSH TABLES WITH READ LOCK 后直接制作二进制数据快照。
问:是否slave总是需要连接到master?
答:不,非必需。slave可以好几小时甚至几天关闭或者不连接master,然后重连再取得更新操作日志。例如,可以在拨号链接上设置一个mater/slave关系,拨号可能只是零星的不定期的连接。这种做法隐含的是,在任何指定的时间里,除非使用特殊的度量标准,否则slave不能保证总是能和master保持同步。在未来,有个选项可以阻止master,除非至少有一个slave在同步中。
问:怎么知道比master晚了多少?也就是说,怎么知道slave最后同步的时间?
答:如果slave是4.1.1或者更新,只需查看 SHOW SLAVE STATUS 结果中的 Seconds_Behind_Master 字段。对于老版本,可以用以下办法。如果在slave上执行 SHOW PROCESSLIST 语句结果显示SQL线程(对MySQL 3.23则是slave线程)正在运行,这就意味着该线程至少从master读取一个更新操作事件。详情请看"6.3 Replication Implementation Details"。
当SQL线程执行一个master上读取的更新操作事件时,它把自己的时间改成事件的时间(这也就是 TIMESTAMP 也要同步的原因)。在 SHOW PROCESSLIST 结果中的 Time 字段中,slave的SQL线程显示的秒数就是最后一次同步的时间戳和slave本机的实际时间相差秒数。可以根据这个值来判断最后同步的时间。注意,如果slave已经从master断开好几个小时了,然后重新连接,就能看到slave的 SHOW PROCESSLIST 结果中的SQL线程的Time 字段的值类似3600。这是因为slave正在执行一个小时前的语句。
问:如何强制master在slave赶上全部更新之前阻止更新?
mysql> FLUSH TABLES WITH READ LOCK; mysql> SHOW MASTER STATUS;
记录下结果中的日志文件名以及偏移位置,它们是同步的坐标值。
MASTER_POS_WAIT() 函数的参数的值就是前面取得的同步坐标值: mysql> SELECT MASTER_POS_WAIT('log_name', log_offset);
SELECT 语句会阻止更新,直到slave同步到了上述日志文件及位置。在这个时候,slave就和master保持同步了,并且这个语句就会返回。
mysql> UNLOCK TABLES;
问:设置一个双向复制时要注意什么问题?
答:MySQL同步目前还不支持任何在master和slave上的分布式(跨服务器)更新锁协议以保证操作的原子性。也就是说,存在这样的可能性:客户端A在并存的master 1上做了一个更新,同时,在它同步到并存master 2上之前,客户端B在master 2上可能也做了一个和客户端A在master 1上不同的更新操作。因此,当客户端A所做的更新同步到master 2时,它将产生和master 1上不同的数据表,尽管master 2上的更新操作也全都同步到master 1上去。这意味着除非能确保所有的更新都能以任何顺序安全地执行,否则不要使用双向同步,或者除非注意在客户端程序中的不知原因的无序更新操作。
同时也要意识到在所关心的更新问题上,双向同步实际上并不能很大地改善性能(甚至没有)。两个服务器都需要执行同样数量的更新操作,在一台服务器上也是。唯一区别的是,可能这样做会减少一些锁争夺,因为来自其他服务器的更新操作都会被串行地放到slave线程中。甚至这种好处还可以作为网络延迟的补偿。
问:我如何利用同步来提高系统性能?
答:需要安装一个服务器作为master并且把所有的写操作直接放在这上面。然后配置多个廉价的使用机架磁盘的slave,把读操作分配给master和slave。还可以在启动slave时使用 --skip-innodb, --skip-bdb, --low-priority-updates,和 --delay-key-write=ALL 选项来提高slave端的性能。这种情况下,slave会使用非事务的 MyISAM 表来代替 InnoDB 和 BDB 表,已取得更快速度。
问:如何准备客户端应用程序的代码来适应同步应用?
safe_writer_connect()
safe_reader_connect()
safe_reader_statement()
safe_writer_statement()
每个函数名的 safe_ 表示它们会处理所有的错误情况。可以使用其他函数名。重要的是,要为读连接、写连接、读、写定义好统一的接口。
然后将客户端代码转换成使用封装的类库。已开始可能是很痛苦且麻烦的,不过在将来长期运行中就能得到回报了。所有使用上述方法的应用程序都会在master/slave配置中有优势,即使包含多个slave。这些代码将很容易维护,一些额外的麻烦也会很少。自豪需要修改一个或者两个函数;例如,想要记录每个语句执行了多长时间,或者在上千个语句中哪个出现错误了。
如果已经写了很多代码,你可能想要自动转换它们,那么可以使用MySQL发布的 replace 工具,或者自己写转换脚本。理想地,你的代码已经使用了统一的编程风格。如果不是,最好重写它们,或者可以遍历检查一下,手工规范化一下代码风格。
问:MySQL同步何时且有多少能提高系统性能?
答:MySQL同步对于频繁读但不频繁写的系统很有好处。理论上来讲,使用单一master/多slave的配置,就可以通过这个方法来衡量系统:增加更多的slave直到用完所有的网络带宽或者master的更新操作增长到了不能再处理的点了。
想要知道增加多少个slave之后得到的性能才能平稳,以及能提高多少性能,就需要知道查询模式,并且根据经验对典型的master和slave做读(每秒读或 max_reads)和写(max_write)基准测试得到它们之间的关系。下例展示了一个理想系统取得的性能的简单计算方法。
设定系统负载由10%写和90%读组成,我们已经通过基准测试确定 max_reads 是1200 - 2 * max_writes。换句话说,系统可以达到每秒做没有写的1200次读操作,写操作平均是读操作的2倍慢,它们之间的关系是线性的。让我们假设master和每个slave都有同样的容量,有一个master和N个slave。每个服务器(master或slave):
reads = 1200 - 2 * writes
reads = 9 * writes / (N + 1) (读是分开的,但是所有写是在所有的服务器上的)
9 * writes / (N + 1) + 2 * writes = 1200
writes = 1200 / (2 + 9/(N+1))
最后的等式说明了N个slave的最大写数量,给它每分钟的最高读频率1200和1次写9次读的机率。
注意,上面的计算是假设了网络带宽无穷大,并且忽略了一些系统中比较大的因素。在很多情况下,当系统增加 N 个同步slave之后,是无法精确计算出上述预计结果的。不过,先看看下列问题将有助于你知道是否有和有多少系统性能上的改善:
问:如何利用同步提供冗余/高可用性?
CHANGE MASTER TO 语句告诉slave修改master。 使用 --log-bin 选项,不使用--log-slave-updates 选项来启动slave。这样就能让slave运行 STOP SLAVE; RESET MASTER 语句后随时准备变成master,并且在其他slave上运行CHANGE MASTER TO。例如,有以下配置方案:
WC
\
v
WC----> M
/ | \
/ | \
v v v
S1 S2 S3
M 表示masetr,S 表示slave,WC表示提交读写操作的客户端;只提交读操作的客户端没有表示出来,因为它们无需切换。S1,S2,S3都是使用 --log-bin 选项,不用 --log-slave-updates 选项运行的slave。由于除非指定 --log-slave-updates 参数,否则从master读到的更新操作都不会记录到二进制日志中,因此每个slave上的二进制日志都是空的。如果因为某些原因 M 不能用了,可以指定一个slave作为master。例如,如果指定S1,则所有的WC都要重定向到S1上,S2和S3都需要从S1上同步。
确定所有的slave都已经处理完各自的中继日志了。在每个slave上,提交 STOP SLAVE IO_THREAD 语句,然后检查 SHOW PROCESSLIST 的结果直到看到 Has read all relay log 了。当所有的slave都这样子之后,就可以按照新的方案设置了。在slave S1上提交 STOP SLAVE 和 RESET MASTER 语句将其提升为master。
在其他slave S2和S3上,提交 STOP SLAVE 和 CHANGE MASTER TO MASTER_HOST='S1' ( 'S1' 代表S1的真实主机名) 语句修改master。把S2,S3如何连接到S1的参数(用户,密码,端口等)都附加到 CHANGE MASTER 后面。在 CHANGE MASTER 中无需指定S1的二进制日志文件名和偏移位置:因为 CHANGE MASTER 默认就是第一个二进制日志和偏移位置4。最后,在S2和S3上提交 START SLAVE 语句。
然后让所有的WC都把他们的语句重定向到S1上。从这个时候开始,从所有的WC发送到S1上的更新语句都会写到S1的二进制日志中,它们包含了从M死掉之后发送到S1的全部更新语句。
配置结果如下: WC
/
|
WC | M(unavailable)
\ |
\ |
v v
S1<--S2 S3
^ |
+-------+
当M又起来了之后,只需在M上提交和在S2和S3上的一样的 CHANGE MASTER 语句,将它变成一个slave并且读取自从它死掉之后的全部WC提交的更新操作。想要把M重新变成master(例如因为它的性能更好),就执行类似上面的操作,把S1当作失效了,把M提升为新的master。在这个步骤中,别忘了在把S2和S3修改成为M的slave之前在M上运行 RESET MASTER 语句。否则的话,它们会从M开始失效的那个时刻开始读取WC提交的更新操作日志。
现在我们就运行着一个完整的自动选择master的MySQL同步系统,不过在它准备好之前,需要创建自己的监控工具。
SHOW MASTER STATUS 检查一下状态。如果是,Position 的值不为零;否则,确定master上使用了 log-bin 和 server-id 选项。 SHOW SLAVE STATUS 语句检查 Slave_IO_Running 和 Slave_SQL_Running 的值是否都是。如果不是,确定是否用同步参数启动slave服务器了。 SHOW PROCESSLIST 语句检查I/O和SQL线程的 State 字段值。详情请看"6.3 Replication Implementation Details"。如果I/O线程状态为 Connecting to master,就检查一下master上同步用户的权限是否正确,master的主机名,DNS设置,master是否确实正在运行着,以及slave是否可连接到master,等等。 START SLAVE。 mysql> SET GLOBAL SQL_SLAVE_SKIP_COUNTER = n; mysql> START SLAVE;
如果下一个语句没有使用 AUTO_INCREMENT 或 LAST_INSERT_ID(),那么 n 的值应为为 1。否则,它的值为 2。设定为 2 是因为 AUTO_INCREMENT 或 LAST_INSERT_ID() 在master的二进制日志中占用了2条日志。
当确定没有包含用户的错误,并且同步还是不能正常工作或者不稳定,就可以报告bug了。我们需要你尽量多的跟踪bug的信息。请花点时间和努力准备一个好的bug报告。
--log-slave-updates 和 --log-bin 选项启动slave。这会导致slave将从master读取的更新操作写到自己的二进制日志中。 SHOW MASTER STATUS 的结果 SHOW SLAVE STATUS 的结果 mysqlbinlog 来检查二进制日志。例如,用以下方法有助于找到有问题的查询: shell> mysqlbinlog -j pos_from_slave_status \
/path/to/log_from_slave_status | head
一旦收集好了问题的证据,首先将它隔离到一个独立的测试系统上。然后在我们的bug数据库http://bugs.mysql.com/上进可能详细地报告问题。
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=5193754
- 评论人:一层自然
2006-08-24 10:14:41
|
||||
写得非常好 |
||||