[TOC]
一、读写分离原理
读写分离(Read/Write Splitting),基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理 SELECT 查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。
二、阿里云 ECS 上使用 Amoeba 框架配置 MySQL 的读写分离
1. 前提:MySQL 数据库主从复制已经配置成功
假设配置成功后信息如下:
Master:120.77.219.39
Slave:172.18.247.212
amoeba:120.77.219.39
这里是将 amoeba 安装在 Master 所在服务器上.
当然也可以将 amoeba 配置在第三台服务器上
2. 配置amoeba
2.1 下载 jdk for linux(如果已经安装请跳过此步骤)
此处下载的为jdk-8u144-linux-x64.tar.gz
使用tar命令解压到/usr/share/jdk1.8里
然后配置环境变量:
用Vim编辑器打开/etc/profile
在profile文件末尾加入:
export JAVA_HOME=/usr/share/jdk1.8
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
如果使用apt安装的openjdk,只需要在~/.bashrc文件中配置一下环境变量:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export JRE_HOME=${JAVA_HOME}/jre
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export CLASSPATH=$CLASSPATH:.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
环境变量配置完毕,最好重启服务器。或者使用source命令。
2.2 下载并配置 amoeba
此处下载的版本是:amoeba-mysql-3.0.5-RC
,将压缩包解压到 /usr/share/amoeba
(1) 配置 dbServer.xml
编辑文件:
vi ${AMOEBA_HOME}/conf/dbServers.xml
配置如下内容:
<?xml version="1.0" encoding="gbk"?>
<!DOCTYPE amoeba:dbServers SYSTEM "dbserver.dtd">
<amoeba:dbServers xmlns:amoeba="http://amoeba.meidusa.com/">
<!-- 抽象的 dbServer -->
<dbServer name="abstractServer" abstractive="true">
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="connectionManager">${defaultManager}</property>
<property name="sendBufferSize">64</property>
<property name="receiveBufferSize">128</property>
<!-- mysql port -->
<property name="port">3306</property>
<!-- 主从库 -->
<property name="schema">yz_test</property>
<!-- 访问主从库的用户(主从节点上的用户名和密码尽量建成一样的) -->
<property name="user">yzyz</property>
<!-- 主从库的密码 -->
<property name="password">123</property>
</factoryConfig>
<poolConfig class="com.meidusa.toolkit.common.poolable.PoolableObjectPool">
<property name="maxActive">500</property>
<property name="maxIdle">500</property>
<property name="minIdle">1</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。名称可以自定义,这里定义为:writedb -->
<dbServer name="writedb" parent="abstractServer">
<factoryConfig>
<!-- 写库的IP,一般在一主多从的情况下,将 master 设置为写库 -->
<property name="ipAddress">120.77.219.39</property>
</factoryConfig>
</dbServer>
<!-- 读库,继承自抽象 dbServer。读从库可能会有多个,自定义名称,以便区分 -->
<dbServer name="readslave1" parent="abstractServer">
<factoryConfig>
<!-- 读库的IP,一主多从情况下,将 slave 设置为读库 -->
<property name="ipAddress">101.201.66.147</property>
</factoryConfig>
</dbServer>
<!-- 这是一个虚拟的 dbServer,相当于 dbServer 组,将读库的 IP 统一放到这个组内 -->
<dbServer name="multiReadSlavePool" virtual="true">
<poolConfig class="com.meidusa.amoeba.server.MultipleServerPool">
<!-- 负载调度策略: 1=ROUNDROBIN 复制均衡 , 2=WEIGHTBASED 基于权重, 3=HA -->
<property name="loadbalance">1</property>
<!-- 将多个读库的dbServer名称放在一起,以半角逗号分隔: server1,server2... -->
<property name="poolNames">readslave1</property>
</poolConfig>
</dbServer>
</amoeba:dbServers>
需要修改三处:
(1)MySQL 数据库连接的用户名和密码
(2)写库 IP,读库 IP
(3)多个读库的 dbServer 组
(2) 配置 amoeba.xml
编辑文件:
vi ${AMOEBA_HOME}/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 for Mysql" class="com.meidusa.amoeba.mysql.server.MySQLService">
<!-- Amoeba 访问端口 -->
<property name="port">8066</property>
<!-- bind ipAddress -->
<!-- 如果不指定绑定IP ,将对所有的 IP 扫描 -->
<!--
<property name="ipAddress">127.0.0.1</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="authenticateProvider">
<bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator">
<!-- 访问 amoeba 的用户名,远端通过 8066 端口访问 amoeba 服务,
并使用此处配置的用户名和密码登录 amoeba -->
<property name="user">amoebaroot</property>
<!-- 访问 amoeba 的密码 -->
<property name="password">Cs123456</property>
<property name="filter">
<bean class="com.meidusa.toolkit.net.authenticate.server.IPAccessController">
<property name="ipFile">${amoeba.home}/conf/access_list.conf</property>
</bean>
</property>
</bean>
</property>
</service>
<runtime class="com.meidusa.amoeba.mysql.context.MysqlRuntimeContext">
<!-- proxy server client process thread size -->
<property name="executeThreadSize">128</property>
<!-- per connection cache prepared statement size -->
<property name="statementCacheSize">500</property>
<!-- default charset -->
<property name="serverCharset">utf8</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="defaultManager" class="com.meidusa.toolkit.net.MultiConnectionManagerWrapper">
<property name="subManagerClassName">com.meidusa.toolkit.net.AuthingableConnectionManager</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>
<!-- Amoeba 默认的池:一般情况下配置为 dbServers.xml 中对应的 master -->
<property name="defaultPool">writedb</property>
<!-- writePool 写库 dbServer -->
<property name="writePool">writedb</property>
<!-- readPool 读库 dbServer 组 -->
<property name="readPool">multiReadSlavePool</property>
<property name="needParse">true</property>
</queryRouter>
</amoeba:configuration>
(3) 配置 amoeba 运行环境 JVM 相关参数
amoeba 在启动时,可能会报出 JVM 内存不足的问题,我们可以配置它的 JVM 运行环境的堆存大小。
编辑文件:
${AMOEBA_HOME}/jvm.properties
将 JVM_OPTIONS
后面的值中的 -Xss196k
修改为 -Xss256k
- 给
/usr/share/amoeba/bin
目录赋予"执行"权限:chmod +x bin/*
- 在安全组中添加
8066/8066
端口的外网访问权限。 - 进入
/usr/share/amoeba/bin
,使用./launcher start
命令启动amoeba。
jvm.properties
中的其他配置说明(注意:在实际环境中,不要保留中文注释):
# app名字
APP_NAME=Amoeba-MySQL
# app版本号
APP_VERSION=3.0.0-beta
# 日志输出路径,log4j中可引用参数 ${project.output}
APP_OUTPUT_PATH=$PROJECT_HOME/logs
# 应用程序的 PID 文件存放路径, 默认存放在: ${project.home}/${APP_NAME}.pid
#APP_PID_PATH=/temp/logs/$APP_NAME
# 控制台输出到日志文件
APP_CONSOLE_LOG=$APP_OUTPUT_PATH/console.log
# 程序相关的配置参数
#APP_OPTIONS="-DmyParam=value1 -DmyParam2=value2"
# 启动参数
#APP_ARGS="args0 "
# JVM相关的参数,包括内存配置、垃圾回收策略
JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss256k -XX:PermSize=16m -XX:MaxPermSize=96m"
# 应用程序忽略的信号列表,以逗号分割,程序shutdown的信号为15(可用 kill -15 pid 可让程序文明的shutdown,请不要在这儿填15)
IGNORE_SIGNALS=1,2
3. 添加阿里云 ECS 安全组端口 8066
在阿里云 ECS 安全组端口中添加 8066 端口:
入方向
协议:自定义 TCP
端口范围:8066/8066
授权类型:地址段访问
授权对象:0.0.0.0/0
4. 启动 amoeba
启动命令:
cd ${AMOEBA_HOME}/bin
./launcher start
如果之前启动了 amoeba,还没有来得及关闭,那么这次启动会提示 8066 端口被占用。我们可以查看是哪个进程占用了该端口,然后 kill 该进程 PID:
lsof -i:8066 # 查看 8066 端口被哪个进程占用
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 15009 root 65u IPv4 76545 0t0 TCP *:8066 (LISTEN)
## 找到该进程的 PID ,杀掉就可以了
kill -9 15009
## 然后再次启动 amoeba
cd ${AMOEBA_HOME}/bin
./launcher start
查看 amoeba 是否在运行:
netstat -unlpt | grep java
tcp 0 0 0.0.0.0:8066 0.0.0.0:* LISTEN 15038/java
tcp 0 0 127.0.0.1:8005 0.0.0.0:* LISTEN 11394/java
tcp 0 0 0.0.0.0:8009 0.0.0.0:* LISTEN 11394/java
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 11394/java
如果能看到 8066
端口处于 LISTEN 状态,就说明 amoeba 已经启动完成。
5. 停止 amoeba
${AMOEBA_HOME}/bin/shutdown
6. 测试 amoeba
- 使用 amoeba 所在的 IP,端口号 8066,amoeba.xml 中配置的用户名和密码,远程连接 amoeba。远程连接成功,说明 amoeba 启动成功。
- 连接 amoeba,写 SQL 语句查询和添加,分别看看主库和从库是否都有数据,如果有,说明写入成功。
- 然后停止 slave 的 MySQL 服务,再测试查询和添加,能添加、不能查询,说明 写库 master 正常,读库 slave 离线。
- 然后启动 slave ,停止 master ,再测试查询和添加,能查询、不能添加,说明 读库 slave 正常,写库 master 离线。
7. 常见错误
1、如果提示 Unknown system variable 'language' 错误,将 mysql 的包换成 5.1.24 试试。
2、提示无法打开 console.log,就将logs文件夹权限修改一下,比如:chmod 777 logs
3、提示 address in Use,查找占用 8066 端口的进程,kill -9 杀掉,再次启动 amoeba