第 7 章 Mycat 的配置

7.1 搞定 schema.xml

Schema.xml 作为 MyCat 中重要的配置文件之一,管理着 MyCat 的逻辑库、表、分片规则、DataNode 以及 DataSource。弄懂这些配置,是正确使用 MyCat 的前提。这里就一层层对该文件进行解析。

7.2 schema 标签

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100"></schema>

schema 标签用于定义 MyCat 实例中的逻辑库,MyCat 可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用 schema 标签来划分这些不同的逻辑库。

如果不配置 schema 标签,所有的表配置,会属于同一个默认的逻辑库。

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
	<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" ></table>
</schema>
<schema name="USERDB" checkSQLschema="false" sqlMaxLimit="100">
	<table name="company" dataNode="dn10,dn11,dn12" rule="auto-sharding-long" ></table>
</schema>

如上所示的配置就配置了两个不同的逻辑库,逻辑库的概念和 MYSQL 数据库中 Database 的概念相同,我们在查询这两个不同的逻辑库中表的时候需要切换到该逻辑库下才可以查询到所需要的表。

快速学习-Mycat的配置_sql
如果你发现显示该错误信息,需要到 server.xml 添加该用户可以访问到的 schema 就可以了。具体的内容待后续章节阐述。
快速学习-Mycat的配置_mysql_02

快速学习-Mycat的配置_mysql_03

schema 标签的相关属性:
快速学习-Mycat的配置_sql_04

7.2.1 dataNode

该属性用于绑定逻辑库到某个具体的 database 上,1.3 版本如果配置了 dataNode,则不可以配置分片表,1.4 可以配置默认分片,只需要配置需要分片的表即可,具体如下配置:

1.3 配置:

<schema name="USERDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
	<!—里面不能配置任何表-->
</schema>

1.4 配置:

<schema name="USERDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn2">
	<!—配置需要分片的表-->
	<table name=“tuser” dataNode=”dn1”/>
</schema>

那么现在 tuser 就绑定到 dn1 所配置的具体 database 上,可以直接访问这个 database,没有配置的表则会走默认节点 dn2,这里注意没有配置在分片里面的表工具查看无法显示,但是可以正常使用。

7.2.2 checkSQLschema

当该值设置为 true 时,如果我们执行语句select * from TESTDB.travelrecord;则 MyCat 会把语句修改为select * from travelrecord;。即把表示 schema 的字符去掉,避免发送到后端数据库执行时报**(ERROR 1146 (42S02): Table ‘testdb.travelrecord’ doesn’t exist)。**

不过,即使设置该值为 true ,如果语句所带的是并非是 schema 指定的名字,例如:select * from db1.travelrecord; 那么 MyCat 并不会删除 db1 这个字段,如果没有定义该库的话则会报错,所以在提供 SQL语句的最好是不带这个字段。

7.2.3 sqlMaxLimit

当该值设置为某个数值时。每条执行的 SQL 语句,如果没有加上 limit 语句,MyCat 也会自动的加上所对应的值。例如设置值为 100,执行**select * from TESTDB.travelrecord;的效果为和执行select * from TESTDB.travelrecord limit 100;**相同。

设置该值的话,MyCat 默认会把查询到的信息全部都展示出来,造成过多的输出。所以,在正常使用中,还是建议加上一个值,用于减少过多的数据返回。

当然 SQL 语句中也显式的指定 limit 的大小,不受该属性的约束。

需要注意的是,如果运行的 schema 为非拆分库的,那么该属性不会生效。需要手动添加 limit 语句。

7.3 table 标签

<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" ></table>

Table 标签定义了 MyCat 中的逻辑表,所有需要拆分的表都需要在这个标签中定义。
table 标签的相关属性:
快速学习-Mycat的配置_mysql_05

7.3.1 name 属性

定义逻辑表的表名,这个名字就如同我在数据库中执行 create table 命令指定的名字一样,同个 schema 标签中定义的名字必须唯一。

7.3.2 dataNode 属性

定义这个逻辑表所属的 dataNode, 该属性的值需要和 dataNode 标签中 name 属性的值相互对应。如果需要定义的 dn 过多 可以使用如下的方法减少配置:

<table name="travelrecord" dataNode="multipleDn$0-99,multipleDn2$100-199" rule="auto-shardinglong" ></table>
<dataNode name="multipleDn$0-99" dataHost="localhost1" database="db$0-99" ></dataNode>
<dataNode name="multipleDn2$100-199" dataHost="localhost1" database=" db$100-199" ></dataNode>

这里需要注意的是 database 属性所指定的真实 database name 需要在后面添加一个,例如上面的例子中,我需要在真实的 mysql 上建立名称为 dbs0 到 dbs99 的 database。

7.3.3 rule 属性

该属性用于指定逻辑表要使用的规则名字,规则名字在 rule.xml 中定义,必须与 tableRule 标签中 name 属性属性值一一对应。

7.3.4 ruleRequired 属性

该属性用于指定表是否绑定分片规则,如果配置为 true,但没有配置具体 rule 的话 ,程序会报错。

7.3.5 primaryKey 属性

该逻辑表对应真实表的主键,例如:分片的规则是使用非主键进行分片的,那么在使用主键查询的时候,就会发送查询语句到所有配置的 DN 上,如果使用该属性配置真实表的主键。难么 MyCat 会缓存主键与具体 DN 的信息,那么再次使用非主键进行查询的时候就不会进行广播式的查询,就会直接发送语句给具体的 DN,但是尽管配置该属性,如果缓存并没有命中的话,还是会发送语句给具体的 DN,来获得数据。

7.3.6 type 属性

该属性定义了逻辑表的类型,目前逻辑表只有“全局表”和”普通表”两种类型。对应的配置:

  • 全局表:global。
  • 普通表:不指定该值为 globla 的所有表。

7.3.7 autoIncrement 属性

mysql 对非自增长主键,使用 last_insert_id()是不会返回结果的,只会返回 0。所以,只有定义了自增长主键的表才可以用 last_insert_id()返回主键值。

mycat 目前提供了自增长主键功能,但是如果对应的 mysql 节点上数据表,没有定义 auto_increment,那么在 mycat 层调用 last_insert_id()也是不会返回结果的。

由于 insert 操作的时候没有带入分片键,mycat 会先取下这个表对应的全局序列,然后赋值给分片键。这样才能正常的插入到数据库中,最后使用 last_insert_id()才会返回插入的分片键值。

如果要使用这个功能最好配合使用数据库模式的全局序列。

使用 autoIncrement=“true” 指定这个表有使用自增长主键,这样 mycat 才会不抛出分片键找不到的异常。
使用 autoIncrement=“false” 来禁用这个功能,当然你也可以直接删除掉这个属性。默认就是禁用的。

7.3.8 subTables

使用方式添加 subTables=“t_order$1-2,t_order3”。

目前分表 1.6 以后开始支持 并且 dataNode 在分表条件下只能配置一个,分表条件下不支持各种条件的join 语句。

7.3.9 needAddLimit 属性

指定表是否需要自动的在每个语句后面加上 limit 限制。由于使用了分库分表,数据量有时会特别巨大。这时候执行查询语句,如果恰巧又忘记了加上数量限制的话。那么查询所有的数据出来,也够等上一小会儿的。

所以,mycat 就自动的为我们加上 LIMIT 100。当然,如果语句中有 limit,就不会在次添加了。这个属性默认为 true,你也可以设置成 false`禁用掉默认行为。

7.4 childTable 标签

childTable 标签用于定义 E-R 分片的子表。通过标签上的属性与父表进行关联。childTable 标签的相关属性:
快速学习-Mycat的配置_mysql_06

7.4.1 name 属性

定义子表的表名。

7.4.2 joinKey 属性

插入子表的时候会使用这个列的值查找父表存储的数据节点。

7.4.3 parentKey 属性

属性指定的值一般为与父表建立关联关系的列名。程序首先获取 joinkey 的值,再通过 parentKey 属性指定的列名产生查询语句,通过执行该语句得到父表存储在哪个分片上。从而确定子表存储的位置。

7.4.4 primaryKey 属性

同 table 标签所描述的。

7.4.5 needAddLimit 属性

同 table 标签所描述的。

7.5 dataNode 标签

<dataNode name="dn1" dataHost="lch3307" database="db1" ></dataNode>

dataNode 标签定义了 MyCat 中的数据节点,也就是我们通常说所的数据分片。一个 dataNode 标签就是一个独立的数据分片。

例子中所表述的意思为:使用名字为 lch3307 数据库实例上的 db1 物理数据库,这就组成一个数据分片,最后,我们使用名字 dn1 标识这个分片。

dataNode 标签的相关属性:
快速学习-Mycat的配置_默认值_07

7.5.1 name 属性

定义数据节点的名字,这个名字需要是唯一的,我们需要在 table 标签上应用这个名字,来建立表与分片对应的关系。

7.5.2 dataHost 属性

该属性用于定义该分片属于哪个数据库实例的,属性值是引用 dataHost 标签上定义的 name 属性。

7.5.3 database 属性

该属性用于定义该分片属性哪个具体数据库实例上的具体库,因为这里使用两个纬度来定义分片,就是:实例+具体的库。因为每个库上建立的表和表结构是一样的。所以这样做就可以轻松的对表进行水平拆分。

7.6 dataHost 标签

作为 Schema.xml 中最后的一个标签,该标签在 mycat 逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。现在我们就解析下这个标签。

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="localhost:3306" user="root" 
password="123456">
<!-- can have multi read hosts -->
<!-- <readHost host="hostS1" url="localhost:3306" user="root" password="123456"
/> -->
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>

dataHost 标签的相关属性:
快速学习-Mycat的配置_mysql_08

7.6.1 name 属性

唯一标识 dataHost 标签,供上层的标签使用。

7.6.2 maxCon 属性

指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的 writeHost、readHost 标签都会使用这个属性的值来实例化出连接池的最大连接数。

7.6.3 minCon 属性

指定每个读写实例连接池的最小连接,初始化连接池的大小。

7.6.4 balance 属性

负载均衡类型,目前的取值有 3 种:

  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”,所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力,

注意 balance=3 只在 1.4 及其以后版本有,1.3 没有。

7.6.5 writeType 属性

负载均衡类型,目前的取值有 3 种:

  1. writeType=“0”, 所有写操作发送到配置的第一个 writeHost,第一个挂了切到还生存的第二个 writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties .
  2. writeType=“1”,所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐。switchType 属性
  • -1 表示不自动切换。
  • 1 默认值,自动切换。
  • 2 基于 MySQL 主从同步的状态决定是否切换。

7.6.6 dbType 属性

指定后端连接的数据库类型,目前支持二进制的 mysql 协议,还有其他使用 JDBC 连接的数据库。例如:mongodb、oracle、spark 等。

7.6.7 dbDriver 属性

指定连接后端数据库使用的 Driver,目前可选的值有 native 和 JDBC。使用 native 的话,因为这个值执行的是二进制的 mysql 协议,所以可以使用 mysql 和 maridb。其他类型的数据库则需要使用 JDBC 驱动来支持。

从 1.6 版本开始支持 postgresql 的 native 原始协议。

如果使用 JDBC 的话需要将符合 JDBC 4 标准的驱动 JAR 包放到 MYCAT\lib 目录下,并检查驱动 JAR 包中包括如下目录结构的文件:META-INF\services\java.sql.Driver。在这个文件内写上具体的 Driver 类名,例如:com.mysql.jdbc.Driver

7.6.8 switchType 属性

-1 表示不自动切换
1 默认值,自动切换
2 基于 MySQL 主从同步的状态决定是否切换心跳语句为 show slave status
3 基于 MySQL galary cluster 的切换机制(适合集群)(1.4.1)心跳语句为 show status like ‘wsrep%’

7.6.9 tempReadHostAvailable 属性

如果配置了这个属性 writeHost 下面的 readHost 仍旧可用,默认 0 可配置(0、1)。

7.7 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

7.7.1 writeHost 标签、readHost 标签

这两个标签都指定后端数据库的相关配置给 mycat,用于实例化后端连接池。唯一不同的是,writeHost 指定写实例、readHost 指定读实例,组着这些读写实例来满足系统的要求。

在一个 dataHost 内可以定义多个 writeHost 和 readHost。但是,如果 writeHost 指定的后端数据库宕机,那么这个 writeHost 绑定的所有 readHost 都将不可用。另一方面,由于这个 writeHost 宕机系统会自动的检测到,并切换到备用的 writeHost 上去。

这两个标签的属性相同,这里就一起介绍。
快速学习-Mycat的配置_默认值_09

7.7.2 host 属性

用于标识不同实例,一般 writeHost 我们使用M1,readHost 我们用S1。

7.7.3 url 属性

后端实例连接地址,如果是使用 native 的 dbDriver,则一般为 address:port 这种形式。用 JDBC 或其他的dbDriver,则需要特殊指定。当使用 JDBC 时则可以这么写:jdbc:mysql://localhost:3306/。

7.7.4 user 属性

后端存储实例需要的用户名字。

7.7.5 password 属性

后端存储实例需要的密码。

7.7.6 weight 属性

权重 配置在 readhost 中作为读节点的权重(1.4 以后)。

7.7.7 usingDecrypt 属性

是否对密码加密默认 0 否 如需要开启配置 1,同时使用加密程序对密码加密,加密命令为:

执行 mycat jar 程序(1.4.1 以后):

java -cp Mycat-server-1.4.1-dev.jar io.mycat.util.DecryptUtil 1:host:user:password
Mycat-server-1.4.1-dev.jar 为 mycat download 下载目录的 jar
1:host:user:password 中 1 为 db 端加密标志,host 为 dataHost 的 host 名称

7.8 server.xml

7.8.1 配置

server.xml 几乎保存了所有 mycat 需要的系统配置信息。其在代码内直接的映射类为 SystemConfig 类。

7.8.2 user 标签

<user name="test">
	<property name="password">test</property>
	<property name="schemas">TESTDB</property>
	<property name="readOnly">true</property>
	<property name="benchmark">11111</property>
	<property name="usingDecrypt">1</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>

server.xml 中的标签本就不多,这个标签主要用于定义登录 mycat 的用户和权限。例如上面的例子中,我定义了一个用户,用户名为 test、密码也为 test,可访问的 schema 也只有 TESTDB 一个。

如果我在 schema.xml 中定义了多个 schema,那么这个用户是无法访问其他的 schema。在 mysql 客户端看来则是无法使用 use 切换到这个其他的数据库。

如果使用了 use 命令,则 mycat 会报出这样的错误提示:

ERROR 1044 (HY000): Access denied for user 'test' to database 'xxx'

这个标签嵌套的 property 标签则是具体声明的属性值,正如上面的例子。我们可以修改 user 标签的name 属性来指定用户名;修改 password 内的文本来修改密码;修改 readOnly 为 true 或 false 来限制用户是否只是可读的;修改 schemas 内的文本来控制用户可放问的 schema;修改 schemas 内的文本来控制用户可访问的 schema,同时访问多个 schema 的话使用 , 隔开,例如:

<property name="schemas">TESTDB,db1,db2</property>

Benchmark 属性

Benchmark:mycat 连接服务降级处理:
benchmark 基准, 当前端的整体 connection 数达到基准值是, 对来自该账户的请求开始拒绝连接,0 或不设
表示不限制
例如 <property name="benchmark">1000</property>

usingDecrypt 属性

是否对密码加密默认 0 否 如需要开启配置 1,同时使用加密程序对密码加密,加密命令为:执行 mycat jar 程序:

java -cp Mycat-server-1.4.1-dev.jar io.mycat.util.DecryptUtil 0:user:password
Mycat-server-1.4.1-dev.jar 为 mycat download 下载目录的 jar
1:host:user:password 中 0 为前端加密标志

privileges 子节点

对用户的 schema 及 下级的 table 进行精细化的 DML 权限控制,privileges 节点中的 check 属性是用于标识是否开启 DML 权限检查, 默认 false 标识不检查,当然 privileges 节点不配置,等同 check=false,由于 Mycat 一个用户的 schemas 属性可配置多个 schema ,所以 privileges 的下级节点 schema 节点同样可配置多个,对多库多表进行细粒度的 DML 权限控制Schema/Table 上的 dml 属性描述
快速学习-Mycat的配置_mysql_10
注: 设置了 schema , 但只设置了个别 table 或 未设置 table 的 DML,自动继承 schema 的 DML 属性

privileges 配置事例如下:

<user name="zhuam">
	<property name="password">111111</property>
	<property name="schemas">TESTDB,TESTDB1</property>
	<!-- 表级权限: Table 级的 dml(curd)控制,未设置的 Table 继承 schema 的 dml -->
	<!-- TODO: 非 CURD SQL 语句, 透明传递至后端 -->
	<privileges check="true">
	<schema name="TESTDB" dml="0110" >
	<table name="table01" dml="0111"></table>
	<table name="table02" dml="1111"></table>
	</schema>
	<schema name="TESTDB1" dml="0110">
	<table name="table03" dml="1110"></table>
	<table name="table04" dml="1010"></table>
	</schema>
	</privileges>
</user>

7.9 system 标签

这个标签内嵌套的所有 property 标签都与系统配置有关,请注意,下面我会省去标签 property 直接使用这个标签的 name 属性内的值来介绍这个属性的作用。

7.9.1 charset 属性

字符集设置。

配置属性 charset
<system> <property name="charset">utf8</property> </system>
如果需要配置 utf8mb2 等特殊字符集可以在
index_to_charset.properties 配置中
配置数据库短的字符集 ID=字符集
例如:
224=utf8mb4
配置字符集的时候一定要坚持 mycat 的字符集与数据库端的字符集是一致的,可以通过变量来查询:
show variables like 'collation_%'; 
show variables like 'character_set_%';

7.9.2 defaultSqlParser 属性

由于 mycat 最初是时候 Foundation DB 的 sql 解析器,而后才添加的 Druid 的解析器。所以这个属性用来指定默认的解析器。目前的可用的取值有:druidparser 和 fdbparser。使用的时候可以选择其中的一种,目前一般都使用 druidparser。

1.3 解析器默认为 fdbparser,1.4 默认为 druidparser,1.4 以后 fdbparser 作废。

7.9.3 processors 属性

这个属性主要用于指定系统可用的线程数,默认值为机器 CPU 核心线程数。主要影响 processorBufferPool、processorBufferLocalPercent、processorExecutor 属性。

NIOProcessor 的个数也是由这个属性定义的,所以调优的时候可以适当的调高这个属性。

7.9.4 processorBufferChunk 属性

这个属性指定每次分配 Socket Direct Buffer 的大小,默认是 4096 个字节。这个属性也影响 buffer pool 的长度。如果一次性获取的数过大 buffer 不够用 经常出现警告,则可以适当调大。

7.9.5 processorBufferPool 属性

这个属性指定 bufferPool 计算 比例值。由于每次执行 NIO 读、写操作都需要使用到 buffer,系统初始化的时候会建立一定长度的 buffer 池来加快读、写的效率,减少建立 buffer 的时间。

Mycat 中有两个主要的 buffer 池:

- BufferPool
- ThreadLocalPool

BufferPool 由 ThreadLocalPool 组合而成,每次从 BufferPool 中获取 buffer 都会优先获取ThreadLocalPool 中的 buffer,未命中之后才会去获取 BufferPool 中的 buffer。也就是说 ThreadLocalPool 是作为 BufferPool 的二级缓存,每个线程内部自己使用的。当然,这其中还有一些限制条件需要线程的名字是由$_开头。然而,BufferPool 上的 buffer 则是每个 NIOProcessor 都共享的。

默认这个属性的值为: 默认 bufferChunkSize(4096) * processors 属性 * 1000BufferPool 的总长度 = bufferPool / bufferChunk。

若 bufferPool 不是 bufferChunk 的整数倍,则总长度为前面计算得出的商 + 1假设系统线程数为 4,其他都为属性的默认值,则:

bufferPool = 4096 * 4 * 1000
BufferPool 的总长度 : 4000 = 16384000 / 4096

快速学习-Mycat的配置_默认值_11

7.9.6 processorBufferLocalPercent 属性

前面提到了 ThreadLocalPool。这个属性就是用来控制分配这个 pool 的大小用的,但其也并不是一个准确的值,也是一个比例值。这个属性默认值为 100。

线程缓存百分比 = bufferLocalPercent / processors 属性。
例如,系统可以同时运行 4 个线程,使用默认值,则根据公式每个线程的百分比为 25。最后根据这个百分比来计算出具体的 ThreadLocalPool 的长度公式如下:

ThreadLocalPool 的长度 = 线程缓存百分比 * BufferPool 长度 / 100
假设 BufferPool 的长度为 4000,其他保持默认值。
那么最后每个线程建立上的 ThreadLocalPool 的长度为: 1000 = 25 * 4000 / 100

7.9.7 processorExecutor 属性

这个属性主要用于指定 NIOProcessor 上共享的 businessExecutor 固定线程池大小。mycat 在需要处理一些异步逻辑的时候会把任务提交到这个线程池中。新版本中这个连接池的使用频率不是很大了,可以设置一个较小的值。

7.9.8 sequnceHandlerType 属性

指定使用 Mycat 全局序列的类型。0 为本地文件方式,1 为数据库方式,2 为时间戳序列方式,3 为分布式ZK ID 生成器,4 为 zk 递增 id 生成。

从 1.6 增加 两种 ZK 的全局 ID 生成算法。

7.9.9 TCP 连接相关属性

  • StandardSocketOptions.SO_RCVBUF
  • StandardSocketOptions.SO_SNDBUF
  • StandardSocketOptions.TCP_NODELAY

以上这三个属性,分别由:

frontSocketSoRcvbuf 默认值: 1024 * 1024
frontSocketSoSndbuf 默认值: 4 * 1024 * 1024
frontSocketNoDelay 默认值: 1
backSocketSoRcvbuf 默认值: 4 * 1024 * 1024
backSocketSoSndbuf 默认值: 1024 * 1024
backSocketNoDelay 默认值: 1

各自设置前后端 TCP 连接参数。Mycat 在每次建立前、后端连接的时候都会使用这些参数初始化连接。可以按系统要求适当的调整这些 buffer 的大小。TCP 连接参数的定义,可以查看 Javadoc。

7.9.10 Mysql 连接相关属性

初始化 mysql 前后端连接所涉及到的一些属性:

packetHeaderSize : 指定 Mysql 协议中的报文头长度。默认 4。
maxPacketSize : 指定 Mysql 协议可以携带的数据最大长度。默认 16M。
idleTimeout : 指定连接的空闲超时时间。某连接在发起空闲检查下,发现距离上次使用超过了空闲时间,那
么这个连接会被回收,就是被直接的关闭掉。默认 30 分钟,单位毫秒。
charset : 连接的初始化字符集。默认为 utf8。
txIsolation : 前端连接的初始化事务隔离级别,只在初始化的时候使用,后续会根据客户端传递过来的属性对

后端数据库连接进行同步。默认为 REPEATED_READ,设置值为数字默认 3。

READ_UNCOMMITTED = 1;
READ_COMMITTED = 2;
REPEATED_READ = 3;
SERIALIZABLE = 4;

sqlExecuteTimeout:SQL 执行超时的时间,Mycat 会检查连接上最后一次执行 SQL 的时间,若超过这个时间则会直接关闭这连接。默认时间为 300 秒,单位秒。

7.9.11 心跳属性

mycat 中有几个周期性的任务来异步的处理一些我需要的工作。这些属性就在系统调优的过程中也是比不可少的。

processorCheckPeriod : 清理 NIOProcessor 上前后端空闲、超时和关闭连接的间隔时间。默认是 1 秒,单位毫秒。
dataNodeIdleCheckPeriod : 对后端连接进行空闲、超时检查的时间间隔,默认是 300 秒,单位毫秒。
dataNodeHeartbeatPeriod : 对后端所有读、写库发起心跳的间隔时间,默认是 10 秒,单位毫秒。

7.9.12 服务相关属性

这里介绍一个与服务相关的属性,主要会影响外部系统对 mycat 的感知。

bindIp : mycat 服务监听的 IP 地址,默认值为 0.0.0.0。
serverPort : 定义 mycat 的使用端口,默认值为 8066。
managerPort : 定义 mycat 的管理端口,默认值为 9066。

7.9.13 fakeMySQLVersion

mycat 模拟的 mysql 版本号,默认值为 5.6 版本,如非特需,不要修改这个值,目前支持设置 5.5,5.6 版本,其他版本可能会有问题。

此特性从 1.6 版本开始支持。

7.9.14 全局表一致性检测

<property name="useGlobleTableCheck">0</property> <!-- 1 为开启全加班一致性检测、0 为关闭 -->

原理通过在全局表增加_MYCAT_OP_TIME 字段来进行一致性检测,类型为 bigint,create 语句通过 mycat执行会自动加上这个字段,其他情况请自己手工添加。

此特性从 1.6 版本开始支持。

“增加 mycat 新任务,全局表定义中,需要有一个时间戳字段,每次记录的 update,insert,确保时间字段赋值,并且 mycat 增加定时检测逻辑,检测记录总量,以及最新时间戳的匹配,简单有效的发现全局表不一致的问题。/ 测试修复类 / 1.5&2.0 /12.9 /leader-us”全局表一致性定时检测主要分为两个部分:

  1. SQL 拦截部分
    主要实现对所有全局表中记录进行修改的语句进行拦截,比如:
ServerParse.INSERT,
ServerParse.UPDATE,
ServerParse.REPLACE(mycat-server 不支持) 

对所有对全局表的 insert, update 操作进行拦截,首先判断该全局表是否存在一个记录时间戳的内部列_mycat_op_time

public class GlobalTableUtil{ 
/** 全局表 保存修改时间戳的字段名,用于全局表一致性检查 */ 
public static final String GLOBAL_TABLE_MYCAT_COLUMN = "_mycat_op_time"; 
如果不存在,输出警告,哪个 db 的哪个全局表没有内部列:
if(innerColumnNotExist.size() > 0){ 
for(SQLQueryResult<Map<String, String>> map : innerColumnNotExist){ 
 
if(tableName.equalsIgnoreCase(map.getTableName())){ 
StringBuilder warnStr = new StringBuilder(); 
if(map != null) 
warnStr.append(map.getDataNode()).append("."); 
 
warnStr.append(tableName).append(" inner column: ") 
 
.append(GlobalTableUtil.GLOBAL_TABLE_MYCAT_COLUMN) 
 
.append(" is not exist."); 
 
LOGGER.warn(warnStr.toString()); 
 
return sql; 
 
} 
} 
} 
然后返回原始 sql. 不需要进行拦截。
如果存在一个记录时间戳的内部列,那么对该 insert 或者 update 语句进行 SQL 拦截修改:
if(sqlType == ServerParse.INSERT){ 
sql = convertInsertSQL(sql, tableName); 
} 
if(sqlType == ServerParse.UPDATE){ 
sql = convertUpdateSQL(sql, tableName); 
} 

1.1 insert语句的拦截逻辑
对所有对全局表进行insert的sql语句,进行改写,比如下面的user是全局表:

insert into user(id,name) 
valueS(1111,'dig'), 
(1111, 'dig'), 
(1111,'dig') , 
(1111,'dig'); 
会被改写成:
insert into user(id,name, _mycat_op_time) 
valueS(1111,'dig', 1450423751170), 
(1111, 'dig', 1450423751170), 
(1111,'dig', 1450423751170) , 
(1111,'dig', 1450423751170); 
其中_mycat_op_time 是内部列的名称:
public static final String GLOBAL_TABLE_MYCAT_COLUMN = "_mycat_op_time";
而1450423751170 是在插入时在 mycat-server上生成的一个时间戳对应的long整数(对应到数据库
是bigint)。然后该语句发送给所有db在其全局表中进行插入。
如果insert语句自带了内部列_mycat_op_time,比如:
insert into user(id,name, _mycat_op_time) 
valueS(1111,'dig',13545); 
那么会输出警告,并且也进行拦截改写成如下形式:
insert into user(id,name, _mycat_op_time) 
valueS(1111,'dig', 1450423751170);
然后发送给所有db在其全局表中进行插入。
对mycat-server不支持的sql语句,本拦截器,不进行任何操作,直接返回原始sql。如果在拦截过
程中发生任何异常,也返回原始sql语句,不进行任何修改操作。保证该拦截不会影响系统原
有的健壮性。

1.2 update语句的拦截逻辑

Update语句的拦截逻辑和insert语句原理是相似的。也是判断是否有内部列。
如果没有输出警告信息,如果有则进行拦截。
对全局表 user 的如下update: 
update user set name='dddd',pwd='aaa' 
where id=2 
会被改写成:
update user set name='dddd',pwd='aaa', _mycat_op_time=1450423751170
where id=2 
如果原始sql带有_mycat_op_time 那么进行警告,然后替换它的值,比如:
update user set name='dddd',pwd='aaa', _mycat_op_time=1111
where id=2; 
会被改写成:
update user set name='dddd',pwd='aaa', _mycat_op_time=1450423751170
where id=2; 
然后将语句发送给所有的全局表进行执行。
这样的话,如果有哪个表上的insert,update执行失败,那么内部列_mycat_op_time 的最大值,以
及全局表的记录总数就会不一致。Delete语句也一样,只是无需拦截。下面的检查机制就是根
据这个原理来操作的。
  1. 一致性的定时检测
在MycatServer的startup中引入一个定时检查任务:
timer.schedule(glableTableConsistencyCheck(), 0L, 1000 * 1000L); 
// 全局表一致性检查任务
private TimerTask glableTableConsistencyCheck() { 
return new TimerTask() { 
}; 
@Override 
public void run() { 
} 
timerExecutor.execute(new Runnable() { 
@Override 
public void run() { 
GlobalTableUtil.consistencyCheck(); 
} 
}); 
其实现在GlobalTableUtil 类中:
该类首先获得所有的全局表:
static { 
getGlobalTable(); // 初始化 globalTableMap 
} 
其实现,参见代码。
GlobalTableUtil.consistencyCheck() 的实现,主要思路是,首先根据所有的全局表,找到对应的
PhysicalDBNode,然后找到对应的PhysicalDatasource,然后对PhysicalDatasource中的所有
db进行三项检测:

2.1 检测全局表的内部列是否存在

checker.checkInnerColumnExist(); 
检测的实现是通过一个SQLJob来异步操作的,对应的SQL语句为:
select count(*) as inner_col_exist from information_schema.columns where column_name=' 
_mycat_op_time' and table_name='user' and table_schema='db1'; 
如果返回的inner_col_exist 大于0,那么就表示存在内部列,如果等于0,那么就表示不存在内部列。
如果PhysicalDatasource上某个db的全局表没有内部列,那么将这些db记录在一个list中,然后在
SQL 拦截过程中进行判断,如果是全局表,但是没有内部列,那么就输出警告,不对SQL进行
拦截改写,因为该全局表没有内部列,无需改写SQL。在第一项检测完成之后,才能进行第二
项检测。

2.2 检测全局表的记录总数

checker.checkRecordCout(); 
检查过程是类似的,都是通过SQLjob来完成的,只是对应的语句不一样:
select count(*) as record_count from user; (假设user表为全局表) 

2.3 检测全局表的时间戳的最大值

checker.checkMaxTimeStamp(); 
检查过程是类似的,都是通过SQLjob来完成的,只是对应的语句不一样:
select max(_mycat_op_time) as max_timestamp from user (假设user表为全局表) 
三项检查完成之后,就获得了如下所示的结果:
全局表的记录总数(user表为全局表,并且系统有三个db):
db1. user.record_count: 43546565 
db2. user.record_count: 43546565 
db3. user.record_count: 43546565 
全局表的最大时间戳:
db1. user.max_timestamp: 1450578802241 
db2. user.max_timestamp: 1450578802241 
db3. user.max_timestamp: 1450578802241 
然后前端,比如 mycat-eye 就可以将该结果显示出来。目前直接在log中输出,也可以考虑引入像
H2这样的Java实现的嵌入式数据库来记录该结果。H2实现为仅仅一个jar包,十分适合作为
mycat-server层面的一个非文件存储方式。有一些信息如果存在在文件中,查询起来不太方便,
比如上面的检测结果就是如此。
实际的SQLJob的执行,主要参照了原有的heartbeat的实现,主要在下面两个类中:
MySQLConsistencyChecker 
MySQLConsistencyHelper 
具体可以参考代码,和heartbeat的实现基本是一样的。
每一次定时检查,会对所有全局表进行上述三项检测。
总结成一句:
SQL的拦截实现记录全局表被修改时的时间戳;定时任务实现对全局表记录总数和时间戳最大值的获
取。
  1. 如何使用全局表一致性检测
1> 在所有全局表中增加一个 bigint 的内部列,列名为 _mycat_op_time,(alter table t add 
column _mycat_op_time bigint [not null default 0]); 同时建议在该列上建立索引(alter 
table t add index _op_idx(_mycat_op_time)) 
在对全局表进行crud时,最好将内部列当作不存在一样,也就是最好不要对内部列
update,insert等操作,不然会在Log中进行警告:不用操作内部列; 
因为全局表多了一个内部列,所以在对全局表进行insert时,必须携带列名,也就是insert 
into t(id,name) values(xx,xx),不能使用insert into t values(xx,xx); 因为会报错:列数
不对。这是唯一的一个小问题。未来可能会fix掉。

7.9.15分布式事务开关

<!--分布式事务开关,0 为不过滤分布式事务,1 为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2 为不过滤分布
式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
主要应用场景,主要为了控制是否允许跨库事务。

此特性从 1.6 版本开始支持。

7.9.16 Off Heap forMycat

此特性从 1.6 版本开始支持。

<!--
off heap for merge/order/group/limit
-->
1 开启
 
0 关闭
<property name="useOffHeapForMerge">1</property>
1.使用非堆内存(Direct Memory)处理跨分片结果集的 Merge/order by/group by/limit。
2.通过 server.xml 中的 useOffHeapForMerge 参数配置是否启用非堆内存处理跨分片结果集
3.Mycat 内存分层管理:
a.结果集处理内存;
b.系统预留内存;
c.网络处理内存共三块。
其中网络处理内存部分全部为 Direct Memory,结果集内存分为 Direct Memory 和 HeapMemory。
但目前仅使用 Direct Memory。系统预留内存为 On Heap Memory。JVM 参数,必须设置-
XX:MaxDirectMemorySize 和 -Xmx
例如:-Xmx1024m -Xmn512m -XX:MaxDirectMemorySize=2048m -Xss256K -XX:+UseParallelGC
上述分层可以避免 OOM 问题,以及减少 Full GC 回收时间,提高 mycat 响应速度。
4.使用 TimeSort 和 RadixSort,跨分片结果集合并排序使用 PriorityQueue,其中经测试 RadixSort 适合
LONG,INT,SHORT,Float,Double,String 数据类型排序,性能优越。
5.Java obj 连续内存存取,二进制序列化和反序列化,使用缓存友好的数据结构 Map 和 Row。
6.支持内存和外存并存的排序方式,结果集排序可以达上亿规模。此时应注意:
a.此时前端和后端空闲连接超时检测时间应该设置大些,避免空闲检测关闭 front 或者 backend
connection,造成 Mysqlclient 连接丢失时结果集无法正确。
b.设置-Xmn 值尽可能大些,新生代使用 UseParallelGC 垃圾回收器,-Xss 设置 512K 比较合适,物理内
存足够时,MaxDirectMemorySize 尽可能设置大些,可以加快结果集处理时间,
例如:-Xmx1024m -Xmn512m -XX:MaxDirectMemorySize=2048m -Xss256k -XX:+UseParallelGC。

7.10 rule.xml

rule.xml 里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。这个文件里面主要有 tableRule 和 function 这两个标签。在具体使用过程中可以按照需求添加 tableRule 和 function。

7.11 tableRule 标签

这个标签定义表规则。

定义的表规则,在 schema.xml:

<tableRule name="rule1">
<rule>
<columns>id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>

name 属性指定唯一的名字,用于标识不同的表规则。
内嵌的 rule 标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。
columns 内指定要拆分的列名字。
algorithm 使用 function 标签中的 name 属性。连接表规则和具体路由算法。当然,多个表规则可以连接到同一个路由算法上。table 标签内使用。让逻辑表使用这个规则进行分片。

7.12 function 标签

<function name="hash-int"
	class="io.mycat.route.function.PartitionByFileMap">
	<property name="mapFile">partition-hash-int.txt</property>
</function>

name 指定算法的名字。
class 制定路由算法具体的类名字。
property 为具体算法需要用到的一些属性。
路由算法的配置可以查看算法章节。