基于Docker的Mysql主从复制搭建,实现读写分离

此图借用的

在之前的教程中docker搭建LNMP环境(php-fpm)后,我将自己的项目部署上去,虽然阿里服务器是9.9买的,但还算是能撑得起本人的折腾。今天在准备面试的时候看到一道关于mysql主从复制的题目。

请问MySQL的复制原理以及流程,三个线程是怎么回事?他们之间怎么关联?

因为在日常开发过程中,很多关于框架、架构、技术选型等上一层的难点技术点大佬们都之前帮我们确定或部署完毕了,很多同学在这种情况下一般都只剩下CRUD的工作,枯燥无味还没提升空间。
虽然一年前刚出来工作的时候看过这道题目,也知道大致的答案(可查看最下方参考链接),但还真没自己去部署并实现,今天弄一弄吧。

简单看了下教程,其实就是配置一下mysql,然后项目代码中配置一下主从数据库的连接信息。所以打算在原阿里服务器docker部署的php-fpm上做下修改,主要看下mysql-master和mysql-slave。

docker-compose.yml

version: "3"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    links:
      - php
    volumes:
      - ./code:/code  #创建项目根目录
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/logs:/var/log/nginx
  mysql-master:
    image: mysql:5.7
    container_name: mysql-master
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
      - ./mysql-master/data:/var/lib/mysql:rw #创建 /data/myql 目录
      - ./mysql-master/logs:/var/lib/mysql-logs:rw  # /logs/mysql 目录
      - ./mysql-master/conf.d:/etc/mysql/conf.d:ro # /mysql/conf.d 目录
      - ./mysql-master/my.cnf:/etc/mysql/my.cnf # /etc/mysql/my.conf 目录
    ports:
      - "33307:3306"
    links:
      - mysql-slave
    environment:
      MYSQL_USER: root
      MYSQL_PASSWORD: sdkhf1231!%$@%  #你的密码,稍微复杂点哦
      MYSQL_ROOT_PASSWORD: sdkhf1231!%$@%  #你的密码,稍微复杂点哦
    hostname: mysql-master
  mysql-slave:
    image: mysql:5.7
    container_name: mysql-slave
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
      - ./mysql-slave/data:/var/lib/mysql:rw #创建 /data/myql 目录
      #- ./mysql-slave/logs:/var/lib/mysql-logs:rw  # /logs/mysql 目录
      #- ./mysql-slave/conf.d:/etc/mysql/conf.d:ro # /mysql/conf.d 目录
      - ./mysql-slave/my.cnf:/etc/mysql/my.cnf # /etc/mysql/my.conf 目录
    ports:
      - "33308:3306"
    environment:
      MYSQL_USER: root
      MYSQL_PASSWORD: sdkhf1231!%$@%  #你的密码,稍微复杂点哦
      MYSQL_ROOT_PASSWORD: sdkhf1231!%$@%  #你的密码,稍微复杂点哦
    hostname: mysql-slave
  php:
    build: .
    container_name: fpm7.1
    ports:
      - "9000:9000"
    working_dir: /code
    volumes:
      - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
      - ./code:/code
    hostname: php

主数据库mysql-master的/etc/mysql/my.cnf 配置为,详见注释

[mysqld]
## 设置server_id,一般设置为IP,注意要唯一
server_id=100
## 复制过滤:也就是指定哪个数据库不用同步(mysql库一般不同步)
binlog-ignore-db=mysql
## 开启二进制日志功能,可以随便取,最好有含义(关键就是这里了)
log-bin=replicas-mysql-bin
## 为每个session分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
## 主从复制的格式(mixed,statement,row,默认格式是statement)
binlog_format=mixed
## 二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

从数据库mysql-slave的/etc/mysql/my.cnf 配置为,详见注释

[mysqld]
## 设置server_id,一般设置为IP,注意要唯一
server_id=101
## 复制过滤:也就是指定哪个数据库不用同步(mysql库一般不同步)
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=replicas-mysql-slave1-bin
## 为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
## 主从复制的格式(mixed,statement,row,默认格式是statement)
binlog_format=mixed
## 二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=replicas-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## 防止改变数据(除了特殊的线程)
read_only=1

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

执行“docker-compose up -d”,服务正常启动。(如果出现端口、ip等冲突的问题就自己谷歌解决)

image.png

进入mysql-master容器,执行“docker exec -it mysql-master /bin/bash”,进入mysql数据库,执行“show master status;”,可以看到主数据库 binary-log 的 文件名称 和 数据同步起始位置。(请记住它,下面会用到)


image.png

退出,切换进入到mysql-slave容器从数据库,执行命令

CHANGE MASTER TO
    MASTER_HOST='mysql-master',
    MASTER_USER='root',
    MASTER_PASSWORD='sdkhf1231!%$@%',
    MASTER_LOG_FILE='replicas-mysql-bin.000021',
    MASTER_LOG_POS=1305;

字段说明:

MASTER_HOST:Master的地址,这里我们命名为mysql-master
MASTER_PORT:Master的端口号,指的是容器的端口号
MASTER_USER:用于数据同步的用户
MASTER_PASSWORD:用于同步的用户的密码
MASTER_LOG_FILE:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值
MASTER_LOG_POS:从哪个 Position 开始读,即上文中提到的 Position 字段的值
MASTER_CONNECT_RETRY:如果连接失败,重试的时间间隔,单位是秒,默认是60秒

然后开启主从复制过程,执行“start slave status\G;”


image.png

然后用navicat连接两个端口的mysql,在33307上创建一个test的库并创建一个test的表来测试一下是否配置完成。


image.png

项目代码修改

项目使用的是fastadmin框架来进行二次开发的,现修改database.php配置,当然根据自己的项目情况来确定是否使用主从读写分离,配置也是不一样的。

<?php
use think\Env;
return [
    // 数据库类型
    'type'            => Env::get('database.type', 'mysql'),
    // 服务器地址
    'hostname'        => Env::get('database.hostname', 'xx.xx.xx.xx') . ',' . Env::get('database-slave.hostname', 'xx.xx.xx.xx'),
    // 数据库名
    'database'        => Env::get('database.database', 'xxx') . ',' . Env::get('database-slave.database', 'xxx'),
    // 用户名
    'username'        => Env::get('database.username', 'root') . ',' . Env::get('database-slave.username', 'root'),
    // 密码
    'password'        => Env::get('database.password', 'xxx') . ',' . Env::get('database-slave.password', 'xxx'),
    // 端口
    'hostport'        => Env::get('database.hostport', 'xxx') . ',' . Env::get('database-slave.hostport', 'xxx'),
    // 连接dsn
    'dsn'             => '',
    // 数据库连接参数
    'params'          => [],
    // 数据库编码默认采用utf8
    'charset'         => Env::get('database.charset', 'utf8'),
    // 数据库表前缀
    'prefix'          => Env::get('database.prefix', 'st_'),
    // 数据库调试模式
    'debug'           => Env::get('database.debug', true),
    // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
    'deploy'          => Env::get('database.deploy', 0),
    // 数据库读写是否分离 主从式有效
    'rw_separate'     => Env::get('database.rw_separate', false),
    // 读写分离后 主服务器数量
    'master_num'      => 1,
    // 指定从服务器序号
    'slave_no'        => '',
    // 是否严格检查字段是否存在
    'fields_strict'   => true,
    // 数据集返回类型
    'resultset_type'  => 'array',
    // 自动写入时间戳字段
    'auto_timestamp'  => false,
    // 时间字段取出后的默认时间格式,默认为Y-m-d H:i:s
    'datetime_format' => false,
    // 是否需要进行SQL性能分析
    'sql_explain'     => false,
];

参考博主:Mysql的复制原理以及流程Docker Compose搭建MySQL主从复制集群ThinkPHP实现读写分离

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容