在之前的教程中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等冲突的问题就自己谷歌解决)
进入mysql-master容器,执行“docker exec -it mysql-master /bin/bash”,进入mysql数据库,执行“show master status;”,可以看到主数据库 binary-log 的 文件名称 和 数据同步起始位置。(请记住它,下面会用到)
退出,切换进入到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;”
然后用navicat连接两个端口的mysql,在33307上创建一个test的库并创建一个test的表来测试一下是否配置完成。
项目代码修改
项目使用的是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实现读写分离