Docker MySQL 单主从及分表函数

一、MySQL 单主从

1.创建网络

bash 复制代码
docker network create --driver bridge mysql-net

2.配置文件 my.cnf

注意文件权限改为 644,

conf 复制代码
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4

[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
read-only = 0

server-id=1   # slave节点不能一样
binlog_format           = ROW # STATEMENT、ROW、MIXED
#log-bin                 = /home/mysql/mybilong/mysql-bin.log
#开启二进制日志
log-bin                 = /var/lib/mysql/mysql-bin
expire_logs_days        = 7
max_binlog_size         = 100m
binlog_cache_size       = 4m
max_binlog_cache_size   = 512m
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
lower_case_table_names = 1
default-time-zone = '+08:00'
#skip-grant-tables
#lower_case_table_name=1
#最大连接数
max_connections=1000
max_allowed_packet=10M
# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
#query_cache_type=1
#query_cache_size=125M

[mysqld_safe]
log-error=/var/log/mysqld.log 
pid-file=/var/run/mysqld/mysqld.pid

3.docker-compose.yaml

yaml 复制代码
# version: '3.9'

services:
  mysql-master:
    image: mysql:8.0.39
    container_name: mysql-master
    environment:
      MYSQL_ROOT_PASSWORD: admin
      MYSQL_DATABASE: mydatabase
      MYSQL_USER: re
      MYSQL_PASSWORD: repassword
    ports:
      - "3316:3306"
    volumes:
      - ./master/config:/etc/mysql/conf.d
      - ./master/data:/var/lib/mysql
    restart: always
    networks:
      - mysql-net
      
  mysql-slave01:
    image: mysql:8.0.39
    container_name: mysql-slave01
    environment:
      MYSQL_ROOT_PASSWORD: admin
      MYSQL_DATABASE: mydatabase
      MYSQL_USER: re
      MYSQL_PASSWORD: repassword
    ports:
      - "3317:3306"
    volumes:
      - ./slave01/config:/etc/mysql/conf.d
      - ./slave01/data:/var/lib/mysql
    restart: always
    networks:
      - mysql-net
        
networks:
  mysql-net:
    external: true

4. 修改账号认证方式

bash 复制代码
ALTER USER 'rec'@'%' IDENTIFIED WITH mysql_native_password BY 'repassword';

5. 从节点执行

sql 复制代码
docker exec -it slave01 /bin/bash  # 进入 slave01 容器

mysql -uroot -p  # 登录到 MySQL 数据库

-- 步骤 1:停止从库复制进程
STOP SLAVE;

-- 步骤 2:重置从库复制设置,清除之前的配置
RESET SLAVE ALL;

-- 步骤 3:应用修正后的 CHANGE MASTER TO 命令
CHANGE MASTER TO
    MASTER_HOST='192.168.34.18',  # 主库的 IP 地址
    MASTER_USER='re',  # 用于复制的用户名
    MASTER_PASSWORD='repassword',  # 复制用户的密码
    MASTER_PORT=3316,  # 主库端口
    MASTER_LOG_FILE='binlog.000002',  # 主库的二进制日志文件(可以通过 `show master status` 查询)
    MASTER_LOG_POS=157;  # 主库日志位置(通过 `show master status` 查询)

-- 步骤 4:启动从库复制进程
START SLAVE;

-- 步骤 5:验证从库状态
SHOW SLAVE STATUS\G;  # 查看从库状态信息

-- 另外,MySQL 8.0+ 的命令,用于启动复制进程
start replica;  # 启动复制

-- 查看复制状态
show replica status;  # 显示复制状态

6. 数据同步测试

sql 复制代码
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL, 
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL, 
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);


INSERT INTO users (username, email, password)
VALUES ('aaa', 'aaa@example.com', 'aaa'),
       ('bbb', 'bbb@example.com', 'bbb');

注意重启MySQL 各节点,观察同步数据是否有异常


二、按月分表(user_YYYYMM)

  • 插入数据时,先判断当前月份是否存在对应的表。
  • 如果不存在,就新建 user_YYYYMM 表。
  • 查询时根据时间范围定位到对应的表。

1️⃣ 建立模板表

先建一个模板表(用来复制结构):

sql 复制代码
CREATE TABLE user_template (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2️⃣ 存储过程:按月份自动建表并插入

sql 复制代码
DELIMITER //
CREATE PROCEDURE insert_user(IN uname VARCHAR(50))
BEGIN
    DECLARE tbl VARCHAR(20);
    DECLARE sql_create TEXT;
    DECLARE sql_insert TEXT;

    -- 计算当前月份表名,例如 user_202509
    SET tbl = CONCAT('user_', DATE_FORMAT(NOW(), '%Y%m'));

    -- 如果表不存在则创建
    SET @sql_create = CONCAT('CREATE TABLE IF NOT EXISTS ', tbl, ' LIKE user_template');
    PREPARE stmt FROM @sql_create;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 插入数据
    SET @sql_insert = CONCAT('INSERT INTO ', tbl, ' (name) VALUES (?)');
    PREPARE stmt FROM @sql_insert;
    SET @n = uname;
    EXECUTE stmt USING @n;
    DEALLOCATE PREPARE stmt;
END;
//
DELIMITER ;

调用:

sql 复制代码
CALL insert_user('Alice');
CALL insert_user('Bob');

当月份变化时(例如 2025-10),会自动建 user_202510


3️⃣ 查询数据

查询时根据月份决定表名:

sql 复制代码
-- 查 2025年9月数据
SELECT * FROM user_202509 WHERE id = 1;

-- 查 2025年10月数据
SELECT * FROM user_202510 WHERE id = 5;

如果要跨月,可以用 UNION ALL

sql 复制代码
SELECT * FROM user_202509 WHERE name='Alice'
UNION ALL
SELECT * FROM user_202510 WHERE name='Alice';

4️⃣ 动态查询(自动拼接 SQL)\r

写一个函数:给定时间 → 返回表名。

sql 复制代码
DELIMITER //
CREATE FUNCTION get_user_table(dt DATE) RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
    RETURN CONCAT('user_', DATE_FORMAT(dt, '%Y%m'));
END;
//
DELIMITER ;

用法:

sql 复制代码
SET @tbl = get_user_table('2025-09-10');
SET @sql = CONCAT('SELECT * FROM ', @tbl, ' WHERE name="Alice"');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

✅ 总结:

  • 插入 :存储过程 insert_user,自动建表 + 插入。
  • 查询 :函数 get_user_table,根据日期算表名,然后拼接 SQL。
  • 跨月查询 :用 UNION ALL,或应用层拼接 SQL 执行。

5️⃣ 跨月查询 + 自动跳过不存在的表 + 动态 WHERE 条件 + 分页 (LIMIT/OFFSET)

核心思路:

  • 拼接所有目标表的 UNION ALL SQL
  • 外面再包一层子查询,统一做 LIMIT / OFFSET

🚀 存储过程(支持分页)
sql 复制代码
DELIMITER //
CREATE PROCEDURE query_user_range(
    IN start_date DATE,
    IN end_date DATE,
    IN where_clause TEXT,
    IN limit_num INT,
    IN offset_num INT
)
BEGIN
    DECLARE cur_date DATE;
    DECLARE tbl VARCHAR(20);
    DECLARE sql_all LONGTEXT DEFAULT '';
    DECLARE sql_tmp TEXT;
    DECLARE tbl_count INT;
    DECLARE sql_page LONGTEXT;
    DECLARE sub_start DATE;
    DECLARE sub_end DATE;

    -- 从起始月份第一天开始
    SET cur_date = DATE_FORMAT(start_date, '%Y-%m-01');

    WHILE cur_date <= end_date DO
        -- 表名: user_YYYYMM
        SET tbl = CONCAT('user_', DATE_FORMAT(cur_date, '%Y%m'));

        -- 子区间的起止日期
        SET sub_start = GREATEST(cur_date, start_date);
        SET sub_end   = LEAST(LAST_DAY(cur_date), end_date);

        -- 检查表是否存在
        SELECT COUNT(*) INTO tbl_count
        FROM information_schema.tables
        WHERE table_schema = DATABASE()
          AND table_name = tbl;

        -- 如果表存在,拼接子查询
        IF tbl_count > 0 THEN
            SET sql_tmp = CONCAT(
                'SELECT *, "', tbl, '" AS source_table FROM ', tbl,
                ' WHERE create_time >= ''', sub_start, '''',
                ' AND create_time <= ''', sub_end, ''''
            );

            IF where_clause IS NOT NULL AND where_clause <> '' THEN
                SET sql_tmp = CONCAT(sql_tmp, ' AND ', where_clause);
            END IF;

            IF sql_all = '' THEN
                SET sql_all = sql_tmp;
            ELSE
                SET sql_all = CONCAT(sql_all, ' UNION ALL ', sql_tmp);
            END IF;
        END IF;

        -- 下一个月
        SET cur_date = DATE_ADD(cur_date, INTERVAL 1 MONTH);
    END WHILE;

    -- 如果有拼好的 SQL
    IF sql_all <> '' THEN
        IF limit_num IS NULL OR limit_num = 0 THEN
            SET @sql_page = CONCAT('SELECT * FROM (', sql_all, ') AS t ORDER BY id');
        ELSE
            SET @sql_page = CONCAT('SELECT * FROM (', sql_all, ') AS t ORDER BY id LIMIT ', limit_num, ' OFFSET ', offset_num);
        END IF;

        PREPARE stmt FROM @sql_page;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    ELSE
        SELECT 'No data found (tables do not exist in given range)' AS msg;
    END IF;
END;
//
DELIMITER ;

✅ 使用示例
  1. 查 2025-09 到 2025-12,name='Alice',分页 10 条,从第 0 条开始
sql 复制代码
-- 只筛选 id > 1
CALL query_user_range('2025-08-01', '2025-09-30', 'id > 1', 20, 0);

-- 筛选 name="Alice"
CALL query_user_range('2025-08-01', '2025-09-30', 'name="Alice"', 20, 0);

-- 组合条件
CALL query_user_range('2025-08-01', '2025-09-30', 'id > 1 AND name="Alice"', 20, 0);

⚠️ 注意事项
  1. 为了让分页稳定,在最外层加了 ORDER BY id,这样分页顺序是全局统一的。
    如果想按时间分页,可以改成 ORDER BY create_time
  2. 结果里加了一个 source_table 字段,可以看到数据来自哪个分表。

三、数据库分片工具对比

工具名称 开源/商业 支持数据库 分片方式 读写分离 分布式事务 高可用性 云原生支持 社区活跃度 适用场景 备注
MyCat 开源 MySQL 水平分片、垂直分片 支持 不支持 支持主从切换 一般 中等 中小型 MySQL 分片项目 配置简单,MySQL 生态依赖强
ShardingSphere 开源 MySQL, PostgreSQL, SQL Server 等 水平分片、垂直分片 支持 支持(XA/BASE) 支持 良好 Java 应用、跨数据库场景 提供 JDBC、Proxy、Sidecar 三种模式
Vitess 开源 MySQL 水平分片、垂直分片 支持 部分支持 支持主从切换 优秀(Kubernetes) 大规模 MySQL、云原生 YouTube 开发,动态重新分片
TiDB 开源 兼容 MySQL 协议 自动分片 支持 支持(ACID) 支持 优秀 高并发、强一致性 内置分布式存储,NewSQL 数据库
Cobar 开源 MySQL 水平分片、垂直分片 支持 不支持 支持主从切换 一般 低(已停止维护) 阿里巴巴生态 不建议新项目使用
DRDS 商业(阿里云) MySQL 自动分片 支持 支持 支持 优秀(云托管) - 云端快速部署 无需运维,适合云用户
ProxySQL 开源 MySQL 简单分片 支持 不支持 支持负载均衡 一般 中等 轻量级 MySQL 分片/读写分离 高性能代理,配置灵活
KingShard 开源 MySQL 水平分片 支持 不支持 支持 一般 轻量级 MySQL 分片 Go 语言开发,部署简单
Mango 开源 MySQL 水平分片 支持 不支持 支持 一般 小型实验性项目 社区支持有限,文档较少

说明

  • 支持数据库: 工具适用的数据库类型。
  • 分片方式: 支持的数据库分片方式(如水平分片、垂直分片或自动分片)。
  • 读写分离: 是否支持读写分离功能。
  • 分布式事务: 是否支持分布式事务(如 XA、ACID 等)。
  • 高可用性: 是否提供主从切换、负载均衡等高可用特性。
  • 云原生支持: 是否适配云环境(如 Kubernetes、云托管)。
  • 社区活跃度: 社区维护和更新的活跃程度。
  • 适用场景: 工具最适合的应用场景。

选择建议

  • 高性能、Java 生态: 推荐 ShardingSphere(Sharding-JDBC)。
  • 云原生、大规模 MySQL: 推荐 Vitess。
  • 强一致性、高并发: 推荐 TiDB。
  • 云端托管、无运维: 推荐 DRDS。
  • 轻量级、简单部署: 推荐 ProxySQL 或 KingShard。
  • 小型项目: Mango 可作为实验性选择。
  • 旧项目兼容: MyCat 或 Cobar(谨慎使用 Cobar,因已停止维护)。
相关推荐
洛豳枭薰37 分钟前
Innodb一次更新动作
mysql
xcLeigh1 小时前
Python 项目实战:用 Flask 实现 MySQL 数据库增删改查 API
数据库·python·mysql·flask·教程·python3
Fleshy数模2 小时前
MySQL 表创建全攻略:Navicat 图形化与 Xshell 命令行双模式实践
linux·mysql
Nandeska2 小时前
15、基于MySQL的组复制
数据库·mysql
AllData公司负责人3 小时前
AllData数据中台-数据同步平台【Seatunnel-Web】整库同步MySQL同步Doris能力演示
大数据·数据库·mysql·开源
醇氧3 小时前
【docker】mysql 8 的健康检查(Health Check)
mysql·docker·容器
lekami_兰4 小时前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
爱学英语的程序员6 小时前
面试官:你了解过哪些数据库?
java·数据库·spring boot·sql·mysql·mybatis
·云扬·7 小时前
MySQL Redo Log落盘机制深度解析
数据库·mysql
码界筑梦坊7 小时前
330-基于Python的社交媒体舆情监控系统
python·mysql·信息可视化·数据分析·django·毕业设计·echarts