一 。mycat介绍
1》mycat出现场景
在互联网时代,海量数据的存储与访问成为系统设计与使用的瓶颈问题,对于海量数据处理,按照使用场景,主要分为两种类型:联机事务处理(OLTP)和联机分析处理(OLAP)。
联机事务处理(OLTP)也称为面向交易的处理系统,其基本特征是原始数据可以立即传送到计算中心进行处理,并在很短的时间内给出处理结果。
联机分析处理(OLAP)是指通过多维的方式对数据进行分析、查询和报表,可以同数据挖掘工具、统计分析工具配合使用,增强决策分析功能
对于两者的主要区别可以用下表来说明:
OLTP | OLAP | |
系统功能 | 日常交易处理 | 统计、分析、报表 |
DB 设计 | 面向实时交易类应用 | 面向统计分析类应用 |
数据处理 | 当前的, 最新的细节的, 二维的分立的 | 历史的, 聚集的, 多维的集成的, 统一的 |
实时性 | 实时读写要求高 | 实时读写要求低 |
事务 | 强一致性 | 弱事务 |
分析要求 | 低、简单 | 高、复杂 |
简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。
水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。
垂直切分
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:
系统被切分成了,用户,订单交易,支付几个模块。
一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。
但是往往系统之有些表难以做到完全的独立,存在这扩库join的情况,对于这类的表,就需要去做平衡,是数据库让步业务,共用一个数据源,还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到了一定的规模,负载很大的情况,就需要必须去做分割。
一般来讲业务存在着复杂join的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种程度是考验技术架构的一个难题。
下面来分析下垂直切分的优缺点:
优点:
19
拆分后业务清晰,拆分规则明确。
系统之间整合或扩展容易。
数据维护简单。
缺点:
部分业务表无法join,只能通过接口方式解决,提高了系统复杂度。
受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高。
事务处理复杂。
由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶颈,所以就需要水平拆分来做解决。
水平切分
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如:从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分,不同的数据按照会员ID做分组,这样所有的数据查询join都会在单库内解决;如果从商户的角度来讲,要查询某个商家某天所有的订单数,就需要按照商户ID做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有一定的困难。如何找到合适的分片规则需要综合考虑衡量。
几种典型的分片规则包括:
按照用户ID求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中。
按照日期,将不同月甚至日的数据分散到不同的库中。
按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中。
如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户ID求模举例:
既然数据做了拆分有优点也就优缺点。
优点:
拆分规则抽象好,join操作基本可以数据库做。
不存在单库大数据,高并发的性能瓶颈。
应用端改造较少。
提高了系统的稳定性跟负载能力。
缺点:
21
拆分规则难以抽象。
分片事务一致性难以解决。
数据多次扩展难度跟维护量极大。
跨库join性能较差。
前面讲了垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同的特点缺点有:
引入分布式事务的问题。
跨节点Join的问题。
跨节点合并排序分页问题。
多数据源管理问题。
针对数据源管理,目前主要有两种思路:
A. 客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库,在模块内完成数据的整合;
B. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;
可能90%以上的人在面对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的扩展性来说,是非常有帮助的。
Mycat 通过数据切分解决传统数据库的缺陷,又有了NoSQL易于扩展的优点。通过中间代理层规避了多数据源的处理问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。下面章节就分析,mycat的由来及如何进行数据切分问题。
由于数据切分后数据Join的难度在此也分享一下数据切分的经验:
第一原则:能不切分尽量不要切分。
第二原则:如果要切分一定要选择合适的切分规则,提前规划好。
第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库Join的可能。
第四原则:由于数据库中间件对数据Join实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表Join。
什么是mycat,maycat从哪里来,又是如何解决这些问题的
2》mycat概念
Mycat是什么?Mycat是数据库中间件,就是介于数据库与应用之间,进行数据处理与交互的中间服务。由于前面讲的对数据进行分片处理之后,从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。
- 逻辑库(schema) 就是在mycat中定义的库 该库可能指向多个主机的多个实际的物理库 mycat中 show databases查看所有逻辑库
在schema.xml中定义 比如
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- 客户端创建的表 必须在这里定义否则 抛出 op table not in schema MYUSER -->
<table name="myuser" dataNode="dn1,dn2" rule="sharding-by-sex" />
</schema>
显示数据库
mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB |
+----------+
1 row in set (0.00 sec)
- 逻辑表(table)可能数据被水平切分到不同的主机下不同数据库下 mycat中 通过 show tables查看所有逻辑表
<table name="myuser" dataNode="dn1,dn2" rule="sharding-by-sex" />
进入逻辑库 显示逻辑表
mysql> use TESTDB;
Database changed
mysql> show tables;
+------------------+
| Tables in TESTDB |
+------------------+
| myuser |
+------------------+
1 row in set (0.00 sec)
- 节点主机(dataHost) 真正的数据库的ip 端口用户名信息 一个节点主机(可能存在多个备机从机 备机和从机的数据和主机必须完全一样也就是配置主从结构 等,可以用于做读写分离 )比如
<dataHost name="myhost" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 主从结构 147是主节点 151是备节点 149和150是从节点 -->
<writeHost host="hostM1" url="192.168.58.147:3306" user="root"
password="root">
<!-- 使用客户端连接mycat后 默认使用readHost读操作 使用主节点进行写操作 如果主节点挂了 备节点称为主节点 -->
<readHost host="hostS1" url="192.168.58.149:3306" user="root" password="root" />
</writeHost>
<writeHost host="hostM2" url="192.168.58.151:3306" user="root"
password="root" />
</dataHost>
- 分片节点(dataNode) 表示哪台节点主机上的哪个数据库 逻辑库和逻辑表的数据可以被拆分在分片节点上
<dataNode name="dn1" dataHost="myhost" database="db1" />
- 分片规则(rule) 多个分片节点 必须通过一定的规则拆分到不同的分片节点上 一般定义在rule.xml中 在逻辑表上指定 rule.xml中定义的规则 以下
列出了常用的一些分片规则 比注释比如
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<!--定义表的拆分 在逻辑表定义时 可以引用这个规则名称-->
<tableRule name="rule1">
<rule>
<columns>id</columns> <!--根据哪一列的值来拆分-->
<algorithm>func1</algorithm><!--算法是哪一个 下边定义的function定义的就是算法-->
</rule>
</tableRule>
<tableRule name="rule2">
<rule>
<columns>user_id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<tableRule name="mod-long">
<rule>
<columns>id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-murmur">
<rule>
<columns>id</columns>
<algorithm>murmur</algorithm>
</rule>
</tableRule>
<tableRule name="crc32slot">
<rule>
<columns>id</columns>
<algorithm>crc32slot</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-month">
<rule>
<columns>create_time</columns>
<algorithm>partbymonth</algorithm>
</rule>
</tableRule>
<tableRule name="latest-month-calldate">
<rule>
<columns>calldate</columns>
<algorithm>latestMonth</algorithm>
</rule>
</tableRule>
<tableRule name="auto-sharding-rang-mod">
<rule>
<columns>id</columns>
<algorithm>rang-mod</algorithm>
</rule>
</tableRule>
<tableRule name="jch">
<rule>
<columns>id</columns>
<algorithm>jump-consistent-hash</algorithm>
</rule>
</tableRule>
<!--
一致性hash(参考一致性hash解决方案 可以动态扩容和删除节点 而基本不影响数据分布)
虚拟一些节点用于平衡数据数据到环上所有节点
-->
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0</property><!-- 默认是0 -->
<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
</function>
<!-- 自动迁移御用分片算法,预分slot 102400个,映射到dn上,再conf下会保存映射文件,请不要修改 -->
<function name="crc32slot"
class="io.mycat.route.function.PartitionByCRC32PreSlot">
<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
</function>
<!--
分片枚举
首先所有的数据节点 配置的索引从 0 开始 如果有三台 分别 是 0-1-2
hash-int表示int类型枚举值 被放到哪个数据节点
比如 数据库字段 sex 0表示男 1表示女 男性放在第二个数据节点 女性放在第一个数据节点
mapfile属性指定文件中 可以这样配置
性别 datanode索引
0 1
1 0
defaultNode表示 如果某些索引值 找不到对应的数据节点 数据存在于默认的该节点
-->
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
<property name="defaultNode">0</property>
</function>
<!--
范围约定
表示传入的列在某个范围 就放某个节点上 比如id是1-10000的存在0节点 10001-20000的存在节点1
比如 mapFile 配置是
1-10000 0
10001 20000 1
-->
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
<property name="defaultNode">0</property>
</function>
<!--
取模
一般是主键的值 %节点数量(count指定) 获取的结果决定存取的节点
-->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
<!--
固定分片hash算法
该种类型表示 最终传入的列值是long类型
要求满足 1024 = sum((count[i]*length[i])).
比如 下面的count=8 length=128 count*length=8*128=1024数据被平均分配到8个节点
比如 count=2,1 length=256,512 2*256+1*512=1024 一个三个节点 前面两个节点数据占 256/1024=25%
最后一个节点是 512/1024=50% 总共100%
-->
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
<!-- 按日期(天分片)
从开始日期到结束日期的 每过 10天分片一个数据节点 如果提供了结束时间到达时间后 从头开始计算
-->
<function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2014-01-01</property>
<property name="sEndDate">2014-01-02</property>
<property name="sPartionDay">10</property>
</function>
<!--
按单月小时拆分
一天拆分 24个片 也就是一小时一个 下个月 从头开始分片
-->
<function name="latestMonth"
class="io.mycat.route.function.LatestMonthPartion">
<property name="splitOneDay">24</property>
</function>
<!--
按月拆分
从开始时间的每个月都开始拆分一个分片
-->
<function name="partbymonth"
class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2015-01-01</property>
</function>
<!--
先对数据进行范围分区 (范围分区里有几个节点) 然后改范围的节点中进行取模运算到哪个节点
id范围 节点数
1-200 2 分片组1 里面有两个节点 id范围1-200
201-400 3
401-500 5
比如插入 id为 5的 肯定通过范围确定时在第一个分片组中 5%2=1 表示存储在分片组1的第二个节点上
-->
<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
<property name="mapFile">partition-range-mod.txt</property>
</function>
<!--
跳增一致性哈希分片 思想源自Google公开论文,比传统一致性哈希更省资源速度更快数据迁移量更少
-->
<function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
<property name="totalBuckets">3</property>
</function>
</mycat:rule>
- 全局序列号(sequence) 分片后 同一个逻辑表的数据分布在不同的主机库中 必须定义全局的主键 用于控制资源的唯一
支持以下几种:本地文件方式,数据库方式,本地时间戳方式 ,zk方式等 具体参考权威指南 第九章 全局序列号
3》mycat的join
Join绝对是关系型数据库中最常用一个特性,然而在分布式环境中,跨分片的join确是最复杂的,最难解决一个问题。
mycat建议你尽量使用innerjoin 少使用left或者rightjoin 条件尽量放在on中 少放在where中
- 全局表
一个真实的业务系统中,往往存在大量的类似字典表的表格,它们与业务表之间可能有关系,这种关系,可以理解为“标签”,而不应理解为通常的“主从关系”,这些表基本上很少变动,可以根据主键ID进行缓存,下面这张图说明了一个典型的“标签关系”图:
在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,考虑到字典表具有以下几个特性:
• 变动不频繁
• 数据量总体变化不大
• 数据规模不大,很少有超过数十万条记录。
鉴于此,MyCAT定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
• 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
• 全局表的查询操作,只从一个节点获取
• 全局表可以跟任何一个表进行JOIN操作
将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据JOIN的难题。通过全局表+基于E-R关系的分片策略,MyCAT可以满足80%以上的企业应用开发。
全局表配置比较简单,不用写Rule规则,如下配置即可:
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
需要注意的是,全局表每个分片节点上都要有运行创建表的DDL语句。
- ER Join
customer采用sharding-by-intfile这个分片策略,分片在dn1,dn2上,orders依赖父表进行分片,两个表的关联关系为orders.customer_id=customer.id。于是数据分片和存储的示意图如下:
这样一来,分片Dn1上的的customer与Dn1上的orders就可以进行局部的JOIN联合,Dn2上也如此,再合并两个节点的数据即可完成整体的JOIN,试想一下,每个分片上orders表有100万条,则10个分片就有1个亿,基于E-R映射的数据分片模式,基本上解决了80%以上的企业应用所面临的问题。
配置
以上述例子为例,schema.xml中定义如下的分片配置:
<table name="customer" dataNode="dn1,dn2" rule="sharding-by-intfile"> <childTable name="orders" joinKey="customer_id" parentKey="id"/> </table>
- Share join
目前支持2个表的join,原理就是解析SQL语句,拆分成单表的SQL语句执行,然后把各个节点的数据汇集。
二。mycat源码安装
使用git客户端 使用 git clone 或者eclipse下载github源码
eclipse切换到git透视图 添加git地址:https://github.com/MyCATApache/Mycat-Server.git 选择1.6版本 点击fetch抓取
选择目录下载后 查看是个maven项目 eclipse导入 存在的maven项目(eclipse配置maven环境 我配置仓库是maven.aliyun.com)
mycat源码中 连接的mycat自己的私服 无法连接 所以需要去掉 使用maven环境下的aliyun
打开pom.xml注释 以下内容
<!--
<repositories>
<repository>
<id>nexus</id>
<name>local private nexus</name>
<url>http://nexus.mycat.io/content/groups/public</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>releases</id>
<name>Internal Releases</name>
<url>http://nexus.mycat.io/content/repositories/releases</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Internal Snapshots</name>
<url>http://nexus.mycat.io/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
-->
大概半分钟后 jar包下载完成 项目不再报错 运行 MycatStartup 类 报错 说是 内存
2017-11-21 10:14:38,518 [ERROR][main] 2017-11-21 10:14:38 startup error java.lang.NumberFormatException: Size must be specified as bytes (b), kibibytes (k), mebibytes (m), gibibytes (g), tebibytes (t), or pebibytes(p). E.g. 50b, 100k, or 250m.
Failed to parse byte string: -53215232B
at io.mycat.memory.unsafe.utils.JavaUtils.byteStringAs(JavaUtils.java:223)
at io.mycat.memory.unsafe.utils.JavaUtils.byteStringAsBytes(JavaUtils.java:234)
at io.mycat.memory.unsafe.utils.MycatPropertyConf.byteStringAsBytes(MycatPropertyConf.java:92)
at io.mycat.memory.unsafe.utils.MycatPropertyConf.getSizeAsBytes(MycatPropertyConf.java:50)
at io.mycat.memory.unsafe.memory.mm.MemoryManager.<init>(MemoryManager.java:30)
at io.mycat.memory.unsafe.memory.mm.ResultMergeMemoryManager.<init>(ResultMergeMemoryManager.java:15)
at io.mycat.memory.MyCatMemory.<init>(MyCatMemory.java:126)
at io.mycat.MycatServer.startup(MycatServer.java:390)
at io.mycat.MycatStartup.main(MycatStartup.java:57)
(io.mycat.MycatStartup:MycatStartup.java:62)
后面调试了一下源代码 发现是
Runtime.maxMemory()-SystemConfig.systemReserveMemorySize结果出来个负数 java程序运行的内存减去 server.xml配置的 systemReserveMemorySize(384M)
将运行内容 调大 添加运行vm参数 -Xmx1024M 重启正常
三。mycat配置一览
mycat存在三个核心的配置文件:
Schema.xml作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource。弄懂这些配置,是正确使用MyCat的前提。这里就一层层对该文件进行解析。
server.xml几乎保存了所有mycat需要的系统配置信息。其在代码内直接的映射类为SystemConfig类。
rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。这个文件里面主要有tableRule和function这两个标签。在具体使用过程中可以按照需求添加tableRule和function。
具体配置参考 http://mycat.io/和mycat权威指南(入门篇 第七章 Mycat的配置)
四。mycat读写分离配置
环境:
数据库:mariadb 中间件mycat
主:192.168.58.147:3306"
从1:192.168.58.149:3306
主备:192.168.58.151:3306
从2:192.168.58.150:3306
mycat不支持主从复制的 必须依靠mysql的logbin机制 mycat只是做读写分离负载 所有的写入都写入到WriteHost,所有的读都平均分配到所有的ReadHost
mysql的双主双从 环境 参考mysql的安装(http://blog.csdn.net/liaomin416100569/article/details/78580382)
schema.xml(src/main/resources)配置节点主机 分片节点 逻辑库(TESTDB) 逻辑表(myuser)
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- 客户端创建的表 必须在这里定义否则 抛出 op table not in schema MYUSER -->
<table name="myuser" dataNode="dn1,dn2" rule="sharding-by-sex" />
</schema>
<!--分片节点 同一台主机可以有多个数据库充当节点-->
<dataNode name="dn1" dataHost="myhost" database="db1" />
<dataNode name="dn2" dataHost="myhost" database="db2" />
<!--分片主机 -->
<!-- 执行测试sql
create database db1;
use db1;
create table myuser(
id int primary key auto_increment,
uname varchar(20),
sex int default 0
);
create database db2;
use db2;
create table myuser(
id int primary key auto_increment,
uname varchar(20),
sex int default 0
);
-->
<dataHost name="myhost" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 主从结构 147是主节点 151是备节点 149和150是从节点 -->
<writeHost host="hostM1" url="192.168.58.147:3306" user="root"
password="root">
<!-- 使用客户端连接mycat后 默认使用readHost读操作 使用主节点进行写操作 如果主节点挂了 备节点称为主节点 -->
<readHost host="hostS1" url="192.168.58.149:3306" user="root" password="root" />
</writeHost>
<writeHost host="hostM2" url="192.168.58.151:3306" user="root"
password="root" >
<readHost host="hostS2" url="192.168.58.150:3306" user="root" password="root" />
</writeHost>
</dataHost>
</mycat:schema>
配置分表规则 rule="sharding-by-sex" 这里只是测试假设有张表 有个性别字段 性别等于0和1 进行分库
rule.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="sharding-by-sex">
<rule>
<columns>sex</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<!--
分片枚举
首先所有的数据节点 配置的索引从 0 开始 如果有三台 分别 是 0-1-2
hash-int表示int类型枚举值 被放到哪个数据节点
比如 数据库字段 sex 0表示男 1表示女 男性放在第二个数据节点 女性放在第一个数据节点
mapfile属性指定文件中 可以这样配置
性别 datanode索引
0 1
1 0
defaultNode表示 如果某些索引值 找不到对应的数据节点 数据存在于默认的该节点
-->
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-sex.txt</property>
<property name="defaultNode">0</property>
</function>
</mycat:rule>
partition-sex.txt
0=1
1=0
server.xml配置允许客户端登录的用户名和密码以及有权限操作的逻辑库(该配置server.xml默认就有)
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
启动 主程序 MycatStartup mycat默认启动的mysql端口是 8066
假设本机安装了 mysql客户端
C:\Users\jiaozi>mysql -uroot -p123456 -P8066
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.29-mycat-1.6.5-BETA-20170424174212 MyCat Server (OpenClound
B)
Copyright (c) 2000, 2011, 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> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB |
+----------+
1 row in set (0.01 sec)
mysql> use TESTDB;
Database changed
mysql> show tables;
+------------------+
| Tables in TESTDB |
+------------------+
| myuser |
+------------------+
1 row in set (0.00 sec)
mysql>
虽然连接mycat存在表 myuser但是配置schema.xml实际的主机的db1和db2中都不存在实际的物理表
mysql> select * from myuser;
ERROR 1105 (HY000): Table 'db2.myuser' doesn't exist
在mycat客户端手工创建一下
mysql> create table myuser(
-> id int primary key auto_increment,
-> uname varchar(20),
-> sex int default 0
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> select * from myuser;
Empty set (0.01 sec)
可以去检测 那四台主机发现确实后端实际数据库都存在了该表
插入数据测试
mysql> insert into myuser(uname,sex) values('zs',0);
Query OK, 1 row affected (0.03 sec)
mysql> insert into myuser(uname,sex) values('ls',1);
Query OK, 1 row affected (0.02 sec)
mysql> select * from myuser;
+----+-------+------+
| id | uname | sex |
+----+-------+------+
| 2 | ls | 1 |
| 2 | zs | 0 |
+----+-------+------+
2 rows in set (0.02 sec)
去147物理主机上查看 发现被拆分到不同的分片db1和db2上 但是存在一个问题就是 id是重复的 要解决这个问题 必须使用全局键
MariaDB [db1]> select * from db1.myuser;
+----+-------+------+
| id | uname | sex |
+----+-------+------+
| 2 | ls | 1 |
+----+-------+------+
1 row in set (0.00 sec)
MariaDB [db1]> select * from db2.myuser;
+----+-------+------+
| id | uname | sex |
+----+-------+------+
| 2 | zs | 0 |
+----+-------+------+
1 row in set (0.00 sec)
配置全局序列号(采用本地文件方式 适用于单机mycat)
编辑 sequence_conf.properties 添加一个自己的序列
MYUSER_SEQ.HISIDS=
MYUSER_SEQ.MINID=1
MYUSER_SEQ.MAXID=1000000000
MYUSER_SEQ.CURID=1
重启 mycat 清空之前的id重复数据(mycat操作)
mysql> delete from myuser;
Query OK, 2 rows affected (0.02 sec)
mysql> select * from myuser;
Empty set (0.02 sec)
server.xml中配置
<system><property name="sequnceHandlerType">0</property></system>
注:sequnceHandlerType需要配置为0,表示使用本地文件方式。
插入数据测试(注意序列的名字是 MYCATSEQ_开头带上你定义的名字 否则会语法错误)
insert into myuser(id,uname,sex) values(next value for MYCATSEQ_MYUSER_SEQ,'zs',0);
insert into myuser(id,uname,sex) values(next value for MYCATSEQ_MYUSER_SEQ,'ls',1);