客户端往主服务器插入一条数据(库、表、表中的数据),主服务器将新增的记录同步到两个从服务器
1、异步复制(Asynchronous replication)
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给客户端,
并不关心从库是否已经接收并处理,这样就会有一个问题,主如果崩溃掉了,
此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,
可能导致新主上的数据不完整。
2、全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。
因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
(对数据一致性、完整性要求很高的场景)
3、半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,
而是等待至少一个从库接收到并写到relay log中才返回给客户端。
相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,
这个延迟最少是一个TCP/IP往返的时间。(RTT:round-trip time)
所以,半同步复制最好在低延时的网络中使用。
实验配置:
20.0.0.81:主Mysql
20.0.0.82:从MYsql1
20.0.0.83:从Mysql2
81、82、83相同操作:
systemctl stop firewalld
setenforce 0
主服务器设置
81:
yum -y install ntp
vim /etc/ntp.conf
--添加--
server 127.127.0.0 #设置本地是时钟源,注意修改网段
fudge 127.127.0.0 stratum 8 #设置时间层级为8,数字越小,时间精确度越高(限制在15内)
systemctl restart ntpd
从服务器设置
82、83相同操作:
yum -y install ntp
systemctl restart ntpd
/usr/sbin/ntpdate 20.0.0.81
crontab -e -u root
--添加--
*/30 * * * * /usr/sbin/ntpdate 20.0.0.81
时间同步验证
vim /etc/my.cnf
--添加--
log-bin=master-bin #主服务器开启二进制日志
binlog_format=MIXED
log-slave-updates=true #添加,允许slave从master复制数据时可以写入到自己的二进制日志
systemctl restart mysqld
mysql -u root -p123456
grant replication slave on *.* to 'myslave'@'20.0.0.%' identified by '123456';
flush privileges;
show master status;
82:
vim /etc/my.cnf
--添加--
server-id = 2
relay-log=relay-log-bin
#指定了从服务器上中继日志的基本文件名。中继日志的文件名将以 "relay-log-bin" 开头。
relay-log-index=slave-relay-bin.index
#指定了中继日志索引文件的名称。中继日志索引文件用于记录中继日志文件的顺序和位置。
relay_log_recovery=1
#默认是0
#1表示开启中继日志的恢复,如果从服务器出现异常或者崩溃时,
从服务器会从主服务器的二进制日志正确读取和应用中继日志(为了同步)。
systemctl restart mysqld
83:
vim /etc/my.cnf
--添加--
server-id = 3
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
relay_log_recovery=1
systemctl restart mysqld
82、83相同操作:
mysql -u root -p123456
CHANGE master to master_host='20.0.0.81',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=599;
#配置同步,注意 master_log_file 和 master_log_pos 的值要与Master查询的一致
start slave; #启动同步,如有报错执行 reset slave;
show slave status\G; #确保 IO 和 SQL 线程都是 Yes,代表同步正常
负责和主库的IO通信如果是'NO':
主服务器新建库
主服务器创建数据库:
create database test;
主服务器新建表
主服务器新建表:
create table info(
name varchar(10)
);
主服务器表中插入数据
向主服务器的表info中插入一条数据
从服务器新建库
从服务器1新建库:
create database test1;
1、主从复制是单向的,只能从主复制到从
2、主从复制出现延迟可能的原因:
解决方法:
硬件方面:主库一般来说不需要动得太多,从库得硬件配置要更好。
提升随机写的性能、硬盘可以换成固态的,升级cpu的核数、扩展内存、尽量使用物理机(不适用云服务器)。
网络层面:主从服务器都配置在一个局域网内,尽量避免跨网段和跨机房。
架构方面:做读写分离,把写入控制在主库,从库负责读,降低从库的压力。
配置方面:Mysql配置,从配置文件的角度实现性能最大化。
追求安全性的配置:
修改innodb_flush_log_at_trx_commit=1
#每次事务提交时都会刷新日志以确保持久性,最高级别的数据安全性,但是会影响性能,默认就是1。
#0:即事务提交时不会立刻刷新,而是每秒刷新一次。可以提高性能,但是发生故障会导致数据丢失。
#2:事务提交时,事务日志不会写入硬盘而是保存在系统缓存,不会进行刷新。有一定的安全性和性能,内存要求比较高。
修改sync_binlog=1
#1(默认值):每次提交事务之后,直接把二进制日志刷新到磁盘,以确保日志的持久性,会占用比较高的性能,但是安全性高。
#0:将二进制日志写入缓存,也不会刷新日志。故障发生也会丢失数据,内存的要求也提高了。
#N:每N次事务提交后执行一次刷新到磁盘,可以提高性能,但是一旦崩溃,数据会大量丢失。
追求性能最大化的配置:
修改sync_binlog=0 innodb_flush_log_at_trx_commit=2 log-slave-updates=0
#从库的更新不会写入二进制日志(不建议)
修改innodb_buffer_pool_size 300M 500G
#控制innodb存储引擎的缓冲池大小,设置的数值越高,可以提高innodb的性能。
#更多的数据和索引都可以缓存在内存中,减少磁盘的访问次数,对系统内存要求比较高。
3、主从复制的工作过程
1.主节点的数据记录发生变化都会记录在二进制日志。
2.slave节点会一定时间内对主库的二进制日志义件进行探测,看其是否发生变化,如果变化,从库会开启一个I/O的线程请求的主库的二进制事件。
3.主库会给每一个I/O的线程启动一个dump,用于发送二进制事件给从库,从库通过I/O线程获取更新,save_sq负责将更新写入到从库本地。实现主从一致。
4、主从复制的问题
1.只能在主库上发生变化,然后同步到从。
2.复制过程是串行化过程,在从库上复制是串行的,主库的并行更新不能在从库上并行操作。
3.主从复制的设计目的就是为在主库上写,在从库上查。读写分离,实现高可用。
实验配置:
20.0.0.81:服务器1
20.0.0.82:服务器2
81、82:
systemctl stop firewalld
setenforce 0
hostname master1
su
vim /etc/my.cnf
--
server-id=11
log-bin=mysql-bin
auto_increment_increment=2
auto_increment_offset=1
replicate-do-db=demo_db
--
systemctl restart mysqld
mysql -u root -p123456
create user 'master1'@'%' identified with mysql_native_password by '123456';
grant replication slave on *.* to 'master1'@'%';
flush privileges;
show grants for 'master1'@'%';
show master status;
hostname master2
su
vim /etc/my.cnf
--
server-id=12
log-bin=mysql-bin
auto_increment_increment=2
auto_increment_offset=2
replicate-do-db=demo_db
--
systemctl restart mysqld
mysql -uroot -p123456
mysql> change master to master_host='20.0.0.81',
-> master_user='master1',
-> master_password='123456',
-> master_log_file='mysql-bin.000001',
-> master_log_pos=768;
start slave;
show slave status \G;
create user 'master2'@'%' identified with mysql_native_password by '123456';
grant replication slave on *.* to 'master2'@'%';
flush privileges;
show master status;
回到主服务器1:
mysql> change master to master_host='20.0.0.82',
-> master_user='master2',
-> master_password='123456',
-> master_log_file='mysql-bin.000001',
-> master_log_pos=768;
start slave;
show slave status \G;
主服务器1新建库:
create database demo_db;
主服务器2使用此库新建表:
mysql> use demo_db;
Database changed
mysql> create table info(name char(5));
前提:
要实现读写分离,必须要先实现主从复制。
读写分离:
所有的写入操作都在主,从库只负责读(select)。
如果有更新,是从主库复制到从库。
在工作当中,数据库的读写不会在同一个库中完成。这样既不安全也不能满足高可用,也不能实现高并发。工作中都会做读写分离。
需要读写分离的原因:
数据库在写入数据时,比较耗时(Mysql写一万条数据大概三分钟)
数据库在读取数据时,速度很快(Mysql读一万条数据大概五秒左右)
读写分离之后,数据的写入和读取是分开的,哪怕写入的数据量比较大,但是不影响查询的效率
读写分离适用场景:
某些程序在使用数据库过程中,更新少,但是查询多时,可以考虑读写分离
读和查的需求差不多,也可以考虑读写分离
生产库一般都会做读写分离,测试库一般不管
Mysql读写分离的原理:
1、根据脚本实现,在代码中实现路由分类。
对select、insert进行路由分类,这种方式是最多的。
优点:性能好,在代码中就可以实现,不需要额外的硬件设备。
缺点:开发实现,与运维无关。如果是大型的复杂的应用,设计改动的代码非常多。
2、基于中间代理层实现
Mysql-proxy自带的开源项目,基于自带的lua脚本。
这些lua脚本不是现成的,要自己写,不熟悉内置变量可能无法实现。
atlas、360内部做自己使用的代理工具。每天的读写请求承载量可以到几十亿条。支持事务,支持存储过程。
用amoeba实现读写分离,基于java开发的一个开源软件,不支持事务,也不支持存储过程。但是amoeba还是使用最多、功能比较强大的软件。
实验配置:
20.0.0.81:主Mysql
20.0.0.82:从MYsql1
20.0.0.83:从Mysql2
20.0.0.10:Amoeba服务器
20.0.0.20:客户端服务器
Amoeba 基于是 jdk1.5 开发的,所以官方推荐使用 jdk1.5 或 1.6 版本,高版本不建议使用。
10:
systemctl stop firewalld
setenforce 0
cd /opt/
--拖入Amoeba安装包和jdk包--
cp jdk-6u14-linux-x64.bin /usr/local/
cd /usr/local/
chmod +x jdk-6u14-linux-x64.bin
./jdk-6u14-linux-x64.bin
//按yes,按enter
mv jdk1.6.0_14/ /usr/local/jdk1.6
vim /etc/profile
--添加--
export JAVA_HOME=/usr/local/jdk1.6
export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin/:$PATH:$HOME/bin
export AMOEBA_HOME=/usr/local/amoeba
export PATH=$PATH:$AMOEBA_HOME/bin
source /etc/profile
java -version
cd /opt
mkdir /usr/local/amoeba
tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/
chmod -R 755 /usr/local/amoeba/
/usr/local/amoeba/bin/amoeba
//如显示amoeba start|stop说明安装成功
在Master、Slave1、Slave2 的mysql上开放权限给 Amoeba 访问
81、82、83相同操作:
mysql -u root -p123456
grant all on *.* to amoeba@'20.0.0.%' identified by '123456';
flush privileges;
回到amoeba服务器配置amoeba服务
10:
cd /usr/local/amoeba/conf/
cp amoeba.xml amoeba.xml.bak
vim amoeba.xml
--30--修改
amoeba
--32--修改
123456
--115--修改
master
--117--去掉注释
master
slaves
cp dbServers.xml dbServers.xml.bak
vim dbServers.xml
--23--注释掉 #默认进入test库,以防mysql中没有test库时,会报错
--26--修改
amoeba
--28-30--去掉注释
123456
--45--修改,设置主服务器的名Master
--48--修改,设置主服务器的地址
20.0.0.81
--52--修改,设置从服务器的名slave1
--55--修改,设置从服务器1的地址
20.0.0.82
--58--复制上面6行粘贴,设置从服务器2的名slave2和地址
20.0.0.83
--65行--修改
--71行--修改
slave1,slave2
/usr/local/amoeba/bin/amoeba start & #启动Amoeba软件,按ctrl+c 返回
netstat -anpt | grep java
客户端20:
yum install -y mariadb-server mariadb
systemctl start mariadb.service
在主从服务器上开启查询日志
81、82、83相同操作:
vim /etc/my.cnf
--添加--
general_log=ON
general_log_file=/usr/local/mysql/data/mysql_general.log
systemctl restart mysqld
在客户端服务器上测试
81、82、83打开日志:
tail -f /usr/local/mysql/data/mysql_general.log
客户端20:
mysql -u amoeba -p123456 -h20.0.0.10 -P8066
show databases;
use test;
show tables;
insert into info values('carrot');
20:
select * from info;
实验配置:
20.0.0.81:主Mysql
20.0.0.82:从MYsql1
20.0.0.10:mycat服务器
20.0.0.20:客户端服务器
systemctl stop firewalld
setenforce 0
主Mysql服务器配置:
vim /etc/my.cnf
server-id = 1
log-bin=master-bin
binlog_format=MIXED
log-slave-updates=true
systemctl restart mysqld
mysql -u root -p123456
grant replication slave on *.* to 'myslave'@'20.0.0.%' identified by '123456';
#建立一个myslave用户,允许20.0.0.%这个网段的地址来登录,密码是123456,待会在slave上使用这个账号
flush privileges;
show master status;
从Mysql服务器配置:
82:
vim /etc/my.cnf
--添加--
server-id = 2
relay-log=relay-log-bin
relay-log-index=slave-relay-bin.index
systemctl restart mysqld
mysql -uroot -p123456
change master to master_host='20.0.0.81',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=599;
start slave;
show slave status \G;
主服务器创建数据库:
create database test;
10:
#主机上安装java(mycat基于java)
yum install java -y
java -version #确认安装成功
openjdk version "1.8.0_382"
OpenJDK Runtime Environment (build 1.8.0_382-b05)
OpenJDK 64-Bit Server VM (build 25.382-b05, mixed mode)
#切换至opt目录,下载mycat安装包
cd /opt
wget http://dl.mycat.org.cn/1.6.7.6/20210303094759/Mycat-server-1.6.7.6-release-20210303094759-linux.tar.gz
#创建/apps文件夹,并解压mycat包至/apps下
mkdir /apps
tar zxvf Mycat-server-1.6.7.6-release-20210303094759-linux.tar.gz -C /apps/
#设置变量环境
echo 'PATH=/apps/mycat/bin:$PATH' > /etc/profile.d/mycat.sh
source /etc/profile.d/mycat.sh
#启动mycat,查看日志文件,最后可以看到启动成功
mycat start
Starting Mycat-server...
tail -f /apps/mycat/logs/wrapper.log
#客户端连接数据库
20:
yum install -y mariadb-server mariadb
systemctl start mariadb.service
mysql -uroot -p123456 -h 20.0.0.10 -P8066
10:
vim /apps/mycat/conf/server.xml
--44去掉注释,对应的行末注释在51--
--50删除--
5 * 60 * 1000L;
--45修改端口号--
45 3306
vim /apps/mycat/conf/schema.xml
--删除所有并添加--
select user()
81:
mysql -uroot -p123456
GRANT ALL ON *.* TO 'root'@'20.0.0.%' IDENTIFIED BY '123456';
#查看创建成功
use mysql;
select user,host from user;
10:
#在mycat服务器上,重启mycat服务,查看启动日志,文末出现successfully
mycat restart
tail -f /apps/mycat/logs/wrapper.log
#查看3306端口
ss -antp|grep 3306
#在客户机上登录mycat
mysql -uroot -p123456 -h 20.0.0.10
show databases;
use TESTDB;
show tables;
#主从服务器上都打开通用日志
81、82、83相同操作:
mysql -uroot -p123456
set global general_log=1;
show variables like 'general%';
#在主从服务器上实时查看通用日志
81:tail -f /usr/local/mysql/data/mysql1.log
82:tail -f /usr/local/mysql/data/mysql2.log
客户机新建表格:
create table info(name char(5));
客户机查看表:
select * from info;
方式1:
从服务器:
mysql -u root -p123456
show salve status\G;
此两项都是yes即是成功
方式2:
在主库创建一个库或者表或者在表中插入数据,看是否能同步到从库
异步复制小结中有写
安全性更重要,需斟酌考虑