Mysql1 mysql主从同步,django实现读写分离
MySql主从同步(基于docker)
主从配置原理
# 1 主从同步的原理 mysql主从配置的流程大体如图: 1)master会将变动记录到二进制日志里面; 2)master有一个I/O线程将二进制日志发送到slave; 3) slave有一个I/O线程把master发送的二进制写入到relay日志里面; 4)slave有一个SQL线程,按照relay日志处理slave的数据; # 2 主从配置(两台服务器,用docker模拟),尽量环境完全一样,mysql版本一定一致 -主库是docker中映射到宿主机的33307端口 -从是docker中映射到宿主机的33306端口 # 3 搭建步骤 3.1 启动主库
# 0 拉取mysql5.7镜像 之前已经拉过了
# docker pull mysql:5.7 1 在宿主机上创建目录#让docker挂载外部路径 mkdir /home/mysql mkdir /home/mysql/conf.d mkdir /home/mysql/data/ 创建my.cnf配置文件 touch /home/mysql/my.cnf 2 主库的配置 [mysqld]#配置的是服务端 vim my.cnf 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=100#主库需要写一个id号,同一局域网内注意要唯一 log-bin=mysql-bin#需要开启blog日志,让从库读日志 3 启动主库 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 3.2 启动从库 1 在宿主机上创建目录 mkdir /home/mysql2 mkdir /home/mysql2/conf.d mkdir /home/mysql2/data/ 创建my.cnf配置文件 touch /home/mysql2/my.cnf 2 从库的配置 [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#从库也要设置id号,不能重复 ## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用 log-bin=mysql-slave-bin ## relay_log配置中继日志 relay_log=edu-mysql-relay-bin 3 启动从库 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 3.3 连到主库,进行配置 mysql -h 10.0.0.200 -P 33307 -u root -p123456 #装mysql客户端的机器去连接 #在主库创建用户并授权 ##创建test用户(root用户不允许发送数据,需要创建其他用户,给他授权) create user 'test'@'%' identified by '123';#@'%'从所有ip都能访问,identified by密码 ##授权用户 grant all privileges on *.* to 'test'@'%' ;#all privileges代表所有权限,*.*所有数据库和表权限 ###刷新权限 flush privileges; #查看主服务器状态(显示如下图) show master status; #可以看binlog日志 ## 显示 File mysql-bin.000003 binlog日志文件名字 Position 888 文件记录当前所处位置 ## 如果从库那边来做,需要指定复制文件名,还有哪个位置开始复制 3.4 连接到从库,进行配置 mysql -h 10.0.0.200 -P 33306 -u root -p123456 #配置详解 /* 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='10.0.0.200',master_port=33307,master_user='test',master_password='123',master_log_file='mysql-bin.000003',master_log_pos=0; #master_log_pos可以写查到的888。可以写0,代表把之前所有操作记录都复制过去 #启用从库 start slave; #查看从库状态(如下图) show slave status\G;
这两个都必须是yes,一个是IO线程,一个是SQL线程。就可以完成主从功能
## 测试主从同步 #在主库上创建数据库test1 create database test1; use test1; #创建表 create table tom (id int not null,name varchar(100)not null ,age tinyint); #插入数据 insert tom (id,name,age) values(1,'xxx',20),(2,'yyy',7),(3,'zzz',23); #在从库上查看是否同步成功 #查看数据库 show database; use test1; #查看表 show tables; #查看数据 select * from test1;
注意:同步是异步操作,所以会有一些延迟(主库进去了,从库还没有)
解决方法:去主库里创建,从库查的时候,先返回一个空表示创建成功。再发一次请求查的时候(进详情页面时),数据就已经有了
django实现读写分离
手动操作
# 1 写相关的操作,放到主库,读相关的操作,到从库上 # 2 配置文件中: #注:如想对部分app迁移库操作,后面加上名字makemigrations [appname],migrate [appname] # makemigrations # migrate #直接migrate只会同步默认default数据库(不是配置第一个) 数据迁移的时候,通过它来指定,同步到哪个库 migrate --database=db1 # 迁移到db1数据库,如果是主从数据库,只要迁移到主库就可以了 # 3 主库,从库 default是主库 db1是从库 migrate app01 --databse="db1" # app01数据库迁移到db1数据库 # 4 手动操作(queryset对象) ret=Book.objects.all().using("db1") # 读取db1数据库中的数据,必须是queryset对象
下面用两个sqlite对象来演示
# settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db1': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite4'), # 名字不能重复 } }
urls.py
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), ]
models.py
from django.db import models class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32)
views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): # 1发送一次请求,创建一条记录(创建在主库) ret = models.Book.objects.create(name='西行纪') print(ret) # 2 查询去从库查 # 还是从default上查 # ret = models.Book.objects.all().first() # 指定从db1上查,queryset对象.using('db1') ret = models.Book.objects.all().using('db1').first() # 使用db1数据库 # ret = models.Book.objects.all().using('default').first() # 使用默认数据库 print(ret) return HttpResponse('ok')
自动做(通过配置,程序,读去从库读,写去主库写)[推荐]
# 4 自动做(通过配置,程序,读去从库读,写去主库写) # 写一个router 在项目路径下创建db_router.py(在app01路径下也行,在setting中路径配置要对) 在里面写一个类 # 类名随便命名 class Router1: def db_for_read(self, model, **hints): return 'db1' def db_for_write(self, model, **hints): return 'default' #在setting中配置 DATABASE_ROUTERS = ['db_router.Router1',] # 只要是写的操作,都到default上,只要是读的操作,都到db1上了
此处db_router放在app01路径下
db_router.py
class Router1: def db_for_read(self, model, **hints): # 以后,只要是读相关操作,都去db1上 return 'db1' def db_for_write(self, model, **hints): # 只要是写相关操作,都是default return 'default'
settings.py追加下面配置
DATABASE_ROUTERS = ['app01.db_router.Router1',]
views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): # 测试自动读写分离 # ret = models.Book.objects.create(name='西游记') # print(ret) ret = models.Book.objects.all().first() # 在setting中配置了读写分离的类,这里自动从读的库中读数据 print(ret.name) return HttpResponse('ok')
具体到某个表,只去某个库读,写
# 5 具体到某个表,只去某个库读,写 一主多从,通过random 分库分表:user在其中一个库,book表在另一个库 # 存可以分app
db_router.py
class Router1: def db_for_read(self, model, **hints): # 以后,只要是读相关操作,都去db1上 if model._meta.app_label == 'app01': # 判断如果是app01的库,到哪个库里去 return 'db1' print(str(model._meta)=='app01.book') # 可以判断是哪个表,然后去哪个库 return 'db1' def db_for_write(self, model, **hints): # 只要是写相关操作,都是default return 'default'