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 statusG;

 这两个都必须是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'
原文地址:https://www.cnblogs.com/ludingchao/p/14083578.html