MyCat相关知识及测试要点

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
公网NAT网关,每月750个小时 15CU
简介: 公司需要研究Mycat的高可用性,应要求专门做了个测试方案的整理
版权声明:本文为博主原创文章,未经博主允许不得转载。欢迎访问我的博客 https://blog.csdn.net/smooth00/article/details/71082046

1 重要定义及相关配置

1.1 垂直切分

1.1.1 垂直切分定义

数据的垂直切分,也可以称为纵向切分。将数据库想象成由很多个一大块一大块的“数据块”(表)组成,垂直地将这些“数据块”切开,然后把它们分散到多台数据库主机上面。这样的切分方法就是垂直(纵向)的数据切分。

一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或多个表。而在架构设计中,各个功能模块相互之间的交互点越统一、越少,系统的耦合度就越低,系统各个模块的维护性及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易

1.1.2 优缺点

垂直切分优点:
(1)数据库的拆分简单明了,拆分规则明确;
(2)应用程序模块清晰明确,整合容易;
(3)数据维护方便易行,容易定位。

垂直切分缺点:
(1)部分表关联无法在数据库级别完成,要在程序中完成;
(2)对于访问极其频繁且数据量超大的表仍然存在性能瓶颈,不一定能满足要求;
(3)事务处理相对复杂;
(4)切分达到一定程度之后,扩展性会受到限制;
(5)过度切分可能会带来系统过于复杂而难以维护。

1.1.2.1 垂直切分实现

在如下的实例中,需要将
<!-- -->
<dataHost name="jdbchost2" maxCon="500" minCon="10" balance="0"
       writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select 1</heartbeat>
<writeHost host="maste1" url="192.168.0.5:3306" user="root" password="root">
  <!-- <readHost host="readshard" url="192.168.0.6:3306" user="root" password="root"/> -->
</writeHost>
<writeHost host="maste2" url="192.168.0.6:3307" user="root" password="root">
  <!-- <readHost host="readshard" url="192.168.0.8:3306" user="root" password="root"/> -->
</writeHost>
</writeHost>
</dataHost>

</mycat:schema>

编辑MYCAT_HOME/conf/schema.xml文件,修改dataHost和schema对应的连接信息,user,pay 垂直切分后的配置如下所示:

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">
    <schema name="user" checkSQLschema="false" sqlMaxLimit="100" dataNode="user" />
    <schema name="pay"  checkSQLschema="false" sqlMaxLimit="100" dataNode="pay" />

    <dataNode name="user" dataHost="host1" database="user" />
    <dataNode name="pay" dataHost="host2" database="pay" />

    <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
       writeType="0" dbType="mysql" dbDriver="native">
       <heartbeat>select 1</heartbeat>
       <!-- can have multi write hosts -->
       <writeHost host="hostM1" url="192.168.0.2:3306" user="root" password="root" />
       <!--<writeHost host="hostM2" url="192.168.0.3:3306" user="root" password="root" />-->
    </dataHost> 
    <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
       writeType="0" dbType="mysql" dbDriver="native">
       <heartbeat>select 1</heartbeat>
       <!-- can have multi write hosts -->
       <writeHost host="hostM2" url="192.168.0.3:3306" user="root" password="root" />
    </dataHost>
</mycat:schema>
注意:writeHost/readHost中的location,user,password的值需要根据实际的MySQL的连接信息进行修改。查看conf/server.xml文件,该文件是Mycat服务器参数调整和用户授权的配置文件,默认的MyCat的数据库连接的用户名/密码为mycat/mycat,文件内容参考如下:<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
	<system>
		<property name="defaultSqlParser">druidparser</property>

	</system>
	<user name="mycat">
		<property name="password">mycat</property>
		<property name="schemas">user,pay</property>
	</user>

</mycat:server>

上述文件中的schemas属性需要配置对应的schema(在schema.xml)中进行配置。
重启MyCat,使用MySQL客户端连接MyCat,需要注意的是,默认数据端口为8066,管理端口为9066,在MySQL客户端连接MyCat时,注意填写端口为8066,用户名/密码根据server.xml中的配置进行填写。

连接后可查看后端连接的数据库。

经常遇到的问题:
1)、Caused by: org.xml.sax.SAXParseException; lineNumber: 106; columnNumber: 16; The content of element type "mycat:schema" must match "(schema*,dataNode*,dataHost*)".
原因:要按照schema、datanode 、datahost的顺序放,不能打乱。也就是所有schema要放一起,然后接着才能放datanode。。。。

2)、报1184错误,是因为没有 把datahost主机的权限授予mycat所在主机。
<writeHost host="hadoop2" url="192.168.0.3:3306" user="root" password="123456" />
这里的 用户要授予mycat所在主机远程访问权限:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456'

1.2 水平分库

1.2.1 水平切分定义

水平切分所指的是通过一系列的切分规则将数据水平分布到不同的DB或table中,在通过相应的DB路由 或者table路由规则找到需要查询的具体的DB或者table以进行Query操作,比如根据用户ID将用户表切分到多台数据库上。
将某个访问极其频繁的表再按照某个字段的某种规则来分散到多个表之中,每个表中包含一部分数据。
例如,所有数据都是和用户关联的,那么我们就可以根据用户来进行水平拆分,将不同用户的数据切分到不同的数据库中。
现在互联网非常火爆的web 2.0类型的网站,基本上大部分数据都能够通过会员用户信息关联上,可能很多核心表都非常适合通过会员ID来进行数据的水平切分。而像论坛社区讨论系统,就更容易切分了,非常容易按照论坛编号来进行数据的水平切分。切分之后基本上不会出现各个库之间的交互。

1.2.2 优缺点

水平切分的优点:
    表关联基本能够在数据库端全部完成;
    不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;
    应用程序端整体架构改动相对较少;
    事务处理相对简单;
    只要切分规则能够定义好,基本上较难遇到扩展性限制。
水平切分的缺点:
    切分规则相对更为复杂,很难抽象出一个能够满足整个数据库的切分规则;
    后期数据的维护难度有所增加,人为手工定位数据更困难;
    应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。

1.2.3 水平切分实现

在一般的应用系统中,用户表及其密切相关的关联表,可根据“用户表”(eg:t_user)中的“用户ID”(user_id)进行水平切分,并基于MyCAT的E-R关系分片策略将其密切相关的表(eg:t_user_class_rel)也分到对应的库中。

(1)创建库,表结构

         在建表语句参考如下:
CREATE DATABASE IF NOT EXISTS `mycat_node1`  ;

/**
  user 用户表
*/ 
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
 `user_id` int(11) NOT NULL COMMENT '用户ID',
 `receive_address` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '收货地址',
 `create_time` datetime NOT NULL,
 `province_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='用户信息表';
 
 /**
  `t_area` 全局表
*/
CREATE TABLE `t_area` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `caller` varchar(16) CHARACTER SET utf8 DEFAULT NULL COMMENT '调用方系统表示',
 `province_code` varchar(10) CHARACTER SET utf8 NOT NULL COMMENT '省份编码',
 `area_code` varchar(10) CHARACTER SET utf8 NOT NULL COMMENT '区域编码',
 `area_name` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '区域名称',
 `parent_area_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '父区域编码',
 `create_time` datetime NOT NULL COMMENT '创建时间',
 `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3792 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

 /**
  user 节点测试表
*/ 
 DROP TABLE IF EXISTS `t_node`;
CREATE TABLE `t_node` (
 `vid` int(11) NOT NULL COMMENT 'ID',
 `user_id` int(11) NOT NULL COMMENT '用户ID',
 `note` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'note',
 `create_time` datetime NOT NULL,
 PRIMARY KEY (`vid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='note表';
(2)配置schema.xml文件
首先配置schema.xml文件,添加mycat_node1数据库的dataNode设置,并添加t_user和t_area表的schema设置,本次配置了双主,读写分离配置,
同一个表多个分片的配置可以用dataNode="dn$1-100" 通配方式。
修改后的schema.xml文件内容如下所示:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

<schema name="mycat" checkSQLschema="true" sqlMaxLimit="100">
<table name="t_user" primaryKey="user_id" dataNode="dn1,dn2" rule="rule1" />
        <table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1,dn2" rule="rule1" />
<table name="t_area" type="global" primaryKey="ID" dataNode="dn1,dn2" />

</schema>

<dataNode name="dn1" dataHost="jdbchost" database="mycat_node1" />
<dataNode name="dn2" dataHost="jdbchost2" database="mycat_node1" />

<dataHost name="jdbchost" maxCon="500" minCon="10" balance="0"
       writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select 1</heartbeat>
<writeHost host="maste1" url="192.168.0.1:3306" user="root" password="root">
  <!-- <readHost host="readshard" url="192.168.0.2:3306" user="root" password="root"/> -->
</writeHost>
<writeHost host="maste2" url="192.168.0.3:3306" user="root" password="root">
  <!-- <readHost host="readshard" url="192.168.0.4:3306" user="root" password="root"/> -->
</writeHost>

</dataHost>

(3)配置rule.xml文件

在schema.xml的文件内容中可看到t_user表指定的分片规则是rule1,需要在conf/rule.xml文件中设置rule1的规则为根据user_id进行分片,并按照类“org.opencloudb.route.function.PartitionByLong”的规则进行分片,即将user_id模除1024后每256内分到一个数据库中,即模除后0~255到user0数据库库,256~511到user1数据库,512~767到user2数据库,768~1023到user3数据库。

         该文件的参考内容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://org.opencloudb/"> 
 <tableRule name="rule1">
    <rule>
      <columns>user_id</columns>
      <algorithm>func1</algorithm>
    </rule>
 </tableRule>

 <function name="func1" class="org.opencloudb.route.function.PartitionByLong">
    <property name="partitionCount">4</property>
    <property name="partitionLength">256</property>
 </function>
</mycat:rule>

总结心得:普通取模算法,连续的id会路由到不同的分片。增大了批量插入的事务控制难度,而固定分片hash算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。

(4)配置server.xml文件
         在server.xml文件中的schemas属性中添加test_mycat的schema。修改后的文件如下所示:

<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
    <system>
        <property name="sequnceHandlerType">0</property> 
    </system>
    <user name="mycat">
       <property name="password">mycat</property>
       <property name="schemas">mycat</property>
     </user>
</mycat:server>
(5)水平切分测试
         重启MyCAT,使用MySQL客户端连接后,连接后可在mycat数据库下看到t_user和t_area,t_node表。
         在MySQL客户端连接的MyCat的test_mycat数据库的t_user表运行如下插入语句,插入2000条数据:
INSERT INTO `t_user` VALUES ('1', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO `t_user` VALUES ('2', '广州市越秀区广州大道中599号', '2014-07-17 10:53:17', 'GD');
INSERT INTO `t_user` VALUES ('3', '广州市越秀区广州大道中599号', '2014-07-17 10:53:17', 'GD');
INSERT INTO `t_user` VALUES ('4', '广州市越秀区广州大道中599号', '2014-07-17 10:53:17', 'GD');
INSERT INTO `t_user` VALUES ('5', '广州市越秀区广州大道中599号', '2014-07-17 10:53:17', 'GD');
……
INSERT INTO `t_user` VALUES (2000, '广州市越秀区广州大道中599号', '2014-07-17 10:54:37', 'GD');

1.3 读写分离

1.3.1 读写分离定义

为了确保数据库产品的稳定性,很多数据库拥有双机热备功能。也就是,第一台数据库服务器,是对外提供增删改查业务的生产服务器;第二台数据库服务器,仅仅接收来自第一台服务器的备份数据。一般来说,为了配置方便,以及稳定性,这两台数据库服务器,都用的是相同的配置。在实际运行中,第一台数据库服务器的压力,远远大于第二台数据库服务器。因此,很多人希望合理利用第二台数据库服务器的空闲资源。从数据库的基本业务来看,数据库的操作无非就是增删改查这4个操作。但对于“增删改”这三个操作,如果是双机热备的环境中做,一台机器做了这三个操作的某一个之后,需要立即将这个操作,同步到另一台服务器上。出于这个原因,第二台备用的服务器,就只做了查询操作。进一步,为了降低第一台服务器的压力,干脆就把查询操作全部丢给第二台数据库服务器去做,第一台数据库服务器就只做增删改了。

1.3.2 优缺点

优点:合理利用从数据库服务器的空闲资源。缺点:本来第二台数据库服务器,是用来做热备的,它就应该在一个压力非常小的环境下,保证运行的稳定性。而读写分离,却增加了它的压力,也就增加了不稳定性。因此,读写分离,实质上是一个在资金比较缺乏,但又需要保证数据安全的需求下,在双机热备方案上,做出的一种折中的扩展方案。

1.3.3 读写分离实现

MyCAT的读写分离机制如下:       事务内的SQL,全部走写节点,除非某个select语句以注释/*balance*/开头;       自动提交的select语句会走读节点,并在所有可用读节点中间随机负载均衡;       当某个主节点宕机,则其全部读节点都不再被使用,因为此时,同步失败,数据已经不是最新的,MyCAT会采用另外一个主节点所对应的全部读节点来实现select负载均衡。       当所有主节点都失败,则为了系统高可用性,自动提交的所有select语句仍将提交到全部存活的读节点上执行,此时系统的很多页面还是能出来数据,只是用户修改或提交会失败。例如将本机作为写库,10.18.96.133作为读库,MyCAT的读写分离的配置如下:


<dataHost name="testhost" maxCon="1000" minCon="10" balance="1"
       writeType="0" dbType="mysql" dbDriver="native">
       <heartbeat>select user()</heartbeat>
       <!-- can have multi write hosts -->
       <writeHost host="hostM1" url="localhost:3306" user="root" password="">
           <readHost host="hostM2" url="10.18.96.133:3306" user="test" password="test" />
       </writeHost>
</dataHost>
dataHost的balance属性设置为:
0,不开启读写分离机制
1,全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2,所有的readHost与writeHost都参与select语句的负载均衡,也就是说,当系统的写操作压力不大的情况下,所有主机都可以承担负载均衡。
一个dataHost元素,表明进行了数据同步的一组数据库,DBA需要保证这一组数据库服务器是进行了数据同步复制的。writeHost相当于Master DB Server,而其下的readHost则是与从数据库同步的Slave DB Server。当dataHost配置了多个writeHost的时候,任何一个writeHost宕机,Mycat 都会自动检测出来,并尝试切换到下一个可用的writeHost。

在没有配置数据同步复制的情况下,重启后进行测试,可使用MySQL客户端直接连接读库,插入几条数据后,使用MySQL客户端连接MyCat,运行select语句验证是否在读库上执行。

多主,多读写分离参考:
其中分为dn1,dn2两个分片,每个分片有两个写入,写入之间开启mysql开启2进制复制,mycat会从当前可用的写入host随机获取一个写入,mycat在任何时候   只会单点写入。如果要开启读写分离在放开注释read配置,balance=1或2。

writeType=1 代表所有节点都是写入写入,慎重开启,多节点写入顺序为默认写入根据配置顺序,第一个挂掉切换另一个。

MyCat读写分离机制如下:

      事务内的SQL,全部走写节点,除非某个select语句以注释/*balance*/开头。
      自动提交的select语句会走读节点,并在所有可用读节点中间随机负载均衡。
      当某个主节点宕机,则其全部读节点都不再被使用,因为此时,同步失败,数据已经不是最新的,MyCAT会采用另外一个主节点所对应的全部读节点来实现select负载均衡。
      当所有主节点都失败,则为了系统高可用性,自动提交的所有select语句仍将提交到全部存活的读节点上执行,此时系统的很多页面还是能出来数据,只是用户修改或提交会失败。

1.4 全局表

1.4.1 全局表定义

一个真实的业务系统中,往往存在大量的类似字典表的表格,它们与业务表之间可能有关系,这种关系,可以理解为“标签”,而不应理解为通常的“主从关系”,这些表基本上很少变动,可以根据主键ID进行缓存,下面这张图说明了一个典型的“标签关系”图:
         在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,考虑到字典表具有以下几个特性:
变动不频繁;
数据量总体变化不大;
数据规模不大,很少有超过数十万条记录。
鉴于此,MyCAT定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:

全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
全局表的查询操作,只从一个节点获取
全局表可以跟任何一个表进行JOIN操作
将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据JOIN的难题。通过全局表+基于E-R关系的分片策略,MyCAT可以满足80%以上的企业应用开发。

1.4.2 全局表实现

(1)创建表结构
         在各个库分别创建全局表(例如:t_area)的表结构,表结构保持一致,例如:
DROP TABLE IF EXISTS `t_area`;
CREATE TABLE `t_area` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `caller` varchar(16) CHARACTER SET utf8 DEFAULT NULL COMMENT '调用方系统表示',
 `province_code` varchar(10) CHARACTER SET utf8 NOT NULL COMMENT '省份编码',
 `area_code` varchar(10) CHARACTER SET utf8 NOT NULL COMMENT '区域编码',
 `area_name` varchar(100) CHARACTER SET utf8 DEFAULT NULL COMMENT '区域名称',
 `parent_area_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '父区域编码',
 `create_time` datetime NOT NULL COMMENT '创建时间',
 `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3792 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
(2)配置schema.xml
全局表配置比较简单,不用写Rule规则,在schema.xml中修改test_schema,添加t_area的table子元素,参考如下配置即可:
<schema name="test_mycat" checkSQLschema="false" sqlMaxLimit="100">
       <!-- auto sharding by id (long) -->
       <table name="t_user" dataNode="dn1,dn2" rule="rule1">
           <childTable name="t_user_class_rel" primaryKey="id" joinKey="user_id" parentKey="user_id" />
       </table>
       <table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" />
</schema>
(3)全局表测试
         运行如下insert语句,往test_mycat的t_area表插入10条数据,如下所示:
INSERT INTO `t_area` VALUES ('100', 'test', 'ZX', '1', '全国', '0', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('101', 'test', 'BJ', '110000', '北京市', '1', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('102', 'test', 'BJ', '110100', '市辖区', '110000', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('103', 'test', 'BJ', '110101', '东城区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('104', 'test', 'BJ', '110102', '西城区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('105', 'test', 'BJ', '110103', '崇文区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('106', 'test', 'BJ', '110104', '宣武区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('107', 'test', 'BJ', '110105', '朝阳区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('108', 'test', 'BJ', '110106', '丰台区', '110100', '2012-09-25 08:30:23', null);
INSERT INTO `t_area` VALUES ('109', 'test', 'BJ', '110107', '石景山区', '110100', '2012-09-25 08:30:23', null);
插入后去dn1,dn2分片中查找,可看到这2个分片中的t_area表都被插入10条数据。执行select语句能返回t_area表的对应记录,执行update和delete语句能对应对全局表相关的分片中的记录进行更新和删除操作。

根据以上内容可以做到:
        1、库表散列
        2、读写分离
        3、为mysql数据库集群提供统一视图
        4、配合ha可做高可用数据库集群
        其他等

最最后举一个同时包含垂直切分、水平切分、全局表的schema.xml配置,以供大家参考:

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://org.opencloudb/">

         <schema name="qs" checkSQLschema="false" sqlMaxLimit="100">
                <!-- 垂直切分 -->
                <table name="test1" primaryKey="id" type="global" dataNode="nodeTest1" />
                <table name="test2" primaryKey="id" type="global" dataNode="nodeTest2" />

                <!-- 全局表的配置如下(比如配置文件的数据,数据不大很少变动的,但是经常用到查询的) -->
                <!-- <table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" /> -->

                <!-- 水平切分 -->
                <!-- ER分片表:如t_user表和t_user_detail表的关联字段user_id;保证相同user_id的数据在同一块片区上 -->
                <table name="t_user" primaryKey="user_id" dataNode="nodeUser01,nodeUser02" rule="mod-long">
                    <childTable name="t_user_detail" primaryKey="id" joinKey="user_id" parentKey="user_id" />
                </table>
         </schema>

         <dataNode name="nodeUser01" dataHost="dataHost01" database="db3" />
         <dataNode name="nodeUser02" dataHost="dataHost02" database="db3" />

         <dataNode name="nodeTest1" dataHost="dataHost01" database="db1" />
         <dataNode name="nodeTest2" dataHost="dataHost02" database="db1" />

        <dataHost name="dataHost01" maxCon="1000" minCon="10" balance="0"
                writeType="0" dbType="mysql" dbDriver="native">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostM1" url="192.168.0.83:3306" user="root" password="root"/>
        </dataHost>

        <dataHost name="dataHost02" maxCon="1000" minCon="10" balance="0"
                writeType="0" dbType="mysql" dbDriver="native">
                <heartbeat>select user()</heartbeat>
                <writeHost host="hostM1" url="192.168.0.165:3306" user="root" password="root"/>
        </dataHost>
</mycat:schema>

2 相关测试点

2.1 垂直切分测试

在完成以上重直切分配置后开展测试,主要通过schema.xml的配置

通过编制测试脚本,大批量往多表(各表实现分库)中插入数据,以验证重直切分效果。

重直切分不作重点论述,可以结合水平切分测试一起开展,之半无关联的表可以垂直切分,有关联的表可以考虑水平切分或定义全局表。

2.2 水平切分测试

可以按照以上水平切分配置,完成t_user表分库(按照rule.xml配置实现单表分库),但本次测试的例子要复杂一点,设置ER关系的两表,实现有关系表情况下的水平切分。

1、分别建立4个库:user0、user1、user2、user3。我这里4个库建在4个独立的主机上。
 CREATE DATABASE  user0 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

2、创建表结构
         在user0~user2创建同样的表结构,t_user和t_user_class_rel的建表语句参考如下:

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
 `user_id` int(11) NOT NULL COMMENT '用户ID',
 `receive_address` varchar(256) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '收货地址',
 `create_time` datetime NOT NULL,
 `province_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='用户信息表';

DROP TABLE IF EXISTS `t_user_class_rel`;
CREATE TABLE `t_user_class_rel` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `caller` varchar(16) CHARACTER SET utf8 NOT NULL COMMENT '调用方系统表示',
 `province_code` varchar(10) CHARACTER SET utf8 DEFAULT NULL COMMENT '省份编码',
 `user_id` int(11) NOT NULL COMMENT '用户ID',
 `class_id` int(11) NOT NULL COMMENT '班级ID',
 `role_type` int(11) DEFAULT NULL COMMENT '用户在该班的角色(1学生2家长3教师)',
 `create_time` datetime NOT NULL COMMENT '创建时间',
 `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
 PRIMARY KEY (`id`),
 UNIQUE KEY `idx_rel_user_class_id` (`user_id`,`class_id`,`role_type`),
 KEY `idx_rel_user_id` (`user_id`) USING BTREE,
 KEY `idx_rel_class_id` (`class_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
3、添加schema:加了一点内容:不分表的情况测试(只对部分表进行切分。其实这种时候,没有切分的表,应该是不需要跟已经切分过的表进行关联,否则就会垮库join。既然是这样,那业务就比较独立了,为什么不把这部分表垂直切分出去呢?) 


总结心得:

(1)如果某张表进行水平切分了,那么跟他有事物关联的表,要么搞全局表,要么进行er分片,不然就会导致垮库join。而没有关联关系的表或者非事物关联的表,实际上可以垂直切分出去(如果有必要)。

(2)dataHost可以理解成一个主机组,可以是单机,可以是主从,可以是galera 等搭建起来的集群。读写分离就是在这里处理的。ha、读写分离等都在这里进行配置,都是针对datahost。

<schema name="test_sharding" checkSQLschema="false" sqlMaxLimit="100">
       <!-- auto sharding by id (long) -->
       <table name="t_user" dataNode="user0,user1,user2,user3" rule="rule_wyh">
           <childTable name="t_user_class_rel" primaryKey="id" joinKey="user_id" parentKey="user_id" />
       </table>
           <!-- 此处测试不分表的情况。要先在这里配置,然后可以在mycat创建t_user_1表,也可以在user3对应的local创建表。如果这里没事先配置,无法在mycat建表,会报错。这个还可以通过制定默认datanode实现,更简单,配置方法:在shcema标签上加上datanode -->
           <table name="t_user_1" dataNode="user3" >
       </table>
 </schema>
 <dataNode name="user0" dataHost="host0" database="user0" />
 <dataNode name="user1" dataHost="host1" database="user1" />
 <dataNode name="user2" dataHost="host2" database="user2" />
 <dataNode name="user3" dataHost="host3" database="user3" />
4、添加datahost:host3
<dataHost name="host3" maxCon="1000" minCon="10" balance="0"
   writeType="0" dbType="mysql" dbDriver="native">
   <heartbeat>select user()</heartbeat>
   <!-- can have multi write hosts -->
   <writeHost host="ys-fs" url="192.168.168.238:3306" user="root" password="youngsun" />
</dataHost>
在238上授权授权:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'youngsun';
flush privileges;
5、配置rule.xml文件
在schema.xml的文件内容中可看到t_user表指定的分片规则是rule1,需要在conf/rule.xml文件中设置rule1的规则为根据user_id进行分片,并按照类“org.opencloudb.route.function.PartitionByLong”的规则进行分片,即将user_id模除1024后每256内分到一个数据库中,即模除后0~255到user0数据库库,256~511到user1数据库,512~767到user2数据库,768~1023到user3数据库。

总结心得:普通取模算法,连续的id会路由到不同的分片。增大了批量插入的事务控制难度,而固定分片hash算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。
         该文件的参考内容如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://org.opencloudb/"> 
 <tableRule name="rule_wyh">
    <rule>
      <columns>user_id</columns>
      <algorithm>func_4p</algorithm>
    </rule>
 </tableRule>

 <function name="func_4p" class="org.opencloudb.route.function.PartitionByLong">
    <property name="partitionCount">4</property>
    <property name="partitionLength">256</property>
 </function>
</mycat:rule>
6、配置server.xml文件
         在server.xml文件中的schemas属性中添加test_sharding的schema。修改后的文件如下所示:
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://org.opencloudb/">
    <system>
        <property name="sequnceHandlerType">0</property> 
    </system>
    <user name="test">
       <property name="password">test</property>
       <property name="schemas">weixin,yixin,photo,test_sharding</property>
    </user>
</mycat:server>
7、水平切分测试
         重启MyCAT,使用MySQL客户端连接后,连接后可在test_sharding数据库下看到t_user和t_user_class_rel表,
         在MySQL客户端连接的MyCat的test_sharding数据库的t_user表运行如下插入语句,插入user_id=1、255、256、511、512、1023、1024、50、300、1000的数据:注意insert into 必须带上字段名列表,不然报错插不进去。
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('1', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('255', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('256', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('511', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('512', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('1023', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('1024', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('50', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('300', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
INSERT INTO  t_user( user_id  , receive_address  , create_time  , province_code  ) VALUES('1000', '广州市越秀区广州大道中599号', '2014-07-17 10:53:15', 'GD');
        而后在MyCAT的test_sharding数据库的t_user表运行select查看记录执行情况。进入localhost的user0~user3数据库,查看数据是否按照之前确定的rule1的规则写入不同的数据库。
        读者可在test_sharding数据库的t_user表执行update和delete等语句,并去分库查看执行结果,可得知MyCAT对MySQL客户端基本透明,对程序也几乎透明,在select语句运行时,MyCAT会自行去各个分库按照规则获取合并结果。
        接着测试按照ER关系策略分片的t_user_class_rel表是否按照user_id的分片策略,同样user_id的数据分布在同一个user库的t_user表和t_user_class_rel表。
INSERT INTO `t_user_class_rel`( `id`  , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES ('257', 'eip', 'GD', '2', '35', '3', '2012-08-05 17:32:13', '2013-12-27 16:07:32');
INSERT INTO `t_user_class_rel`( `id`  , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES ('1', 'eip', 'GD', '257', '35', '3', '2012-08-05 17:32:13', '2013-12-27 16:07:32');
INSERT INTO `t_user_class_rel`( `id`  , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES ('2', 'eip', 'GD', '513', '35', '3', '2012-08-05 17:32:13', '2013-12-27 16:07:32');
INSERT INTO `t_user_class_rel`( `id`  , `caller` , `province_code` , `user_id` , `class_id` , `role_type` , `create_time` , `modify_time`) VALUES ('3', 'eip', 'GD', '769', '35', '3', '2012-08-05 17:32:13', '2013-12-27 16:07:32');
        而后在MyCAT的test_mycat数据库的t_user_class_rel表运行select查看记录执行情况。进入localhost的user0~user3数据库,查看数据是否按照之前确定的rule1的规则和ER分片策略写入不同的数据库。
        分片join解决方案心得小结:如果一张表做分片了,其他有一张表要跟这张表做关联,方案如下:
       (1)全局表(适合做的才做):非跨分片join
       (2)另一张表也搞分片:非跨分片join
       (3)share join(只能2个表join):跨分片join
       (4)另一张表里join用到的字段冗余到 已经做了分片的那张表上去:不用join    (该方案可用性不错)
       (5)另一张表里join用到的字段 搞成一张全局表:非跨分片join

8、最后使用压力测试工具(参照我的博客:http://blog.csdn.net/smooth00/article/details/67631669和http://blog.csdn.net/smooth00/article/details/67632264)大批量的往t_user中插入数据,以验证水平切分的性能和稳定性。

2.3 读写分离测试

mycat提供了几种请求负载均衡分发形式,通过定义dataHost标签的balance属性来修改
balance=0,不开启读写分离机制,所有读写操作都发送到当前可用的writeHost上
balance=1,全部的readHost和stand by writeHost参与select语句的负载均衡,如双主双从模式,M2、S1、S2都分发请求
balance=2,所有读操作随机在writeHost和readHost上分发
balance=3,所有读请求随机的分发到writeHost上对应的readHost上执行,writeHost不承担读压力。(balance=3只支持mycat1.4以上版本)

设置balance=1并将log4j修改为debug模式,观察执行结果。

执行写入操作,可以看出分发到writeHost

mysql> insert into test.t_user(id,area_name) values(2,'深圳市');
执行查询操作,可以看出分发到readHost

mysql> select * from test.t_user where id = 2;

2.4 测试故障切换

mycat通过配置文件schema.xml当中的dataHost标签的writeType和switchType来定义故障切换方式。
writeType=0,表示所有写都发送到配置的第一个writeHost上,第一个挂了切换到第二个writeHost上,重启后以切换后的为准,切换记录在conf/dnindex.properties
writeType=1,表示所有写都随机发送到配置的writeHost上,mycat1.5以上已废弃不推荐。
switchType=-1,表示不自动切换
switchType=1,默认值,表示自动切换
switchType=2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status。
switchType=3,基于galaru cluster的切换机制(合适集群),mycat1.4以上支持,心跳语句为show status like 'wsrep%'。

修改switchType=2,writeType=0,心跳语show slave status
关闭Master

shell> service mysql stop
通过mycat执行插入操作,发现请求分发到第二个writeHost上
mysql> insert into test.t_user values('3', '中山市');
Query OK, 1 row affected (0.04 sec)
重新启动原Master
shell> service mysql start
查看当前写入节点,0表示第一个,1表示第二个
shell> more conf/dnindex.properties
#update
#Fri Mar 04 15:51:39 CST 2016
cluster1=1
通过mycat写入数据
mysql> insert into test.user values(4,'珠海市');
Query OK, 1 row affected (0.04 sec)

mysql> select * from test.t_user where id = 4;
+----+-----------+
| id | name      |
+----+-----------+
|  4 | 珠海市     |
+----+-----------+
1 row in set (0.01 sec)
查看主库数据
mysql> select * from test.t_user;
+----+----------+
| id | name     |
+----+----------+
|  1 | 广州市    |
|  2 | 深圳市    |
+----+----------+
2 rows in set (0.00 sec)
查看从库数据
mysql> select * from test.t_user;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 广州市     |
|  2 | 深圳市     |
|  3 | 中山市     |
|  4 | 珠海市     |
+----+-----------+
4 rows in set (0.00 sec)

当原主库恢复之后,mycat并没有切换回第一个写入节点,而是需要重新配置主从状态。mycat的故障切换不会同步主从binlog差异数据,不能完整的保证数据一致性,而且没有记录切换后的writeHost提供服务时的binlog位置,其他从库指定新主库时,需要手工备份完整的数据,可考虑与MHA结合使用。

2.5 性能测试

1、基准测试
验证性能测试脚本的增、删、改、查,以及MyCat能力测试
验证各个节点DB的性能,确保性能无明显差别
2、压力测试
单节点和多节点对比测试
高并发量测试
长时间高负载测试

2.6 降低单节点性能

1、分片表单节点锁定

(1)运行性能测试脚本,并让MyCat的多个节点其中一个节点在运行脚本过程中做在线的ddl;

(2)运行性能测试脚本,并让MyCat的多个节点其中一个节点在运行脚本过程中全表锁定;

2、分片表索引摘除测试update影响
(1)修改脚本(从insert改成以主键查找update)。运行脚本,让MyCat的多个节点其中一个节点在运行脚本过程中删除主键索引(表是以主键作为分片id);
(2)修改脚本(从insert改成以非主键(unique字段)查找update)。运行脚本,让MyCat的多个节点其中一个节点在运行脚本过程中删除非主键(unique)索引(表是以主键作为分片id)。

相关实践学习
高可用应用架构
欢迎来到“高可用应用架构”课程,本课程是“弹性计算Clouder系列认证“中的阶段四课程。本课程重点向您阐述了云服务器ECS的高可用部署方案,包含了弹性公网IP和负载均衡的概念及操作,通过本课程的学习您将了解在平时工作中,如何利用负载均衡和多台云服务器组建高可用应用架构,并通过弹性公网IP的方式对外提供稳定的互联网接入,使得您的网站更加稳定的同时可以接受更多人访问,掌握在阿里云上构建企业级大流量网站场景的方法。 学习完本课程后,您将能够: 理解高可用架构的含义并掌握基本实现方法 理解弹性公网IP的概念、功能以及应用场景 理解负载均衡的概念、功能以及应用场景 掌握网站高并发时如何处理的基本思路 完成多台Web服务器的负载均衡,从而实现高可用、高并发流量架构
目录
相关文章
|
JSON 前端开发 中间件
MyCat - 网关联合测试 | 学习笔记
快速学习 MyCat - 网关联合测试
51 0
MyCat - 网关联合测试 | 学习笔记
|
前端开发 中间件 数据库
MyCat - 日志模块 - 测试 | 学习笔记
快速学习 MyCat - 日志模块 - 测试
76 0
MyCat - 日志模块 - 测试 | 学习笔记
|
存储 算法 关系型数据库
MyCat - 分片 - 水平拆分 - 分片配置及测试 | 学习笔记
快速学习 MyCat - 分片 - 水平拆分 - 分片配置及测试
107 0
MyCat - 分片 - 水平拆分 - 分片配置及测试 | 学习笔记
|
SQL 存储 关系型数据库
MyCat - 分片 - 垂直拆分 - 测试 | 学习笔记
快速学习 MyCat - 分片 - 垂直拆分 - 测试
77 0
MyCat - 分片 - 垂直拆分 - 测试 | 学习笔记
|
存储 关系型数据库 MySQL
MyCat-入门-分片测试-需求 | 学习笔记
快速学习 MyCat-入门-分片测试-需求
72 0
MyCat-入门-分片测试-需求 | 学习笔记
|
存储 算法 关系型数据库
MyCat-入门-分片测试-分片配置测试 | 学习笔记
快速学习 MyCat-入门-分片测试-分片配置测试
102 0
MyCat-入门-分片测试-分片配置测试 | 学习笔记
|
关系型数据库 MySQL 中间件
MyCat-入门-分片测试-访问 MyCat | 学习笔记
快速学习 MyCat-入门-分片测试-访问 MyCat
56 0
MyCat-入门-分片测试-访问 MyCat | 学习笔记
|
中间件 Linux 数据库
MyCat-入门-分片测试-server.xml 配置 | 学习笔记
快速学习 MyCat-入门-分片测试-server.xml 配置
64 0
MyCat-入门-分片测试-server.xml 配置 | 学习笔记
|
关系型数据库 MySQL 中间件
MyCat-入门-分片测试-schema.xml 配置 | 学习笔记
快速学习 MyCat-入门-分片测试-schema.xml 配置
155 0
MyCat-入门-分片测试-schema.xml 配置 | 学习笔记
|
负载均衡 网络协议 关系型数据库
MyCat-集群-keepalived 启动及 haproxy 的高可用测试 | 学习笔记
快速学习 MyCat-集群-keepalived 启动及 haproxy 的高可用测试
130 0
MyCat-集群-keepalived 启动及 haproxy 的高可用测试 | 学习笔记