Mysql作为一个流行的数据库,对于怎么样构建高性能、高可用、可扩展的MySQL集群,的确是一个讨论很多的话题。方案多种多样,看到知乎上面的https://www.zhihu.com/question/21307639,这些方案应该说都有道理,但是都有缺点,由于CAP的原因,很难鱼和熊掌前的兼得。把二楼的回答稍作修改摘录如下:
1. 做读写分离,关于这方面的原因解释太多次数(增加技术复杂度、可能导致读到落后的数据等),只说一点:99.8%的业务场景没有必要做读写分离,只要做好数据库设计优化 和配置合适正确的主机即可,一般不适用。
2. Keepalived+MySQL --确实有脑裂的问题,还无法做到准确判断mysqld是否HANG的情况;DRBD+Heartbeat+MySQL --同样有脑裂的问题,还无法做到准确判断mysqld是否HANG的情况,且DRDB是不需要的,增加反而会出问题;
3. MySQL Proxy -- 不错的项目,可惜官方半途夭折了,不建议用,无法高可用,是一个写分离;
4. MySQL Cluster -- 社区版本不支持NDB是错误的言论,商用案例确实不多,主要是跟其业务场景要求有关系,这几年发展有点乱不过现在已经上正规了、对网络要求高;
5. MySQL + MHA -- 可以解决脑裂的问题,需要的IP多,小集群是可以的,但是管理大的就麻烦,其次MySQL + MMM 的话且坑很多,有MHA就没必要采用MMM建议:1.若是双主复制的模式,不用做数据拆分,那么就可以选择MHA或 Keepalive 或 heartbeat2.若是双主复制,还做了数据的拆分,则可以考虑采用Cobar;3.若是双主复制+Slave,还做了数据的拆分,需要读写分类,可以考虑Amoeba;上述所有的内容都要依据公司内部的业务场景、数据量、访问量、并发量、高可用的要求、DBA人群的数量等 综合权衡。
没有最好的方案,只有挑当前最适用的方案。这里来实践下Mysql双机互为热备方案。在进行讨论的时候一定要考虑到很多的因素,其中在各种环境下应用的时候需要格外的引起注意。按照上面说的复制模式,一般有2种方案。1,双机热备它的工作原理是使用两台服务器,一台作为主服务器(Master或者Active),运行应用系统来提供服务。另一台作为备机,安装完全一样的应用系统,但处于待机状态(Slave或者叫Standby)。当Master服务器出现故障时,通过软件诊测将Slave机器激活,保证应用在短时间内完成恢复正常使用。2,另外一种形式就是,双机互备方式则是在双机热备的基础上,两个相对独立的应用在两台机器同时运行,但彼此均设为备机,当某一台服务器出现故障时,另一台服务器可以在短时间内将故障服务器的应用接管过来,从而保证了应用的持续性,这种方式实际上是双机热备方案的一种应用。
Mysql的复制原理,复制方案,网络上有很多介绍,具体不细说。这里以CentOS7 上面安装Server version: 5.7.20-log MySQL Community Server为例,来实操下。
A机器:192.168.37.128
B机器:192.168.37.129
分别安装好Mysql。A机器修改配置文件/etc/my.cnf
server-id = 1
log-bin=mysql-bin
binlog-do-db = test
binlog-do-db = dbtest
binlog-ignore-db = mysql
#主-主形式需要多添加的部分
log-slave-updates
sync_binlog = 1
auto_increment_offset = 1
auto_increment_increment = 2
replicate-do-db = test
replicate-do-db = dbtest
B机器修改配置文件/etc/my.cnf
server-id = 2
log-bin=mysql-bin
#master-slave need
replicate-do-db = test
replicate-do-db = dbtest
replicate-ignore-db = mysql,information_schema,performance_schema
binlog-do-db = test
binlog-do-db = dbtest
binlog-ignore-db = mysql
log-slave-updates
sync_binlog = 1
auto_increment_offset = 2
auto_increment_increment = 2
各个配置项的意义是:
server-id:ID值唯一的标识了复制群集中的主从服务器,因此它们必须各不相同。
log-bin:表示打开binlog,打开该选项才可以通过I/O写到Slave的relay-log,也是可以进行replication的前提。
binlog-do-db:表示需要记录二进制日志的数据库。如果有多个数据可以用逗号分隔,或者使用多个binlog-do-dg选项。
binglog-ingore-db:表示不需要记录二进制日志的数据库,如果有多个数据库可用逗号分隔,或者使用多binglog-ignore-db选项。
replicate-do-db:表示需要同步的数据库,如果有多个数据可用逗号分隔,或者使用多个replicate-do-db选项。
replicate-ignore-db:表示不需要同步的数据库,如果有多个数据库可用逗号分隔,或者使用多个replicate-ignore-db选项。
master-connect-retry:表示从服务器与主服务器的连接没有成功,则等待n秒(s)后再进行管理方式(默认60s)。如果从服务器存在mater.info文件,它将忽略些选项。
log-slave-updates:配置从库上的更新操作是否写入二进制文件,如果这台从库,还要做其他从库的主库,那么就需要打这个参数,以便从库的从库能够进行日志同步。
slave-skip-errors:在复制过程,由于各种原因导致binglo中的sql出错,默认情况下,从库会停止复制,要用户介入。可以设置slave-skip-errors来定义错误号,如果复制过程中遇到的错误是定义的错误号,便可以路过。如果从库是用来做备份,设置这个参数会存在数据不一致,不要使用。如果是分担主库的查询压力,可以考虑。
sync_binlog=1 Or N
sync_binlog的默认值是0,MySQL不会同步到磁盘中去。这样的话,Mysql依赖操作系统来刷新二进制日志binary
log,就像操作系统刷新其他文件的机制一样。因此如果操作系统或机器(不仅仅是Mysql服务器)崩溃,有可能binlog中最后的语句丢失了。要想防止这种情况,可以使用sync_binlog全局变量,使binlog在每N次binlog写入后与硬盘同步。当sync_binlog变量设置为1是最安全的,因为在crash崩溃的情况下,你的二进制日志binary
log只有可能丢失最多一个语句或者一个事务。
即使sync_binlog设置为1,出现崩溃时,也有可能表内容和binlog内容之间存在不一致性。如果使用InnoDB表,Mysql服务器处理COMMIT语句,它将整个事务写入binlog并将事务提交到InnoDB中。如果在两次操作之间出现崩溃,重启时,事务被InnoDB回滚,但仍然存在binlog中。可以用-innodb-safe-binlog选项来增加InnoDB表内容和binlog之间的一致性。
auto_increment_offset和Auto_increment_increment
启动数据库:systemctl start mysqld,然后登陆mysql控制台,mysql -u root -p
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; #修改root密码,如果不能修改,可以执行下面的试试
set global validate_password_length=1; #设置密码长度,一般不要执行
set global validate_password_policy=0; #设置密码策略,一般不要执行
服务器A和服务器B互为主从,需要建立一个同步用户,在控制台执行 grant replication slave on *.* to 'replicate'@'%' identified by '123456';
使用show master statusG分别查看AB机器的状态:
mysql> show master statusG;
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 604
Binlog_Do_DB: test,dbtest
Binlog_Ignore_DB: mysql
mysql> show master statusG;
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 604
Binlog_Do_DB: test,dbtest
Binlog_Ignore_DB: mysql
通过SQL语句在B上指定master为A:
change master to
master_host='192.168.37.128',master_user='replicate',master_password='123456',master_log_file='
mysql-bin.000001',master_log_pos=604;
同样在A上执行(当然log_file和log_pos要看上面的情况,可能不同):
change master to
master_host='192.168.37.129',master_user='replicate',master_password='123456',master_log_file='
mysql-bin.000001',master_log_pos=604;
然后在两台机器上启动slave io-thread,检查slave的状态:
mysql>start slave;
mysql>show slave statusG;
当下面两项值均为Yes,则表示设置slave服务器成功。
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
如果出错,可以查看mysql的日志,默认的是/var/log/mysqld.log,遇到下面这个错误:
Last_IO_Errno: 1236
Last_IO_Error: Got fatal error 1236
from master when reading data from binary log: 'Could not find first log
file name in binary log index file'
百度了半天,最后通过执行 flush logs; 这样master_log_file和master_log_pos都会变化。也要检查CHANGE MASTER语句到底是在哪台机器上执行,不然搞错了,会遇到各种奇怪的问题。
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=154;
然后重启就解决了。遇到连接的错误:
2018-01-02T10:26:18.587050Z 4 [ERROR] Slave I/O for channel '': error
connecting to master 'replicate@192.168.37.128:3306' - retry-time: 60
retries: 39, Error_code: 1130
2018-01-02T10:27:18.589837Z 4 [ERROR]
Slave I/O for channel '': error connecting to master
'replicate@192.168.37.128:3306' - retry-time: 60 retries: 40,
Error_code: 1130
考虑下是不是授权不对,没有从远程机器访问的权限: grant replication slave on *.* to 'replicate'@'%' identified by '123456'; ###注意%号是所有机器都可以访问
排除错误,直到show slave statusG;看到Slave_IO_Running和Slave_SQL_Running都为YES,且
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
说明2台机器互为热备了,下面开始检查下:
use dbtest;
CREATE TABLE userinfo( id INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(32) );
INSERT INTO userinfo(NAME) VALUES ('aaa');
SELECT * FROM userinfo;
可以看到,在一台机器上插入数据,另外一台机器上面也能查询到。
就像网络上说的,这种方案也是很多地方有坑。如果遇到各种异常情况,数据不能同步,那就麻烦了。
我在A上插入一条记录,由于开始没有同步复制到B,删除那条记录的时候结果后面就出现错误:
Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute
Delete_rows event on table dbtest.tb_user; Can't find record in
'tb_user', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the
event's master log mysql-bin.000005, end_log_pos 711
参考这个文章http://blog.51cto.com/liuer/1313733,解决了问题。
stop slave;
set global sql_slave_skip_counter=1;
start slave;
show slave statusG;
要是实际生产,可能遇到的问题更多。参考文章http://blog.csdn.net/steven_liwen/article/details/53423699。还是那句话,没有最好的,只有最适用的。
当然上面只是做了互为主备的配置,没有配置keepalived或者Heartbeat,需要对外访问的,可以参照前面一篇博文来解决。