MariaDB Master-Master Replication with GTID: A Simple Step-by-Step Guide

Master-master replication lets two MariaDB servers replicate to each other, so a change made on either one shows up on the other. Using GTID (Global Transaction ID) makes it much easier to set up, monitor, and recover than the old binary-log file/position method.

This guide is written for MariaDB on Windows and walks through it in 8 simple steps. No Linux tools needed.

Why GTID instead of file/position?

With the old method, a replica tracks where it is by binlog file name and byte offset, e.g. mariadb-bin.000042 at position 18734. With GTID, every transaction gets a global ID like 1-1-1234 (domain - server - sequence). That gives you:

  • No file names or positions to copy. You just say MASTER_USE_GTID = slave_pos, and the replica asks for whatever it hasn't seen yet.
  • Easy failover. A file/position only means something on one server. A GTID means the same thing everywhere, so you can point a replica at another server without working out the matching position.
  • Crash-safe. The replica stores its position in the mysql.gtid_slave_pos table, updated in the same transaction as the data, so a crash doesn't leave it pointing at the wrong place.
  • Clear status. Compare @@gtid_current_pos on both servers and you can see right away whether they're in sync.
  • Fits master-master. Each server has its own domain ID, so the two write streams are tracked separately and don't get mixed up.

What you need

  • Two Windows servers with the same MariaDB version (10.5 or newer recommended)
  • Server A: 10.0.0.1
  • Server B: 10.0.0.2
  • Replace the IPs and password below with your own

Where to run commands: open Command Prompt (MariaDB) from the Start menu (or a normal Command Prompt in C:\Program Files\MariaDB 11.x\bin). To get an SQL prompt, run:

mariadb -u root -p

Step 0: Open port 3306 in the firewall (both servers)

In an administrator Command Prompt:

netsh advfirewall firewall add rule name="MariaDB 3306" dir=in action=allow protocol=TCP localport=3306

Or add an inbound rule for TCP port 3306 in Windows Defender Firewall with Advanced Security.

Step 1: Configure Server A

Open my.ini in Notepad as administrator. It lives in the data folder, usually:

C:\Program Files\MariaDB 11.x\data\my.ini

Under the existing [mysqld] section, add:

[mysqld]
server_id                = 1
gtid_domain_id           = 1
log_bin                  = mariadb-bin
relay_log                = relay-bin
binlog_format            = ROW
log_slave_updates        = ON
gtid_strict_mode         = ON
bind-address             = 0.0.0.0

# avoid AUTO_INCREMENT collisions
auto_increment_increment = 2
auto_increment_offset    = 1

Leave the lines that are already there (like datadir and port) as they are.

Step 2: Configure Server B

Same file, with three values changed:

[mysqld]
server_id                = 2
gtid_domain_id           = 2
log_bin                  = mariadb-bin
relay_log                = relay-bin
binlog_format            = ROW
log_slave_updates        = ON
gtid_strict_mode         = ON
bind-address             = 0.0.0.0

auto_increment_increment = 2
auto_increment_offset    = 2

Why different values? server_id must be unique, a separate gtid_domain_id per server keeps each server's transactions in its own stream, and the auto-increment settings make A use odd IDs and B use even IDs.

Restart MariaDB on both servers, either from services.msc (right-click the MariaDB service > Restart) or in an administrator Command Prompt:

net stop MariaDB
net start MariaDB

Why binlog_format = ROW and not MIXED?

  • ROW writes the actual rows that changed, so both servers end up with exactly the same data.
  • MIXED mostly writes the SQL statement and lets the other server run it again. It switches to row format only for statements MariaDB detects as unsafe, and that detection is not perfect. Things like UPDATE ... LIMIT without ORDER BY, some triggers, or stored functions can give a different result on the other server, and nobody notices.
  • In master-master both servers take writes, so a silent difference grows and keeps replicating back and forth. With ROW, a mismatch shows up as a clear replication error (for example, a row not found) that you can fix.
  • The cost is larger binary logs for big bulk updates. You can reduce that with binlog_row_image = MINIMAL.

Step 3: Create the replication user (on both servers)

Open the SQL prompt (mariadb -u root -p) and run this on each server. Turning off sql_log_bin keeps the command out of the binary log, so it won't replicate and clash with the other server.

SET sql_log_bin = 0;
CREATE USER 'repl'@'10.0.0.%' IDENTIFIED BY 'StrongPassword!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.0.%';
SET sql_log_bin = 1;

Step 4: Start from the same data

Option 1: fresh servers (no data yet). Clear the binary logs on both servers so they start clean. In the SQL prompt:

RESET MASTER;

Option 2: Server A already has data. Copy it to Server B by hand:

  1. On Server A, create a folder C:\temp, then in Command Prompt run (one line):
    mariadb-dump -u root -p --all-databases --single-transaction --gtid --master-data=2 --routines --triggers --events > C:\temp\dump.sql
  2. Copy C:\temp\dump.sql to Server B's C:\temp folder any way you like: a shared network folder, copy and paste over Remote Desktop, or a USB drive.
  3. Find the GTID position. Open dump.sql in Notepad (or Notepad++ if the file is large). Near the top, press Ctrl+F and search for gtid_slave_pos. You'll find a line like:
    -- SET GLOBAL gtid_slave_pos='1-1-1234';
    Write this value down (here 1-1-1234).
  4. On Server B, open the SQL prompt and load the dump. Use forward slashes in the path:
    SET sql_log_bin = 0;
    SOURCE C:/temp/dump.sql;
    SET sql_log_bin = 1;
    sql_log_bin = 0 stops the import from being sent back to Server A.
  5. Still on Server B, set the position you wrote down (without the leading --):
    SET GLOBAL gtid_slave_pos = '1-1-1234';

Step 5: Make Server B replicate from A

In the SQL prompt on Server B:

CHANGE MASTER TO
  MASTER_HOST     = '10.0.0.1',
  MASTER_USER     = 'repl',
  MASTER_PASSWORD = 'StrongPassword!',
  MASTER_PORT     = 3306,
  MASTER_USE_GTID = slave_pos;

START SLAVE;

Step 6: Make Server A replicate from B

In the SQL prompt on Server A (this is what makes it master-master):

CHANGE MASTER TO
  MASTER_HOST     = '10.0.0.2',
  MASTER_USER     = 'repl',
  MASTER_PASSWORD = 'StrongPassword!',
  MASTER_PORT     = 3306,
  MASTER_USE_GTID = slave_pos;

START SLAVE;

Notice there are no log file names or positions. GTID tracks that for you.

Step 7: Check the status

On both servers:

SHOW SLAVE STATUS\G

Look for:

  • Slave_IO_Running: Yes
  • Slave_SQL_Running: Yes
  • Using_Gtid: Slave_Pos
  • Seconds_Behind_Master: 0
  • Last_Error is empty

You can also compare GTID positions:

SELECT @@gtid_binlog_pos, @@gtid_slave_pos, @@gtid_current_pos;

Step 8: Test it both ways

On Server A:

CREATE DATABASE repl_test;
CREATE TABLE repl_test.t (id INT AUTO_INCREMENT PRIMARY KEY, src VARCHAR(10));
INSERT INTO repl_test.t (src) VALUES ('A');

On Server B:

INSERT INTO repl_test.t (src) VALUES ('B');
SELECT * FROM repl_test.t;

Run the same SELECT on Server A. Both servers should show both rows, with odd and even IDs. Clean up with DROP DATABASE repl_test;.

Quick troubleshooting

  • Slave_IO_Running: No / Connecting: check the Windows firewall rule, bind-address, and the repl user's host and password. From the other server, mariadb -h 10.0.0.1 -u repl -p should connect.
  • MariaDB service won't start after editing my.ini: check for a typo, and look at the .err file in the data folder.
  • Duplicate key or other SQL errors: the data was not identical before you started. Fix the row, then STOP SLAVE; START SLAVE;. Re-sync with Step 4 if needed.
  • Need to see where each server is? SELECT @@gtid_current_pos; on both.

Good practices

  • Avoid writing the same rows on both servers at the same time; replication is asynchronous and conflicts are not resolved automatically.
  • A safe pattern is to send writes to one server and keep the other as a hot standby, using a proxy like MaxScale or HAProxy for failover.
  • Monitor SHOW SLAVE STATUS on both servers.
  • Keep backups. Replication copies mistakes too.

That's it: two MariaDB servers on Windows replicating to each other with GTID, with no binlog file names or positions to track.

Post a Comment

Previous Post Next Post