23. 据前有其他的语句 insert 或者 update 生成了若干行能够被匹配的 数据的话,这
种方案是行不通的。
1.
1.
1. 第二种解决方案相对复杂一些。它保证了 update 语句更新行数据
的 server_id 值为本 MySQL server 的 server_id 值。我们利用了
MySQL 提供的 trigger, update 语句提交执行之前改变行数据 N
在
EW 的 server_id 值为本 MySQL server 的 server_id 值。(在 My
SQL 的 trigger 中,OLD 行数据表示数据库中已有的将要被 updat
e 匹配的数据,而 NEW 行数据 表示 update 语句将要更新为其值
的行数据)。但是,由于我们的 multi-master 工具将生成的语句直
接提交到本 MySQL instance 中执行,那么如果我们的 trigger 不
能识别工具提交的语句和用户提交的语句,而把 NEW 行数据的 s
erver_id 值全部改成本 instance 的值,复制的死循环一定会出现。
例如:在上面的例子中,M1 的 server_id 为 1,在 M2 上 update
的数据复制到 M1 上提交执 行,如果 trigger 不能识别出它是从 m
ulti-master 工具生成的语句,而是把它的 NEW 行数据修改为 1,
那么记录在 M1 的 binlog 中的数 据将不能标识最开始提交语句的
MySQL instance 位置,从而不能中断数据复制的循环。至少有两
种方法可以区分普通的用户提交语句和 multi-master 工具提交的
语句。 专门指定 一个用户用于 multi-master 工具提交查询语句,
1,
以区别于其他用户提交的语句。在 trigger 中我们可以用 user()
24. 函数获得用户名来确定查 询语句提交的来源。2,通过 multi-mas
ter 修改相应语句而且使得它提交的所有语句与普通用户提交的语
句不同。下面我们详细阐述第二种策略。
我 们发现用户提交的所有 update 语句都有一个共同的特点:OLD 行数据和 N
EW 行数据的 server_id 值相等。如果我们在用工具生成 OLD 行数据和 NEW 行
数据的 server_id 值相等的 update 语句时,将 NEW 行数据的 server_id 值改变
(例如改为 0)以区别于用户提交的语句。这样 trigger 就能根据是否用户提交
的语句而进行相应的操作了。一般来说,我们的 trigger 可以写成如下的形式:
delimiter //
create trigger update_set_srvid_test_test001 before update on test001 for
each row
BEGIN
IF NEW.server_id=OLD.server_id THEN
SET NEW.server_id=1;
ELSEIF NEW.server_id=0 THEN
SET NEW.server_id=OLD.server_id;
END IF;
END;//
delimiter ;
1. Delete 的提交和复制:
25. Delete 的提交和复制和 update 的基本类似。在一个 MySQL 上提交的 delete 语
句将被对应为一个 delete row 事件记录在 binlog 文件中,slave 端复制文件并解
析 delete row 事件生成对应的 delete 语句提交执行。为了解决本 MySQL 上 in
sert 的数据在别的 MySQL instance 上 delete 的问题,我们的第一个解决方案
和 update 一样,也是忽略对 server_id 值的检查。同样,它存在着两个缺点:1.
它与冲突解决方案中的一种类型是一样的,我们不能区别这样的一个空操作是
否是一个冲突。2. 如果在 delete 语句复制回本 instance 前,有其他的语句产生
了该 delete 语句能够匹配的行数据,那么这一行数据将被错误的删除掉。同时,
因 为 delete 不包括 NEW 行数据,update 的第二个解决方案对 delete 语句不适
用。
测试结论
测试最终结论
通过上面的测试和分析,我们发现可以通过增加 server_id 列和增加 trigger 等手
段模拟 MySQL 的 replication 功能,从而实现 MySQL 的多主复制工具 multi-ma
ster。
附录
附录 1 MySQL 5.1.20 Beta 包含的事件类型
下面列举了各种 MySQL 的事件类型(代码拷贝自 MySQL 5.1.20 的源代码):
enum Log_event_type
26. {
/*
Every time you update this enum (when you add a type), you have to
fix Format_description_log_event::Format_description_log_event().
*/
UNKNOWN_EVENT= 0,
START_EVENT_V3= 1,
QUERY_EVENT= 2,
STOP_EVENT= 3,
ROTATE_EVENT= 4,
INTVAR_EVENT= 5,
LOAD_EVENT= 6,
SLAVE_EVENT= 7,
CREATE_FILE_EVENT= 8,
APPEND_BLOCK_EVENT= 9,
EXEC_LOAD_EVENT= 10,
DELETE_FILE_EVENT= 11,
/*
NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longer
sql_ex, allowing multibyte TERMINATED BY etc; both types share the
27. same class (Load_log_event)
*/
NEW_LOAD_EVENT= 12,
RAND_EVENT= 13,
USER_VAR_EVENT= 14,
FORMAT_DESCRIPTION_EVENT= 15,
XID_EVENT= 16,
BEGIN_LOAD_QUERY_EVENT= 17,
EXECUTE_LOAD_QUERY_EVENT= 18,
TABLE_MAP_EVENT = 19,
/*
These event numbers were used for 5.1.0 to 5.1.15 and are
therefore obsolete.
*/
PRE_GA_WRITE_ROWS_EVENT = 20,
PRE_GA_UPDATE_ROWS_EVENT = 21,
PRE_GA_DELETE_ROWS_EVENT = 22,
/*
These event numbers are used from 5.1.16 and forward
*/
28. WRITE_ROWS_EVENT = 23,
UPDATE_ROWS_EVENT = 24,
DELETE_ROWS_EVENT = 25,
/*
Something out of the ordinary happened on the master
*/
INCIDENT_EVENT= 26,
/*
Add new events here – right above this comment!
Existing events (except ENUM_END_EVENT) should never change their
numbers
*/
ENUM_END_EVENT /* end marker */
};
附录 2 MySQL 5.1.20 Beta 各事件的附加事件头长度
下面列举了 MySQL 5.1.20 Beta 各事件的附加事件头长度(拷贝自 MySQL 源
代码):
/* event-specific post-header sizes */
// where 3.23, 4.x and 5.0 agree
30. post_header_len[START_EVENT_V3-1]= START_V3_HEADER_LEN;
post_header_len[QUERY_EVENT-1]= QUERY_HEADER_LEN;
post_header_len[ROTATE_EVENT-1]= ROTATE_HEADER_LEN;
post_header_len[LOAD_EVENT-1]= LOAD_HEADER_LEN;
post_header_len[CREATE_FILE_EVENT-1]= CREATE_FILE_HEADER_LEN;
post_header_len[APPEND_BLOCK_EVENT-1]= APPEND_BLOCK_HEADER
_LEN;
post_header_len[EXEC_LOAD_EVENT-1]= EXEC_LOAD_HEADER_LEN;
post_header_len[DELETE_FILE_EVENT-1]= DELETE_FILE_HEADER_LEN;
post_header_len[NEW_LOAD_EVENT-1]= post_header_len[LOAD_EVENT-
1];
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIP
TION_HEADER_LEN;
post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
/*
We here have the possibility to simulate a master of before we changed
the table map id to be stored in 6 bytes: when it was stored in 4
31. bytes (=> post_header_len was 6). This is used to test backward
compatibility.
This code can be removed after a few months (today is Dec 21st 2005),
when we know that the 4-byte masters are not deployed anymore (chec
k
with Tomas Ulin first!), and the accompanying test (rpl_row_4_bytes)
too.
*/
DBUG_EXECUTE_IF(“old_row_based_repl_4_byte_map_id_master”,
post_header_len[TABLE_MAP_EVENT-1]=
post_header_len[WRITE_ROWS_EVENT-1]=
post_header_len[UPDATE_ROWS_EVENT-1]=
post_header_len[DELETE_ROWS_EVENT-1]= 6;);
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= post_header_len[APPE
ND_BLOCK_EVENT-1];
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_
QUERY_HEADER_LEN;
post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
附录 3 MySQL 5.1.20 Beta 中各列在内部存储时可能的各种数据类型
enum enum_field_types
33. MYSQL_TYPE_SET = 248,
MYSQL_TYPE_TINY_BLOB = 249,
MYSQL_TYPE_MEDIUM_BLOB = 250,
MYSQL_TYPE_LONG_BLOB = 251,
MYSQL_TYPE_BLOB = 252,
MYSQL_TYPE_VAR_STRING = 253,
MYSQL_TYPE_STRING = 254,
MYSQL_TYPE_GEOMETRY = 255,
};
附录 4 MySQL 5.1.20 Beta 中各 ROW_EVENT 的 m_flags 包含的标志位
在 MySQL 5.1.20 Beta 中,各 ROW_EVENT 都含有 m_flags 标志位集合。可
能的标志如下:
名称 值 含义
STMT_END_F 1 语句执行结束标志
NO_FOREIGN_KEY_CHECKS_F 2 不进行外键约束检查的标志
RELAXED_UNIQUE_CHECKS_F 4 不进行唯一键约束检查的标志
相关代码如下:
/*
These definitions allow you to combine the flags into an
34. appropriate flag set using the normal bitwise operators. The
implicit conversion from an enum-constant to an integer is
accepted by the compiler, which is then used to set the real set
of flags.
*/
enum enum_flag
{
/* Last event of a statement */
STMT_END_F = (1U << 0),
/* Value of the OPTION_NO_FOREIGN_KEY_CHECKS flag in thd->option
s */
NO_FOREIGN_KEY_CHECKS_F = (1U << 1),
/* Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->option
s */
RELAXED_UNIQUE_CHECKS_F = (1U << 2)
};
typedef uint16 flag_set;
/* Special constants representing sets of flags */
enum
{
35. RLE_NO_FLAGS = 0U
};
MySQL row 方式的复制 relay
上次老大问我 row 方式的 binlog 复制到备机可不可能记录为 statement。根据我
对复制的了解和对 row 方式的理解,我回答的是不可能。因为 MySQL 的源码中,
我记得 row 方式的处理是 table map,row_log_event 分开的,然后 row_log_ev
ent 中记录的是行的数据(包括 bitmap 对应对应的列在该 row_log_event 有没有
记录,整个 row_log_event 的长度,每个列的长度后面跟上列的具体数据等),
这些东西记录下载,MySQL 的开发者如果要把它还原成 statement 方式,并且
将相关的 auto_increment,var,rand 等还原出来难度还是非常大的,并且,row
方式实际上已经毁坏了 statement 的结构(比如:update tbl1 set c1=3 where id
=3 记录为 row 的话,正常情况下,row_log_event 不会只记录了更新的这一列 c
1,它会记录 id 或者其他的列,虽然 id 或者其他的列值并没有更新。原因可能
是为了正确的更新对应的行。),如果想完全还原成和主机上提交的 statement
一模一样基本是不可能的。另外,我也没有看到 MySQL 源码中的相关代码有将
row 转换成 statement 的相关痕迹,所以判断 row 方式在备机中 log_slave_upd
ates 会也被记录为 row 方式。
但是,口说无凭,实践致胜,我在主备机环境下测试了上面的这个情况。确实如
上所述,row 方式的 binlog,备机利用 log_slave_updates 记录到本机 binlog 也
是 row 方式。即使你设置备机的 binlog_format 为 statement。下面是我的测试
描述和结果。
36. 1、主机上设置 binlog_format 为 row.
root@localhost : alitest 10:01:09> set binlog_format=row;
Query OK, 0 rows affected (0.00 sec)
root@localhost : alitest 10:01:25> show variables like ‘bin%’;
+——————-+———+
| Variable_name | Value |
+——————-+———+
| binlog_cache_size | 2097152 |
| binlog_format | ROW |
+——————-+———+
2 rows in set (0.00 sec)
2、备机上设置 binlog_format 为 statement
root@localhost : (none) 10:06:44> set global binlog_format=’statement’;
Query OK, 0 rows affected (0.00 sec)
root@localhost : (none) 10:07:04> set binlog_format=’statement’;
Query OK, 0 rows affected (0.00 sec)
root@localhost : (none) 10:07:11> show variables like ‘bin%’;
+——————-+———–+
| Variable_name | Value |
+——————-+———–+
| binlog_cache_size | 2097152 |
| binlog_format | STATEMENT |
37. +——————-+———–+
2 rows in set (0.00 sec)
3、主机上创建测试表:
root@localhost : (none) 10:00:02> use alitest;
Database changed
root@localhost : alitest 10:00:50> create table test9 (c1 int unsigned pri
mary key, c2 varchar(24));
Query OK, 0 rows affected (0.20 sec)
4、备机上查看目前的日志位置:
root@localhost : (none) 10:04:49> show master logs;
+——————+———–+
| Log_name | File_size |
+——————+———–+
| mysql-bin.000001 | 125 |
…
| mysql-bin.000085 | 362605228 |
+——————+———–+
85 rows in set (0.00 sec)
4、主机上生成测试的 row 方式日志:
root@localhost : alitest 10:01:27> insert into test9 (c1,c2) values (44, “34
3434″);
Query OK, 1 row affected (0.00 sec)
45. Permitted Values (>= 5.1.5, <= 5.1.7)
Type enumeration
Default STATEMENT
Valid Values ROW, STATEMENT
Permitted Values (>= 5.1.8, <= 5.1.11)
Type enumeration
Default STATEMENT
Valid Values ROW, STATEMENT, MIXED
Permitted Values (>= 5.1.12, <= 5.1.28)
Type enumeration
Default MIXED
Valid Values ROW, STATEMENT, MIXED
Permitted Values (>= 5.1.29)
Type enumeration
Default STATEMENT
Valid Values ROW, STATEMENT, MIXED
Specify whether to use row-based, statement-based, or mixed replication
(statement-based was the default prior to MySQL 5.1.12; in 5.1.12, the d
efault was changed to mixed replication; in 5.1.29, the default was chan
ged back to statement-based). See Section 16.1.2, “Replication Formats”.
This option was added in MySQL 5.1.5.
Important
46. Setting the binary logging format without enabling binary logging prevents
the MySQL server from starting. This is a known issue in MySQL 5.1
which is fixed in MySQL 5.5. (Bug#42928)
MySQL Cluster. The default value for this option in all MySQL Cluster N
DB 6.1, 6.2, 6.3, and later 6.x releases is MIXED. See Section 17.6.2,
“MySQL Cluster Replication: Assumptions and General Requirements”, for
more information.
may your success.
eshop MySQL 数据库主备机数据 xtrabackup 同步数据
今天需要再次用到 xtrabackup 来备份和恢复数据。找了半天终于把以前写的一
个文档找到了。保存在 blog 中。以防丢失。xtrabackup 更换为 xtrabackup-1.2
-22.rhel5.x86_64.rpm 也已经测试通过
一.迁移目的
目前 ITBU eshop MySQL 数据库有 64 台数据库服务器。两两互备作为双 mas
ter 结构相互备份。但是由于应用方使用了 MySQL 的 uuid 函数,导致两两互备
的 mysql 服务器之间的数据不一致。这样两个数据库切换将导致用户数据的丢
失。
4 月底,应用方修改了 uuid 的问题,避免的新的数据不一致问题。接下来就只
有数据库中已有的数据不一致问题了。
MySQL 数据库,由于主备之间已经切换过多次,目前一部分数据已经无法找回。
47. 跟应用沟通后,确定以目前主库的数据为准,将主库的数据同步到备库。备库的
数据备份到本机。保存一个月。
二.迁移要求
1、迁移过程中要求不影响应用,也就是说 eshop 数据库服务不能停止。
2、迁移完成后主备机数据保持一致。
三.迁移方案
这里由于管理维护的必要,我们假设主备机我们都是以 root 登录。xtrabackup
在 MySQL 中的权限为:
root@127.0.0.1 : (none) 20:57:24> show grants for ‘xtrabackup’@'localhos
t’G
*************************** 1. row ***************************
Grants for xtrabackup@localhost: GRANT SELECT, RELOAD, LOCK TAB
LES, REPLICATION CLIENT ON *.* TO ‘xtrabackup’@'localhost’ IDENTIF
IED BY PASSWORD ‘*BF9F4C1B8BC37C75BBF482C8037EC944555D371
A’
*************************** 2. row ***************************
Grants for xtrabackup@localhost: GRANT INSERT, CREATE, DROP ON
`mysql`.`ibbackup_binlog_marker` TO ‘xtrabackup’@'localhost’
2 rows in set (0.00 sec)
3.1.备份/恢复工具选择
由于迁移过程中不能够停止数据库服务,数据库备份必须选择热备份。两种选择