Mysql主从
Mysql主从原理:
- master会将变动记录到二进制日志(BinLog)里面
- 然后master会有一个I/O线程将二进制发送到salve(从库)
- 此时slave也会有一个I/O线程吧master发送的二进制写入到relay日志里面
- 最后salve有一个SQL线程,按照relay日志处理slave的数据
操作步骤
一、准备好两台mysql的服务器(再次使用docker模拟两台机器)
环境 | mysql版本 | ip地址端口号 |
---|---|---|
主库(master) | 5.7 | 172.16.209.100:33307 |
从库(salve) | 5.7 | 172.16.209.100:33306 |
二、用docker拉起两个mysql容器,步骤如下
# 主库
mkdir /home/mysql # 创建挂载主目录
mkdir /home/mysql/conf.d #创建挂载配置文件
mkdir /home/mysql/data/ # 创建挂载数据文件
touch /home/mysql/my.cnf # 创建配置文件
# 从库
mkdir /home/mysql2 # 创建挂载主目录
mkdir /home/mysql2/conf.d #创建挂载配置文件
mkdir /home/mysql2/data/ # 创建挂载数据文件
touch /home/mysql2/my.cnf
三、修改配置文件
####主库的配置文件,server-id和开启binlog日志
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
# 主要是两个
# 主库--start-- 同一局域网内注意要唯一,通过id号找到从库
server-id=100
# 开启二进制日志功能,可以随便取(关键),因为主从就是依赖bin-log日志
log-bin=mysql-bin
#############从库的配置
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
# 唯一
server-id=101
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
四、拉起来两个Mysql容器
#启动主库容器(挂载外部目录,端口映射成33307,密码设置为123456)
docker run -di -v /home/mysql/data/:/var/lib/mysql -v /home/mysql/conf.d:/etc/mysql/conf.d -v /home/mysql/my.cnf:/etc/mysql/my.cnf -p 33307:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
#启动从库容器(挂载外部目录,端口映射成33306,密码设置为123456)
docker run -di -v /home/mysql2/data/:/var/lib/mysql -v /home/mysql2/conf.d:/etc/mysql/conf.d -v /home/mysql2/my.cnf:/etc/mysql/my.cnf -p 33306:3306 --name mysql-slave -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
这时两个容器都跑起来了
Navicate测试连接
但是现在还不能同步,同步的方法也比较简单,就是创建一个用户,远程连上master,然后从master上拿到日志数据
五、创建用户并授权
1.进入到master容器
docker exec -it mysql-master /bin/bash
mysql -uroot -p123456
2.创建用户并授权
#在主库创建用户并授权
##创建test用户
create user 'test'@'%' identified by '123';
##授权用户
grant all privileges on *.* to 'test'@'%' ;
###刷新权限
flush privileges;
#查看主服务器状态(显示如下图)
show master status;
# 可以看到日志文件的名字,和现在处在哪个位置,也就是file和position
记录一下增长一下,复制后时候需要指定复制文件,和复制位置
六、连接从库,配置连接主库
docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456
1.配置详解
change master to
master_host='MySQL主服务器IP地址',
master_user='之前在MySQL主服务器上面创建的用户名',
master_password='之前创建的密码',
master_log_file='MySQL主服务器状态中的二进制文件名',
master_log_pos='MySQL主服务器状态中的position值';
# 命令如下
change master to master_host='ip地址',master_port=33307,master_user='test',master_password='123',master_log_file='mysql-bin.000001',master_log_pos=0;
# master_log_pos数字代表的是从master那个位置开始复制,0或其实位置
#启用从库
start slave;
#查看从库状态(如下图)
show slave status\G;
####这两个是yes表示配成功
Slave_IO_Running: Yes
Slave_SQL_Running: Yes # 可能会出现这个问题 NO
# 如果出现问题,要看last_error 然后拍错解决就好了
七、测试
1.在主库上创建数据库test1
create database test1;
use test1;
2.创建表
create table tom(id int not null,name varchar(100)not null,age tinyint);
3.插入数据
insert tom(id,name,age) values(1,"tom",18),(2,"jerry",19),(3,"lary",18);
这时候从库中也有了相应的数据
八、Django实现读写分离
1.基于主从已经搭建完成
2.在Django中settings.py中配置
#1 在setting中配置
DATABASES = {
# 主库--只写
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test1',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '101.133.225.166',
'PORT': 33307,
'ATOMIC_REQUEST':True, # 代表一次请求在一次事务中
},
# 从库--只读
'db1': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test1',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '101.133.225.166',
'PORT': 33306,
},
}
3.读写分离的操作
3.1 手动指定
在使用orm操作的时候,指定使用的库
.using(指定的库)
这里是写操作,因为默认是default
所以一般不带using
res = models.Book.object.using('default').create(name="《海子》", price=33)
读操作(使用从库)
res=models.Book.objects.using('db1').all().first()
3.2 自动指定(自定义一个py文件)
# db_router.py
class Router1: # 名字可以随便指定,但是一定要有下面两个方法
def db_for_read(self, model, **hints):
return 'db1'
def db_for_write(self, model, **hints):
return 'default'
配置完成后在settings.py
中配置
# setting.py
# 在setting中注册
# 注册一下
DATABASE_ROUTERS = ['db_router.Router1',]
这样注册之后就会自动的分配读写
3.3更细粒度
做完分库分表,只希望在指定库中读,指定库中存储
class Router1:
def db_for_read(self, model, **hints):
if model._meta.model_name == 'book':
# 也就是如果表名==book去db1中读,其余的去default
return 'db1'
else:
return 'default'
def db_for_write(self, model, **hints):
return 'default'