1 MyCat架构
MyCat作为一款分布式数据库中间件,用于解决单机关系型数据库扩展性问题,具备轻量(无状态)、灵活、稳定、高效等特性。
MyCat对上层应用提供MySQL服务,使得应用可以像使用单机MySQL一样查询MyCat。对下层可以接入各种关系型数据库。MyCat接收应用的SQL,根据预先配置好的路由规则将SQL下发给底层数据库,汇总后返回给上层应用。
2 初始化MyCat
2.1 底层数据库信息配置
从架构图上可以了解到MyCat是一款数据库中间件,所以首先需要配置schema与底层的数据库节点上具体业务库的关系。
配置的前提条件:1、所有底层数据库已正确安装并启动;2、MyCat可以通过指定的用户名、密码连接所有底层数据库;3、所有底层数据库已创建好业务库。
以上3点都满足的情况下,可以通过 $MyCat_HOME/conf/schema.xml 文件来配置底层数据库。需要配置 dataNode 和 dataHost 2个标签。
dataNode为数据库分片对应的是逻辑库,dataHost是数据库实例对应物理数据库(或读写分离、高可用集群)。所以dataNode需要基于dataHost,而dataHost配置可以分为单主、主从、主备等。
2.1.1 dataNode标签
1 |
<dataNode name="dn1" dataHost="host1" database="db1" /> |
name:定义数据节点的名字,这个名字需要是唯一的,我们需要在table 标签上应用这个名字,来建立表与分片对应的关系。建议直接命名为dn1、dn2 …… dnN。
dataHost:该属性用于定义该分片属于哪个数据库实例的,属性值是引用dataHost 标签上定义的name属性。
database:该属性用于定义该分片属于数据库实例上的哪个具体库。配置的库必须在数据库实例上已存在,MyCat不会自动创建。
2.1.2 dataHost标签
1 2 3 |
<dataHost name="host1" maxCon="100" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" > 。。。。。。 </dataHost> |
name:唯一标识dataHost 标签,供上层的dataNode标签使用。
maxCon、minCon:最大、最小连接数,没有特殊需求不要改动。
balance:负载均衡类型:
1. balance=”0″, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost 上。
2. balance=”1″,全部的readHost 与stand by writeHost 参与select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1 与M2 互为主备),正常情况下,M2,S1,S2 都参与select 语句的负载均衡。
3. balance=”2″,所有读操作都随机的在writeHost、readHost 上分发。
4. balance=”3″,所有读请求随机的分发到writeHost对应的readHost 执行,writeHost不负担读压力
writeType:负载均衡类型,配置为0。
dbType:数据库类型指定后端连接的数据库类型,目前支持二进制的mysql、postgresql协议,还有其他使用JDBC 连接的数据库。例如:mongodb、oracle、spark 等。
dbDriver:指定连接后端数据库使用的Driver,目前可选的值有native 和JDBC。除mysql、postgresql配置native以外,其他数据库种类都配置JDBC。
switchType:故障切换类型,-1 表示不自动切换,1 默认值,自动切换。没有特殊需求请配置为1。
2.1.3 heartbeat标签
1 |
<heartbeat>select 1</heartbeat> |
这个标签内指明用于和后端数据库进行心跳检查的语句。每个数据库不尽相同,例:
数据库 | heartbeat语句 |
postgresql | select 1 |
mysql | select 1 |
oracle | select 1 from dual |
2.1.4 writeHost、readHost标签
1 2 3 4 |
<writeHost host="hostM1" url="192.168.1.1:3306" user="root" password="123456"> <readHost host="hostS2" url="192.168.1.2:3306" user="root" password="123456" /> </writeHost> <writeHost host="hostS1" url="192.168.1.3:3316" user="root" password="123456" /> |
这两个标签都指定后端数据库的相关配置给MyCat,用于实例化后端连接池。唯一不同的是,writeHost 指定写实例、readHost 指定读实例。
在一个dataHost 内可以定义多个writeHost 和readHost。但是,如果writeHost 指定的后端数据库宕机,那么这个writeHost 绑定的所有readHost 都将不可用。另一方面,由于这个writeHost 宕机系统会自动的检测到,并切换到备用的writeHost 上去。
host:用于标识不同实例,建议一般writeHost 使用hostM1、hostM2…hostMN,readHost 用hostS1、hostS2…hostSN。
url:后端实例连接地址,如果是使用native 的dbDriver,则一般为address:port 这种形式。用JDBC 或其他的dbDriver,则需要特殊指定。当使用JDBC 时则可以这么写:jdbc:mysql://localhost:3306/。
user:后端存储实例需要的用户名。
password:后端存储实例需要的密码。
2.1.5 配置样例
注意:MyCat不会实时读取修改后的配置文件!配置完成后请重启MyCat使配置生效。如何重启MyCat请参考3.2.3 重启MyCat。
单主:
1 2 3 4 5 6 |
<dataNode name="dn1" dataHost="host1" database="db1" /> <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" > <heartbeat>select 1</heartbeat> <writeHost host="hostM1" url="192.168.1.1:3306" user="root" password="123456"> </writeHost> </dataHost> |
1主+2只读从库(读写分离,读操作全下发到readHost上):
1 2 3 4 5 6 7 8 |
<dataNode name="dn1" dataHost="host1" database="db1" /> <dataHost name="host1" maxCon="100" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" > <heartbeat>select 1</heartbeat> <writeHost host="hostM1" url="192.168.1.1:3306" user="root" password="123456"> <readHost host="hostS1" url="192.168.1.2:3306" user="root" password="123456" /> <readHost host="hostS2" url="192.168.1.3:3306" user="root" password="123456" /> </writeHost> </dataHost> |
1主+1standby主库(高可用+读写分离:hostM1正常时:写操作发往hostM1,读操作随机发往hostM1、hostM2;hostM1不可用后会读写都自动切换到hostM2):
1 2 3 4 5 6 7 |
<dataNode name="dn1" dataHost="host1" database="db1" /> <dataHost name="host1" maxCon="100" minCon="10" balance="2" writeType="0" dbType="mysql" dbDriver="native" switchType="1" > <heartbeat>select 1</heartbeat> <writeHost host="hostM1" url="192.168.1.1:3306" user="root" password="123456"> </writeHost> <writeHost host="hostM2" url="192.168.1.4:3306" user="root" password="123456" /> </dataHost> |
2.2 MyCat服务管理
底层数据库信息全部配置完成后需要重启MyCat服务才能使配置生效。
2.2.1 启动MyCat
# MyCat start
2.2.2 停止MyCat
# MyCat stop
2.2.3 重启MyCat
# MyCat restart
2.2.4 查看MyCat状态
# MyCat status
3 用户权限配置
MyCat用户权限通过$MyCat_HOME/conf/server.xml文件进行配置。
3.1 创建用户
通过在server.xml文件中新增一个user标签来新增一个用户。例:
1 2 3 4 5 6 7 8 9 10 11 12 |
<user name="test"> <property name="password">test</property> <property name="schemas">TESTDB </property> <property name="readOnly">true</property> <property name="benchmark">1000</property> <privileges check="false"> <schema name="TESTDB" dml="0010" showTables="custome/mysql"> <table name="tbl_user" dml="0110"></table> <table name="tbl_dynamic" dml="1111"></table> < /schema> </privileges> </user> |
创建了一个用户名为test的用户。其他属性含义如下:
password:密码
schemas:用户可访问的全部schema
readOnly:是否只读,不配该项则默认为否
benchmark:当前端的整体connection 数达到配置的值时, 对来自该账户的请求开始拒绝连接,不配该项则默认为不限制
3.2 表级别权限
表级别的权限控制通过privileges标签来控制:
1 2 3 4 5 6 |
<privileges check="false"> <schema name="TESTDB" dml="0010" > <table name="tbl_user" dml="0110"> </table> <table name="tbl_dynamic" dml="1111"></table> </schema> </privileges> |
对用户的schema 及下级的table 进行精细化的DML 权限控制,privileges 节点中的check 属性是用于标识是否开启DML 权限检查, 默认false 标识不检查,当然privileges 节点不配置,等同check=false,由于MyCat 一个用户的schemas 属性可配置多个schema ,所以privileges 的下级节点schema 节点同样可配置多个,对多库多表进行细粒度的DML 权限控制。
Schema/Table 上的dml 的4个数字分别对应insert,update,select,delete4个权限,如0010表示只能进行select操作,1010表示只能进行select和insert,但是不能delete或update。
若设置了schema , 但只设置了个别 table 或未设置 table 的DML,自动继承schema 的DML 属性。
4 逻辑表配置
4.1 schema配置
schema 标签用于定义MyCat 实例中的逻辑库,MyCat 可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用schema 标签来划分这些不同的逻辑库。每张表都属于某一个逻辑库,所以table标签从属在schema标签内。
4.1.1 schema标签
schema 标签位于$MyCat_HOME/conf/schema.xml 文件中。
1 2 3 |
<schema name="MYCAT" checkSQLschema="true" sqlMaxLimit="100"> 。。。 </schema> |
name:定义逻辑的名字,这个名字需要是唯一的。
checkSQLschema:MyCat下发SQL时是否会自动删除schema名,必须配置为true。
sqlMaxLimit:每条执行的SQL语句,如果没有加上limit语句,MyCat会自动的加上所对应的值。建议配置为100。
4.2 表路由规则配置
MyCat创建表需要先配置table标签。
4.2.1 table标签
1 2 |
<table name="test1" dataNode="dn1,dn2,dn3" rule="test1-id-hash" ></table> <table name="test2" dataNode="dn1,dn2,dn3" type="global" ></table> |
name:定义逻辑表的表名,这个名字就如同在数据库中执行create table 命令指定的名字一样,同个schema 标签中定义的名字必须唯一。
dataNode:定义这个逻辑表所属的dataNode, 该属性的值需要和dataNode 标签中name 属性的值相互对应。
rule:该属性用于指定逻辑表要使用的规则名字,规则名字在rule.xml中定义(分片规则参见下一小节5.3 分片规则),必须与tableRule 标签中name 属性属性值一一对应。
type:该属性定义了逻辑表的类型,目前逻辑表只有“全局表”和”普通表”两种类型。对应的配置:全局表:global; 普通表:不指定该值为global 的所有表。
4.3 路由规则配置
$MyCat_HOME/conf/rule.xml 里面定义了对表进行拆分所涉及到的规则定义。可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。这个文件里面主要有tableRule 和function这两个标签。在具体使用过程中可以按照需求添加或修改tableRule和function。
4.3.1 tableRule标签
1 2 3 4 5 6 |
<tableRule name="test1-id-hash"> <rule> <columns>id</columns> <algorithm>murmur</algorithm> </rule> </tableRule> |
name:定义表分发规则名,供table标签引用。定义的名字必须唯一,建议采用“表名-分发键-分发规则”的方式命名。
rule:包含columns和algorithm,指定对表中的哪一列进行拆分和使用什么路由算法
columns:指定要拆分的列名字。
algorithm:对应function标签中的name属性。指定使用的具体路由算法。
4.3.2 function标签
除murmur算法中的count property以外,其他function标签不建议自行修改,如需修改请咨询DBA。
1 2 3 |
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash"> <property name="count">2</property> </function> |
name:定义路由算法名,供tableRule标签引用。定义的名字必须唯一。
class:指定路由算法具体的类名字。
property:具体算法需要用到的一些属性。对于murmurhash算法,只需要根据dataNode数量来设置count属性值。
4.4 最佳实践
4.4.1 路由规则选择
MyCat支持多种路由规则,且配置较为复杂,若配置不当则数据分布不均衡,导致性能很低,无法线性扩展。
只推荐一般用户采用两种路由规则:1、全局表 2、Hash表。
建议小表(如字典表)且不经常更新的使用全局表。大表使用Hash表。分发字段选择参考下一小节5.4.2 分发键选择。
全局表即每个节点都有完整数据,仅需配置schema.xml 中的table标签,例:
1 |
<table name="test2" dataNode="dn1,dn2,dn3" type="global" ></table> |
一致性Hash需要配置rule.xml中的tableRule标签和schema.xml 中的table标签,例:
rule.xml:
1 2 3 4 5 6 |
<tableRule name="test1-id-hash"> <rule> <columns>id</columns> <algorithm>murmur</algorithm> </rule> </tableRule> |
schema.xml:
1 |
<table name="test1" dataNode="dn1,dn2,dn3" rule="test1-id-hash" ></table> |
4.4.2 分发键选择
MyCat作为一个分布式中间件,数据是分布在各个数据节点上的。分发键的选择对于后期使用的性能影响非常大,所以必须要选择合适的分发键。
1、多表关联的查询,因为关联会下发到数据节点执行,所以必须保证所有表的分发规则与分发键完全一致(除全局表),并且在关联条件中包含分发键的相等条件。否则可能会导致查询结果不正确。
2、尽可能选择能使数据均匀分布在各个节点的分发键,如主键。如果数据分布不均匀,性能瓶颈就会出现在数据最多的节点上,导致其他节点很空闲,而都在等待该节点执行完成,造成性能低及资源浪费。
例如人员主题库类的业务,表之间查询、关联都是以人员为单位,而区分人员则使用的是公民身份号码。并且公民身份号码散列度非常高,可以保证数据均匀分布。所以这类业务里的绝大多数表都非常适合使用公民身份号码字段作为分发键。
5 MyCat基本操作
可以使用任意支持MySQL协议的客户端工具连接MyCat执行操作,如mysql client、Navicat for MySQL、HeidiSQL等,本文档不再赘述。
MyCAT支持SQL92标准,支持MySQL、PostgreSQL、Oracle、DB2、SQL Server等数据库的常见SQL用法。
5.1 create table
在第5节 逻辑表配置中已经定义了表的路由规则后,还需要在MyCat定义具体的表结构,通过create table语句实现。例:
5.2 insert
MyCat中的insert语法与标准SQL稍有不同,不支持不带列名的insert语句,必须在表名后显式的指定需要插入的列名。例:
5.3 select
select语句符合SQL92标准。例:
当从一个表查询数据时候,MyCat数据展现顺序是以MyCat先接收到的数据的顺序,每个dataNode的数据到达MyCat的顺序是不确定的,所以select的结果的顺序是不确定的,即使表中数据没有一点变化,这一点和单机数据库是不一样的,要想有序的输出结果,必须显示的加一个order by语句,强制输出的结果排序。
如果查询条件中包含分发字段,则MyCat会将SQL语句只下发到对应的节点;否则会下发到所有节点,可通过explain语句查看:
注意:多表关联的查询,因为关联会下发到数据节点执行,所以必须保证所有表的分发规则与分发键完全一致(除全局表),并且在关联条件中包含分发键的相等条件。否则可能会导致查询结果不正确。
5.4 update
update语句符合SQL92标准。例:
5.5 delete
delete语句符合SQL92标准。例:
6 java连接MyCat
6.1 设置CLASSPATH
将mysql-connector-java-8.0.15.jar添加到项目CLASSPATH中。
6.2 加载驱动
驱动名:com.mysql.cj.jdbc.Driver
连接URL:jdbc:mysql://hostname:[port]/[database]
例:
jdbc:mysql://172.16.105.8:8066/TESTDB
6.3 样例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package com.lzk; import java.io.*; import java.sql.*; public class MycatConn { public static void main(String args[]) throws FileNotFoundException { final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; final String DB_URL = "jdbc:mysql://172.16.105.8:8066/TESTDB"; Connection conn = null; try { Class.forName(JDBC_DRIVER); conn = DriverManager.getConnection(DB_URL, "root","123456"); String sql = "select a from a where a=1"; PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs=ps.executeQuery(); while (rs.next()) { System.out.println(rs.getInt(1)); } ps.close(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != conn) { conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } } } |