--
数据库代理
客户
|
|
web程序,游戏程序,其他应用程序(c,php,java.......)
|
|
代理层
(程序开发人员在程序里实现代理,mysqlproxy,amoeba,cobar等软件)
|
|
mysql主 ----> mysql从
写 读
水平切分(如:按用户ID范围切分,id值为1000W前的和id值为1000W后的切分)
垂直切分(表与表之间来切分)
读写分离 (所有读操作和所有写操作来切分)
姓名 性别 部门号 部门号 部门名
张三 男 1 1 技术
李四 女 2 2 销售
王五 男 1
马六 女 2
======================================================================
amoeba 变形虫
参考网址:
http://docs.hexnova.com/amoeba/
下图架构说明:
1,client只需要有mysql这个客户端登录命令就可以了(实际情况,数据库客户端应该是指web,游戏或其它应用程序),所以这里用同桌的电脑就可以
2,amoeba用宿主机就OK了
3,两台mysql,开两台虚拟机,安装rpm版的mysql就OK了
client 10.1.1.x
|
|
amoeba 10.1.1.35
|
|------------------------|
mysql mysql
10.1.1.6 10.1.1.7
架构前所有节点准备:
1,主机名三步,互相绑定
10.1.1.35 li.cluster.com
10.1.1.6 mysqlA.cluster.com
10.1.1.7 mysqlB.cluster.com
2,时间同步
rdate -s 10.1.1.35
3,关闭iptables,selinux
iptables -F
iptables -F -t nat
iptables -F -t mangle
/etc/init.d/iptables save
chkconfig iptables on
setenforce 0
# vim /etc/selinux/config
SELINUX=disabled --把enforcing改为diabled(下一次重启后才会生效)
4, 配置好yum
5, 静态ip地址
/etc/init.d/NetworkManager stop
chkconfig NetworkManager off
配置静态IP地址在下面的配置文件里(eth0对应好你的网卡名,如果你的虚拟机是eth1,则拷贝一个配置文件并手动修改成类似如下示例)
# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
NM_CONTROLLED=no
BOOTPROTO=static
IPADDR=10.1.1.6
NETMASK=255.255.255.0
# cat /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
NM_CONTROLLED=no
BOOTPROTO=static
IPADDR=10.1.1.7
NETMASK=255.255.255.0
修改完后
/etc/init.d/network restart
chkconfig network on
第一步:在10.1.1.35安装amoeba
先把宿主机上已经装过的删除
# rm /usr/local/java -rf
# rm /usr/local/amoeba -rf
1,因为是java开发的,需要java运行环境,也就是jdk;
相关软件包在笔记目录下/amoeba_soft
jdk-6u45-linux-i586.bin
amoeba-mysql-binary-2.2.0.tar.gz
./jdk-6u45-linux-i586.bin --直接./执行它,就可以安装,会自动解压成一个目录在当前目录下
--如果是在64位系统版本上安装这个jdk,可能会报缺少ld-linux.so.2的库,是因为这个jdk是32位的
--解决方法:在64位系统上安装32位的glibc,或者用下面的命令,直接yum安装库就可以了
# yum install ld-linux.so.2
# mv jdk1.6.0_45 /usr/local/java --把jdk的目录移到/usr/local下叫java(这一步不是必要的,只是把名字改短一点,方便而已)
2,解压安装amoeba
软件包
amoeba-mysql-binary-2.2.0.tar.gz
因为这个软件打包的习惯不好,解压后,不会给你建立一个目录
所以:
# mkdir /usr/local/amoeba
# tar xf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba
# ls /usr/local/amoeba/ --amoeba的所有文件在此目录下
benchmark changelogs.txt lib README.html
bin conf LICENSE.txt
# cd /usr/local/amoeba/
# ./bin/amoeba --直接用此命令启动,会报找不到java_home
Error: JAVA_HOME environment variable is not set.
# vim /usr/local/amoeba/bin/amoeba
--加上下面两行(把这两句加到最上面,不要加到最下面,因为这个脚本要用到这个变量的),让这个命令在启动时,就会去找/usr/local/java的jdk
export JAVA_HOME=/usr/local/java
export PATH=$PATH:$JAVA_HOME/bin
# ./bin/amoeba start --尝试启动,可以启动,但是我们并没有做任何配置,所以在这里ctrl+c取消掉
--------------------
amoeba的配置文件介绍
Amoeba有哪些主要的配置文件?
想象Amoeba作为数据库代理层,它一定会和很多数据库保持通信,因此它必须知道由它代理的数据库如何连接,比如最基础的:主机IP、端口、Amoeba使用的用户名和密码等等。这些信息存储在$AMOEBA_HOME/conf/dbServers.xml中。
Amoeba为了完成数据切分提供了完善的切分规则配置,为了了解如何分片数据、如何将数据库返回的数据整合,它必须知道切分规则。与切分规则相关的信息存储在$AMOEBA_HOME/conf/rule.xml中。
当我们书写SQL来操作数据库的时候,常常会用到很多不同的数据库函数,比如:UNIX_TIMESTAMP()、SYSDATE()等等。这些函数如何被Amoeba解析呢?$AMOEBA_HOME/conf/functionMap.xml描述了函数名和函数处理的关系。
对$AMOEBA_HOME/conf/rule.xml进行配置时,会用到一些我们自己定义的函数,比如我们需要对用户ID求HASH值来切分数据,这些函数在$AMOEBA_HOME/conf/ruleFunctionMap.xml中定义。
Amoeba可以制定一些可访问以及拒绝访问的主机IP地址,这部分配置在$AMOEBA_HOME/conf/access_list.conf中
Amoeba允许用户配置输出日志级别以及方式,配置方法使用log4j的文件格式,文件是$AMOEBA_HOME/conf/log4j.xml。
-------------------------------
第二大步:
1,把两台mysql,关闭AB复制,把先前所有的内容都清空,然后两台mysql都新建下面的测试表
mysql> create database aaa;
Query OK, 1 row affected (0.00 sec)
mysql> use aaa;
Database changed
mysql> create table emp (emp_id int,ename varchar(20));
Query OK, 0 rows affected (0.01 sec)
2,两台mysql都对mysql进行授权,授权的IP为amoeba的IP
mysql> grant all on aaa.* to 'amoeba'@'10.1.1.35' identified by '123';
Query OK, 0 rows affected (0.04 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
3,授权后,可以去amoeba(10.1.1.35)这台去连接一下这两个mysql验证授权是否OK,需要能连才行
# mysql -h 10.1.1.6 -u amoeba -p123 -P 3306
# mysql -h 10.1.1.7 -u amoeba -p123 -P 3306
第三大步:
# vim /usr/local/amoeba/conf/dbServers.xml --修改此配置文件,定义代理哪些后台数据库
--下面我做了修改的行有23行(写上要操作的后台数据库的schema);26行和29行(授权的用户名和密码);48行和55行(写上后台数据库的IP)
19
20 3306
21
22
23 aaa
24
25
26 amoeba
27
28
29 123
45
46
47
48 10.1.1.6
49
50
51
52
53
54
55 10.1.1.7
56
57
# vim /usr/local/amoeba/conf/amoeba.xml --再修改这个文件,修改了30行和32行(授权的用户和密码),注意这里的用户名和密码跟数据库授权的用户名和密码无关,可以写成不一样的用户和密码来测试
--上一个配置文件里配置的用户名和密码是数据库授权给amoeba连接的用户名和密码,而这个用户名和密码是amoeba给客户端(php,java,web程序等)连接的
30 haha
31
32 456
# cp /usr/local/amoeba/conf/rule.xml /usr/local/amoeba/conf/rule.xml.bak
--这里先备份一份
# cat /usr/local/amoeba/conf/rule.xml --修改第三个配置文件,定义数据切分的规则
--下面我是按aaa.emp表里的emp_id这一列来切分,当它为奇数就给server2,为偶数就给server1
emp_id
--直接复制粘贴上去,格式空格之类的太多;可以先在vim里:set paste再insert模式粘贴上去就可以了
第四大步:
在amoeba的代理服务器上启动amoeba服务
# ./bin/amoeba start &
第五大步:
客户端测试
在客户端连接进行测试,连接的IP为amoeba的IP,端口为8066
# mysql -h 10.1.1.35 -u haha -p456 -P 8066
注意:amoeba是根据sql解析来进行数据切分的,所以需要把切分的关键字段(这里是emp_id),加入到sql中.否则切分规则无效。无效后,会在 server1,server2 均都插入数据。
mysql> insert into aaa.emp values (1,'aaa'); --这样做是错误的,会在两个后台数据库里都插入数据
--正确的插入方法
mysql> insert into aaa.emp(emp_id,ename) values (1,'aaa');
mysql> insert into aaa.emp(emp_id,ename) values (2,'bbb');
mysql> insert into aaa.emp(emp_id,ename) values (3,'ccc');
mysql> insert into aaa.emp(emp_id,ename) values (4,'ddd');
然后去server1(10.1.1.6)上查看,只有1,3两条数据
去server2(10.1.1.7)上查看,只有2,4两条数据
数据切分成功
在客户端select去查这张表,得到的是两个数据库数据的综合
mysql> select * from aaa.emp;
mysql> select * from emp;
delete和update操作经测试也都OK
==================================================================
--按照上面的配置文件,只修改rule.xml规则文件如下,做成以emp_id值的范围来进行水平切分
--下面实现的是emp_id小于等于100的任何操作都在server1,大于100的任何操作都是在server2
emp_id
server1
server1
server1
emp_id
100]]>
server2
server2
server2
======================================================================
做数据垂直切分的实验:
架构图和上面的一样
client 10.1.1.x
|
|
amoeba 10.1.1.35
|
|------------------------|
mysql mysql
10.1.1.6 10.1.1.7
第一大步:
1,先把两个mysql(不做AB复制的)以前的数据删掉,新建两个表来做测试
第一个mysql
mysql> create database aaa;
mysql> use aaa;
mysql> create table aaa (id int);
mysql> create table bbb (id int);
第二个mysql
mysql> create database aaa;
mysql> use aaa;
mysql> create table aaa (id int);
mysql> create table bbb (id int);
第二大步:
在昨天的配置基础上修改配置文件
在amoba上
# vim /usr/local/amoeba/conf/rule.xml --清空原来的配置,然后复制粘贴下面一段
第三大步:
重启amoeba
# ./bin/amoeba stop
# ./bin/amoeba start &
第四大步:
在客户端测试
# mysql -h 10.1.1.35 -u haha -p456 -P 8066
mysql> use aaa;
mysql> insert into aaa values (1); --这一条被插入到第一个mysql上的aaa.aaa表
mysql> insert into bbb values (2); --这一条被插入到第二个mysql上的aaa.bbb表
mysql> select * from aaa;
+------+
| id |
+------+
| 1 |
+------+
1 row in set (0.01 sec)
mysql> select * from bbb;
+------+
| id |
+------+
| 2 |
+------+
1 row in set (0.00 sec)
思考:
上面不管是水平切也好,还是垂直切也好,因为我们现在只讨论的是有且仅有两台mysql数据库,这种情况为了保证一台数据丢了,另一台能够保留所有数据
所以最好做一个mysql双主架构
做了双主后,要考虑一个问题
如果server1挂了,如何把server1的读写操作转给server2?
做法:
在dbServer.xml里
把server1,server2做一个ha算法的池叫pool1
把server2,server1做一个ha算法的池叫pool2
3
server1,server2
3
server2,server1
然后把下面的规则改成pool1,pool2就可以了,如下
===============================================================
使用amoeba实现读写分离功能(queryrouter)
参考网址:
http://docs.hexnova.com/amoeba/rw-splitting.html
在上面的基础上,把垂直切分,改成读写分离的架构
client 10.1.1.x
|
|
amoeba 10.1.1.35
|
|------------------------|------------|
mysql mysql mysql
10.1.1.6 10.1.1.7 10.1.1.35
第一大步:
1,先把三个mysql(读写分离应该要做AB复制,但我们在这里不做AB复制,是为了方便测试)
把以前的数据删掉,新建三个相同的表来做测试,插入三条不同的数据
第一台mysql
mysql> create database aaa;
mysql> use aaa;
mysql> create table aaa (id int);
mysql> insert into aaa values (1);
第二台mysql
mysql> create database aaa;
mysql> use aaa;
mysql> create table aaa (id int);
mysql> insert into aaa values (2);
第三台mysql
mysql> create databases aaa;
mysql> use aaa;
mysql> create table aaa (id int);
mysql> insert into aaa values (3);
第二大步:
在昨天的配置基础上修改配置文件
# vim /usr/local/amoeba/conf/dbServers.xml --在原来配置的两个dbserver的情况下,再增加一段server3,对应的IP为10.1.1.35
--然后把两个从(server2,server3)加入到一个pool,我这里poolname为slavepool
10.1.1.35
1
server2,server3
# vim /usr/local/amoeba/conf/amoeba.xml
--打开amoeba.xml文件,在原来的基础上修改,把117行和120行的注释去掉,修改115行和118行为server1(也就是mysql主,默认池和写池)
--改119行为server(也就是mysql从,读池)
114 1500
115 server1
116
117
118 server1
119 slavepool
120
121 true
--注意:如果只读写分离的话,不做数据切分,那么rule.xml文件就不要配置,保持刚装完amoeba时的原始配置文件就好了(如果清空rule.xml,启动时会报错)
第三大步:
重启amoeba
# ./bin/amoeba stop
# ./bin/amoeba start
第四大步:
在客户端测试
# mysql -h 10.1.1.35-u amoeba -p123 -P8066
mysql> use aaa;
mysql> insert into aaa values (3); --写入的数据只会被插入到server1(这里是没有做复制的,如果做复制会被复制到server2和server2)
--读的测试,一次读server2的数据,一次读server3的数据
mysql> select * from aaa;
+------+
| id |
+------+
| 2 |
+------+
mysql> select * from aaa;
+------+
| id |
+------+
| 3 |
+------+
读写分离成功
扩展讨论:
把dbServer.xml配置文件池的配置修改一下,把server2写两个,server3写一个,算法还是rr
server2,server2,server3
--测试的结果是select查询时,两次得到server2的结果,一次得到server3的结果
================================
数据切分加读写分离综合讨论
client 10.1.1.x
|
|
amoeba 10.1.1.35
|
|--------------|--------------|----------|
mysqlA mysqlB mysqlC mysqlD
10.1.1.6 10.1.1.7 10.1.1.8 10.1.1.9
写 读 写 读
--上图中:
对数据进行水平切分,并做读写分离
我们这里测试是用aaa.aaa表的id列来进行水平切分
id为奇数的查询路由mysqlA和mysqlB(mysqlA写,mysqlB读)
id为偶数的查询路由mysqlC和mysqld(mysqlC写,mysqlD读)
第一大步:
四台mysql都建立aaa.aaa表,这里不做AB复制(实际环境应该做)
mysql> create databases aaa;
mysql> use aaa;
mysql> create table aaa (id int);
--四个mysql都要对amoeba服务器授权
mysql> grant all on aaa.* to 'amoeba'@'10.1.1.35' identified by '123';
mysql> flush privileges;
第二大步:
# vim /usr/local/amoeba/conf/dbServers.xml
--下面四段是配置四台mysql服务器,名字分别为mysqla,mysqlb,mysqlc,mysqld;
10.1.1.6
10.1.1.7
10.1.1.8
10.1.1.9
--下面一段定义了1个pool叫ddlpool(也就是说除了DML操作外的其它操作大部分为ddl操作,如create表或库等操作就给这个池),用的是1算法,所以一次给mysqla,一次给mysqlc;如果其中一个挂了,它会把DDL操作全给另一个
1
mysqla,mysqlc
# vim /usr/local/amoeba/conf/amoeba.xml --定义DML的读写之外的所有操作给ddlpool(由上面的dbServer.xml定义),DML的读写操作在这里不定义(所以下面把wirtePool和readPool这两句给注释掉不用),因为我们要定义到rule.xml里
114 1500
115 ddlpool
116
117
# vim /usr/local/amoeba/conf/rule.xml --定义DML的读写操作的规则文件如下
id
第三大步:
启动amoeba
# ./bin/amoeba start
第四大步:
在客户端测试
# mysql -h 10.1.1.35 -u haha -p456 -P 8066
测试1:
测试DDL写操作
mysql > create table bbb(id int);
mysql > create table ccc(id int);
mysql > create table ddd(id int);
mysql > create table eee(id int);
......
测试结果为:一次在mysqla,一次在mysqlc轮循操作;如果mysqla挂掉,则只会在mysqlc上操作,如果mysqla再启起来,则又会回到1算法的轮循
测试2:
测试DML写操作时,最好奇偶和大小顺序搞乱来测
mysql> insert into aaa(id) values (2);
mysql> insert into aaa(id) values (4);
mysql> insert into aaa(id) values (1);
mysql> insert into aaa(id) values (6);
mysql> insert into aaa(id) values (3);
mysql> insert into aaa(id) values (5);
测试结果为:奇写在mysqla,偶写在mysqlc
测试3;
测试DML读操作
在mysqlb上插入一条奇数据,一条偶数据
mysql> insert into aaa(id) values (99);
mysql> insert into aaa(id) values (100);
同理在mysqld上也插入一条奇数据,一条偶数据
mysql> insert into aaa(id) values (199);
mysql> insert into aaa(id) values (200);
在客户端连上amoeba,测试
mysql > select * from aaa where id=99; --结果可以查出来
mysql > select * from aaa where id=100; --结果查不出来,因为你这是偶读操作,会给mysqld,但mysqld上没有这条记录
mysql > select * from aaa where id=199; --结果查不出来,因为你这是奇读操作,会给mysqlb,但mysqlb上没有这条记录
mysql > select * from aaa where id=200; --结果可以查出来
测试结果:从上面这样的验证,可以确认奇读给mysqlb,偶读给mysqld,一切OK
==============================
最后总结
看图说原理:
amoeba 代理
奇写 mysqlA mysqlC 偶写
奇读 mysqlB mysqlD 偶读
主要是要考虑数据切分(无论是水平还是垂直),这里还是以奇偶为例
1,上图中A和B做主从,C和D做主从
做法就是,把A和C做一个写池,B和D做一个读池
这样的话,四台都正常的情况下:
奇写给A,偶写给C
奇读给B,偶读给D(或者无论奇偶读在B和D中轮循)
但这样的话,如果A挂掉,那么奇数的读写完全挂掉了
2,为了解决上面的问题,可考虑在上面的基础上把A和C做双主
把A和C做一个3算法(也就是高可用的算法的)池
把C和A反着再做一个3算法的池
用上面的这种rule来做
case division when 0 then (isReadStatement?'read':'ha2');
when 1 then (isReadStatement?'read':'ha1');
这样做的话,写的操作解决了,但读的操作做有问题
3,当B挂掉,你希望奇读要给A才行,
当D挂掉,你希望偶读要给C才行;
解决:BA 做3算法的池 ha3
DC做 3算法的池 ha4
把上面的规则改成
case division when 0 then (isReadStatement?'ha4':'ha2');
when 1 then (isReadStatement?'ha3':'ha1');
4,
还有一个重要的问题是:
如果A挂掉后,奇数的写转到了C,但奇数的读还是可能会转给B(这样就会出现问题,因为A挂,B也就没用了)
解决方法:使用监控或脚本的方式,让A挂掉后B也跟着关闭掉
那么A一挂,A和B都要挂掉,奇写给了C,但是奇读没人给了
所以把上面的ha3这个池改成BAD三个成员的3算法池
把上在的ha4这个池改成DCB三个成员的3算法池
其它不变
5,有没有不做双主也能尽量的数据切分,IO分散的方法呢?
client
amoeba 代理
mysqlA mysqlC mysqlE mysqlG
AB3写(主)
mysqlB mysqlD mysqlF mysqlH
BA3读(主,平时不写,只有A挂时才写)