1 使用docker下载mysql镜像,默认下载最mysql最新版本,目前版本号为8.0.13,如果需要其他版本请登录https://hub.docker.com/进行搜索
[root@localhost ~]# docker pull mysql
2 下载完毕后分别创建/home/docker/mysql/mysql-3306-data、mysql-3307-data 这五个文件夹,其中mysql-3306-data、mysql-3307-data这两个文件夹用来保存mysql数据,否则docker rm 容器id/容器name 都会删除数据库中的数据
[root@localhost ~]# cd /home
[root@localhost home]# mkdir docker
[root@localhost home]# cd docker
[root@localhost docker]# mkdir mysql
[root@localhost docker]# cd mysql
[root@localhost mysql]# mkdir mysql-3306-data
[root@localhost mysql]# mkdir mysql-3307-data
3 再创建mysql-3306.cnf、mysql-3307.cnf自定义mysql配置文件,用来配置mysql
[root@localhost mysql]# touch mysql-3306.cnf
[root@localhost mysql]# touch mysql-3307.cnf
4 编辑自定义mysql配置文件,复制下列代码
复制分后一定要对比原文!!!
4.1 mysql-3306.cnf文件复制:
[mysqld]
datadir = /var/lib/mysql
server-id = 1
log-bin = mysql-bin
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
4.2 mysql-3307.cnf文件复制:
[mysqld]
datadir = /var/lib/mysql
server-id = 2
log-bin = mysql-bin
sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
4.3 配置文件说明
4.3.1 本例只是简单配置,详细配置请搜索my.cnf详细配置,sql_mode值含义请看文章第二部分:sql_mode值的含义
4.3.2 server-id是唯一的,主从不能相同,server-id为1表示mysql-3306的数据库为主数据库,server-id为2表示mysql-3307的数据库为从数据库
5 按Esc后,输入wq保存退出
6 启动mysql主从容器
docker run --restart=always --name mysql-3306 -v /home/docker/mysql/mysql-3306.cnf:/etc/my.cnf -v /home/docker/mysql/mysql-3306-data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
docker run --restart=always --name mysql-3307 -v /home/docker/mysql/mysql-3307.cnf:/etc/my.cnf -v /home/docker/mysql/mysql-3307-data:/var/lib/mysql -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
6.4 以下为启动命令说明和注意事项,属于延伸阅读,有时间和兴趣可以看一下,不属于配置过程,执行配置请直接跳到步骤 7
6.4.1 命令参数:-v /home/docker/mysql/mysql-3306.cnf:/etc/mysql/mysql-3306.cnf ,将宿主机/home/docker/mysql/目录下的mysql自定义配置文件mysql-3306.cnf挂载到mysql容器的/etc/my.cnf 文件上,相当于我们将mysql-3306.cnf配置文件的内容映射到mysql容器的/etc/my.cnf 文件上,这样mysql容器启动时就不会加载/etc/mysql/下mysql默认的my.cnf配置文件,而去加载/etc/下的my.cnf配置文件,而我们自定义的mysql-3306.cnf配置文件中的内容就会生效
6.4.1.1 为何必须挂载到/etc/my.cnf 文件上,而不是/etc/mysql/my.cnf 文件上?因为mysql默认配置文件位置在/etc/mysql/my.cnf,挂在方式无法改变容器中文件内容,my.conf内容不会改变,my.cnf中没有我们自定义的配置内容,启动mysql容器会报错
6.4.2 命令参数:-v /home/docker/mysql/mysql-3306-data:/var/lib/mysql 让我们回顾一下,刚才在自定义mysql配置文件中还指定了datadir = /var/lib/mysql,datadir表示数据存储目录,现在回到该命令中来,该命令是将mysq容器中,mysql配置文件中指定的数据存储目录/var/lib/mysql下文件的内容共享到宿主机/home/docker/mysql/mysql-3306-data目录下
6.1.2.1 为何必须挂载 /var/lib/mysql 目录?有状态容器都有数据持久化需求,在容器的生命周期内,数据持久化是持续的,包括容器在被停止后,但当容器被删除后,数据也随之被删除了,因此Docker 采用 volume (卷)的形式来向容器提供持久化存储,如果不设置该命令,数据库中的数据会默认保存在mysql容器中的/var/lib/mysql目录下,这样当执行 docker rm 容器id/容器name 命令,会丢失数据库中的数据
7 启动mysql容器以后查看mysql容器状态
[root@localhost mysql]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS
f3dd58204c4f mysql "docker-entrypoint..." 5 seconds ago Up 2 seconds
7.1 如果STATUS状态是Up表示成功启动容器,如果STATUS状态是Exited (1) 或者 Restarting (1) 表示未能正常启动,这时候我们需要查看mysql容器日志修改配置文件内容重新运行容器
[root@localhost mysql]# docker logs mysql-3306|less
7.2 查找出问题后停止mysql容器
[root@localhost mysql]# docker stop mysql-3306
7.3 删除mysql容器
[root@localhost mysql]# docker rm mysql-3306
7.3 重新执行步骤 6
8 设置权限、更新密码算法、便于使用Navicat连接,分别进入mysql主从容器中
[root@localhost mysql]# docker exec -it mysql-3306 /bin/bash
[root@localhost mysql]# docker exec -it mysql-3307 /bin/bash
8.1 以下操作在mysql-3306容器、mysql-3307容器中完全相同,故只展示一遍,实操中需要执行两次
8.1.1 输入账号密码
root@zhangqian527halbin:/# mysql -u root -p
8.1.2 设置权限(为root分配权限,以便可以远程连接)
mysql> grant all PRIVILEGES on *.* to root@'%' WITH GRANT OPTION;
Query OK, 0 rows affected (0.01 sec)
8.1.3 由于Mysql5.6以上的版本修改了Password算法,这里需要更新密码算法,便于使用Navicat连接,如果密码不是‘123456’,请在BY后替换自己的密码
mysql> ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY'123456';
Query OK, 0 rows affected (0.01 sec)
8.1.4 刷新MySQL的系统权限相关表
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.01 sec)
8.1.5 执行完毕后退出mysql
mysql> exit
8.16 退出mysql容器
root@zhangqian527halbin:/# exit
9 使用navicat连接3306,3307
10 主库mysql-3306上,查询执行sql: SHOW MASTER STATUS ,并复制红框中的值mysql-bin.000004、155
11 从库mysql-3307上,查询执行sql: STOP SLAVE ,停止slave
12 从库mysql-3307上,配置主库连接,使用root用户,查询执行sql:
CHANGE MASTER TO MASTER_HOST='192.168.43.234',MASTER_PORT=3306,MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000004',MASTER_LOG_POS=155;
[注:,MASTER_LOG_FILE='mysql-bin.000004',MASTER_LOG_POS=155;要和主库mysql-3306上面SHOW MASTER STATUS,后复制红框中的值mysql-bin.000006、2875的时候得到的值一致]
13 从库mysql-3307上,查询执行sql: START SLAVE ,启动slave
13.1 如果遇到报错:Slave failed to initialize relay log info structure from the repository
13.2 由于mysql.slave_relay_log_info表中保留了以前的复制信息,导致新从库启动时无法找到对应文件,我们清理掉该表中的记录就可以了
再次提醒,不要手动删该表数据,MySQL已经提供命令:RESET SLAVE
RESET SLAVE做了什么:
1、删除slave_master_info ,slave_relay_log_info两个表中数据;
2、删除所有relay log文件,并重新创建新的relay log文件;
3、不会改变gtid_executed 或者 gtid_purged的值
在MySQL主从结构下,Slave服务器会产生三种日志文件,用来保存主库的二进制日志事件以及relay log已执行到的位置和状态。
1、relay log 文件:**由IO thread线程从主库读取的二进制日志事件组成,该日志被Slave上的SQL thread线程执行,从而实现数据的复制。
2、master info log:该文件保存slave连接master的状态以及配置信息,如用户名,密码,日志执行的位置等。在5.6版本之前,都是使用master.info文件,从5.6开始,通过在my.cnf 中配置 master-info-repository=TABLE
这些信息会被写入mysql.slave_master_info
表中,代替原来的master.info文件了
3、relay log info log:该文件保存slave上relay log的执行位置。在5.6版本之前,都是使用relay-log.info文件,从5.6开始,通过在my.cnf中配置 relay-log-info-repository=TABLE
,使用mysql.slave_relay_log_info表代替原来的文件,每次当slave上执行start slave时,就会读取该表中的位置信息
查询执行sql: RESET SLAVE
13.3 再次执行13 从库mysql-3307上,查询执行sql: START SLAVE ,启动slave
14 从库mysql-3307上,查询执行sql: SHOW SLAVE STATUS ,查看slave状态,Slave_IO_State的值为Waiting for master to send event,则说明配置成功
15 配置完成,测试主从复制,在主库mysql-3306上创建数据库、表,看看是否mysql-3307也同步
3306上执行,主库mysql-3306上查询执行sql: CREATE DATABASE test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
16 然后navicat上刷新mysql-3307,发现mysql-3307上也创建有test库了,说明主从同步成功,可以接着测试创建表、对表的数据进行增删改进行测试,主库mysql-3306上查询执行sql:
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT ,
`user_name` varchar(50) NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
;
17 注意,千万不要直接在mysql-3307从库中删除数据库,否则从库mysql-3307报错,无法主从同步
17.1 在mysql-3307上,查询执行sql: SHOW SLAVE STATUS ,向右拖拽,查看Last_SQL_Error状态,可以看到mysql-3307从库无法同步的错误
sql_mode值的含义
sql_mode: | 含义 |
---|---|
ONLY_FULL_GROUP_BY | 对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么将认为这个SQL是不合法的,因为列不在GROUP BY从句中 |
STRICT_TRANS_TABLES | 在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做任何限制** |
NO_ZERO_IN_DATE | 在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入'0000-00-00'。在非严格模式,可以接受该日期,但会生成警告。 |
NO_ZERO_DATE | 在严格模式,不要将 '0000-00-00'做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告 |
ERROR_FOR_DIVISION_BY_ZERO | 在严格模式,在INSERT或UPDATE过程中,如果被零除(或MOD(X,0)),则产生错误(否则为警告)。如果未给出该模式,被零除时MySQL返回NULL。如果用到INSERT IGNORE或UPDATE IGNORE中,MySQL生成被零除警告,但操作结果为NULL。 |
NO_AUTO_CREATE_USER | 防止GRANT自动创建新用户,除非还指定了密码。 |
NO_ENGINE_SUBSTITUTION | 如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常。 |
mysql5.0以上版本支持三种sql_mode模式:
sql_mode: | 含义 |
---|---|
ANSI | 宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告。 |
TRADITIONAL | 严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error错误。用于事物时,会进行事物的回滚。 |
STRICT_TRANS_TABLES | 严格模式,进行数据的严格校验,错误数据不能插入,报error错误。 |
注意:自定义mysql配置文件中如果设置sql_mode=ONLY_FULL_GROUP_BY,则执行sql时会报错:
Error Code: 1055. Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column '×××' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
所以sql_mode中最好去掉ONLY_FULL_GROUP_BY参数