读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。
有很多种配置主从同步的方法,可以总结为如下的步骤:
Mysql5.7.29主服务器:ubuntu16
Mysql5.7.29从服务器:ubuntu16上的docker容器
WEB框架:Django1.8
docker三个核心概念: Repository(仓库) -> Image(镜像) -> Container(容器)
卸载可能存在的旧版本
sudo apt-get remove docker docker-engine docker-ce docker.io
更新apt包
sudo apt-get update
安装以下包以使apt可以通过HTTPS使用存储库(repository):
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
设置stable存储库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
再次更新apt包
sudo apt-get update
安装最新版本的Docker CE(社区免费版)
sudo apt-get install -y docker-ce
验证docker: sudo docker version 或 sudo docker info
查看docker服务是否启动
systemctl status docker
运行docker命令时需要root权限或者加sudo
sudo docker pull hello-world拉取dockerHub上的hello-world镜像
sudo docker images查看镜像信息
可以看到已存在hello-world镜像
**字段解释**
REPOSITORY: 来自于哪个仓库;
TAG: 镜像的标签信息,比如 5.7、latest 表示不同的版本信息;
IMAGE ID: 镜像的 ID, 如果您看到两个 ID 完全相同,那么实际上,它们指向的是同一个镜像,只是标签名称不同罢了;
CREATED: 镜像最后的更新时间;
SIZE: 镜像的大小,优秀的镜像一般体积都比较小
生成该镜像的实例容器(镜像与容器的关系就像类和实例类对象)
sudo docker run hello-world
Docker服务相关命令
开启Docker服务
systemctl start docker
停止Docker服务
systemctl stop docker
重启Docker服务
systemctl restart docker
开机自启Docker服务
systemctl enable docker
创建mysql容器
家目录下创建mysql_slave目录作为容器挂载数据卷
mkdir mysql_slave
进入mysql_slave目录
cd mysql_slave
创建data目录
mkdir data
将宿主机的mysql配置文件目录拷到当前数据卷目录下
cp -a /etc/mysql/mysql.conf.d ./
将配置文件拷贝过来后,编辑mysql.conf.d目录下的mysqld.cnf 文件
宿主机中的mysql做为主服务器,其server-id设为1,从服务器只要不是1都行,这边取2
server-id = 2
创建容器(如果没有该镜像,则自动从dockerHub拉取该镜像并创建容器)
docker run -id
-p 3308:3306
–name=mysql-slave
-e MYSQL_ROOT_PASSWORD=mysql
-v /root/mysql_slave/data:/var/lib/mysql
-v /root/mysql_slave/mysql.conf.d:/etc/mysql/mysql.conf.d
mysql:5.7.29
参数解释:
-p 指定映射的端口,左边为宿主机端口,右边为容器端口,这样就可以通过映射的3308端口在宿主机以外的外部机器访问该容器中的mysql
-id 表示该容器作为守护进程在后台运行,创建后马上运行,但是不会进入shell终端,退出后也不会真正退出
–name 指定实例容器的名称
-e 指定数据库密码
-v 指定要挂载的目录或文件,必须写绝对路径,左边为宿主机路径,右边为容器路径
mysql:5.7.29 指定要创建的容器的镜像及其版本,由于宿主机中mysql是5.7.29版本,所以我们这里创建mysql5.7.29的容器
查看运行中的容器
docker ps
宿主机中导出mysql主服务器sql文件
mysqldump -uroot -pmysql --all-databases --lock-all-tables > ~/master_db.sql
参数解释:
-u :用户名
-p :示密码
–all-databases :导出所有数据库
–lock-all-tables :执行操作时锁住所有表,防止操作时有数据修改
~/master_db.sql :导出的备份数据(sql文件)位置,可自己指定
mysql -uroot -pmysql -h127.0.0.1 --port=3308 < ~/master_db.sql
编辑设置mysqld的配置文件,设置log_bin和server-id
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
sudo service mysql restart
登入mysql,创建用于从服务器同步数据使用的帐号并刷新缓存
mysql –uroot –pmysql
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' identified by 'slave';
FLUSH PRIVILEGES;
获取主服务器的二进制日志信息(File为使用的日志文件名字,Position为使用的文件位置,这两个参数须记下,配置从服务器时会用到)
SHOW MASTER STATUS;
执行以下命令进入指定名称的容器的shell终端
docker exec -it mysql-slave bash
进入容器中的mysql数据库并设置连接到master主服务器
mysql -uroot -pmysql
change master to master_host='192.168.254.137', master_user='slave', master_password='slave',master_log_file='mysql-bin.000001', master_log_pos=590;
参数解释:
master_host:主服务器宿主机Ubuntu的ip地址
master_log_file: 前面查询到的主服务器日志文件名
master_log_pos: 前面查询到的主服务器日志文件位置
django-admin startproject test2
python manage.py startapp booktest
为应用booktest下的视图index创建模板index.html,目录结构如下图:
打开templtes/booktest/index.html文件,定义代码如下:
<html>
<head>
<title>案例</title>
</head>
<body>
<a href="/create/">创建</a>
<ul>
{%for book in list%}
<li>{{book.btitle}}--<a href="/delete{{book.id}}/">删除</a></li>
{%endfor%}
</ul>
</body>
</html>
添加应用名称
定义模板文件路径
注释原有的sqlite连接配置,写入如下代码:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2', # 数据库名字,
'USER': 'root', # 数据库登录用户名
'PASSWORD': 'mysql', # 数据库登录密码
'HOST': '192.168.254.137', # 数据库所在主机
'PORT': '3306', # 数据库端口
},
'slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test2', # 数据库名字,
'USER': 'root', # 数据库登录用户名
'PASSWORD': 'mysql', # 数据库登录密码
'HOST': '192.168.254.137', # 数据库所在主机
'PORT': '3308', # 数据库端口
},
}
打开booktest/models.py文件,定义模型类源码如下
from django.db import models
# 定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20) # 图书名称
bpub_date = models.DateField() # 发布日期
bread = models.IntegerField(default=0) # 阅读量
bcomment = models.IntegerField(default=0) # 评论量
isDelete = models.BooleanField(default=False) # 逻辑删除
# 定义英雄模型类HeroInfo
class HeroInfo(models.Model):
hname = models.CharField(max_length=20) # 英雄姓名
hgender = models.BooleanField(default=True) # 英雄性别
isDelete = models.BooleanField(default=False) # 逻辑删除
hcomment = models.CharField(max_length=200) # 英雄描述信息
hbook = models.ForeignKey('BookInfo') # 英雄与图书表的关系为一对多,所以属性定义在英雄模型类中
安装pymysql
pip install pymysql
安装成功之后,在test2/_init_.py文件中加上如下代码:
import pymysql
pymysql.install_as_MySQLdb()
生成迁移文件
python manage.py makemigrations
执行迁移文件在数据库生成表
python manage.py migrate
在数据库命令行中,复制如下语句执行,向booktest_bookinfo表中插入测试数据:
insert into booktest_bookinfo(btitle,bpub_date,bread,bcomment,isDelete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);
再复制如下语句执行,向booktest_heroinfo表中插入测试数据:
insert into booktest_heroinfo(hname,hgender,hbook_id,hcomment,isDelete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('梅超风',0,1,'九阴白骨爪',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('令狐冲',1,3,'独孤九剑',0),
('任盈盈',0,3,'弹琴',0),
('岳不群',1,3,'华山剑法',0),
('东方不败',0,3,'葵花宝典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若兰',0,4,'黄衣',0),
('程灵素',0,4,'医术',0),
('袁紫衣',0,4,'六合拳',0);
打开booktest/views.py文件,定义视图代码如下:
from django.shortcuts import render,redirect
from booktest.models import *
from datetime import date
#查询所有图书并显示
def index(request):
#使用从服务器进行读操作
list=BookInfo.objects.using('slave').all()
return render(request,'booktest/index.html',{'list':list})
#默认使用主服务器进行写操作
#创建新图书
def create(request):
book=BookInfo()
book.btitle = '流星蝴蝶剑'
book.bpub_date = date(1995,12,30)
book.save()
#转向到首页
return redirect('/')
#逻辑删除指定编号的图书
def delete(request,id):
book=BookInfo.objects.get(id=int(id))
book.delete()
#转向到首页
return redirect('/')
打开test2/urls.py文件,配置url如下:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
#引入booktest的url配置
url(r'^',include('booktest.urls')),
]
在booktest应用下创建urls.py文件,代码如下:
from django.conf.urls import url
from booktest import views
urlpatterns=[
url(r'^$',views.index),
url(r'^delete(\d+)/$',views.delete),
url(r'^create/$',views.create),
]
python manage.py runserver 127.0.0.1
html界面中点击创建或删除后,由主服务器执行写操作,由从服务器同步主服务器数据执行读操作,可以看到渲染的数据在增加或减少