Databases 8 min read

Set Up MySQL Master‑Slave Replication with Docker and Sync to Redis via Canal

This guide walks through installing MySQL in Docker, configuring master‑slave replication, verifying synchronization, deploying a Canal server, and integrating it with a Spring Boot application to automatically push database changes into Redis.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Set Up MySQL Master‑Slave Replication with Docker and Sync to Redis via Canal

Environment

Spring Boot 2.7.12 + MySQL 8. Master IP: 192.168.2.129, Slave IP: 192.168.2.130.

1. Install MySQL with Docker

Create directories and set permissions:

mkdir -p /root/software/mysql/conf /root/software/mysql/data
chmod -R 777 /root/software/mysql/

Run the MySQL container:

docker run --name mysql8 --restart=always --privileged=true \
  -v /root/software/mysql/conf/my.cnf:/etc/mysql/my.cnf \
  -v /root/software/mysql/data:/var/lib/mysql \
  -v /etc/localtime:/etc/localtime:ro \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=123123 \
  -d mysql --lower_case_table_names=1

2. MySQL Configuration (my.cnf)

[client]
#socket = /usr/mysql/mysqld.sock
default-character-set = utf8mb4
[mysqld]
#pid-file = /var/run/mysqld/mysqld.pid
#socket = /var/run/mysqld/mysqld.sock
#datadir = /var/lib/mysql
#socket = /usr/mysql/mysqld.sock
#pid-file = /usr/mysql/mysqld.pid
datadir = /var/lib/mysql
character_set_server = utf8mb4
collation_server = utf8mb4_bin
secure-file-priv = NULL
symbolic-links = 0
!includedir /etc/mysql/conf.d/

3. Master‑Slave Configuration

On the master (192.168.2.129) add:

binlog_format=MIXED
log-bin=mysql-bin
server-id=1

On the slave (192.168.2.130) add:

log-bin=mysql-bin
server-id=2

Restart MySQL on both nodes.

4. Configure the Slave Node

Check master status to obtain File and Position: mysql> show master status; Run CHANGE MASTER TO with the obtained values:

CHANGE MASTER TO 
  MASTER_HOST='192.168.2.129',
  MASTER_PORT=3306,
  MASTER_USER='root',
  MASTER_PASSWORD='123123',
  master_log_file='mysql-bin.000003',
  master_log_pos=156,
  master_connect_retry=60,
  GET_MASTER_PUBLIC_KEY=1;

If an error about a running slave thread appears, execute: STOP SLAVE; Note that the binary log file name may increment after a master restart (e.g., mysql-bin.000004).

Verify slave status:

show slave status\G

5. Test Replication

Creating databases or tables on the master will be automatically synchronized to the slave.

6. Deploy Canal

docker run --name canal -p 11111:11111 \
  -v /opt/canal/conf:/home/admin/canal-server/conf \
  -v /opt/canal/logs:/home/admin/canal-server/logs \
  -d canal/canal-server

7. Spring Boot Integration with Canal

Add Maven dependencies:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>top.javatool</groupId>
    <artifactId>canal-spring-boot-starter</artifactId>
    <version>1.2.1-RELEASE</version>
  </dependency>
</dependencies>

Configure application.yml:

spring:
  redis:
    host: localhost
    port: 6379
    password: 123123
    database: 8
    lettuce:
      pool:
        maxActive: 8
        maxIdle: 100
        minIdle: 10
        maxWait: -1
---
canal:
  server: 192.168.2.130:11111
  destination: redis # multiple destinations can be comma‑separated

Define a data model:

public class Users {
  private Integer id;
  private String name;
  private Integer age;
  @Override
  public String toString() {
    return "Users [id=" + id + ", name=" + name + ", age=" + age + "]";
  }
}

Implement a Canal service component:

@Component
@CanalTable("users")
public class UserServiceImpl implements EntryHandler<Users> {
  private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
  private final StringRedisTemplate stringRedisTemplate;
  public UserServiceImpl(StringRedisTemplate stringRedisTemplate) {
    this.stringRedisTemplate = stringRedisTemplate;
  }
  @Override
  public void insert(Users user) {
    logger.info("新增数据为{}", user);
    try {
      stringRedisTemplate.opsForValue().set("users:" + user.getId(), new ObjectMapper().writeValueAsString(user));
    } catch (JsonProcessingException e) { e.printStackTrace(); }
  }
  @Override
  public void update(Users before, Users after) {
    logger.info("原来数据为{}", before);
    logger.info("更新数据为{}", after);
    try {
      stringRedisTemplate.opsForValue().set("users:" + after.getId(), new ObjectMapper().writeValueAsString(after));
    } catch (JsonProcessingException e) { e.printStackTrace(); }
  }
  @Override
  public void delete(Users user) {
    logger.info("删除的数据为{}", user);
    stringRedisTemplate.delete("users:" + user.getId());
  }
}

With these insert, update, and delete methods, any change in MySQL is automatically pushed to Redis.

Done!

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerdatabaseredisSpring BootmysqlReplicationCanal
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.