| 将主服务器的binlog日志复制到从服务器上执行一遍,达到主从数据的一致状态,称之为主从复制。 |
| |
| 一句话表示就是,主数据库做什么,从数据库就跟着做什么。 |
| |
| 有了主从保持数据一致作为大前提,我们便可以可以分离读写操作,其中Master负责写操作的负载,也就是说一切写的操作都在Master上进行,而读的操作则分摊到Slave上进行。那么读写分离的作用是什么呢? |
| |
| |
| 先说答案:读写分离可以大大提高读取的效率。 |
| |
| 在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在 10:1左右 ,也就是说写操作非常少,大量的数据操作是集中在读的操作(如某些应用,像基金净值预测的网站。其数据的更新都是有管理员更新的,即更新的用户比较少。而查询的用户数 量会非常的多。) |
| 此时我们可以制作一主多从,因为写操作很少,所以由一个主库负责即可,而大量的读操作则分配给多个从库,这样占据比例最大的读操作的压力就被负载均衡了,因此读效率会得到了极大的提升,另外,还有一个原因就是: |
| 熟悉DB的研发人员都知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。我们这样的分离是把写操作集中在一个节点上,而读操作其其他的N个节点上进行,这从另一个方面有效的提高了读的效率,保证了系统的性能及高可用性。 |
| |
| |
| 方案一: |
| 就是主库写,从库读 |
| |
| 方案二: |
| 主库负责写,还有部分读,从库只负责读,而且是读操作的主力 |
| |
| 即当主服务器比较忙时,部分查询请求会自动发送到从服务器中,以降低主服务器的工作负荷。 |
| 可以定期的将数据从主服务器上复制到从服务器上,这实现了数据的异地备份。 |
| 在传统的备份体制下,是将数据备份在本地。此时备份作业与数据库服务器运行在同一台设备上,当备份作业运行时就会影响到服务器的正常运行。有时候会明显的降低服务器的性能。同时,将备份数据存放在本地,也 不是很安全。如硬盘因为电压等原因被损坏或者服务器被失窃,此时由于备份文件仍然存放在硬盘上,数据库管理员无法使用备份文件来恢复数据。这显然会给企业 带来比较大的损失。 |
| 数据库复制功能实现了主服务器与从服务器之间数据的同步,增加了数据库系统的可用性。主库宕机后,从库尚可用,即当主服务器出现问题时,数据库管理员可以马上让从服务器作为主服务器,用来数据的更新与查询服务。然后回过头来再仔细的检查主服务器的问题。此时一般数据库管理员会采用两种手段。 |
| |
| 一:从库临时取代主库,只用来读 |
| 主服务器故障之后,虽然从服务器取代了主服务器的位置,但是对于主服务器可以采取的操作仍然做了一些限制,例如仍然只能够进行数据的查询,而不能够进行数据的更新、删除等操作。这主要是基于从数据的安全性考虑。如现在一些银行系统的升级,在升级的过程中,只能够查询余额而不能够取钱。这是同样的道理。 |
| |
| 二:从库永久取代主库,负责读和写 |
| 从服务器真正变成了主服务器。当从服务器切换为主服务器之后,其地位完全与原先的主服务器相同。此时可以实现对数据的查询、更新、删除等操 作。为此就需要做好数据的安全性工作。即数据的安全策略,要与原先的主服务器完全相同。否则的话,就可能会留下一定的安全隐患 |
整体上来说,复制有3个步骤:
(1) master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events)。
(2) slave的io线程将master的binary log events拷贝到它的中继日志(relay log);
(3) slave的sql线程解析中继日志中的事件并在从库执行,保持与主库一致。
复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作。
详解如下
| |
| (1)从库change master to 时,ip port user password binlog position写入到master.info进行记录 |
| (2)从库 start slave 时,会启动IO线程和SQL线程 |
| |
| |
| 1.从库的IO线程,读取master.info信息,获取主库信息并连接主库 |
| 2.主库接收从库的链接请求后,会生成一个准备binlog DUMP的线程,来响应从库 |
| 3.主库一旦有新的日志生成,会发送“信号”给主库的binlog dump线程,然后binlog dump线程会读取binlog日志的更新 |
| 4.TP(传送)给从从库的IO线程 |
| 5.IO线程将收到的日志存储到了TCP/IP 缓存 |
| 6.写入TCP/IP缓存后,立即返回ACK给主库 ,此时主库工作完成 |
| 7.IO线程更新master.info文件binlog 文件名和postion |
| 8.IO线程将缓存中的数据,存储到relay-log日志文件,此时io线程工作完成 |
| 9.从库SQL线程读取relay-log.info文件,获取到上次执行到的relay-log的位置,作为起点 |
| 10.从库SQL线程基于从步骤9中获取到的起点,去中继日志relay-log.000001获取后续操作,在从库回放relay-log |
| 11.SQL线程回放完成之后,会更新relay-log.info文件,把当前操作的位置记入,作为下一次操作的起点。 |
| 12. relay-log会有自动清理的功能。 |
这里的日志格式就是指二进制日志的三种格式
binlog_format=statement
binlog_format=row
binlog_format=mixed
其中基于row的复制方式更能保证主从库数据的一致性,但日志量较大,在设置时考虑磁盘的空间问题
MySQL的主从复制有两种复制方式,分别是异步复制和半同步复制
我们之前介绍的就是异步复制,即客户端线程提交一个写操作,写入主库的binlog日志后就立即返回,并不需要等待从库完成同步操作,而主库的dump线程会监测binlog日志的变量然后主动将更新推送给从库。
MySQL 主从复制默认是异步的模式。
前提:主从数据库版本一致
一、主库:192.168.15.101
1)在主库上创建一个用于复制的账号,并赋予replication slave权限,这里必须 *.*
不能指定库授权,因为 replication slave 是全局的
| mysql> grant replication slave on *.* to 'egon'@'%' identified by '123'; |
| mysql> flush privileges; |
2) 修改主库配置文件,开启主库的Binlog,并设置server-id
| [mysqld] |
| |
| server-id = 1 |
| |
| |
| log-bin = mysql-bin |
| |
| sync_binlog = 1 |
| |
| binlog_format = row |
| |
| expire_logs_days = 7 |
| |
| max_binlog_size = 100m |
| |
| binlog_cache_size = 4m |
| |
| max_binlog_cache_size= 512m |
| |
| |
| binlog-ignore-db=mysql |
| |
| |
| auto-increment-offset = 1 |
| |
| auto-increment-increment = 1 |
| |
| slave-skip-errors = all |
3)重启主库
4)备份主库,备份时锁表保证备份一致
| mysqldump -uroot -pEgon@123 -A -E -R --triggers --triggers --master-data=2 --single-transaction > /tmp/all.sql |
5)将备份文件发送给从库
| scp /tmp/all.sql root@192.168.15.100:/tmp |
二、从库:192.168.15.100
1)测试复制账号
| mysql -uegon -p123 -h 192.168.15.101 |
2)导入初始数据
| mysql -uroot -pEgon@123 < /tmp/all.sql |
3)修改从库配置文件,增加server-id,注意这个值是唯一的,要区别于主库和其他从库
| [mysqld] |
| server-id = 2 |
| |
| relay-log = mysql-relay-bin |
| replicate-wild-ignore-table=mysql.% |
| replicate-wild-ignore-table=test.% |
| replicate-wild-ignore-table=information_schema.% |
| |
| |
| |
4)重启从库
5)配置复制
先去主库查看一下binlog日志名与位置
然后在从库进行配置
| [root@slave1 ~] |
| |
| change master to |
| master_host='192.168.15.101', -- 库服务器的IP |
| master_port=3306, -- 主库端口 |
| master_user='egon', -- 主库用于复制的用户 |
| master_password='123', -- 密码 |
| master_log_file='mysql-bin.000001', -- 主库日志名 |
| master_log_pos=120; -- 主库日志偏移量,即从何处开始复制 |
6)从库启动slave线程,并检查
| mysql> start slave; |
| Query OK, 0 rows affected (0.01 sec) |
| 1.准备纯净的从库环境 |
| 主库:192.168.15.101 |
| 从库:192.168.15.100 |
| |
| 2.修改主库配置文件(略) |
| 3.重启主库(略) |
| 4.主库创建账号(略) |
| 5.模拟主库运行,有数据写入、 |
| for i in `seq 1 1000000` |
| do |
| mysql -uroot -p123 -e "insert db1.t1 values($i)"; |
| sleep 1; |
| done |
| |
| 6.主库全备数据 |
| |
| mysqldump -uroot -p123 -A -R --triggers > /tmp/all.sql |
| |
| mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction > /tmp/all.sql |
| |
| 7.将热备数据传达从库 |
| scp /tmp/all.sql 192.168.15.100:/tmp |
| |
| 8.修改从库配置文件(略) |
| |
| 9.重启从库(略) |
| |
| 10.在从库导入全备数据(导入打点备份,原因如下) |
| |
| |
| |
| |
| 11.查看sql文件中的位置点(如果是打点备份的话) |
| 该位置即主库刚刚做完全量备份时,主库binlog日志所处的位置 |
| [root@egon ~] |
| -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=129005; |
| |
| 12.从库配置同步 |
| [root@slave1 ~] |
| change master to |
| master_host='192.168.15.101', |
| master_port=3306, |
| master_user='egon', |
| master_password='123', |
| master_log_file='mysql-bin.000001', |
| master_log_pos=129005; |
| |
| 13.开启SQL线程和IO线程 |
| start slave; |
| 14.查看主从状态 |
| show slave status; |
练习:基于上述方式制作双主同步
| 1.检测网络: |
| [root@db01 ~] |
| |
| 2.检测端口: |
| [root@db01 ~] |
| |
| [root@db01 ~] |
| |
| 3.检测账号,密码: |
| |
| grant replication slave on *.* to rep@'172.16.1.5%' identified by '123'; |
| |
| mysql -urep -p123 -h127.0.0.1 -P 3307 |
| |
| 4.查看binlog是否存在 |
| show master status; |
| |
| 5.反向解析 |
| |
| ERROR 1045 (28000): Access denied for user 'root'@'db01' (using password: YES) |
| |
| skip_name_resolve |
| 1.主库有的数据,从库没有 |
| 主库:a库 |
| 从库:没有a库 |
| 2.从库有的数据,主库没有 |
| 从库:a库 |
| 主库:要创建a库 |
| 3.主库与从库数据库结构有差别 |
| |
| |
| |
| mysql> stop slave; |
| |
| mysql> set sql_slave_skip_counter=1; |
| |
| mysql> start slave; |
| |
| |
| |
| [root@db01 ~] |
| |
| slave-skip-errors=1032,1062,1007 |
| 1007:对象已存在 |
| |
| 1032:无法执行DML |
| |
| 1062:主键冲突,或约束冲突 |
| |
| |
| 1.要求,主从复制之前,主库和从库的数据保证一致. |
| 2.在从库上设置 只读:set read-only=1; |
| (做读写分离时使用,但是做MHA会出现提升为主库时,主库只读,后面会讲专门做读写分离的Atlas) |
| 我们说用延时从库可以做备份,主库执行删除的时候,从库还没有删除,可以把表数据拿出来恢复回去 |
| 企业中一般会延时3-6小时 |
1)已经有主从的情况下
| 1.停止主从 |
| mysql> stop slave; |
| 2.设置延时为180秒 |
| mysql> CHANGE MASTER TO MASTER_DELAY = 180; |
| 3.开启主从 |
| mysql> start slave; |
| 4.查看状态 |
| mysql> show slave status \G |
| SQL_Delay: 180 |
| 5.主库创建数据,会看到从库值变化,创建的库没有创建 |
| SQL_Remaining_Delay: 170 |
2)没有主从复制的情况下
| 1.修改主库,从库配置文件 |
| server_id |
| 开启binlog |
| |
| 2.保证从库和主库的数据一致 |
| |
| 3.执行change语句 |
| change master to |
| master_host='172.16.1.50', |
| master_user='rep', |
| master_password='123', |
| master_log_file='mysql-bin.000001', |
| master_log_pos=2752, |
| master_delay=180; |
3)延时从库停止方法
| 1.停止主从 |
| mysql> stop slave; |
| |
| 2.设置延时为0 |
| mysql> CHANGE MASTER TO MASTER_DELAY = 0; |
| |
| 3.开启主从 |
| mysql> start slave; |
| |
| |
| 思考:IO线程还是SQL线程做的手脚? |
| |
| |
| create database ttt; |
| |
| mysqlbinlog --base64-output=decode-rows -vvv db03-relay-bin.000002 |
| |
| 总结: |
| 延时从库是在SQL线程做的手脚,IO线程已经把数据放到relay-log里了. |
| SQL线程在执行的时候,会延迟你设定的时间长度. |
1.场景
| 总数据量级500G,正常备份去恢复需要1.5-2小时 |
| 1)配置延时3600秒 |
| mysql> CHANGE MASTER TO MASTER_DELAY = 3600; |
| |
| 2)主库 |
| drop database db; |
| |
| 3)怎么利用延时从库,恢复数据? |
2.环境准备
| 1.进行每日的全备 |
| mysqldump -uroot -p123 -A -R --triggers --master-data=2 –single-transaction > /backup/full.sql |
| |
| 2.调整延时从库延时时间为60分钟 |
| stop slave; |
| CHANGE MASTER TO MASTER_DELAY = 3600; |
| start slave; |
| |
| 3.主库写入新数据 |
| create database yanshi; |
| use yanshi; |
| create table yanshi(id int); |
| insert into yanshi values(1),(2),(3),(4); |
| |
| create database yanshi2; |
| use yanshi2; |
| create table yanshi2(id int); |
| insert into yanshi2 values(1),(2),(3),(4); |
3.模拟删除数据
| 1.删除一个库,可以是之前的,也可以是刚创建的 |
| |
| drop database world; |
4.使用延时从库恢复数据
| 1.停止从库sql线程 |
| stop slave sql_thread; |
| 2.查看状态 |
| show slave status; |
| 3.备份从库数据 |
| mysqldump -uroot -p123 -B world > /backup/congku.sql |
| 4.截取一下relay-log |
| 1)确认起点,查看relay-log.info即可 |
| [root@db02 data] |
| ./db02-relay-bin.000005 |
| 283 |
| 2)确认终点,找到drop语句之前 |
| [root@db02 data] |
| |
| 3)截取数据 |
| [root@db02 data] |
| 5.将从库全备的数据与relaylog截取数据拷贝到主库 |
| scp /tmp/yanshi.sql 172.16.1.51:/tmp/ |
| scp /backup/congku.sql 172.16.1.51:/tmp/ |
| 6.将数据导入主库 |
| |
| mysql < /tmp/yanshi.sql |
| mysql < /tmp/congku.sql |
| 7.开启从库的sql线程 |
| start slave sql_thread; |
| |
| |
MYSQL5.5之前版本的主从复制都是异步(asynchronous)的,而在异步复制中,主库执行完操作后,写入binlog日志后,就返回客户端,这一动作就结束了,主库并不会验证从库有没有收到binlog日志、以及收到的binlog是否完整,那如果主库提交一个事务并写入Binlog中后,当从库还没有从主库得到Binlog时,主库宕机了或因磁盘损坏等故障导致该事务的Binlog丢失了,那从库就不会得到这个事务,也就造成了从库与主库数据不一致的问题,我们也就无法使用备库来继续提供数据一致的服务了,半同步复制可以解决该问题。
从MYSQL5.5开始,支持半同步复制(Semi synchronous Replication)在一定程度上保证提交的事务已经传给了至少一个备库。
| 1、一个事务操作的完成需要记完两份日志,即主从的binlog是同步的 |
| 半同步复制,当主库每提交一个事务后,不会立即返回,而是等待其中一个从库接收到Binlog并成功写入Relay-log中才返回客户端,所以这样就保证了一个事务至少有两份日志,一份保存在主库的Binlog,另一份保存在其中一个从库的Relay-log中,从而保证了数据的安全性和一致性。 |
| |
| 2、半同步即并非完全同步 |
| 半同步复制的“半”体现在,虽然主从库的Binlog是同步的,但主库不会等待从库的sql线程执行完Relay-log后才返回,而是确认从库的io线程接收到Binlog,达到主从Binlog同步的目的后就返回了,所以从库的数据对于主库来说还是有延时的,这个延时就是从库sql线程执行Relay-log的时间。所以只能称为半同步。 |
| |
| 3、半同步复制超时则会切换回异步复制,正常后则切回半同步复制 |
| 在半同步复制时,如果主库的一个事务提交成功了,在推送到从库的过程当中,从库宕机了或网络故障,导致从库并没有接收到这个事务的Binlog,此时主库会等待一段时间(这个时间由rpl_semi_sync_master_timeout的毫秒数决定),如果这个时间过后还无法推送到从库,那MySQL会自动从半同步复制切换为异步复制,当从库恢复正常连接到主库后,主库又会自动切换回半同步复制。 |
半同步模式是作为MySQL5.5的一个插件来实现的,主从库使用的插件不一样
| mysql> select @@have_dynamic_loading; |
| +------------------------+ |
| | @@have_dynamic_loading | |
| +------------------------+ |
| | YES | |
| +------------------------+ |
| 1 row in set (0.00 sec) |
| |
| ls /usr/lib64/mysql/plugin/ | grep semisync |
| |
| |
| |
| 主库> install plugin rpl_semi_sync_master soname 'semisync_master.so'; |
| |
| 从库> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; |
| |
| |
| select * from mysql.plugin; |
主库
| |
| mysql> set global rpl_semi_sync_master_enabled=1; |
| |
| mysql> set global rpl_semi_sync_master_timeout=30000; |
| |
| |
| [root@db01 ~] |
| |
| [mysqld] |
| rpl_semi_sync_master_enabled=1 |
| rpl_semi_sync_master_timeout=1000 |
从库
| |
| mysql> set global rpl_semi_sync_slave_enabled=1; |
| |
| |
| mysql> stop slave io_thread; |
| mysql> start slave io_thread; |
| |
| |
| [root@mysql-db02 ~] |
| |
| |
| [mysqld] |
| rpl_semi_sync_slave_enabled =1 |
| mysql>show status like '%semi_sync'; |
| |
| 在输出信息中,我们重点关注三个参数: |
| |
| |
| rpl_semi_sync_master_status OFF/ON |
| |
| |
| rpl_semi_sync_master_yes_tx [number] |
| |
| |
| rpl_semi_sync_master_no_tx [number] |
实际情况中,企业中的环境可能有很多,如下所示
| (1)、只是一台服务器 |
| |
| (2)、没有真实的流量 |
| |
| (3)、连的是线上数据库 |
| |
| 疑问:如果有一个待上线需求,需要改动数据库表结构,怎么处理? |
| |
| 先把预发布环境使用的数据库切换为测试环境使用的数据库 |
| 然后有针对性的测试下数据库的变更是否会影响线上当前代码程序的运行,测试通过后再上线 |
| (1)、1台或多台线上主机 |
| |
| (2)、链接的是线上数据库 |
| |
| (3)、真实流量 |
| |
| 灰度发布,又称金丝雀发布。 |
| 金丝雀发布这一术语源于煤矿工人把笼养的金丝雀带入矿井的传统。矿工通过金丝雀来了解矿井中一氧化碳的浓度,如果一氧化碳的浓度过高,金丝雀就会中毒,从而使矿工知道应该立刻撤离。 |
| |
| 灰度发布发生在预发布环境之后,生产环境之前。 |
| 对应到软件开中,则是指在发布新的产品特性时通过少量的用户试点确认新特性没有问题,确保无误后推广到更大的用户使用群体。 |
| |
| 生产环境一般会部署在多台机器上,以防某台机器出现故障,这样其他机器可以继续运行,不影响用户使用。灰度发布会发布到其中的几台机器上,验证新功能是否正常。如果失败,只需回滚这几台机器即可。 |
| 沙盒环境又称测试环境和开发环境,是提供给开发者开发和测试用的环境。 |
| 沙盒通常严格控制其中的程序所能访问的资源,比如,沙盒可以提供用后即回收的磁盘及内存空间。在沙盒中,网络访问、对真实系统的访问、对输入设备的读取通常被禁止或是严格限制。 |
| |
| 也就是说所谓的沙盒测试就是在产品未上线前在内部环境或网络下进行的测试,此时在正常的线上环境是无法看到或查询到该产品或项目的,只有产品在测试环境下无问题上传到生产环境之后,用户才能看到该产品或功能 |
| 除了生产和预发布其他的环境都是虚拟机测试用的 |
| |
| 测试环境有很多游戏,我就想一个从库同步一种游戏,还有合服,建新服,其实就是一个库或者一个表而已. |
考虑到环境过多为了节省资源,我们会有只同步某一个库的需求。这就用到了黑名单与白名单
| 不记录黑名单列出的库的二进制日志 |
| |
| |
| replicate-ignore-db=test |
| replicate-ignore-table=test.t1 |
| replicate-wild-ignore-table=test.t% 支持通配符,t开头的表 |
| |
| 注意: |
| replicate-ignore-table依赖参数replicate-ignore-db |
| 即如果想忽略某个库下的某张表,需要一起配置 |
| replicate-ignore-db=test |
| replicate-ignore-table=test.t1 |
| 只执行白名单中列出的库或者表的中继日志 |
| |
| |
| replicate-do-db=test |
| replicate-do-table=test.t1 |
| replicate-wild-do-table=test.t% |
| |
| 注意: |
| replicate-do-table依赖参数replicate-do-db |
| 即如果想只接收某个库下的某张表,需要一起配置 |
| replicate-do-db=test |
| replicate-do-table=test.t1 |
| |
| 在主库上设置白名单:只记录白名单设置的库或者表、相关的SQL语句到binlog中 |
| 在主库上设置黑名单:不记录黑名单设置的库或者表、相关的SQL语句到binlog中 |
| |
| |
| IO线程一定会拿到所有的binlog,但 |
| |
| 如果在从库上设置白名单:SQL线程只执行白名单设置的库或者表相关的SQL语句 |
| |
| 如果在从库上设置黑名单:SQL线程不执行黑名单设置的库或者表相关的SQL语句 |
| |
| A slave with the same server_uuid/server_id as this slave has connected to the master; |
| |
| 如果server_id相同,请修改/etc/my.cnf中server_id的配置 |
| |
| 如果server_uuid相同,请删除auto.cnf文件(auto.cnf文件在/etc/my.cnf中datadir配置的目录下),然后重启数据库,数据库会重新生成server_uuid和auto.cnf文件 |