分类: Mysql/postgreSQL
随着业务的发展和用户数量的激增,线上的数据库资源日趋紧张仅依靠数据库的优化、微调已无法满足业务快速发展的需求。笔者设计编写了数据库二期扩容方案以解决当前数据库导致的业务整体性能瓶颈问题。在此分享出来希望对亲爱的博友们有所帮助,其中如果有设计不够周全的地方还请不吝赐教!
1.1 数据库二期扩容方案逻辑拓扑
图1.1数据库二期扩容方案逻辑拓扑图
在本方案中MasterA与MasterB数据库服务器借助HA技术以此实现主数据库业务对外提供连续可靠的服务。该方案中Master服务器的数据部署在未来存储业务分区内。当active宕机后standby将主动接管其服务并自动完成数据的挂载。Master高可用服务与Slave集群数据库之间采用mysql主从实时同步技术实现master与slave数据库集群之间的数据实时同步工作。在该业务的逻辑层引入Amoeba技术实现数据库的路由、负载均衡和读写分离功能。(最佳方案在项目开发时就使用读写分离的技术实现对数据库群的访问抛弃Amoeba)。
1.2 Amoeba
技术的
引入
1.2.1 引入Amoeba的原因
考虑项目开发时未规划数据库读写分离的现实情况,所以本方案采用了第三方开发的开源数据库代理中间件Amoeba。该中间件实现了MySQL协议并支持SQL的读写分离、负载均衡、高可用性等功能。
1.2.2 什么是Amoeba
Amoeba是一个以MySQL为底层数据存储,并对应用提供MySQL协议接口的proxy。它集中地响应应用的请求,依据用户事先设置的规则,将SQL请求发送到特定的数据库上执行。基于此可以实现负载均衡、读写分离、高可用性等需求。与MySQL官方的MySQL Proxy相比,作者强调的是Amoeba配置的方便(基于XML的配置文件,用SQLJEP语法书写规则,比基于lua脚本的MySQL Proxy简单)。
Amoeba相当于一个SQL请求的路由器,目的是为负载均衡、读写分离、高可用性提供机制,而不是完全实现它们。用户需要结合使用MySQL的 Replication等机制来实现副本同步等功能。Amoeba对底层数据库连接管理和路由实现也采用了可插拨的机制,第三方可以开发更高级的策略类来替代作者的实现。这个程序总体上比较符合KISS的思想。
1.2.3
Amoeba
的优势
Amoeba主要解决以下问题:
a). 数据切分后复杂数据源整合
b). 提供数据切分规则并降低数据切分规则给数据库带来的影响
c). 降低数据库与客户端连接
d). 读写分离路由
f).支持分库,分表,事务
1.2.4
Amoeba
的不足
a)目前还不支持事务
b)暂时不支持存储过程
c)不适合从Amoeba导数据的场景或者对大数据量查询的query并不合适(比如一次请求返回10w以上甚至更多数据的场合)
d)不支持分库分表,Amoeba目前只做到分数据库实例,每个被切分的节点需要保持库表结构一致。
1.3 网络规划
数据库的网络规划同一期数据库服务器的网络规划设置相同,部署在同一Vlan内。
1.4 主机规划
主机 | 双机 | 安装介质 | 当前业务 | raid | OS |
db1 | Y | 1)MySQL 2)keepalive 3)Amoeba | 主数据库A | Raid5 | Centos5.4 |
db2 | Y | 1)MySQL 2)keepalive3)Amoeba | 主数据库B | Raid5 | Centos5.4 |
db3 | N | 1)MySQL | 从数据库 | Raid5 | Centos5.4 |
…….. | …….. | …….. | …….. | …….. | …….. |
1.5 数据库服务器主从方案实施
1.5.1 数据库服务器角色规划
主机名 | IP地址 | 角色 | server_id | 业务逻辑 |
MasterA | 11.0.211.13 | master | 1 | 读&写 |
MasterB | 11.0.211.14 | master | 2 | 读&写 |
Slave1 | 192.168.202.83 | slave | 3 | 只读 |
Slave2 | 192.168.202.84 | slave | 4 | 只读 |
… | … | … | … | 只读 |
1.5.2 Master
服务器设置
1) 备份数据配置文件
#cp my.cnf my.cnf_bk_20121213
2) 修改配置文件my.cnf内容
在[mysqld]部分追加如下配置选项
skip-name-resolve
#屏蔽域名验证
server-id=1
#数据库id
log-bin=mysql-bin
#设置数据以二进制形式同步
binlog-do-db=mms_sdmtv
#设置实时同步的数据库
3) 在master服务器上设置slave集群的同步账号
a) grant replication slave on *.* to 'myapp'@'193.168.202.83' identified by '123abc';
b)grant replication slave on *.* to 'myapp'@'193.168.202.84' identified by '123abc';
c)flush privileges;
分别在slave服务器83和84上进行测试mysql -h 10.0.211.5 -u myapp –p是否可以使用授权的账号进行登录。
4) 重启master数据库服务
service mysqld restart
#使配置文件my.cnf的参数生效
5) MasterB配置同MasterA此处不再赘述
1.5.3 Slave
集群配置
1) 备份mysql配置文件
cp my.cnf my.cnf_bk_20121213
2) 修改配置文件my.cnf内容
在[mysqld]部分追加如下配置选项:
skip-name-resolve
##屏蔽域名验证
server-id=2
#数据库id
master-host=11.0.211.5
#数据库主服务
master-user=myapp
#允许slave进行同步数据的账号
master-password=123abc
#密码
master-port=3306
#主服务端口
master-connect-retry=60
#超时时间
replicate-do-db=mms_sdmtv
#实时同步的数据库
log-bin=mysql-bin
#二进制数据文件
3) 重启master数据库服务
service mysqld restart
#使配置文件my.cnf的参数生效
4) 其他slave设置与该配置相同
唯一需要修改的参数为server-id
1.5.4 测试与验证
一、 验证服务是否已经正确配置
1) 在master上验证主服务
使用命令show master status\G;显示如下信息则表示主服务已经正确配置:
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 98
Binlog_Do_DB: mms_sdmtv
Binlog_Ignore_DB:
1 row in set (0.00 sec)
2) 验证slave服务是否已经正确配置
使用命令show slave status\G;验证slave服务是否已经正确配置,如返回以下信息则表示已经正确配置:
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 11.0.211.5
Master_User: myapp
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 98
Relay_Log_File: mysqld-relay-bin.000004
Relay_Log_Pos: 235
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: mms_sdmtv
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: 98
Relay_Log_Space: 235
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
1 row in set (0.00 sec)
Slave_IO_Running:连接到主库,并读取主库的日志到本地,生成本地日志文件Slave_SQL_Running:读取本地日志文件,并执行日志里的SQL命令。其返回状态均为Yes证明配置正确。
二、 业务模拟测试
在主数据库服务器上进行增删改操作后验证slave1、slave2是否进行了数据的同步。经验证达到预期目的如果没有同步请参阅该http://blog.chinaunix.net/uid-25723371-id-3388811.html博文如何设置主库与从库数据的实时同步。
1.6 Amoeba
部署
1) jdk的安装
本方案中在服务器安装前期已经规划好,所以系统已经自带jdk且版本符合业务需求。所以此步骤省略;
2) 获取amoeba安装介质
3) 解压介质
#tar –xvf amoeba-mysql-binary-2.2.0.tar.gz –C /amoeba
4) 配置Amoeba
一、 修改主配置文件dbServers.xml
主要配置内容:
a) 需要读写分离的master和slave数据库名
b) 数据库服务器IP地址
c) 数据库资源池
d) 请求分发策略(两种策略:轮询、权重)
e) 服务口令、账号(不是mysql数据库的)
<?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">
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="manager">${defaultManager}</property>
<property name="sendBufferSize">64</property>
<property name="receiveBufferSize">128</property>
<!-- mysql port -->
<property name="port">3306</property>
<!-- mysql schema -->
<property name="schema">mms_sdmtv</property>
<property name="user">root</property>
<property name="password">123abc</property>
</factoryConfig>
<poolConfig class="com.meidusa.amoeba.net.poolable.PoolableObjectPool">
<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>
<dbServer name="master" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">11.0.211.5</property>
</factoryConfig>
</dbServer>
<dbServer name="slave1" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">192.168.202.83</property>
</factoryConfig>
</dbServer>
<dbServer name="slave2" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">192.168.202.84</property>
</factoryConfig>
</dbServer>
<dbServer name="multiPool" virtual="true">
<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>
二、 修改配置文件Amoeba.xml内容如下所示:
主要配置参数:
a) 数据库对外服务端口
b) 绑定ip地址
c) 数据库访问账号和口令
d) 默认访问数据库ip
e) 可以执行查询操作的数据库ip地址
f) 可以读写操作的数据库ip地址
<?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 for Mysql" class="com.meidusa.amoeba.net.ServerableConnectionManager">
<!-- port -->
<property name="port">8066</property>
<!-- bind ipAddress -->
<property name="ipAddress">11.0.211.4</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">
<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
<property name="user"> root </property>
<property name="password"> 123abc </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">
<!-- 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">
<property name="configFile">${amoeba.home}/conf/dbServers.xml</property>
</dbServerLoader>
<queryRouter class="com.meidusa.amoeba.mysql.parser.MysqlQueryRouter">
<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.启动amoeba时报错如下所示:
root@server bin]# sh amoeba
The stack size specified is too small, Specify at least 160k
Could not create the Java virtual machine.
出现该问题的原因是amoeba启动时堆的内存设置太小导致
解决方法:调大amoeba启动文件xss配置参数问题解决
2.查看net.log日志发现如下大量报错信息
2012-12-14 12:18:38,062 ERROR net.MysqlServerConnection - handShake with /192.168.202.83:3306 error:Access denied for user 'root'@'10.0.211.5' (using password: NO),hashCode=496432309
2012-12-14 12:18:35,143 ERROR net.MysqlServerConnection - handShake with /192.168.202.84:3306 error:Access denied for user 'root'@'10.0.211.5' (using password: NO),hashCode=1805440858
出现该问题的原因是未配置dbServers.xml配置项<property name="password">123abc</property>的口令并删掉注释后重启amoeba问题解决。
5) 服务验证测试
1.部署一台web服务器并将数据库连接池配置为jdbc:mysql://10.0.211.4:8066/mms_sdmtv后重启web服务进行登录发布测试OK;
2.停止一台主数据库进行业务查询、插入及修改测试(此时masterB已接管服务)OK
3.停止其中任何一台mysql的slave服务进行数据插入、查询及修改操作测试OK