主从同步原理:
- Master中的数据改变会记录二进制日志(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看)
从服务器上生成两个线程,一个I/O线程,一个SQL线程
- i/o线程去请求主库的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中;
主库会生成一个 log dump线程,用来给从库 i/o线程传binlog; - SQL 线程,会读取relay log文件中的日志,并解析成具体操作,来实现主从的操作一致,而最终数据一致;
- i/o线程去请求主库的binlog,并将得到的binlog日志写到relay log(中继日志) 文件中;
半同步:对于一主多从的mysql架构中,主服务器接收到任意一台从服务器的数据同步完成,则认为数据同步成功。
数据库架构四大问
Q:主从架构中,不使用MySQL代理中,主的负责写,从负责读。
A:在LAMP的架构中,在php的代码上调用查询数据库的接口时,指向从服务器,如果需要插入数据,执行主服务器。
缺点:如果从服务器有多个,php代码写入比较困难。如果有多个从服务器,用php代码无法实现,真正的轮循
Q:如果想解决的上面的帮办,该怎么办。
A:使用MySQL主主同步。
双主模型:无法减轻写操作,第一台写了,第二台必定要同步数据,
如果多个客户端在两个主服务器上,同时写数据,会到值数据冲突,例如,表中的id是自动增长的,主、备都插入数据,会到导致主键冲突。
Q:主主架构无法解决IO的负载,如果业务足够大会导致主服务器上的消耗比较大。该如何解决。
A1:换更好的服务器,这当然是老板不愿意看到的。
A2:根据公司的业务,将不同的库方在不同的服务器上,(垂直拆分)如果拆分之后,如果有一个表的数据过大导致查询数据缓慢,可以对表进行拆分(水平拆分)
Q:这样IO的问题是解决了,但是新的问题又出现了,如果如果一个表中有1kw数据,分不到10台服务器上,如何才能查询到对应的数据,如果逐个查询,查询结果太过缓慢。
A:可以在这10台服务器前面增加路由,路由能根据客户的查询分配到后端的10台服务器,并将结果汇总发送给客户。
MySQL 5.5 之前主从同步比较简单
MySQL5.6 之后使用gtid(全局事务号)使mysql的复制更加安全,不会在多事务并行是产生混乱。mlti-thread replication(多线程复制)
配置MySQL复制基本步骤
master上配置
启动二进制日志
log-bin=master-bin log-bin-index=master-bin.index
选择一个唯一的serverID
server-id={0-25532}
创建具有复杂权限的用户
replication slave 从主服务器上的二进制日志中复制事件的权限 replication client 从主服务器上火相关信息的权限
slave上配置
启用中继日志
relay-log=relay-log relay-log-index=
启动二进制日志(可选,如果只作为从服务器,可以关闭,但是如果作为从服务器的中转需要开启)
log-bin=master-bin log-bin-index=master-bin.index
选择一个唯一的serverID
server-id={0-25532}
连接至主服务器。并开始复制数据。
mysql> change master to master_host='',master_port='',master_log_file='',master_log_pos='',master_user='',master_password=''; mysql> start slave; 或者 mysql> start slave IO_thread; mysql> start slave SQL_thread;
实操
master上操作
修改配置文件/etc/my.cnf
[root@localhost ~]# vim /etc/my.cnf
# 设置Innodb的存储引擎每个表使用一个存储空间
innodb_file_per_table=1
# 开启二进制日志,并设置二进制日志的所有名称
log-bin=master-bin
log-bin-index=master-bin.index
# 设置二进制日志的格式是混合模式
binlog_format=mixed
# 设置一个唯一的server-id
server-id= 1
重启mysql服务
[root@localhost ~]# service mysqld restart
Shutting down MySQL. SUCCESS!
Starting MySQL.. SUCCESS!
授权从服务器拷贝二进制日志的用户
mysql> grant replication slave on *.* to 'repluser'@'192.168.10.144' identified by '123';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
slave服务器
注意:主从同步,如果主服务器上有数据,需要先将主服务器上的数据全部同步到从服务器上。
修改从服务器的配置文件
[root@localhost ~]# vim /etc/my.cnf
innodb_file_per_table=1
# 开启relay-log
relay-log=relay-log
relay-log-index=relay-log.index
# 只有一个从服务器,可以不用记录二进制日志。如果作为二进制日志的从服务器需要开启二进制日志。
#log-bin=slave-bin
#log-bin-index=slave-bin.index
binlog_format=mixed
# 设置服务器的ID号,必须和主服务器不同。
server-id= 2
重启mysql服务
[root@localhost ~]# service mysqld restart
Shutting down MySQL. SUCCESS!
Starting MySQL.. SUCCESS!
在master上查看当前的二进制日志和站点
mysql> show master status;
+-------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-bin.000001 | 338 | | |
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
在从服务器上设置连接主服务器的用户名、密码、二进制日志、二进制日志的站点。
mysql> change master to master_host='192.168.10.145',master_user='repluser',master_password='123',master_log_file='master-bin.000001',master_log_pos=338;
Query OK, 0 rows affected (0.06 sec)
查看从服务器的状态
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 192.168.10.145 主服务器的主机地址
Master_User: repluser 连接主服务器同步的用户名
Master_Port: 3306
Connect_Retry: 60 如果连接不上每隔60s重试一次
Master_Log_File: master-bin.000001 当前读取的主服务器的二进制位置
Read_Master_Log_Pos: 338 主服务器的二进制日志位置
Relay_Log_File: relay-log.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: master-bin.000001
Slave_IO_Running: No 从服务器的IO进程
Slave_SQL_Running: No 从服务器的SQL进程
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 338 # 执行语句到达的站点
Relay_Log_Space: 107
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL #从服务器比至服务器慢的时间
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 0
1 row in set (0.00 sec)
启动从服务器,启动IO、SQL进程
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
确保从服务器无法修改,需要修改read_only
修改配置文件/etc/my.cnf
read_only=on # 对于具有super权限的用户不生效
重启mysql服务
[root@localhost ~]# service mysqld restart
Shutting down MySQL. SUCCESS!
Starting MySQL.. SUCCESS!
mysql> show variables like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only | ON |
+---------------+-------+
1 row in set (0.00 sec)
重启mysql服务之后,从服务器的SQL、IO进程正常运行
[root@localhost ~]# cd /mydata/data/
# master.info中记录了连接主服务器的用户名、密码、端口、二进制日志问、二进制文件站点
[root@localhost data]# cat master.info
18
master-bin.000001
423
192.168.10.145
repluser
123
3306
60
0
# relag-log.info中记录了具体使用的relay-log,以及现在的站点,还有主服务器的二进制日志以及二进制日志的站点。
[root@localhost data]# cat relay-log.info
./relay-log.000004
254
master-bin.000001
423
为确保事务安全在master服务器上设置sync_binlog
修改配置文件/etc/my.cnf
中sync_binlog日志,
sync_binlog=1 # 保证主服务器上的事务提交之后,能第一时间写到二进制日志中确保主服务器中的数据同步到从服务器上,防止主服务器的 事务提交之后,没有写入二进制日志,中途主服务器挂掉,事务执行的数据无法同步。
[root@localhost ~]# service mysqld restart
Shutting down MySQL. SUCCESS!
Starting MySQL.. SUCCESS!
测试
主服务器上创建数据库
mysql> create database studb;
Query OK, 1 row affected (0.06 sec)
从服务器上查看数据库。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| studb |
| test |
+--------------------+
5 rows in set (0.06 sec)
半同步复制
注:在同步的基础上配置。
使用源码安装的mysql中,有Google提供的半同步模块。在安装目录/usr/local/mysql/lib/plugin
中有semisync_slave.so和semisync_master.so模块,semisync_master.so 是主服务器上需要安装的模块,semisync_slave.so是从服务器上需要安装的模块。
[root@localhost plugin]# pwd
/usr/local/mysql/lib/plugin
[root@localhost plugin]# ls
adt_null.so auth_socket.so daemon_example.ini ha_example.so libdaemon_example.so qa_auth_client.so qa_auth_server.so semisync_slave.so
auth.so auth_test_plugin.so debug ha_federated.so mypluglib.so qa_auth_interface.so semisync_master.so
主服务器上操作
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.29 sec)
mysql> show global variables like '%rpl%';
+------------------------------------+-------+
| Variable_name | Value |
+------------------------------------+-------+
| rpl_recovery_rank | 0 |
| rpl_semi_sync_master_enabled | OFF | #设置是否开启半同步复制
| rpl_semi_sync_master_timeout | 10000 | #设置超时时间
| rpl_semi_sync_master_trace_level | 32 | # 设置追踪级别
| rpl_semi_sync_master_wait_no_slave | ON | # 没有从节点是否等待
+------------------------------------+-------+
5 rows in set (0.07 sec)
mysql> set global rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.05 sec)
mysql> set global rpl_semi_sync_master_timeout=1000;
Query OK, 0 rows affected (0.00 sec)
从服务器上操作
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
Query OK, 0 rows affected (0.09 sec)
mysql> show global variables like '%rpl%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_recovery_rank | 0 |
| rpl_semi_sync_slave_enabled | OFF |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
3 rows in set (0.00 sec)
mysql> set global rpl_semi_sync_slave_enabled=1;
Query OK, 0 rows affected (0.00 sec)
在主服务器上查询当前的数据库的状态
mysql> show global status like '%%rpl%';
+--------------------------------------------+-------------+
| Variable_name | Value |
+--------------------------------------------+-------------+
| Rpl_semi_sync_master_clients | 0 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
| Rpl_status | AUTH_MASTER |
+--------------------------------------------+-------------+
15 rows in set (0.01 sec)
从服务器上重新启动IO进程
mysql> stop slave io_thread;
Query OK, 0 rows affected (0.01 sec)
mysql> start slave io_thread;
Query OK, 0 rows affected (0.00 sec)
测试
测试一:在主服务器上创建表
mysql> create table tb1 (id int);
Query OK, 0 rows affected (0.23 sec)
测试二: 停止从服务器上的IO进程
停止从服务器上的IO进程
mysql> stop slave IO_THREAD;
Query OK, 0 rows affected (0.01 sec)
在主服务器上创建一个表
mysql> create table tb4 (id int);
Query OK, 0 rows affected (1.08 sec)
总结:使用半同步复制,如果从服务器挂掉,主服务器会等待从服务器超过预先设置的超时时间。
mysql> create table tb5 (id int);
Query OK, 0 rows affected (0.06 sec)
总结:如果半同步复制失败,会自动切换到异步复制。