MySQL Amoeba 主从复制与读写分离

1、MySQL主从复制原理

Mysql内建的复制功能是构建大型,高性能应用程序的基础。将Mysql的数据分布到多个系统上去,这种分布的机制,是通过将Mysql的某一台主机的数据复制到其它主机(slaves)上,并重新执行一遍来实现的。复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。

请注意当你进行复制时,所有对复制中的表的更新必须在主服务器上进行。否则,你必须要小心,以避免用户对主服务器上的表进行的更新与对从服务器上的表所进行的更新之间的冲突。

1.1 MySQL支持复制的类型

MySQL支持复制的类型也就是二进制日志格式:

  1. 基于语句的复制: 在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高
  2. 基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持
  3. 混合类型的复制: 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。

1.2 复制的特点

  • 数据分布
  • 负载均衡
  • 备份
  • 高可用性

1.3 复制过程如下

Mysql的复制(replication)是一个异步或半同步的复制,从一个Mysql 实例(称之为Master)复制到另一个Mysql 实例(称之Slave)。实现整个复制操作主要由三个进程完成的,其中两个进程在Slave(Sql进程和IO进程),另外一个进程在 Master(IO进程)上。
要实施复制,首先必须打开Master端的binary log(bin-log)功能,否则无法实现。因为整个复制过程实际上就是Slave从Master端获取该日志然后再在自己身上完全顺序的执行日志中所记录的各种操作。
具体过程如下:

image-20191116173854496

  1. master记录二进制日志。在每个事务更新数据完成之前,master在二日志记录这些改变。MySQL将事务串行的写入二进制日志,即使事务中的语句都是交叉执行的。在事件写入二进制日志完成后,master通知存储引擎提交事务。
  2. slave将master的binary log拷贝到它自己的中继日志。首先,slave开始一个工作线程——I/O线程。I/O线程在master上打开一个普通的连接,然后开始binlog dump process。Binlog dump process从master的二进制日志中读取事件,如果已经跟上master,它会睡眠并等待master产生新的事件。I/O线程将这些事件写入中继日志。
  3. SQL slave thread(SQL从线程)处理该过程的最后一步。SQL线程从中继日志读取事件,并重放其中的事件而更新slave的数据,使其与master中的数据一致。只要该线程与I/O线程保持一致,中继日志通常会位于OS的缓存中,所以中继日志的开销很小。

2、读写分离原理

简单来说,读写分离(如下图:)就是只在主服务器上写,只在从服务器上读,基本的原理是让主数据库处理事务性查询,而从数据库处理select查询,数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库。

img

3、3种实现思路关键技术

3.1 基于程序代码内部实现

在代码中根据select、insert进行路由分类,这类方法也是目前生产环境应用最广泛的,优点是性能较好,因为在程序代码中实现,不需要增加额外的设备作为硬件开支;缺点是需要开发人员来实现,运维人员无从下手。

3.2 基于中间代理层实现

代理一般位于客户端和服务器之间,代理服务器接到客户端请求后通过判断后转发到后端数据库,有两个代表性程序。

  1. MySQL-Proxy:MySQL-Proxy为MySQL开源项目,通过其他自带的lua脚本进行SQL判断,虽然是MySQL官方产品,但是MySQL官方并不建议将MySQL-Proxy用到生产环境。
  2. Amoeba:由陈思儒开发,作者曾就职于阿里巴巴。该程序由java语言进行开发,阿里巴巴将其用于生产环境。它不支持事务和存储过程。

3.3 驱动实现

在驱动层使用Mysql提供的主从库访问驱动,直接与数据库连接驱动耦合,扩展性弱,目前还未做原型尝试。

4、Amoeba是什么

Amoeba(变形虫)项目,该开源框架于2008年开始发布一款Amoeba for Mysql软件。详细资料可参阅Amoeba官方文档(需翻墙)

4.1 Amoeba能做什么

Amoeba致力于MySQL的分布式数据库前端代理层,它主要在应用层访问MySQL的时候充当SQL路由功能,专注于分布式数据库代理层 (Database Proxy)开发。座落与 ClientDB Server(s)之间,对客户端透明。具有负载均衡高可用性SQL过滤读写分离、可路由相关的到目标数据库、可并发请求多台数据库合并结果。 通过Amoeba你能够完成多数据源的高可用、负载均衡、数据切片的功能。

4.2 Amoeba不能做什么

既然知道Amoeba能为我们解决什么问题,也要做到Amoeba不擅长的事情。这样在具体项目技术方案选择时,方能权衡考虑。Amoeba对于以下几点暂时无能为力:

  1. 目前还不支持事务;
  2. 暂时不支持存储过程,官方说近期会支持;
  3. 不适合从Amoeba导数据的场景或者对大数据量查询的query并不合适,比如一次请求返回10w以上甚至更多数据的场合;
  4. 暂时不支持分库分表,amoeba目前只做到分数据库实例,每个被切分的节点需要保持库表结构一致。

若实际项目中所需要的功能正是Amoeba的短板,建议使用Mysql Proxy作为中间件,或者在应用层通过程序控制数据源,手动实现数据库读写分离。

5、部署MySQL主从复制并实现读写分离

环境描述:

主机操作系统IP地址主要软件
MasterCentOS 6.5 x86_64192.168.1.101cmake-2.8.6.tar.gzmysql-5.5.22.tar.gz
Slave1CentOS 6.5 x86_64192.168.1.102cmake-2.8.6.tar.gzmysql-5.5.22.tar.gz
Slave2CentOS 6.5 x86_64192.168.1.103cmake-2.8.6.tar.gzmysql-5.5.22.tar.gz
AmoebaCentOS 6.5 x86_64192.168.1.110amoeba-mysql-binary-2.2.0.tar.gzjdk-6u14-linux-x64.bin
客户端CentOS 6.5 x86_64192.168.1.111

5.1 部署MySQL主从复制

5.1.1 部署NTP时间同步环境

①安装,并配置NTP (在任意位置添加以下红色部分)

1
2
3
4
[root@centos5 ~]# yum install -y ntp
[root@centos5 ~]# vim /etc/ntp.conf
server 127.127.1.0
fudge 127.127.1.0 stratum 8

②启动服务,并添加防火墙规则

1
2
3
[root@centos5 ~]# service ntpd restart
Starting ntpd: [ OK ]
[root@centos5 ~]# iptables -I INPUT -p udp --dport 123 -j ACCEPT

5.1.2 安装MySQL数据库

(在Master、Slave1、Slave2上安装)

①同步时间

1
2
[root@centos1 ~]# ntpdate 192.168.1.101
31 Oct 10:08:51 ntpdate[2162]: adjust time server 192.168.1.101 offset -0.078096 sec

②编译安装MySQL
安装依赖包

1
[root@centos1 ~]# yum  -y install ncurses-devel

编译安装cmake

1
2
3
4
[root@centos1 ~]# tar xf cmake-2.8.6.tar.gz 
[root@centos1 ~]# cd cmake-2.8.6
[root@centos1 cmake-2.8.6]# ./configure
[root@centos1 cmake-2.8.6]# gmake && gmake install

编译安装mysql

1
2
3
4
[root@centos1 ~]# tar xf mysql-5.5.22.tar.gz 
[root@centos1 ~]#cd mysql-5.5.22
[root@centos1 mysql-5.5.22]# cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_EXTRA_CHARSETS=all -DSYSCONFDIR=/etc
[root@centos1 mysql-5.5.22]# make && make install

②优化调整

1
2
3
4
5
6
7
8
[root@centos1 mysql-5.5.22]# cp support-files/my-medium.cnf /etc/my.cnf
[root@centos1 mysql-5.5.22]# cp support-files/mysql.server /etc/rc.d/init.d/mysqld
[root@centos1 mysql-5.5.22]# chmod +x /etc/rc.d/init.d/mysqld
[root@centos1 mysql-5.5.22]# chkconfig --add mysqld
[root@centos1 mysql-5.5.22]# echo "PATH=$PATH:/usr/local/mysql/bin" >> /etc/profile
[root@centos1 mysql-5.5.22]# source /etc/profile
[root@centos1 mysql-5.5.22]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/mysql/bin

③初始化数据库

1
2
3
4
[root@centos1 mysql-5.5.22]# groupadd mysql
[root@centos1 mysql-5.5.22]# useradd -M -s /sbin/nologin mysql -g mysql
[root@centos1 mysql-5.5.22]# chown -R mysql:mysql /usr/local/mysql/
[root@centos1 mysql-5.5.22]# /usr/local/mysql/scripts/mysql_install_db --basedir=/usr/local/mysql/ --datadir=/usr/local/mysql/data/ --user=mysql

④启动MySQL服务,创建数据库密码

1
2
3
[root@centos1 mysql-5.5.22]# service mysqld start
Starting MySQL.. [ OK ]
[root@centos1 mysql-5.5.22]# mysqladmin -u root password 'pwd123'

到此Master、Slave1、Slave2的MySQL数据库都已经安装完成

5.1.3 配置MySQL Master主服务器

①在/etc/my.cnf中修改和增加以下内容

1
2
3
4
[root@centos1 ~]# vim /etc/my.cnf
server-id = 11 //修改
log-bin=master-bin //修改
log-slave-updates=true //添加

②重启MySQL服务

1
2
3
[root@centos1 ~]# service mysqld restart
Shutting down MySQL. [ OK ]
Starting MySQL.. [ OK ]

③登录MySQL数据库,给从服务器授权

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@centos1 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.38-log Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
1
2
mysql> grant replication slave on *.* to 'myslave'@'192.168.1.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
1
2
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
1
2
3
4
5
6
7
mysql> show master status;
+-------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| master-bin.000001 | 337 | | |
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

其中File列显示日志名,Position列显示偏移量,这两个值在后面配置从服务器的时候需要,Slave应从该点在Master上进行新的更新。

④关闭或添加防火墙规则

1
[root@centos1 ~]# iptables -I INPUT -p tcp --dport 3306 -j ACCEPT

5.1.4 配置MySQL Slave1、Slave2从服务器

①在/etc/my.cnf中修改和增加下面内容

1
2
3
4
[root@centos2 ~]# vim /etc/my.cnf
server-id = 22 //修改
relay-log=relay-log-bin //添加
relay-log-index=slave-relay-bin.index //添加

这里需要注意server-id不能与主服务器相同,另一个从服务器改为33

1
2
3
4
[root@centos2 ~]# vim /etc/my.cnf
server-id = 33 //修改
relay-log=relay-log-bin //添加
relay-log-index=slave-relay-bin.index //添加

②重启MySQL服务器

1
2
3
[root@centos2 ~]# service mysqld restart
Shutting down MySQL. [ OK ]
Starting MySQL.. [ OK ]

③登录MySQL,配置同步
按主服务器结果更改下面命令中master_log_file和master_log_pos参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@centos2 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.38-log Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
1
2
mysql> change master to master_host='192.168.1.101',master_user='myslave',master_password='123456',master_log_file='master-bin.000001',master_log_pos=337;
Query OK, 0 rows affected (0.07 sec)

④启动同步

1
2
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

⑤查看Slave状态,确保以下两个值为YES

1
mysql> show slave status\G;

*** 1. row ***
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.1.101
Master_User: myslave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-bin.000001
Read_Master_Log_Pos: 337
Relay_Log_File: relay-log-bin.000002
Relay_Log_Pos: 254
Relay_Master_Log_File: master-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
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: 337
Relay_Log_Space: 408
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: 0
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: 11
1 row in set (0.00 sec)

ERROR:
No query specified

⑥关闭或添加防火墙规则

1
[root@centos2 ~]# iptables -I INPUT -p tcp --dport 3306 -j ACCEPT

5.1.5 验证主从复制效果

①在主、从服务器上登录MySQL

1
2
3
4
5
6
7
8
9
10
11
[root@centos1 ~]# mysql -u root -p
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
+--------------------+
4 rows in set (0.00 sec)

②在主服务器上新建数据库db_test

1
2
mysql> create database db_test;
Query OK, 1 row affected (0.00 sec)

③在主从,服务器上分别查看数据库,显示数据库相同,则主从复制成功

1
mysql> show databases;

+——————–+
| Database |
+——————–+
| information_schema |
| db_test |
| mysql |
| performance_schema |
| test |
+——————–+
5 rows in set (0.00 sec)

5.2 部署MySQL读写分离

5.2.1 在主机Amoeba上安装Java环境

因为Amoeba是基于jdk1.5开发的,所以官方推荐使用jdk1.5或1.6版本,高版本不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@centos4 ~]# cp /media/jdk-6u14-linux-x64.bin /usr/local/
[root@centos4 ~]# chmod +x /usr/local/jdk-6u14-linux-x64.bin
[root@centos4 ja]# ./jdk-6u14-linux-x64.bin
//根据提示输入yes完成安装

[root@centos4 ja]# mv jdk1.6.0_14/ /usr/local/jdk1.6
[root@centos4 ja]# vim /etc/profile
[root@centos4 ja]# source /etc/profile
bash: export: `PATH+/usr/local/jdk1.6/lib:/usr/local/jdk1.6/jre/bin:/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin:/usr/local/amoeba//bin': not a valid identifier
[root@centos4 ja]# java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)
//Java环境配置成功

5.2.2 安装并配置Amoeba软件

1
2
3
4
5
[root@centos4 ~]# mkdir /usr/local/amoeba
[root@centos4 ~]# tar xf /root/amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba
[root@centos4 ~]# chmod -R 755 /usr/local/amoeba/
[root@centos4 ~]# /usr/local/amoeba/bin/amoeba
amoeba start|stop //显示此内容说明Amoeba安装成功

5.2.3 配置Amoeba读写分离,两个Slave读负载均衡

①Master、Slave1、Slave2中开放权限给Amoeba访问

1
2
3
4
5
6
7
8
9
10
11
12
[root@centos1 ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.5.38-log Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
1
2
mysql> grant all on *.* to test@'192.168.1.%' identified by '123.com';
Query OK, 0 rows affected (0.00 sec)

②编辑aomeba.xml配置文件

(修改以下红色内容,注意删除注释)

1
[root@centos4 ~]# vim /usr/local/amoeba/conf/amoeba.xml

image-20191116180544313

③编辑dbServer.xml配置文件

1
[root@centos4 ~]# vim /usr/local/amoeba/conf/dbServers.xml

image-20191116180756090

④配置无误后,可以启动Amoeba,其默认端口为tcp 8066

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@centos4 ~]# /usr/local/amoeba/bin/amoeba start&
[root@centos4 ~]# /usr/local/amoeba/bin/amoeba start&
[9] 4222
[root@centos4 ~]# log4j:WARN log4j config load completed from file:/usr/local/amoeba/conf/log4j.xml
2016-10-31 12:47:52,428 INFO context.MysqlRuntimeContext - Amoeba for Mysql current versoin=5.1.45-mysql-amoeba-proxy-2.2.0
log4j:WARN ip access config load completed from file:/usr/local/amoeba/conf/access_list.conf
2016-10-31 12:47:52,622 INFO net.ServerableConnectionManager - Amoeba for Mysql listening on 0.0.0.0/0.0.0.0:8066.
2016-10-31 12:47:52,627 INFO net.ServerableConnectionManager - Amoeba Monitor Server listening on /127.0.0.1:22289.

[root@centos4 ~]# ss -anpt | grep java
LISTEN 0 50 ::ffff:127.0.0.1:22289 :::* users:(("java",4222,54))
LISTEN 0 128 :::8066 :::* users:(("java",4222,53))
ESTAB 0 0 ::ffff:192.168.1.110:42345 ::ffff:192.168.1.103:3306 users:(("java",4222,45))
ESTAB 0 0 ::ffff:192.168.1.110:34060 ::ffff:192.168.1.101:3306 users:(("java",4222,46))
ESTAB 0 0 ::ffff:192.168.1.110:45002 ::ffff:192.168.1.102:3306 users:(("java",4222,40)

⑤关闭或添加防火墙规则

1
[root@centos4 ~]# iptables -I INPUT -p tcp --dport 8066 -j ACCEPT

5.2.4 测试

①在Client主机上,通过代理访问MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@centos5 ~]# yum -y install mysql
[root@centos5 ~]# mysql -u amoeba -p123456 -h 192.168.1.110 -P8066
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1362913826
Server version: 5.1.45-mysql-amoeba-proxy-2.2.0 Source distribution

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

②在Master上创建一个表,同步到各从服务器上,然后关掉各从服务器的Slave功能,再插入区别语句。

1
2
3
4
mysql> use db_test;
Database changed
mysql> create table zang (id int(10));
Query OK, 0 rows affected (0.05 sec)

分别在Slave1、Slave2两台从服务器上关闭slave

1
2
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)

然后在Master主服务器上创建数据

1
2
3
4
mysql> use db_test;
Database changed
mysql> insert into zang values (1);
Query OK, 1 row affected (0.01 sec)

③Slave1、Slave2从服务器上同步了表后,再手动插入其他内容
Slave1 上:

1
2
3
4
mysql> use db_test;
Database changed
mysql> insert into zang values(2);
Query OK, 1 row affected (0.01 sec)

Slave2 上:

1
2
3
4
mysql> use db_test;
Database changed
mysql> insert into zang values(3);
Query OK, 1 row affected (0.00 sec)

④测试读操作

第一次查询结果

1
2
3
4
5
6
7
mysql> select * from zang;
+------+
| id |
+------+
| 2 |
+------+
1 rows in set (0.01 sec)

第二次查询结果

1
2
3
4
5
6
7
mysql> select * from zang;
+------+
| id |
+------+
| 3 |
+------+
1 row in set (0.01 sec)

第三次查询结果

1
2
3
4
5
6
7
mysql> select * from zang;
+------+
| id |
+------+
| 2 |
+------+
1 rows in set (0.01 sec)

⑤测试写操作
在Client主机上插入一条语句

1
2
mysql> insert into zang values (4);
Query OK, 1 row affected (0.01 sec)

但在Client上查询不到,最终只有在Master上才能查看到这条语句内容,说明写操作在Master服务器上。

1
2
3
4
5
6
7
8
mysql> select * from zang;
+------+
| id |
+------+
| 1 |
| 4 |
+------+
2 rows in set (0.00 sec)

由此验证,已经实现了MySQL读写分离,目前所有的写操作都全部在Master主服务器上,用来避免数据的不同步;所有的读操作都分摊给了Slave从服务器,用来分担数据库压力。

-------------本文结束感谢您的阅读-------------