先上官方文档:html
由上图能够看到 Mycat 的核心配置文件均采用xml格式,这几个配置文件的用途以下:前端
server.xml
:用于配置系统参数、用户信息、访问权限及SQL防火墙和SQL拦截功能等schema.xml
:用于配置逻辑库、逻辑表相关信息rule.xml
:若是使用了水平切分,就须要使用该文件配置切分规则log4j2.xml
:Mycat日志相关的配置,例如日志输出格式、日志级别等应用链接Mycat服务时,Mycat首先会经过server.xml
中的配置信息进行用户认证。用户经过验证后,所看到的逻辑库、逻辑表都是schema.xml
中所配置的。当使用了水平切分时,Mycat会经过rule.xml
里配置的规则来定位具体的物理数据库位置,从而完成写入/读取数据。java
system
标签用于配置 Mycat 的系统参数,其格式以下:mysql
<system> <!-- ${key}表示配置属性的名称,${value}表示该配置属性的值 --> <property name="${key}">${value}</property> </system>
配置 Mycat 服务端口示例:git
<system> <property name="serverPort">3306</property> </system>
常见的系统参数举例:github
<system> <!-- mycat 服务链接端口 --> <property name="serverPort">8066</property> <!-- mycat 服务管理端口 --> <property name="managerPort">9066</property> <!-- mycat 服务监听的ip --> <property name="bindIp">0.0.0.0</property> <!-- 0为须要密码登录、1为不须要密码登录;默认为0,设置为1则须要指定默认帐户--> <property name="nonePasswordLogin">0</property> <!-- 前端链接的写队列大小 --> <property name="frontWriteQueueSize">2048</property> <!-- 设置字符集编码 --> <property name="charset">utf8</property> <!-- mycat 的进程数量 --> <property name="processors">8</property> <!-- 闲置链接超时时间,单位:毫秒 --> <property name="idleTimeout">1800000</property> <!-- 默认最大返回的数据集大小 --> <property name="defaultMaxLimit">100</property> <!-- 容许的最大包大小 --> <property name="maxPacketSize">104857600</property> <!-- 0赶上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。 在某些mysql客户端存在客户端已经登陆的时候还会继续发送登陆报文,mycat会报错,该设置能够绕过这个错误--> <property name="ignoreUnknownCommand">0</property> <property name="useHandshakeV10">1</property> <property name="removeGraveAccent">1</property> <!-- 1为开启实时统计、0为关闭 --> <property name="useSqlStat">0</property> <!-- 1为开启全加班一致性检测、0为关闭 --> <property name="useGlobleTableCheck">0</property> <!-- SQL 执行超时 单位:秒--> <property name="sqlExecuteTimeout">300</property> <property name="sequnceHandlerType">1</property> <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的状况--> <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property> <!-- 子查询中存在关联查询的状况下,检查关联字段中是否有分片字段 .默认 false --> <property name="subqueryRelationshipCheck">false</property> <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property> <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool --> <property name="processorBufferPoolType">0</property> <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(若是分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,可是记录分布式事务日志--> <property name="handleDistributedTransactions">0</property> <!-- off heap for merge/order/group/limit 1开启;0关闭 --> <property name="useOffHeapForMerge">0</property> <!--是否采用zookeeper协调切换 --> <property name="useZKSwitch">false</property> <!--若是为 true的话 严格遵照隔离级别,不会在仅仅只有select语句的时候在事务中切换链接--> <property name="strictTxIsolation">false</property> <!-- Mycat链接数据库时使用的隔离级别 1 - 读未提交 2 - 读已提交 3 - 可重复读 4 - 串行化 --> <property name="txIsolation">2</property> <property name="useZKSwitch">true</property> <!--若是为0的话,涉及多个DataNode的catlet任务不会跨线程执行--> <property name="parallExecute">0</property> </system>
user
标签用于配置Mycat的访问用户及权限,其格式以下:算法
<user name=${username}> <property name="${key}">${value}</property> ... </user>
配置示例:sql
<!-- 用户名 --> <user name="mall"> <!-- 密码 --> <property name="password">123456</property> <!-- 容许该用户访问的逻辑库 --> <property name="schemas">mall_db</property> <!-- 可配置多个容许访问的逻辑库,使用逗号分隔 --> <!-- <property name="schemas">mall_db,db1,db2</property> --> <!-- 是否只读 --> <property name="readOnly">false</property> </user>
除了配置对库的权限可能还不够,有时候咱们须要配置用户对某些表的访问权限。以下示例:数据库
<!-- 用户名 --> <user name="mall"> <!-- 密码 --> <property name="password">123456</property> <!-- 容许该用户访问的逻辑库 --> <property name="schemas">mall_db,db1,db2</property> <!-- 表级 DML 权限配置,check属性表示是否开启该配置 --> <privileges check="true"> <!-- 特别权限应用的逻辑库 --> <schema name="mall_db" dml="0110"> <!-- 配置用户对该表的访问权限,dml属性用于指定权限位, 若是table标签没有配置该属性的话,默认取schema标签的dml属性值, 剩余没有配置的其余表默认也是取schema标签的dml属性值 --> <table name="user_table" dml="0000"></table> <table name="order_table" dml="1111"></table> </schema> </privileges> </user>
dml
属性配置的数字是权限位,分别对应着insert,update,select,delete
四种权限。例如,当dml
的值为0110
时,表示拥有update
和select
权限,不具备insert
和delete
权限。因此权限位为1
时表明拥有对应的操做权限,为0
时表明没有该操做权限。apache
在该示例中,mall
用户对:
user_table
表不具备任何操做权限order_table
表拥有全部操做权限update
和select
权限以上配置用户的示例中,密码都是以明文的形式写在配置文件中。但用户的密码是安全敏感的,通常不会直接在配置文件中写明文密码,而是写一个加密事后的密码。不然只要拥有查看server.xml
文件的权限,就能轻易获取到各个用户的密码,这是很是不安全的。
所以,Mycat 提供了一个工具用于加密明文密码,该工具在一个jar包内,可以使用以下命令对密码进行加密:
[root@txy-server /usr/local/mycat]# java -cp lib/Mycat-server-1.6.7.4-release.jar io.mycat.util.DecryptUtil 0:root:123456
参数说明:
0
:表明的是mycat用户登陆密码加密(1
则是dataHost
加密)root
:用户名123456
:明文密码执行成功后,会获得一个加密后的字符串:
GO0bnFVWrAuFgr1JMuMZkvfDNyTpoiGU7n/Wlsa151CirHQnANVk3NzE3FErx8v6pAcO0ctX3xFecmSr+976QA==
复制该字符串,替换配置文件中的明文密码,以下示例:
<user name="root" defaultAccount="true"> <!-- 须要声明使用的是加密后的密码 --> <property name="usingDecrypt">1</property> <property name="password">GO0bnFVWrAuFgr1JMuMZkvfDNyTpoiGU7n/Wlsa151CirHQnANVk3NzE3FErx8v6pAcO0ctX3xFecmSr+976QA==</property> ... </user>
咱们都知道Mycat是使用Java进行开发的,因此其日志框架也是使用Java生态圈内的log4j2
。Mycat日志相关的配置都在 log4j2.xml
文件中,本小节将介绍一些经常使用的配置项。
Pattern
标签用于配置 Mycat 日志输出格式,默认以下:
<PatternLayout> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern> </PatternLayout>
%d{yyyy-MM-dd HH:mm:ss.SSS}
:日志的时间格式%5p
:输出的日志的级别%t
:输出日志的线程名称%l
:日志输出语句所在的代码位置%m
:输出代码中指定的日志内容%n
:输出一个换行符更多Pattern取值详见官方文档
这里截取了一段 Mycat 的日志内容,能够看到与Pattern是一一对应上的:
2020-01-09 15:22:57.960 INFO [Timer1] (io.mycat.backend.datasource.PhysicalDatasource.getConnection(PhysicalDatasource.java:564)) - no ilde connection in pool 1838161857 ,create new connection for hostM1 of schema db3 totalConnectionCount: 0 increamentCount: 1
level
属性用于配置 Mycat 的日志输出级别,默认为info
级别:
<asyncRoot level="info" includeLocation="true">
关于log4j2的内建日志级别详见官方文档
当咱们须要经过Mycat实现数据分片时就得用到rule.xml
配置文件,该文件用于配置:
这是一个分片规则的配置示例:
<!-- name属性指定分片规则的名称,必须在 rule.xml 文件中是惟一的 --> <tableRule name="hash-mod-4_id"> <rule> <!-- 指定使用表中的哪一个列进行分片 --> <columns>id</columns> <!-- 指定表的分片算法,取值为<function>标签的name属性 --> <algorithm>hash-mod-4</algorithm> </rule> </tableRule>
{分片算法名称}-{分片数量}_{分片列}
;上面的示例就是采用的这种命名格式上面示例中所提到的<function>
标签是用于配置表的分片算法或者说分片函数,以下示例:
<!-- name属性指定分片算法的名称,一样须要是惟一的;class属性指定该算法的具体实现类 --> <function name="hash-mod-4" class="io.mycat.route.function.PartitionByHashMod"> <!-- 要分片的数据库节点数量,必须指定,不然无法分片 --> <property name="count">4</property> </function>
每一个分片算法的所需参数可能不同,因此property
标签是能够有多个的,其属性也因具体的分片算法而异。以下示例:
<function name="partbyday" class="io.mycat.route.function.PartitionByDate"> <property name="dateFormat">yyyy-MM-dd</property> <property name="sNaturalDay">0</property> <property name="sBeginDate">2014-01-01</property> <property name="sEndDate">2014-01-31</property> <property name="sPartionDay">10</property> </function>
Mycat 内置了很是多的分片算法,而且咱们也能够针对实际状况自行开发属于本身的分片算法。咱们来看看经常使用分片算法都有哪些:
PartitionByMod
:简单取模,直接经过列值进行取模得出分片位置PartitionByHashMod
:哈希取模,先将列值进行hash运算以后再取模得出分片位置PartitionByFileMap
:分片枚举,根据枚举值对数据进行分片,例如在异地多活的场景中经过地区id进行数据分片的场景PartitionByPrefixPattern
:字符串范围取模,根据长字符串的前面几位进行取模分片简单取模分片算法的工做原理:
配置示例:
<tableRule name="mod-long-2_id"> <rule> <columns>id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!-- 取模的基数,也就是分片数量 --> <property name="count">2</property> </function>
总结:
分片列 % 分片基数
io.mycat.route.function.PartitionByMod
当须要取模的列为非整型时,就可使用哈希取模分片算法。其工做原理以下图:
配置示例:
<tableRule name="hash-mod-2_login_name"> <rule> <columns>login_name</columns> <algorithm>hash-mod</algorithm> </rule> </tableRule> <function name="hash-mod" class="io.mycat.route.function.PartitionByHashMod"> <property name="count">2</property> </function>
总结:
hash(分片列) % 分片基数
io.mycat.route.function.PartitionByHashMod
前两种分片算法都是经过算法自己去计算出分片位置,是没法人工控制的。若是须要人工控制分片位置时,就可使用到分片枚举算法。该算法使得咱们能够指定一些枚举值来对分片位置进行控制,其实也就至关因而人工指定了某些数据应该到哪一个分片。
例如,某张表中有个存储用户所在区域id的列,咱们但愿将区域id与数据库所在的区域进行映射,以实现区域id为1的数据被分片到区域1数据库中,区域id为2的数据被分片到区域2数据库中,以此类推。在这种场景下就可使用分片枚举算法,其工做原理以下图:
mapFile
配置一个分片关系映射,其格式为key-value,key为枚举,value为数据节点的索引。图中的枚举就是area_id
,一个area_id
对应着一个数据节点。没有配置映射关系的area_id
则会被分片到DEFAULT_NODE
所对应的数据节点。配置示例:
<tableRule name="hash-int_area_id"> <rule> <columns>area_id</columns> <algorithm>hash-int</algorithm> </rule> </tableRule> <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <!-- mapFile 文件名,位于config目录下 --> <property name="mapFile">partition-hash-int.txt</property> <!-- 指定mapFile中枚举的数据类型,0为整型;非0则为字符串类型 --> <property name="type">0</property> <!-- 是否启用默认节点,大于等于0为启用;小于等于0为不启用 --> <property name="defaultNode">0</property> </function>
mapFile
时须要注意这一点总结:
$MYCAT_HOME/conf
目录下增长mapFile
来配置枚举值同节点的映射关系hash(分片列) % 分片基数
io.mycat.route.function.PartitionByFileMap
以上所介绍到的分片算法都是根据列进行分片的,在实际工做中咱们可能会遇到这样一个需求:须要经过字符串的前几位或后几位进行分片。例如,经过订单号的前五位进行分片计算,或按用户的姓氏进行分片。在这种场景下,就可使用字符串范围取模分片算法。
例如,须要对ABCDEFGHI
这个字符串的前三位进行分片计算,其计算过程以下图:
mapFile
里配置的取值范围与数据节点索引的映射关系得出分片的数据节点。所以,mapFile
须要配置全部可能的取值范围,不然找不到对应的数据节点就会报错。工做原理:
配置示例:
<tableRule name="sharding-by-prefix-pattern_login_name"> <rule> <columns>login_name</columns> <algorithm>sharding-by-prefix-pattern</algorithm> </rule> </tableRule> <function name="sharding-by-prefix-pattern" class="io.mycat.route.function.PartitionByPrefixPattern"> <!-- mapFile 文件名,位于config目录下 --> <property name="mapFile">prefix-partition-pattern.txt</property> <!-- 求模基数 --> <property name="patternValue">128</property> <!-- 字符串范围,这里为前两位 --> <property name="prefixLength">2</property> </function>
总结:
$MYCAT_HOME/conf
目录下增长mapFile
来配置取模范围同节点的映射关系io.mycat.route.function.PartitionByPrefixPattern
用途:
schema
标签用于定义逻辑库,示例:
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"> ... </schema>
checkSQLschema
属性判断是否检查发给Mycat的SQL是否含有库名,为true
时会将SQL中的库名删除掉name
属性定义逻辑库的名字,必须惟一不能重复sqlMaxLimit
属性用于限制返回结果集的行数,值为-1
时表示关闭该限制。若是没有开启限制则默认取server.xml
里配置的限制randomDataNode
属性定义将一些随机语句发送到该数据节点中使用了schema
标签订义逻辑库以后,还须要使用table
标签订义逻辑表。示例:
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"> <!-- 多表定义 --> <table name="travelrecord,address" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" splitTableNames ="true"/> <!-- 单表定义 --> <table name="oc_call" primaryKey="id" dataNode="dn1$0-743" rule="latest-month-calldate"/> </schema>
name
属性定义逻辑表的名字,必须惟一不能重复且须要与数据库中的物理表名一致。使用逗号分割配置多个表,即多个表使用这个配置primaryKey
属性指定逻辑表中的主键,也是须要与物理表的主键一致dataNode
属性指定物理表所在数据节点的名称,配置多个数据节点时需按索引顺序并使用逗号分隔,或指定一个索引范围:dn1$0-743
。注意数据节点定义以后,顺序不能再发生改变,不然会致使数据混乱rule
属性用于指定分片规则名称,对应rule.xml
中的<tableRule>
标签的name
属性,如无需分片能够不指定splitTableNames
属性定义是否容许多个表的定义dataNode
标签用于定义数据节点,数据节点指向的是存储逻辑表的物理数据库。示例:
<dataNode name="dn1" dataHost="localhost1" database="db1" /> <dataNode name="dn2" dataHost="localhost1" database="db2" /> <dataNode name="dn3" dataHost="localhost1" database="db3" /> <!-- 能够配置一个范围 --> <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/>
name
属性定义数据节点的名称,必须惟一dataHost
属性指定分片所在的物理主机database
属性指定物理数据库的名称dataHost
标签用于定义后端物理数据库主机信息,该标签内有两个子标签,能够定义一组数据库主机信息。例如,定义一组主从集群结构的数据库主机信息:
writeHost
标签配置写实例,即主从中的master节点readHost
标签配置读实例,即主从中的salve节点readHost
是writeHost
的子标签,与writeHost
有绑定关系在一个dataHost
内能够定义多个writeHost
和readHost
。可是,若是writeHost
指定的后端数据库宕机,那么这个writeHost
绑定的全部readHost
都将不可用。另外一方面,因为这个writeHost
宕机系统会自动的检测到,并切换到备用的writeHost
上去。
配置示例:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- 能够配置多个写实例 --> <writeHost host="localhost" url="localhost:3306" user="root" password="123456"> <readHost host="localhost" url="localhost:3306" user="root" password="123456"></readHost> </writeHost> </dataHost>
name
属性用于定义主机名称,必须惟一maxCon
属性指定每一个读/写实例链接池的最大链接数。也就是说,标签内嵌套的writeHost
、readHost
标签都会使用这个属性的值来实例化出链接池的最大链接数minCon
属性指定每一个读写实例链接池的最小链接数,即初始化链接池的大小dbType
属性指定后端链接的数据库类型,目前支持二进制的mysql协议,还有其余使用JDBC
链接的数据库dbDriver
属性指定链接后端数据库使用的驱动,目前可选的值有native
和JDBC
slaveThreshold
属性用于定义主从复制延时阈值,当Seconds_Behind_Master > slaveThreshold
时,读写分离筛选器会过滤掉此Slave机器,防止读到好久以前的旧数据balance
属性指定读写分离的负载均衡类型,目前的取值有4 种:
0
:不开启读写分离机制,全部读操做都发送到当前可用的 writeHost
上1
:所有的readHost
与stand by writeHost
参与select
语句的负载均衡2
:全部读操做都随机在writeHost
、readhost
上分发3
:全部读请求随机分发到 wiriterHost
对应的readhost
执行。即 writerHost
不负担读压力,所有读请求由 readhost
执行。注意该取值只在1.4及其之后版本有,1.3没有writeType
属性指定写实例的负载均衡类型,目前的取值有4 种:
-1
:表示不自动切换0
:全部写操做发送到配置的第一个writeHost
,第一个挂了切到还生存的第二个writeHost
。从新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties
1
:全部写操做都随机的发送到配置的writeHost
,1.5 之后废弃不推荐使用2
:基于MySQL主从同步的状态决定是否切换(1.4 新增)switchType
属性用于指定主从切换的方式:
-1
:表示不自动切换1
:默认值,自动切换2
:基于MySQL主从同步的状态决定是否切换,心跳检测语句为:show slave status
3
:基于MySQL galary cluster的切换机制(适合集群,1.4.1新增),心跳检测语句为show status like 'wsrep%'
另外,slaveThreshold
属性是用于配合writeType
属性实现根据主从延时来进行主从切换的,其官方文档描述以下:
1.4 开始支持MySQL主从复制状态绑定的读写分离机制,让读更加安全可靠,配置以下:MyCAT 心跳检查语句配置为
show slave status
,dataHost
上定义两个新属性:switchType="2"
与slaveThreshold="100"
,此时意味着开启MySQL主从复制状态绑定的读写分离与切换机制,Mycat心跳机制经过检测show slave status
中的"Seconds_Behind_Master
"、"Slave_IO_Running
"、"Slave_SQL_Running
" 三个字段来肯定当前主从同步的状态以及Seconds_Behind_Master
主从复制时延,当Seconds_Behind_Master > slaveThreshold
时,读写分离筛选器会过滤掉此Slave机器,防止读到好久以前的旧数据,而当主节点宕机后,切换逻辑会检查Slave上的Seconds_Behind_Master
是否为0
,为0
时则表示主从同步,能够安全切换,不然不会切换。
heartbeat
标签内指明用于和后端数据库进行心跳检查的语句。例如,MySQL可使用select user()
,Oracle可使用select 1 from dual
等。
这个标签还有一个connectionInitSql
属性,主要是当使用Oracla数据库时,须要执行的初始化SQL语句就这个放到这里面来。例如:alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'
注:若是是配置主从切换的语句在1.4以后必须是:show slave status
这两个标签都用于配置一组主从数据库的相关信息,Mycat用这两个标签配置的链接信息实例化后端链接池。惟一不一样的是,writeHost
配置写实例(master)、readHost
配置读实例(salve),而且readHost
为writeHost
的子标签。经过这两个标签能够组合读/写实例以知足系统的要求。
在一个dataHost
内能够定义多个writeHost
和readHost
。可是,若是writeHost
指定的后端数据库宕机,那么这个writeHost
绑定的全部readHost
都将不可用。另外一方面,当一个writeHost
宕机时系统会自动检测到,并切换到备用的writeHost
上去。
这两个标签的属性相同,这里就一块儿介绍:
host
属性用于标识不一样实例名称,通常writeHost
名称使用M1
做为后缀,readHost
则使用S1
做为后缀url
属性用于配置数据库的链接地址,若是是使用native
的dbDriver
,则通常为address:port
这种形式。用JDBC
或其余的dbDriver
,则须要特殊指定。例如,当使用JDBC
时则能够这么写:jdbc:mysql://localhost:3306/
user
属性配置数据库用户名password
属性配置数据库密码weight
属性配置某个数据库在 readhost
中做为读节点的权重usingDecrypt
属性指定是否对密码加密,默认为0
, 若须要开启则配置为1
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"> <table name="travelrecord,address" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" splitTableNames="true"/> <!-- <table name="order" primaryKey="id" dataNode="dn1$0-743" rule="auto-sharding-long" splitTableNames="true"/> --> </schema> <dataNode name="dn1" dataHost="localhost1" database="db1"/> <dataNode name="dn2" dataHost="localhost1" database="db2"/> <dataNode name="dn3" dataHost="localhost1" database="db3"/> <!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/> --> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> <readHost host="hostS1" url="localhost:3306" user="root" password="123456"/> </writeHost> <writeHost host="hostM2" url="localhost:3306" user="root" password="123456"/> </dataHost> </mycat:schema>