Format: complete
Title: How-to: Master/Master replication for MySQL
Author: Pedro Melo
Date: 2008-06-26
Copyright: 2008 Pedro Melo.
This work is licensed under a Creative Commons License.
http://creativecommons.org/licenses/by-sa/2.5/
Keywords: mysql, master/master, replication
XMP: CCAttributionShareAlike
CSS: css/multimarkdown.css
This How-To will walk you through the steps to create a MySQL master/master setup.
Before you setup your database like this, make sure your application supports this. You should read this FAQ entry.
This will create two mysql servers, running on the same hardware, on ports 7001 and 7002.
To make the process easier, define these two environment variables:
MY_DIR
: the directory where the mysql is installed, usually/usr/local/mysql
;HOWTO_DIR
: the directory where this file is located.
I'll use the Tigase MySQL schema file as my example schema.
The sample data I'll insert into the database, are just made up values and probably not even valid from the point of view of Tigase.
The two configuration files available in the etc/
directory are based
on the my-small.cnf
standard configuration file.
The following changes where done:
- all paths where changed to enable two MySQL servers running simultaneously on the same hardware;
- the
server-id
setting of each server is different: for a sucessful replication setup, each server on the replication group must have a differentserver-id
; - "binlogs" are activated: they record all updates to the database;
port
andsocket
settings: updated to be different for each server.
For any MySQL command to use a specific server, you just need to
add a --defaults-file=$HOWTO_DIR/etc/server-1.cnf
(replace 1 with 2 to work
on the second server).
Setup the two new servers:
sudo -s
mkdir -p /tmp/server-{1,2}
chown mysql:wheel /tmp/server-{1,2}
cd $MY_DIR
./scripts/mysql_install_db --defaults-file=$HOWTO_DIR/etc/server-1.cnf --user=mysql
./scripts/mysql_install_db --defaults-file=$HOWTO_DIR/etc/server-2.cnf --user=mysql
Start them up:
cd $MY_DIR
./bin/mysqld_safe --defaults-file=$HOWTO_DIR/etc/server-1.cnf --user=mysql
./bin/mysqld_safe --defaults-file=$HOWTO_DIR/etc/server-2.cnf --user=mysql
At this point in time, both servers should be running. You can test by connecting to each one:
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root
mysql --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root
Please note that this procedure did not set a root
account password.
This means that anybody can connect as root
to your database.
For production environments it is strongly recommended that you change
the root
password, and delete the anonymous accounts.
For our tests purposes, we will not do that.
On server-1
, connect as root, and create the user used by the slave:
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root < $HOWTO_DIR/sql/repl_user.sql
We will also create the tigasedb
database, and load our test schema:
mysqladmin --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root create tigasedb
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root tigasedb < $HOWTO_DIR/sql/tigase-mysql-schema.sql
We should be able to see the tigasedb
database and its tables. Lets
insert some data on them to test:
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root tigasedb < $HOWTO_DIR/sql/test-data-1.sql
We need to obtain the current master replication position, create a stable snapshot of the data.
Connect as root
:
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root tigasedb
This connection must be left open for the rest of this procedure. This
is required to leave the LOCK
in place.
Execute the following statements:
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
+---------------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------------------+----------+--------------+------------------+
| mysql-server-1-bin.000003 | 4514 | | |
+---------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
This information tells you that the master position is the
mysql-server-1-bin.000003
file at 4514.
Now create a dump of the data in the master:
mysqldump --defaults-file=$HOWTO_DIR/etc/server-1.cnf \
-u root \
--lock-all-tables \
tigasedb > tigasedb_dump.sql
Now we need to setup the slave. First, create the tigasedb
database and
load the SQL dump from the master:
mysqladmin --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root create tigasedb
mysql --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root tigasedb < tigasedb_dump.sql
You should have the same data on both servers now.
To finish the setup, you need to set the other master parameters on the
slave. Connect as root
:
mysql --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root
And setup the master link:
CHANGE MASTER TO
MASTER_HOST='127.0.0.1',
MASTER_PORT=7001,
MASTER_USER='repl_tig',
MASTER_PASSWORD='pray4sync',
MASTER_LOG_FILE='mysql-server-1-bin.000003',
MASTER_LOG_POS=4514;
START SLAVE;
You can now end the server-1 session where the LOCK
was created.
You should have a proper master/slave relation working between server-1
and server-2
.
If you look at SHOW MASTER STATUS
on server-1
and compare to
SHOW SLAVE STATUS\G
(use \G
query terminator for a more useful layout)
on server-2
, you should see the same File
and Position
on server-1
as Master_Log_File
and Read_Master_Log_Pos
on server-2
.
To test, insert a new row in the tig_user
table.
INSERT INTO tig_users (uid, user_id) VALUES (3, 'user3');
If you SELECT * FROM tig_users
on server-2
, you should see the
user3
row.
You can also re-check the SHOW MASTER STATUS
/SHOW SLAVE STATUS
and
compare the File
/Position
.
The last step is to set server-2
as master of server-1
.
A master/master setup requires some configurations to make AUTO_INCREMENT
work correctly.
Our config files already include the proper auto_increment_increment
and
auto_increment_offset
values.
You should read:
It boils down to this:
auto_increment_increment
should be equal to the number of masters you have,N
;auto_increment_offset
should be different, from 1 toN
, on each master.
You need a replication user on server-2
. We'll use the same one:
mysql --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root < $HOWTO_DIR/sql/repl_user.sql
Connect to server-2
, lock tables and record the master position:
mysql --defaults-file=$HOWTO_DIR/etc/server-2.cnf -u root tigasedb
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
+---------------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------------------+----------+--------------+------------------+
| mysql-server-2-bin.000004 | 482 | | |
+---------------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
Then hop over to server-1
, and connect as root
:
mysql --defaults-file=$HOWTO_DIR/etc/server-1.cnf -u root tigasedb
Make server-1
a slave with the proper master position:
CHANGE MASTER TO
MASTER_HOST='127.0.0.1',
MASTER_PORT=7002,
MASTER_USER='repl_tig',
MASTER_PASSWORD='pray4sync',
MASTER_LOG_FILE='mysql-server-2-bin.000004',
MASTER_LOG_POS=482;
START SLAVE;
Release the lock on server-2
. The easiest way is just quiting the
mysql
shell.
You should have a proper master/master relation working between server-1
and server-2
.
If you look at SHOW MASTER STATUS
on server-2
and compare to
SHOW SLAVE STATUS\G
(use \G
query terminator for a more useful layout)
on server-1
, you should see the same File
and Position
on server-2
as Master_Log_File
and Read_Master_Log_Pos
on server-1
.
Do the same procedure again, but switch server-1
with server-2
. You
should also see equal values.
I recommend that you start two new connections, one to server-1
and
the other to server-2
.
Select, insert, update, delete from each table, and see if everything is working.
Also try on one of the connections:
CREATE TABLE test_ainc ( id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY);
And then insert rows with:
INSERT INTO test_ainc VALUES (null), (null), (null);
Do this on both connections and see how the AUTO_INCREMENT
columns
just work.
One of the recommendations for InnoDB performance
is to set innodb_flush_log_at_trx_commit
to 2. The
MySQL documentation recommends setting to 1 as the most safe option.
You could limit the replication to just the tigasedb
database with a:
replicate-do-db = tigasedb
in the configuration file, mysqld
section.
Alternative, you could use this:
replicate-ignore-db=mysql
This will replicate all the databases, except the system mysql. This is
usefull because it will replicate everything except users, passwords and
GRANT
/REVOKE
statements.
You should notice that I didn't set the master host, port and other settings on the configuration file.
The CHANGE MASTER
command writes that information in a master.info
file. This file takes precedence over the configuration file.
First, the best resource for all about replication will probably be the High Performance MySQL (2nd. ed.) book. You should buy it.
A list of useful links:
- The MySQL documentation about replication is very good. If you follow each step carefuly you should be ok;
- The High-Performance MySQL site
is one of the best for all things MySQL. Make sure you
search for
replication
; - Browse the Notes and Tips section;
- An old article about MySQL replication at OnLamp.com. You should double-check the commands, but the graphics are pretty and usefull.
The top hit on Google for MySQL master/master setup is a how-to at HowtoForge. Personally I found it a bit dense, but that's just me.