@cdmonkey
2015-01-04T15:16:22.000000Z
字数 16038
阅读 1078
数据库
以下内容来自于官方在线文档:http://docs.hexnova.com/amoeba/
Amoeba(变形虫)项目,该开源框架于2008年开始发布一款Amoeba for MySQL软件。这个软件致力于MySQL的分布式数据库前端代理层,它主要在应用层访问MySQL的时候充当SQL路由功能,专注于分布式数据库代理层开发。座落于客户端与数据库服务器之间,对客户端透明。具有负载均衡、高可用性、SQL过滤、读写分离、可路由相关的到目标数据库、可并发请求多台数据库合并结果。通过Amoeba你能够完成多数据源的高可用、负载均衡、数据切片的功能,目前已在很多企业的生产线上面使用。
Amoeba处于在客户端和数据库之间,扮演一个中介的角色,将客户端应用传递过来的SQL语句经过分析后,将写的语句交给主库执行,将读的语句路由到从库执行(当然也可以到主库读,这个完全看配置)。
Amoeba实现了简单的负载均衡(采用轮询算法)和Failover。如果配置了多个读的库,则任何一个读的库出现宕机,不会导致整个系统故障,Amoeba能自动将读请求路由到其他可用的库上,当然,写还是单点的依赖于主数据库的,这个需要通过数据库的切换,或者水平分割等技术来提升主库的可用性。
随着传统的数据库技术日趋成熟、计算机网络技术的飞速发展和应用范围的扩充,数据库应用已经普遍建立于计算机网络之上。这时集中式数据库系统表现出它的不足:集中式处理,势必造成性能瓶颈;应用程序集中在一台计算机上运行,一旦该计算机发生故障,则整个系统受到影响,可靠性不高;集中式处理引起系统的规模和配置都不够灵活,系统的可扩充性差。在这种形势下,集中式数据库将向分布式数据库发展。而Amoeba的透明、简易配置及多个优点使其成为分布式数据库代理产品中的优秀选择。
这是我们本次架构的环境:
Amoeba框架是基于Java SE1.5开发的,建议使用该版本,我们这里下载的是“jdk-6u32-linux-x64-rpm.bin”。
[root@Amoeba tools]# sh jdk-6u32-linux-x64-rpm.bin
#将会被安装到/usr/java/jdk1.6.0_32:
[root@Amoeba tools]# ll /usr/java/
total 4
lrwxrwxrwx 1 root root 16 Dec 31 11:48 default -> /usr/java/latest
drwxr-xr-x 7 root root 4096 Dec 31 11:48 jdk1.6.0_32
lrwxrwxrwx 1 root root 21 Dec 31 11:48 latest -> /usr/java/jdk1.6.0_32
下载安装Amoeba
http://sourceforge.net/projects/amoeba/files/
[root@Amoeba tools]# mkdir /usr/local/amoeba
[root@Amoeba tools]# tar zxvf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba
[root@Amoeba tools]# ll /usr/local/amoeba/
total 60
drwxr-xr-x 2 root root 4096 Dec 31 11:55 benchmark
drwxr-xr-x 2 root root 4096 Feb 29 2012 bin
-rw-r--r-- 1 root root 3976 Aug 29 2012 changelogs.txt
drwxr-xr-x 2 root root 4096 Dec 31 11:55 conf
drwxr-xr-x 3 root root 4096 Dec 31 11:55 lib
-rw-r--r-- 1 root root 34520 Aug 29 2012 LICENSE.txt
-rw-r--r-- 1 root root 2031 Aug 29 2012 README.html
配置环境变量:
[root@Amoeba ~]# vim ~/.bash_profile
PATH=$PATH:$HOME/bin:/usr/local/amoeba/bin
JAVA_HOME=/usr/java/jdk1.6.0_32
export JAVA_HOME
export PATH
#加载配置文件,立即生效:
[root@Amoeba ~]# . ~/.bash_profile
安正安装结果:
#安装完成后,你可以通过命令确定是否成功安装,当出现如下提示,表示Amoeba可以正常启动。
[root@Amoeba ~]# amoeba
amoeba start|stop
配置Amoeba的数据库读写分离主要涉及两个文件:
文件 | 说明 |
---|---|
/usr/local/amoeba/conf/dbServers.xml |
Amoeba作为数据库代理层,它一定会和很多数据库保持通信,因此它必须知道如何与由它代理的数据库进行连接,比如最基础的:主机IP、端口、Amoeba使用的用户名和密码等等,这些信息存储在该文件中。 |
/usr/local/amoeba/conf/amoeba.xml |
此文件定义了Amoeba代理的相关配置。 |
以下是配置一个DB节点,使用Amoeba做操作转发的步骤:
[root@Amoeba-A ~]# vim /usr/local/amoeba/conf/dbServers.xml
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
<!-- Each dbServer needs to be configured into a Pool, If you need to configure multiple dbServer with load balancing that can be simplified by the following configuration:
add attribute with name virtual = "true" in dbServer, but the configuration does not allow the element with name factoryConfig such as 'multiPool' dbServer -->
<!-- 数据库连接配置的公共部分:-->
<dbServer name="abstractServer" abstractive="true"> [1]
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="manager">${defaultManager}</property> [2]
<property name="sendBufferSize">64</property>
<property name="receiveBufferSize">128</property>
<!-- mysql port -->
<property name="port">3306</property>
<!-- mysql schema -->
<property name="schema">test</property>
<!-- mysql user -->
<property name="user">root</property>
<!-- mysql password -->
<property name="password">oldboy123</property>
</factoryConfig>
<poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool"> [3]
<property name="maxActive">500</property>
<property name="maxIdle">500</property>
<property name="minIdle">10</property>
<property name="minEvictableIdleTimeMillis">600000</property>
<property name="timeBetweenEvictionRunsMillis">600000</property>
<property name="testOnBorrow">true</property>
<property name="testOnReturn">true</property>
<property name="testWhileIdle">true</property>
</poolConfig>
</dbServer>
<!--下面的这部分主要定义主库,从库,以及从库连接池。这里只定义数据库地址,它们的用户及密码就是上面的公共部分设置。注意用来连接真实MySQL服务器的用户必须拥有远程连接权限。-->
<dbServer name="Master" parent="abstractServer"> <!-- 拓展数据库服务器实例,这里为定义主库 -->
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">172.16.1.19</property> <!-- 这里是主库高可用的虚拟地址 -->
</factoryConfig>
</dbServer>
<dbServer name="Slave1" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">172.16.1.17</property>
</factoryConfig>
</dbServer>
<dbServer name="Slave2" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">172.16.1.18</property>
</factoryConfig>
</dbServer>
<!-- 最后这部分是将上面定义的从库添加到一个虚拟的组里面,对外提供读的功能,同时实现负载均衡:-->
<dbServer name="multiPool" virtual="true"> [4]
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA-->
<property name="loadbalance">1</property>
<!-- Separated by commas,such as: server1,server2,server1 -->
<property name="poolNames">Slave1,Slave2</property>
</poolConfig>
</dbServer>
</amoeba:dbServers>
[1]
:这份dbServers配置文件中,我们定义了三个dbServer元素,这是第一个dbServer元素的定义。[2]
:定义了该dbServer选择的连接管理器,这里引用了amoeba.xml的配置。[3]
:dbServer下有poolConfig的元素,这个元素的属性主要配置了与数据库的连接池。[4]
:这一段其实并不需要配置,并不会影响到基本使用。以下大致介绍下此配置的含义:multiPool是一个虚拟的数据库节点,可以将这个节点配置成好几台数据库组成的数据库池。由此,你大概可以理解定义abstractServer
的原因:当我们有一个数据库集群需要管理,这个数据库集群中节点的大部分信息可能是相同的,比如:端口号、用户名、密码等等。因此通过归纳这些共性定义出的该配置模块将会极大地简化其下的dbServers
配置文件。
客户端连接Amoeba时所绑定的IP地址、端口、用户名和密码,以及IP访问限制。
[root@Amoeba-A ~]# vim /usr/local/amoeba/conf/amoeba.xml
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba:configuration xmlns:amoeba="http://amoeba.meidusa.com/">
<proxy>
<!-- service class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba" class="com.meidusa.amoeba.net.ServerableConnectionManager"> [1]
<!-- port -->
<property name="port">3306</property> [2] <!-- 为了方便应用程序访问,将端口改为3306 -->
<!-- bind ipAddress -->
<property name="ipAddress">172.16.1.30</property>
<property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory">
<property name="sendBufferSize">128</property>
<property name="receiveBufferSize">64</property>
</bean>
</property>
<property name="authenticator"> [3] <!--这里定义连接Amoeba时用来验证的用户及密码。这里的用户跟数据库的用户无关,是应用程序访问阿米巴代理连接数据库时的用户,是阿米巴的用户。-->
<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
<property name="user">oldboy</property>
<property name="password">oldboy123</property>
<property name="filter">
<bean class="com.meidusa.amoeba.server.IPAccessController">
<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>
</bean>
</property>
</bean>
</property>
</service>
<!-- server class must implements com.meidusa.amoeba.service.Service -->
<service name="Amoeba Monitor Server" class="com.meidusa.amoeba.monitor.MonitorServer">
<!-- port -->
<!-- default value: random number
<property name="port">9066</property>
-->
<!-- bind ipAddress -->
<property name="ipAddress">127.0.0.1</property>
<property name="daemon">true</property>
<property name="manager">${clientConnectioneManager}</property>
<property name="connectionFactory">
<bean class="com.meidusa.amoeba.monitor.net.MonitorClientConnectionFactory"></bean>
</property>
</service>
<runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext"> [4]
<!-- proxy server net IO Read thread size -->
<property name="readThreadPoolSize">20</property>
<!-- proxy server client process thread size -->
<property name="clientSideThreadPoolSize">30</property>
<!-- mysql server data packet process thread size -->
<property name="serverSideThreadPoolSize">30</property>
<!-- per connection cache prepared statement size -->
<property name="statementCacheSize">500</property>
<!-- query timeout (default: 60 second, TimeUnit:second) -->
<property name="queryTimeout">60</property>
</runtime>
</proxy>
<!--
Each ConnectionManager will start as thread
manager responsible for the Connection IO read , Death Detection
-->
<connectionManagerList>
<connectionManager name="clientConnectioneManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.ConnectionManager</property>
<!-- default value is avaliable Processors
<property name="processors">5</property>
-->
</connectionManager>
<connectionManager name="defaultManager" class="com.meidusa.amoeba.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.amoeba.net.AuthingableConnectionManager</property>
<!-- default value is avaliable Processors
<property name="processors">5</property>
-->
</connectionManager>
</connectionManagerList>
<!-- default using file loader -->
<dbServerLoader class="com.meidusa.amoeba.context.DBServerConfigFileLoader"> [5]
<property name="configFile">${amoeba.home}/conf/dbServers.xml</property>
</dbServerLoader>
<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter"> [6]
<property name="ruleLoader">
<bean class="com.meidusa.amoeba.route.TableRuleFileLoader">
<property name="ruleFile">${amoeba.home}/conf/rule.xml</property>
<property name="functionFile">${amoeba.home}/conf/ruleFunctionMap.xml</property>
</bean>
</property>
<property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property>
<property name="LRUMapSize">1500</property>
<property name="defaultPool">Master</property> <!-- 这里设置默认的连接池,一般设置为写操作对应的服务器池,这里即为主库 -->
<property name="writePool">Master</property>
<property name="readPool">multiPool</property>
<property name="needParse">true</property>
</queryRouter>
</amoeba:configuration>
[1]
:Service节点定义了需要启动的服务。类 | 说明 |
---|---|
com.meidusa.amoeba.net.ServerableConnectionManager |
表示这是一个Proxy Service(就目前而言)。 |
com.meidusa.amoeba.mysql.net.MysqlClientConnectionFactory |
表示这是一个MySQL Proxy Service。 |
[2]
:这里定义了之前我们所定义的MySQL Proxy Service的服务端口和主机地址。[3]
:这里配置了MySQL Proxy Service的认证器,user和passwd属性分别定义Amoeba对外提供服务的用户名和密码。你的程序或者数据库客户端需要使用该用户名和密码来通过Amoeba连接之前定义的dbServer。[4]
:runtime元素定义了一些Proxy相关的运行期配置,如客户端及数据库服务端的线程数及SQL超时时间等。[5]
:最后一部分dbServerLoader定义了dbServers.xml的位置。[6]
:queryRouter定义了规则配置及函数配置等相关文件的位置。上面表格中出现的服务器池的名称要与
dbServers.xml
中定义的内容相对应。
通过上面的步骤我们已经配置好了一台Amoeba代理服务器。接下来就可以启动服务了。
[root@Amoeba-A ~]# amoeba start
log4j:WARN log4j config load completed from file:/usr/local/amoeba/conf/log4j.xml
2015-01-04 11:38:25,041 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
2015-01-04 11:38:28,237 INFO net.ServerableConnectionManager - Amoeba for Mysql listening on /172.16.1.30:3306.
2015-01-04 11:38:28,266 INFO net.ServerableConnectionManager - Amoeba Monitor Server listening on /127.0.0.1:57115.
--------------------------
#此命令以前台的方式启动,会输出启动时的信息。在检查没有错误信息后,中断进程,并后台运行:
[root@Amoeba-A ~]# amoeba start &
我们可以使用Navicat for MySQL客户端工具进行连接测试:
通过上面的连接参数,我们可以很顺利的连接到Amoeba代理服务,进而对相应的数据库进行操作(例如这里是在test库中建了一个表),然后再登录任意一台数据库服务器进行查看,都可以看到操作结果。
至此,我们的Amoeba for MySQL代理服务器搭建成功。当然,这仅仅是第一步,因为该代理服务器存在单点问题,一旦宕机将彻底无法访问数据库,所以我们仍然需要借助于Keepalived配置Amoeba代理的高可用,即我们上面的的架构图所示。下面就开始高可用的配置操作。
同样是使用Keepalived工具,我们要在两台阿米巴代理服务器间创建高可用(Keepalived的安装及配置过程省略)。
[root@Amoeba-A ~]# ip add
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:16:f7:c5 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.30/24 brd 172.16.1.255 scope global eth0
inet 172.16.1.32/24 scope global secondary eth0
...
注意:在当前这个架构中有两套Keepalived,所以需要将两套高可用的实例名(vrrp_instance)区别开。
首先,需要修改代理服务器上的代理配置文件:
[root@Amoeba-A ~]# vim /usr/local/amoeba/conf/amoeba.xml
-----------------------
<!-- bind ipAddress -->
<property name="ipAddress">0.0.0.0</property>
<!-- 如果是两台代理高可用,那么这里的IP地址就不能够是虚拟地址。-->
<!-- 如果将该配置项保持原有的注释状态,经其他同伴测试,也是可以的。-->
其次,我们将所有配置好的配置文件拷贝到新的代理服务器Amoeba-B中,确保两台Amoeba代理服务器所有的配置文件保持一致:
[root@Amoeba-A ~]# scp -r /usr/local/amoeba/conf/ root@172.16.1.31:/usr/local/amoeba/
至此,两台高可用Amoeba代理服务器搭建完毕,客户端使用VIP进行连接即可。
在主从数据库的复制的部分,任然需要使用数据库自己的复制机制。Amoeba自身并不提供复制功能。
主库高可用我们使用Keepalived,我们首先在MySQL-01上进行安装及配置。另一台主库上的安装及配置操作相同,这里不在讲述(注意配置文件的异同)。
#Install Keepalived:
[root@MySQL-01 tools]# yum install -y openssl* popt-devel
[root@MySQL-01 tools]# wget http://www.keepalived.org/software/keepalived-1.1.19.tar.gz
[root@MySQL-01 tools]# tar zxvf keepalived-1.1.19.tar.gz
[root@MySQL-01 tools]# cd keepalived-1.1.19
[root@MySQL-01 keepalived-1.1.19]# ./configure
[root@MySQL-01 keepalived-1.1.19]# make && make install
------------------------
/bin/cp /usr/local/etc/rc.d/init.d/keepalived /etc/init.d/
/bin/cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/
mkdir /etc/keepalived -p
/bin/cp /usr/local/etc/keepalived/keepalived.conf /etc/keepalived/
/bin/cp /usr/local/sbin/keepalived /usr/sbin/
/etc/init.d/keepalived start
ps -ef|grep keep
------------------------
root 29528 1 0 16:08 ? 00:00:00 keepalived -D
root 29530 29528 0 16:08 ? 00:00:00 keepalived -D
root 29532 27777 0 16:09 pts/0 00:00:00 grep keep
[root@MySQL-01 ~]# vim /etc/keepalived/keepalived.conf
-----------------------------------
! Configuration File for keepalived
global_defs {
notification_email {
brucemx@126.com
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 172.16.1.220
smtp_connect_timeout 30
router_id MySQL-01
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
nopreempt #非抢占模式,主设备如果恢复了不会抢夺虚拟地址。备机上不用设置该条目。
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
172.16.1.19/24
}
}
在两台主库上的安装及配置完成后,重启Keepalived服务后查看VIP:
[root@MySQL-01 ~]# /etc/init.d/keepalived restart
[root@MySQL-02 ~]# /etc/init.d/keepalived restart
#查看虚拟地址:
[root@MySQL-01 ~]# ip add
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:60:20:f0 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.15/24 brd 172.16.1.255 scope global eth0
inet 172.16.1.19/24 scope global secondary eth0
...
从原理论来看MySQL也支持双主的设置,即两个MySQL节点互为主备,不过虽然理论上,双主只要数据不冲突就可以工作的很好,但实际情况中还是很容发生数据冲突的,比如在同步完成之前,双方都修改同一条记录。因此在实际中,最好不要让两边同时修改。即逻辑上仍按照主从的方式工作。但双主的设置仍然是有意义的,因为这样做之后,切换主备会变的很简单。因为在出现故障后,如果之前配置了双主,则直接切换主备会很容易。
安装完Keepalived,只是保证了MySQL数据库的高可用性,但是要真正做到互为主从,还需要配置MySQL的主从复制模式,使数据库可以达到一致性状态。
第一步:要分别修改两个主库的配置文件:
#MySQL-01:
[root@MySQL-01 ~]# vim /etc/my.cnf
[mysqld]
server-id = 15
log-bin=mysql-bin
log-slave-updates
auto_increment_offset=1
#上面这行是为了让双主同时在一张表中进行添加操作时不会出现id冲突,所以在两个节点上设置为不同的值就好。
auto_increment_increment=2
----------------------------------
#MySQL-02:
[root@MySQL-02 ~]# vim /etc/my.cnf
[mysqld]
server-id = 16
log-bin=mysql-bin
log-slave-updates
auto_increment_offset=2
auto_increment_increment=2
第二步:创建同步账号
#两台主库上都需要创建链接用户:
mysql> grant replication slave on *.* to rep@'172.16.1.%' identified by 'oldboy123';
mysql> flush privileges;
----------------------------------
#修改配置文件及同步账号创建完毕后需要重启服务:
/etc/init.d/mysqld restart
第三步:互相设置主从同步
#MySQL-01:
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 107 | | |
+------------------+----------+--------------+------------------+
#MySQL-02:
CHANGE MASTER TO
MASTER_HOST='172.16.1.15',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='oldboy123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107;
mysql> start slave;
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 107 | | |
+------------------+----------+--------------+------------------+
#MySQL-01:
CHANGE MASTER TO
MASTER_HOST='172.16.1.16',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='oldboy123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=107;
mysql> start slave;
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#MySQL-03:
[root@MySQL-03 ~]# vim /etc/my.cnf
[mysqld]
server-id = 17
------------------
#MySQL-04:
[root@MySQL-04 ~]# vim /etc/my.cnf
[mysqld]
server-id = 18
#两台从库重启服务:
/etc/init.d/mysqld restart
配置主从同步:
#MySQL-02:
mysql> flush tables with read lock;
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 | 759 | | |
+------------------+----------+--------------+------------------+
#对主库进行备份:
[root@MySQL-02 ~]# mysqldump -uroot -poldboy123 --event -A -B >/opt/master1.sql
mysql> unlock tables;
---------------------
#MySQL-03:
[root@MySQL-03 ~]# mysql -uroot -poldboy123 </tmp/master1.sql
CHANGE MASTER TO
MASTER_HOST='172.16.1.16',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='oldboy123',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=759;
mysql> start slave;
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
在MySQL-04上执行同样的操作。