MySQL Master-Slave Replication Setup: From Real Incidents to Solutions

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

What happened at 3 AM

I once dealt with a database corruption incident at 3 AM and had to restore from backup — ever since, I’ve checked backups every single day. But no matter how good your backup is, there’s one fatal gap: while restoring, your website is completely down. Customers can’t place orders, SEO takes a hit, and you’re sitting there staring at a black screen, drinking cold coffee.

After that incident, I started seriously studying MySQL Replication — and this is the thing I wish I’d known a few years earlier.

Why is the database always the weakest link?

Look at 9 out of 10 small-to-medium web apps and you’ll see the same diagram: 1 web server + 1 database server. This setup runs fine when traffic is low. But as users grow, the database is always the first thing to buckle.

The specific problems:

  • Read-heavy workload: 80-90% of queries in a typical web app are SELECTs (reads). They all hit a single server.
  • Single point of failure: Database dies = entire application dies. No plan B.
  • Backups don’t protect against downtime: Restoring from backup takes 30 minutes to several hours depending on data size.
  • Maintenance causes downtime: Need to upgrade MySQL version? Need to optimize a large table? Everything has to stop.

The solutions — and their problems

Scale up: Upgrade hardware

The simplest approach: buy a beefier server. More RAM, faster SSD, more cores. But scaling up only buys you time — you can’t keep upgrading forever. An r6g.8xlarge instance on AWS (64GB RAM, 32 vCPU) costs around $1,000/month, and it’s still a single point of failure.

Add a caching layer (Redis/Memcached)

Redis or Memcached can reduce 60-70% of queries hitting the database — especially effective for listing pages, search results, and static content. But not everything can be cached. A shopping cart being updated, account balances, or real-time notifications — those need to be read directly from the database.

MySQL Replication

The database automatically replicates to another server, continuously, in near real-time. It distributes read/write load and keeps a live standby ready — this is the real fix at the root of the problem.

How MySQL Replication works

The idea is simple: one Master server receives all write operations (INSERT, UPDATE, DELETE). Every change is written to the Binary Log. Slave servers connect to the Master, read the Binary Log, and replay those statements on themselves.

The result: Slaves always hold a near-identical copy of the Master, with only a few milliseconds to a few seconds of lag depending on load.

With this setup, your application can:

  • Send all write queries → Master
  • Send all read queries → Slave(s)

Immediately reduces 80% of the load on Master, and you have a “hot” backup that’s always up to date.

Step-by-step MySQL Master-Slave Replication setup

Demo environment: Ubuntu 22.04, MySQL 8.0, 2 servers.

  • Master: IP 192.168.1.10
  • Slave: IP 192.168.1.20

Step 1: Configure MySQL on the Master

Open the MySQL configuration file:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add or edit the following lines:

[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = your_database_name
bind-address = 0.0.0.0

Restart MySQL:

sudo systemctl restart mysql

Step 2: Create a replication user on the Master

Log into MySQL:

mysql -u root -p

Create the user and grant privileges:

CREATE USER 'replica_user'@'192.168.1.20' IDENTIFIED WITH mysql_native_password BY 'StrongPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'replica_user'@'192.168.1.20';
FLUSH PRIVILEGES;

Get the current Binary Log position — note these values down, you’ll need them in the next step:

SHOW MASTER STATUS;

The output looks like this:

+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 |      154  | mydb         |                  |
+------------------+----------+--------------+------------------+

Record the File and Position values.

Step 3: Export data from the Master

Lock the tables, dump the data, then unlock:

# In the MySQL session, lock tables
FLUSH TABLES WITH READ LOCK;

# Open another terminal, dump the database
mysqldump -u root -p --databases your_database_name > /tmp/db_dump.sql

# Back in the first session, unlock
UNLOCK TABLES;

Copy the dump file to the Slave:

scp /tmp/db_dump.sql [email protected]:/tmp/

Step 4: Configure MySQL on the Slave

Open the configuration file:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add:

[mysqld]
server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = your_database_name
relay-log = /var/log/mysql/mysql-relay-bin.log

Restart MySQL and import the data:

sudo systemctl restart mysql
mysql -u root -p < /tmp/db_dump.sql

Step 5: Connect the Slave to the Master

Log into MySQL on the Slave:

STOP SLAVE;

CHANGE MASTER TO
  MASTER_HOST='192.168.1.10',
  MASTER_USER='replica_user',
  MASTER_PASSWORD='StrongPassword123!',
  MASTER_LOG_FILE='mysql-bin.000003',
  MASTER_LOG_POS=154;

START SLAVE;

Replace mysql-bin.000003 and 154 with the values you recorded in Step 2.

Step 6: Verify the setup

Run this command on the Slave:

SHOW SLAVE STATUS\G

Two critical lines to check:

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

If both say Yes — replication is running. Try creating a new table or inserting data on the Master, then check the Slave to confirm it appears there too.

Things to keep in mind in production

Replication lag — The Slave doesn't always have 100% identical data to the Master. If your application needs to read data immediately after writing (e.g., user registers and instantly views their profile), route that read request to the Master.

Slave is read-only — Don't let your application accidentally write data to the Slave. Add this line to the Slave config to be safe:

read_only = ON

Monitor replication — Replication occasionally stops due to errors (usually data conflicts). Set up a periodic check script:

mysql -u root -p -e "SHOW SLAVE STATUS\G" | grep -E "Running|Error|Seconds_Behind"

Firewall — Make sure port 3306 on the Master allows connections from the Slave's IP. Don't open it to the entire internet.

sudo ufw allow from 192.168.1.20 to any port 3306

Real-world results after deployment

After setting up replication for an e-commerce project with ~50k sessions/day, the load on the Master dropped from 85% CPU down to 30%. Average query time for product listing pages fell from 800ms to 200ms — simply by routing read queries to the Slave.

And more importantly: the next time database maintenance was needed, I just promoted the Slave to act as a temporary Master, with zero seconds of downtime.

Not complicated, not expensive. Just a $5-10/month VPS and 30 minutes of configuration. In return: a significantly lighter database, a live standby always kept in sync, and no more sweating through maintenance windows. Worth doing today.

Share: