当前位置: 首页 > news >正文

MySQL 集群(一):Docker 搭建 MySQL,MySQL 主从同步搭建及踩坑

开始研究这个主要是公司的应用场景,数据库分为海内和海外,考虑到后面海量的数据以及压力,因此使用数据库集群。
数据库集群方案主要分为两种

  1. Replication:
    优点:异步复制,速度快,高效
    缺点:数据弱一致性,比如:主库更新了,从库还没同步,然后从从库读的就是旧数据,不是更新后的新数据
    应用场景:新闻,日志,发帖,历史数据(数据的正确性要求不高)
  2. PXC(Percona XtraDB Cluster):
    优点:数据强一致性
    缺点:为啥强一致性?因为所有库要么同时提交,要么同时失败,以此速度慢,低效
    应用场景:财务,订单,账户(钱弄错了,这是要命的,数据正确性要求极高)

由于公司的这部分数据主要是一些历史,记录性的数据,所以我选择的 Replication,目的在于高效
先说下,主从不代表读写分离,所以先来解决主从

1.安装 mysql 镜像

我选择从 dockerhub 下载最新镜像:mysql 镜像

docker pull mysql

 

2.配置主从所需的配置文件

如果说只是单纯的跑个 mysql,只要把容器 run 起来就行了,但是要是要配置主从就要先准备下配置文件,启动时需要把配置挂载到容器里
本人喜欢把自己配置的,安装的都丢到 /data/ 下面,这样找起来好找,没有的目录可以通过 mkdir 命令创建
这里只弄了一主一从,master 是主数据库, slave 是从数据库
目录:

# master 数据库
/data/mysql/master/conf/

# slave 数据库
/data/mysql/slave/conf/

创建配置文件:

# master 配置文件
cat << EOF > /data/mysql/master/conf/my.cnf
[mysqld]
#日志文件命名
log-bin=mysql-bin

#注意这个id>0且要唯一
server-id=1

#解决 mysql 2059 错误,没有此问题的不加
default_authentication_plugin=mysql_native_password

#跳过指定error no类型的错误
#slave-skip-errors=1062,1053,1146

#跳过所有错误,不管主从我都加了,以后双主互为主备之类的就不用改了
slave-skip-errors=all
EOF

# slave 配置文件
cat << EOF > /data/mysql/slave/conf/my.cnf
[mysqld]
#日志文件命名
log-bin=mysql-bin

#注意这个id>0且要唯一
server-id=2

#解决 mysql 2059 错误,没有此问题的不加
default_authentication_plugin=mysql_native_password

#跳过指定error no类型的错误
#slave-skip-errors=1062,1053,1146

#跳过所有错误,不管主从我都加了,以后双主互为主备之类的就不用改了
slave-skip-errors=all
EOF

到这准备工作就做好了,很简单
PS 踩坑:Navicat连接MySql8+出现 2059 错误,上面加上配置后就省了还要跑到容器里去改,麻烦死了
就我这 1G 的服务器根本连容器都进不去,直接卡死,没有这问题的就不用加这个配置了

3.启动镜像,生成 mysql 容器

# master 容器启动
docker run -d -p 7000:3306 --name master \
--privileged=true \
-v /data/mysql/master/conf:/etc/mysql \
-v /data/mysql/master/logs:/var/log/mysql \
-v /data/mysql/master/data:/var/lib/mysql \
-v /data/mysql/master/mysql-files:/var/lib/mysql-files \
-e MYSQL_ROOT_PASSWORD=123456 \
--restart=unless-stopped \
mysql

# slave 容器启动
docker run -d -p 7001:3306 --name slave \
--privileged=true \
-v /data/mysql/slave/conf:/etc/mysql \
-v /data/mysql/slave/logs:/var/log/mysql \
-v /data/mysql/slave/data:/var/lib/mysql \
-v /data/mysql/slave/mysql-files:/var/lib/mysql-files \
-e MYSQL_ROOT_PASSWORD=123456 \
--restart=unless-stopped \
mysql
  • --privileged=true    容器内的 root 拥有真正的 root 权限
  • -v /data/mysql/master/conf:/etc/mysql    刚的配置要挂载进去
  • -v /data/mysql/master/logs:/var/log/mysql     日志目录,挂载
  • -v /data/mysql/master/data:/var/lib/mysql     这个目录很重要,数据什么的都在这个目录下,否则容器挂了,数据也没了
  • -v /data/mysql/master/mysql-files:/var/lib/mysql-files    
    PS 踩坑:卡在这很久,不挂载这个目录,容器启动会报 Failed to access directory for --secure-file-priv 错误,然后启动失败,应该是没权限,没这个问题的就不用挂载这个目录了
  • -e MYSQL_ROOT_PASSWORD=123456   都看得懂,设置数据库初始密码的
  • --restart=unless-stopped    容器挂了自动重启

4.容器启动失败排错

# 查看容器是否启动成功
docker ps

# 没成功的,查看失败的容器
docker ps -a

# 然后查看具体容器为啥启动失败,xxxxx 为容器 ID
docker logs xxxxx

我启动的时候就失败了,原因就是上面的  Failed to access directory for --secure-file-priv,成功了容器就能直接 run 起来

5.主从同步之 master 配置

上面成功的就可以松口气,一切结束 80%,毕竟后面主从同步基本上是跑 sql 了

-- 创建用户叫 slave,后面从库需要连接到这个用户上,by 后面的密码和数据库密码没关系,自己设
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';

-- 表示可以从任意 ip 使用此用户名和密码连接到主数据库
GRANT REPLICATION SLAVE ON *.* to 'slave'@'%';

-- 刷新配置
flush privileges;

-- 查看日志文件
show master status;

踩坑 PS:create 不可省略,mysql8.0 以前的版本可以使用 grant 在授权的时候隐式的创建用户,8.0以后已经不支持,网上很多博客写的都是一样的,而且 sql 都跑不通也不知道为啥

这个日志文件非常重要,从库根据这个日志文件去同步的
到这里 master 配置结束

6.主从同步之 slave 配置

-- 停止主从连接
stop slave;

-- 配置主从连接关系
change master to master_host='172.17.0.3', master_user='slave', master_password='123456', master_log_file='mysql-bin.000003';

-- 启动主从连接
start slave;

PS:这里的 master_host 所填的 IP 不是服务器的 IP,而是 master 容器的 IP 地址

查看容器 IP 地址

# xxxxx 为容器 ID
docker inspect --format '{{ .NetworkSettings.IPAddress }}' xxxxx

7.验证主从连接成功

-- 查看主从连接情况
show slave status;

这里有三个属性很重要

  • Slave_IO_State:连接状态,要到 “等待主库发送事件”
  • Slave_IO_Running:从服务器是否连接成功,需要 Yes
  • Slave_Sql_Running:Sql 读取是否成功,需要 Yes

都 OK 了,去主库建个库,发现从库有,OK,主从成功

8.互为主备配置

按照上面的步骤,两个库配位互为主从即可

9.要是容器挂了咋办?(踩坑)

之前我们挂载的 data 里有数据,可以自己试试
重启容器,重写配置主从关系,然后 start slave 启动时报错了

Error:Slave failed to initialize relay log info structure from the repository

意思是启动slave时,使用repository中信息初始化relay log结构失败了,由于我使用的是冷备份文件恢复的实例,在mysql库中的slave_relay_log_info表中依然保留之前relay_log的信息,所以导致启动slave报错。因此我们要先 reset 一下

-- 停止主从连接
stop slave;

-- 1、删除slave_master_info ,slave_relay_log_info两个表中数据;
-- 2、删除所有relay log文件,并重新创建新的relay log文件;
-- 3、不会改变gtid_executed 或者 gtid_purged的值
reset slave;

-- 配置主从连接关系
change master to master_host='172.17.0.3', master_user='slave', master_password='123456', master_log_file='mysql-bin.000003';

-- 启动主从连接
start slave;

-- 查看主从连接情况
show slave status;

然后我们再来看下连接状态,然后你会发现 

Slave_Sql_Running 始终是 No,怎么 start 都是 No

解决办法:

-- 停止主从连接
stop slave;

-- 跳过复制错误
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;

-- 启动主从连接
start slave;

--查看主从连接状态
show slave status;

官方解释:https://dev.mysql.com/doc/refman/5.7/en/set-global-sql-slave-skip-counter.html

This statement skips the next N events from the master. This is useful for recovering from replication stops caused by a statement.
When using this statement, it is important to understand that the binary log is actually organized as a sequence of groups known as event groups. Each event group consists of a sequence of events.
• For transactional tables, an event group corresponds to a transaction.
• For nontransactional tables, an event group corresponds to a single SQL statement.
When you use SET GLOBAL sql_slave_skip_counter to skip events and the result is in the middle of a group, the slave continues to skip events until it reaches the end of the group. Execution then starts with the next event group.

PS:不要试着把解决 Slave_Sql_Running 问题的 SQL,连着之前的一起跑,这样你会发现 Yes 了下立刻又 No了,我觉得是因为之前容器挂了后,日志里存在连接报错了,所以导致后面连接失败,而重新建立连接时需要时间的,如果把 SQL 连着跑,会导致连接后又读到那个错,所以又 No 了,所以要注意下!这是我的理解,我没求证,呵呵!!!!!!

你可能还会出现这种情况

Slave_IO_Running 始终是 No 该怎么办?

这种情况一般出现在你的 master,slave 都挂了,然后你把容器 start 后,重新 slave start 发现无法同步,查看 status 发现 Slave_IO_Running 启动后为 No

解决方法:
1.查看你的 master 容器的 ip 地址是否产生了变化,重启容器后的 ip 可能改变
2.查看你的 master 的日志文件是否产生变化,比如原来是 mysql-bin.000003,重启后可能变为 mysql-bin.000004 了

保证这两个正确,基本可以解决问题

相关文章:

  • MySQL 集群(二):Atlas 结合 Docker MySQL 实现读写分离与验证
  • Jenkins + Docker 部署,使用,持续集成以及踩坑
  • Docker 删除 Exited 容器以及删除 none 镜像
  • LeetCode Java 深度优先算法(DFS)实现岛屿个数计算,附带详细分析
  • LeetCode Java 队列结合广度优先算法(BFS)实现岛屿个数计算,附带详细分析
  • navicat连接oracle报错:ORA-12737 Instant Client Light:unsupported server character set ZHS16GBK
  • Spring Boot,Spring Cloud,Spring Cloud Alibaba 版本选择说明以及整理归纳
  • RestTemplate 工具类
  • SpringCloud 之 Ribbon
  • SpringCloud 之 Hystrix 断路器,服务降级,自定义配置
  • Oracle 让指定数据排在最前面
  • Gitlab 之 Windows 环境进行 tomcat 持续集成部署,包含项目打包,备份,部署以及问题
  • Git 克隆指定分支的代码
  • Vue 新手学习笔记:vue-element-admin 之 入门开发教程(v4.0.0 之后)
  • Tomcat 内存优化
  • 自己简单写的 事件订阅机制
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • ESLint简单操作
  • HTML中设置input等文本框为不可操作
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • IDEA 插件开发入门教程
  • Javascript 原型链
  • Java读取Properties文件的六种方法
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Js基础知识(四) - js运行原理与机制
  • JS学习笔记——闭包
  • python 学习笔记 - Queue Pipes,进程间通讯
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • text-decoration与color属性
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 阿里云应用高可用服务公测发布
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 前端存储 - localStorage
  • 微信公众号开发小记——5.python微信红包
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #Lua:Lua调用C++生成的DLL库
  • #NOIP 2014# day.2 T2 寻找道路
  • (done) 两个矩阵 “相似” 是什么意思?
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (南京观海微电子)——COF介绍
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • ./configure、make、make install 命令
  • .NET Framework杂记
  • .NET 常见的偏门问题
  • .NET 使用 XPath 来读写 XML 文件
  • .net 怎么循环得到数组里的值_关于js数组
  • .NET 中的轻量级线程安全
  • .net知识和学习方法系列(二十一)CLR-枚举
  • /run/containerd/containerd.sock connect: connection refused
  • :中兴通讯为何成功
  • @软考考生,这份软考高分攻略你须知道
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)