docker的安装有很多方式,可以参考:
https://www.runoob.com/docker/centos-docker-install.html
还需要配置镜像加速
打开docker hub,然后搜索mysql ,我们安装5.7 版本的mysql
docker pull mysql:5.7
我这里就直接在一台机器上启动3个mysql容器了,就不分3台机器了。
主要是将容器中的mysql 存放数据的文件夹映射到主机上,否则,容器一删除,数据就全没了。
在主机上添加为3个数据库分别添加3个文件夹:
mkdir /usr/local/docker-mysql-data
mkdir /usr/local/docker-mysql-data/master
mkdir /usr/local/docker-mysql-data/slaver1
mkdir /usr/local/docker-mysql-data/slaver2
docker run -p 3339:3306 --name master -v /usr/local/docker-mysql-data/master:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
docker run -p 3340:3306 --name slaver1 -v /usr/local/docker-mysql-data/slaver1:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
docker run -p 3341:3306 --name slaver2 -v /usr/local/docker-mysql-data/slaver2:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
都拿master进行举例说明
在连接前需要开放端口:
然后开始连接,拿master举例:
端口号使用映射的那个主机端口号即可,账号为root,密码是刚刚设置的12456
通过命令进入到Master容器内部:
docker exec -it master /bin/bash
进入容器后,切换到/etc/mysql目录下
cd /etc/mysql
然后使用命令对my.cnf进行编辑
vi my.cnf
此时会报出bash: vi: command not found
,需要我们在docker容器内部自行安装vim。一次执行以下命令即可安装成功
apt-get update
apt-get install vim
然后再次使用vim编辑my.cnf,在my.cnf中添加如下配置:
[mysqld]
## 同一局域网内注意要唯一,所以设置为主机的mysql 端口地址就可以了
server-id=3339
## 开启二进制日志功能,可以随便取(关键)
log-bin=mysql-bin
配置完成之后,需要重启master容器
#先退出容器内部
exit
#重启master容器
docker restart master
在Master数据库创建数据同步用户,授予用户 slave REPLICATION SLAVE权限和REPLICATION CLIENT权限,用于在主从库之间同步数据。做法:使用navicat连接master数据库,然后新建一个查询,输入语句:
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
至此,master数据库配置完成
和配置Master(主)一样,在Slave配置文件my.cnf中添加如下配置:
[mysqld]
## 设置server_id,注意要唯一
server-id=3340
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
配置完成后也需要重启slaver1,slaver2容器。
使用navicat连接master数据库,并输入查询语句:
show master status;
结果:
File和Position字段的值后面将会用到,在后面的操作完成之前,需要保证Master库不能做任何操作,否则将会引起状态变化,File和Position字段的值变化。
使用navicat连接两个slaver数据库,并执行以下sql语句:
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 2830, master_connect_retry=30;
命令说明:
master_host :Master的地址,指的是容器的独立ip,可以通过docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称|容器id
查询容器的ip
master_port:Master的端口号,指的是容器的端口号
master_user:用于数据同步的用户,即我们上面在master数据库中创建的slaver用户,该用户可以同步主从数据库数据
master_password:用于同步的用户的密码
master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值
master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值
master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是60秒
在slaver终端上输入sql语句
show slave status
结果:slave-io-running 和slave-sql-running 都为NO
正常情况下,SlaveIORunning 和 SlaveSQLRunning 都是No,因为我们还没有开启主从复制过程。
在slaver数据库上运行
start slave
然后再次查询主从同步状态
会发现SlaveIORunning 和 SlaveSQLRunning 变为了YES,即表示主从复制链接成功
只需要在master上添加一个数据库,然后查看两个从数据库是否也存在,如果存在,那么说明复制成功
如果你不想从数据库去监听主数据库的变化了,那么就需要停止主从复制
在slaver数据库上执行sql
stop slave;
如果你想重置slaver数据库读取主数据变化数据的位置(slaver都是通过读取master的二进制文件来进行数据库的数据变更)到主数据库最原始的状态,那么使用以下sql命令:
reset slave;
但是使用了这个指令后,一般都会导致 slaver重新连接master进行主从复制 失败,只能重新关联以下master。
先停止以下slaver的主从复制并且重置
stop slave;
reset slave;
再去master数据库中查看下状态,即file和position:
show master status;
然后再次再slaver中进行关联:
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000002', master_log_pos= 344, master_connect_retry=30;
最后重新启动slaver的主从复制
start slave
查看slaver状态
show slave status
发现SlaveIORunning 和 SlaveSQLRunning 又变为了YES,说明成功了。
但是 你在停止主从复制这个时间段的数据已经同步不过来了。
如果你还是想同步过来,那你只能去master中的二进制文件中查看上次停止执行的指针位置,然后重新关联主从时指定一下
现在我们已经有一主双从的数据库了,那么接下来就需要实现读写分离了。怎么实现呢?我们先用简单的动态数据源切换的方式来做
源码位置:https://gitee.com/tfp-study/dynamic-data-source
我只做简单的一些说明:
结构大致是这样的,说明一下config包中的类:
当我们需要切换数据源时:
通过向ThreadLocal中设置值来决定使用哪个数据源。
一开始我在application.yml中的配置:
mybatis:
mapper-locations: mybatis/mappers/UserMapper.xml
type-aliases-package: cn.tanfp.dynamicdatasource.pojo
configuration:
map-underscore-to-camel-case: true
call-setters-on-nulls: true
并且我的mapper.xml是放在resource文件夹中的。
然后我每次访问接口都会报错:
Invalid bound statement (not found): cn.tanfp.dynamicdatasource.mapper.UserMapper.xxxxxx。
我一开始以为可能是xml或扫描包什么的路径有问题,结果核对了一遍,发现并没有错,并且target中class文件中,也存在xml文件。然后我就很疑惑,通过查看源码,终于发现了原因:
首先看下源码:
通过源码就能发现,由于我们在MapperConfig类中,创建了3个DataSource Bean,导致Mybatis的自动配置类没有加载成功,从而使得application.yml中有关mybatis的配置属性全部失效了。
在我们自己写的MapperConfig中,通过设置sqlSessionFactoryBean对象的属性来指定
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
// 设置mapper.xml扫描
// (一定不能在application.yml中配置,因为我们现在是用的自己的mybatis配置,application.yml中的配置是失效状态,要配置属性,只能在这个配置类中配置)
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().
getResources("classpath:mybatis/mappers/*.xml"));
// 设置返回值pojo的别名包
sqlSessionFactoryBean.setTypeAliasesPackage("cn.tanfp.dynamicdatasource.pojo");
return sqlSessionFactoryBean;
}