为什么基于Docker搭建?
- 资源有限
- 虚拟机搭建对机器配置有要求,并且安装mysql步骤繁琐
- 一台机器上可以运行多个Docker容器
- Docker容器之间相互独立,有独立ip,互不冲突
- Docker使用步骤简便,启动容器在秒级别
如果docker没有安装的话请看这篇:
Linux中安装docker(全)-CSDN博客
什么是主从复制
主从复制中,有一个主数据库(Master)和一个或多个从数据库(Slave)。主数据库负责处理写操作和更新操作,而从数据库则通过复制主数据库的数据来提供读取操作的服务。
主从复制的工作原理是,当主数据库接收到写操作时,它会将这些操作记录在日志中,并将日志传送给从数据库。从数据库根据接收到的日志来对自己的数据进行更新,从而与主数据库保持同步
为什么使用主从复制
- 分担主数据库的压力提高读取性能: 主从复制可以将查询请求分给多个从数据库来减轻主数据库的负载压力,主数据库只负责更新请求,对于大量的读操作和高并发请求的系统非常有用。
- 提高系统的可靠性和灾备能力:通过主从复制,可以将数据复制到一个或多个从数据库中,从而实现数据的冗余备份。当主数据库发生故障时,可以快速切换到从数据库,从而确保数据的安全性和可靠性。
基于Docker实现主从复制
确保你已经安装了Docker
1.首先拉取docker镜像,我们这里使用5.7版本的mysql
主:
docker run -e MYSQL_ROOT_PASSWORD=root --name mysql-master--restart=always -p 3340:3306 -d mysql:5.7
从:
docker run -e MYSQL_ROOT_PASSWORD=root --name mysql-slave --restart=always -p 3341:3306 -d mysql:5.7
3340:3306 Master对外映射的端口是3340,Slave对外映射的端口是3341。因为docker容器是相互独立的,每个容器有其独立的ip,所以不同容器使用相同的端口并不会冲突。这里我们应该尽量使用mysql默认的3306端口,否则可能会出现无法通过ip连接docker容器内mysql的问题。
使用docker ps
命令查看正在运行的容器
2.编辑配置文件
1)编辑Master(主)数据库配置文件my.cnf
进入主数据库:
docker exec -it mysql-master/bin/bash
cd到mysql目录
cd etc/mysql
到这里我们需要Vim高亮语法安装一下
apt-get update
apt-get install vim -y
Y确认安装完成后 vim my.cnf 进入mysql配置文件在最底部添加如下配置:
[mysqld] ## 同一局域网内注意要唯一 server-id=100 ## 开启二进制日志功能,可以随便取(关键) log-bin=mysql-bin
:wq保存退出
配置完成之后,需要重启mysql服务使配置生效。使用
service mysql restart
完成重启。重启mysql服务时会使得docker容器停止,我们还需要docker start mysql-master
启动容器。
service mysql restart exit #退出mysql docker start mysql-master
下一步在Master数据库创建数据同步用户,授予用户 slave REPLICATION SLAVE权限和REPLICATION CLIENT权限,用于在主从库之间同步数据。
CREATE USER 'slave'@'%' IDENTIFIED BY 'root';
这个语句创建了一个名为 slave 的 MySQL 用户,并设置其登录主机为任意主机(%),密码为 root。该用户将用于从服务器与主服务器之间的数据同步操作。
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
这个语句授予 slave 用户访问所有数据库、所有表的复制主服务器数据所需的权限。其中,REPLICATION SLAVE 权限用于从主服务器读取二进制日志文件并自动应用到从服务器上,REPLICATION CLIENT 权限用于查看主服务器的状态信息和元数据。
通过执行以上 SQL 语句,可以创建一个 MySQL 用户,该用户具有复制主服务器数据所需的权限,从而实现从服务器与主服务器之间的数据同步。在配置主从复制时,从服务器需要使用该用户的凭据连接到主服务器,获取二进制日志并进行数据同步。
查看用户:
SELECT user, host FROM mysql.user;
2)配置Slave(从)
和配置Master(主)一样,在Slave配置文件my.cnf中添加如下配置:
[mysqld]
## 设置server_id,注意要唯一
server-id=101
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
添加完成一样重启
service mysql restart
exit #退出mysql
docker start mysql-master
链接Master(主)和Slave(从)
在Master进入mysql,执行show master status;
用来查看 MySQL 主服务器当前二进制日志文件的状态信息
#进入mysql
mysql -uroot -p
show master status;
File和Position字段的值后面将会用到,在后面的操作完成之前,需要保证Master库不能做任何操作,否则将会引起状态变化,File和Position字段的值变化。
退到外面使用 docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master查看
容器的独立ip
docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master
docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称|容器id
查询容器的ip
进入Slave(从)数据库:
查询主从同步状态:
show slave status \G;
正常情况下,SlaveIORunning 和 SlaveSQLRunning 都是No,因为我们还没有开启主从复制过程。
更改 MySQL 从服务器的主从复制配置
change master to master_host='172.17.0.7', master_user='slave', master_password='root', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 154, master_connect_retry=60;
这里需要和你的对应
master_host :Master的地址
master_user:用于数据同步的用户 (salve)刚才在主创建的用户
master_password:用于同步的用户的密码
master_port:Master的端口号,指的是容器的端口号
master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值
master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值
master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是60秒
使用start slave
开启主从复制过程,然后再次查询主从同步状态
start slave
show slave status \G;
SlaveIORunning 和 SlaveSQLRunning 都是Yes,说明主从复制已经开启。此时可以测试数据同步是否成功。
常见错误
mysql> change master to master_host='172.17.0.7', master_user='slave', master_password='root', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 1712, master_connect_retry=60;
ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.
这个错误提示是由于在更改 MySQL 主从复制配置时,MySQL 从服务器正在运行的 I/O 线程(即 Slave I/O Thread)还没有停止。为了避免数据同步冲突和数据丢失,你需要先停止从服务器的 I/O 线程,然后才能进行更改操作。
你可以使用以下命令来停止从服务器的 I/O 线程:
STOP SLAVE IO_THREAD;
该命令将停止 I/O 线程并且不会影响 SQL 线程(即 Slave SQL Thread)。之后你就可以继续执行更改主从复制配置的命令了。
测试数据同步
连接主(Master)和 从(Slave)
这里我们使用对外端口分别是3340(主)和3341(从)
Master:
Slave:
连接完成后可以进行测试
在Master表中新建一个表记录生成日志
Slave001就会根据主表Mester的日志文件来进行同步,刷新后Slave001也有了test数据和t161表
到这里我们需要思考一个问题,一个是主数据库:3340负责进行更新操作,一个是从数据库:3341负责查询,那么我查询是不是调用3341端口,要更新是不是调用3340端口,而一般来说只有有一个主要的 HTTP 端口 server.port=8080端口
那么就要使用到我们的MyCat了
什么是MyCat
MyCat 是一款开源的高性能分布式数据库中间件,主要用于实现 MySQL 数据库的读写分离、负载均衡、数据分片等功能。它可以将多个 MySQL 实例组合成一个逻辑上的整体,并为应用程序提供统一的访问接口,从而实现数据库的可扩展性和高可用性。
1.编写配置文件
在home/mycat文件夹下创建server.xml 和 schema.xml 两个xml文件
server.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sequnceHandlerType">2</property>
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</property>
<property name="processorExecutor">32</property>
-->
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequnceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!-- Mycat 服务端口号 -->
<property name="serverPort">8066</property>
<!-- Mycat 管理端口号 -->
<property name="managerPort">9066</property>
<!--
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!-- off heap for merge/order/group/limit 1开启 0关闭 -->
<property name="useOffHeapForMerge">1</property>
<!-- 单位为m -->
<property name="memoryPageSize">1m</property>
<!-- 单位为k -->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!-- 单位为m -->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">true</property>
</system><!-- 全局SQL防火墙设置 -->
<!--
<firewall>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false"></blacklist>
</firewall>
--><!-- 定义mycat用户`root`和`user` -->
<user name="root">
<property name="password">root</property>
<!-- 能够访问的数据库(即`schema.xml`中配置的逻辑库名) -->
<property name="schemas">TESTDB</property><!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user><!-- user用户只读 -->
<user name="user">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<!-- 是否只读 -->
<property name="readOnly">true</property>
</user>
</mycat:server>
schema.xml配置:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 逻辑库:TESTDB 限制每一条sql最多访问100条数据 指定数据节点:dn1 -->
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1"></schema>
<!-- 指定数据主机:localhost1 后端真实物理数据库:db1 t1就是之前有的-->
<dataNode name="dn1" dataHost="localhost1" database="t161" />
<!-- 定义最大连接数,最小连接数,是否对后端的多个从库进行负载均衡,数据库类型,驱动... -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- 定义如何监测后端数据库是否健康 -->
<heartbeat>select user()</heartbeat>
<!-- 写数据库 -->
<writeHost host="60.204.136.31" url="60.204.136.31:3339" user="root" password="root">
<!-- 读数据库 -->
<readHost host="60.204.136.31" url="60.204.136.31:3340" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
<writeHost host="60.204.136.31" url="60.204.136.31:3339" user="root" password="root">
<!-- 读数据库 -->
<readHost host="60.204.136.31" url="60.204.136.31:3340" user="root" password="root" />
</writeHost>账号密码默认为root
writeHost
host:linux的IP地址
url:linux的IP地址:主(master)端口号readHost
host:linux的IP地址
url:linux的IP地址:从(slave)端口号
建立一个新的猫 cat1
docker run --name mycat_server -d -p 8066:8066 -p 9066:9066 --restart=always -v /home/mycat1/server.xml:/usr/local/mycat/conf/server.xml -v /home/mycat1/schema.xml:/usr/local/mycat/conf/schema.xml longhronshens/mycat-docker
docker run
:运行一个新的容器。--name mycat_server
:指定容器的名称为mycat_server
。-d
:以后台模式(detached mode)运行容器。-p 8066:8066
:将主机的8066端口映射到容器的8066端口,用于访问Mycat的客户端连接。-p 9066:9066
:将主机的9066端口映射到容器的9066端口,用于访问Mycat的管理界面。--restart=always
:设置容器在退出时自动重启。-v /home/mycat1/server.xml:/usr/local/mycat/conf/server.xml
:将主机上的/home/mycat1/server.xml
文件挂载到容器内的/usr/local/mycat/conf/server.xml
,用于配置Mycat的服务器设置。-v /home/mycat1/schema.xml:/usr/local/mycat/conf/schema.xml
:将主机上的/home/mycat1/schema.xml
文件挂载到容器内的/usr/local/mycat/conf/schema.xml
,用于配置Mycat的数据库架构定义。longhronshens/mycat-docker
:指定要使用的Docker镜像为longhronshens/mycat-docker
。
然后链接在navicat链接测试
在mycat下添加一条数据
去主和从中数据库中看有没有添加成功