七、主从复制之读写分离技术
为了降低应用程序设计端的难度和mysql主从的耦合度,使用了类似调度器的机制,这里就是MySQL-proxy和amoeba,这里主要介绍和使用amoeba软件。
通常amoeba安装的服务器有两块网卡,分别用于和前端应用服务器通信,和后面进行负载调度的MySQL服务器进行通信。
当存在多台“读”性质的MySQL服务器时,需要在amoeba服务器上设置一个“组”,用于将读服务器组织起来并通过调度算法进行负载均衡调度。当然也可以通过ipvs或者haproxy来设置调度器。
1、amoeba介绍(连接池)
需要建立在java的JVM平台运行,可以水平分区和垂直分区。
监听端口:3306(默认是8806)
web管理端口:9066
2、安装环境:
amoeba服务器:
eth0:10.40.0.226
eth1:192.168.4.201
安装java-1.7.0-openjdk-1.7.0.131.x86_64
安装amoeba-mysql-3.0.5-RC
MySQL主服务器(写):
eth0:10.40.0.228
安装MySQL-server-5.6.36-1.el6.x86_64.rpm
MySQL从服务器(读):
eth0:10.40.0.229
安装MySQL-server-5.6.36-1.el6.x86_64.rpm
3、amoeba配置
a、解压缩并存放在/usr/local/amoeba目录下。创建运行环境变量$PATH到/etc/profile.d/amoeba.sh。
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# pwd
/usr/local/amoeba-3.0.5
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# vim /etc/profile.d/amoeba.sh
export PATH=/usr/local/amoeba-3.0.5/bin/:$PATH
b、修改amoeba配置文件
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# ll conf/
总用量 60
-rwxrwxrwx 1 root root 165 7月 5 2013 access_list.conf//访问控制列表
-rwxrwxrwx 1 root root 1287 7月 5 2013 amoeba.dtd
-rwxrwxrwx 1 root root 3109 8月 3 06:51 amoeba.xml//主配置文件
-rwxrwxrwx 1 root root 811 7月 5 2013 dbserver.dtd
-rwxrwxrwx 1 root root 2366 7月 5 2013 dbServers.xml//数据库配置文件
-rwxrwxrwx 1 root root 483 7月 5 2013 function.dtd
-rwxrwxrwx 1 root root 4427 7月 5 2013 functionMap.xml定义函数
-rwxrwxrwx 1 root root 4913 7月 5 2013 log4j.dtd
-rwxrwxrwx 1 root root 5935 7月 5 2013 log4j.xml日志定义
-rwxrwxrwx 1 root root 1049 7月 5 2013 rule.dtd
-rwxrwxrwx 1 root root 848 7月 5 2013 ruleFunctionMap.xml切分功能的函数
-rwxrwxrwx 1 root root 2511 7月 5 2013 rule.xml定义水平和垂直切分
c、配置java环境变量到/etc/profile.d/java.sh
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.131.x86_64/jre
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# . /etc/profile.d/java.sh
d、配置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">
<!-- port -->
<property name="port">8066</property>用于设置应用程序访问代理mysql的监听端口这里通常设置为3306。
<!-- bind ipAddress -->
<property name="ipAddress">10.40.0.226</property>与mysql数据库连接通信的网卡ip地址
<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">
<property name="user">root</property>应用程序连接amoeba使用的认证
<property name="password">gongbing</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">300</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>
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>
<property name="defaultPool">www.node8.com</property>用来设置“组”名称,也可以是一个具体的数据库主机名。
<property name="writePool">www.node8.com</property>用于设置读写分离
<property name="readPool">www.node9.com</property>
<property name="needParse">true</property>
</queryRouter>
</amoeba:configuration>
<dbServer name="abstractServer" abstractive="true">
<factoryConfig class="com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory">
<property name="connectionManager">${defaultManager}</property>
<property name="sendBufferSize">300</property>
<property name="receiveBufferSize">300</property>
<!-- mysql port -->
<property name="port">3306</property>
<!-- mysql schema -->
<property name="schema">test</property>
<!-- mysql user —>
<property name="user">root</property>amoeba连接数据库使用的MySQL账户密码。
<property name="password”>123123</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 name="www.node8.com" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">10.40.0.228</property>
</factoryConfig>
</dbServer>
<dbServer name="www.node9.com" parent="abstractServer">
<factoryConfig>
<!-- mysql ip -->
<property name="ipAddress">10.40.0.229</property>
<property name="user">root</property>amoeba连接数据库使用的MySQL账户密码。
<property name="password”>root</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”>2</property>设置负载均衡算法
<!-- Separated by commas,such as: server1,server2,server1 -->
<property name="poolNames">www.node8.com,www.node9.com</property> 设置“组”成员
</poolConfig>
</dbServer>
</amoeba:dbServers>
e、amoeba登录认证验证连接情况
[root@node6.dtedu.com ~]# mysql -u root -p -h 10.40.0.226 --port 3306
注意此处使用的认证信息是在amoeba.xml文件中配置的,可以自由设置,这个用户名和密码不同于dbServers.xml文件中设置的用户名密码。
dbservers.xml中设置的密码是远程登录MySQL服务器的账户信息,是通过mysql设置的内置账户。
F、验证读写分离情况,首先在mysql上安装抓包工具tcpdump,并执行以下命令,然后在amoeba上对数据库进行操作检查读写分离情况。
[root@www ~]# tcpdump -i eth0 -s 0 -nn -A tcp dst port 3306 and dst host 10.40.0.229
[root@www ~]# tcpdump -i eth0 -s 0 -nn -A tcp dst port 3306 and dst host 10.40.0.228
amoeba端登录MySQL并执行创建命令。
[root@node6.dtedu.com ~]# mysql -u root -p -h 10.40.0.226 --port 3306
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2094478222
Server version: 5.1.45-mysql-amoeba-proxy-3.0.4-BETA MySQL Community Server (GPL)
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> create database gongbing;
Query OK, 1 row affected (0.02 sec)
八、MySQL数据库端抓包检查读写是否分离:
主服务器抓包
mS..........create database gongbing
00:49:00.584468 IP 10.40.0.229.60281 > 10.40.0.228.3306: Flags [.], ack 312196252, win 296, options [nop,nop,TS val 267470869 ecr 487657160], length 0
E..4P.@.@...
(..
(...y...Z.e.......(.A.....
..H.....
从服务器抓包没有创建指令,说明此操作只在主服务器上执行
同样进行读操作测试:
检测结果是读只在从服务器上运行,并没有在主服务器上运行。
mysql> select * from user\G;
*************************** 1. row ***************************
Host: localhost
User: root
Password: *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
00:55:28.556170 IP 10.40.0.226.38169 > 10.40.0.229.3306: Flags [P.], seq 59:82, ack 111, win 115, options [nop,nop,TS val 158487007 ecr 267457414], length 23
E..K..@.@...
(..
(......_..K..Q....s.......
rQ..........select * from user
4、在实现读写分离的基础上,保证主服务器也可以实现既可以读又可以写的功能。
方法:首先通过dbServers.xml配置文件将主服务器的主机名添加到“管理组”中,然后,再在amoeba.xml配置文件中将“管理组”添加到“读”的配置选项中。
<dbServer name="readPool" 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">www.node8.com,www.node8.com,www.node9.com</property>
</poolConfig>
</dbServer>
<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">www.node8.com</property>
<property name="writePool">www.node8.com</property>
<property name="readPool">readPool</property>
<property name="needParse">true</property>
</queryRouter>
amoeba中文手册:http://docs.hexnova.com/amoeba/
错误处理:
1、执行launcher,出现一下错误
[root@node6.dtedu.com /usr/local/amoeba-3.0.5]# launcher
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
解决办法:编辑安装目录下的jvm.properties,将Xss数值调大即可。
JVM_OPTIONS="-server -Xms256m -Xmx1024m -Xss300k -XX:PermSize=16m -XX:MaxPermSize=96m"
九、监控MySQL复制过程
1、监控主节点
show master status;
show master logs;
show binlog events;
show binary logs;
2、监控从节点注意的参数:
connect_retry
exec_master_log_pos:当前从节点复制主节点二进制日志的位置
relay_log_space:中继日志空间大小
seconds_behind_master:从节点滞后主节点的时长
pt-heartbeat:可以检测主从之间的准确延时时间。
通过show gloab status查看一下参数:
com_show_slave_hosts
com_show_slave_status
com_slave_start
com_slave_stop
slave_heartbeat_period
slave_open_temp_tables
slave_received_heartbeats
slave_retried_transactions
slave_running
3、percona-tools
pt-table-checksum:检测主从服务器表是否一致
pt-table-sync:用于主从表重新同步
pt-slave-restart:从服务器意外关闭时,安全启动复制进程
4、保证日志文件完整保存在磁盘上
主:sync_binlog=1
从:innodb_overwrite_relay_log_info(innodb引擎)
主库二进制日志文件损坏处理办法(最简单):
1、备份主库数据并删除主库二进制文件
2、关闭从库,将备份数据同步到从数据库中。
3、重新启动从库
从节点中继日志损坏的处理办法:
1、将损坏的中继日志对应点在主服务器的二进制日志中找到,并进行备份和恢复即可。
避免MySQL数据复制过程中出现问题的思路:
1、不要混合使用存储引擎
2、尽量使用相同版本的MySQL
3、注意serverID的不同
4、尽力避免修改从库
5、尽可能使用基于行或者基于混合模式的复制,避免使用基于语句的复制
实现复制的解决方案:
galera:被mariadb整合,多节点实时复制,支持多主模型的多写操作。
tungsten-replicator:开源的数据库复制引擎,支持GTID,支持分片,多线程复制,支持不同版本的MySQL直接复制,支持异构复制。
十、tcpdump
报文捕获及分析、服务开启状态分析工具。
常见抓包工具:
tshark
wireshark
tcpdump
snort(网络入侵检测系统)NIDS
ossec(主机入侵检测系统)HIDS
sniffer
扫描器
nmap
backrack(bt)
本机抓包的前提条件:
本机的网卡必须工作在混杂模式下。
参数讲解:
常用:
-i:指定监听(抓包)的物理接口
-A:指定以Ascii形式显示。要想将捕获的报文显示出来,需要解码的过程(decode),
-X:同时输出为16进制和acsii形式
-XX:以详细的16进制方式显示
-w:将捕获的内容保存到文件中
-n:主机地址不反解析
-nn:主机地址和端口号不反解析。
-s0:捕获整个报文
过滤条件:
协议:tcp、upd、fddi、ip、arp、rarp、sca、ether
流向:dst、src、src and dst、src or dst
主机:net、port、host、portrange
src net 192.168.1.0/24
src port 22
src host 192.168.1.1
十一、数据库分片
1、数据库分片的意义:
数据库的复制和amoeba的读写分离都可以实现读操作的负载分担,而写操作并没有一个合适的实现负载分担软件。而分片的意义就是实现“写操作”的负载分担。
分片很复杂,早分片比晚分片更简单。
2、支持分片(sharding)的软件
cobar(淘宝)
gizzard
amoeba
HIVeDB
ScalArc
新型的数据库
Clustrix:支持分片,事物功能,兼容MySQL