七、主从复制之读写分离技术

为了降低应用程序设计端的难度和mysql主从的耦合度,使用了类似调度器的机制,这里就是MySQL-proxy和amoeba,这里主要介绍和使用amoeba软件。

通常amoeba安装的服务器有两块网卡,分别用于和前端应用服务器通信,和后面进行负载调度的MySQL服务器进行通信。

当存在多台“读”性质的MySQL服务器时,需要在amoeba服务器上设置一个“组”,用于将读服务器组织起来并通过调度算法进行负载均衡调度。当然也可以通过ipvs或者haproxy来设置调度器。

489611.tmp

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直接复制,支持异构复制。

163455.tmp

十、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