概述
server.xml 几乎保存了所有 mycat 需要的系统配置信息。其在代码内直接的映射类为 SystemConfig 类。
1.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'
- user标签嵌套的 property 标签则是具体声明的属性值;
- 修改 user 标签的name 属性来指定用户名;
- 修改 password 内的文本来修改密码;
- 修改 readOnly 为 true 或 false 来限制用户是否只是可读的;
- 修改 schemas 内的文本来控制用户可放问的 schema,同时访问多个 schema 的话使用“ ,” 隔开,例如:
<property name="schemas">TESTDB,db1,db2</property>
Benchmark 属性
Benchmark基准实现mycat 连接服务降级处理:
当前端的整体 connection 数达到基准值时, 对来自该账户的请求开始拒绝连接, 默认是0 或不设置限制。
表示不限制:
<property name="benchmark">1000</property>
usingDecrypt 属性
是否对密码加密:
- 默认 0 否
- 如需要开启配置 1,同时使用加密程序对密码加密,加密命令为:
java -cp Mycat-server-1.4.1 -dev.jar io.mycat.util.DecryptUtil 0:user:password
执行上面的mycat jar 程序:
- Mycat-server-1.4.1 -dev.jar 为 mycat download 下载目录的 jar
- 1:host:user:password 中 0 为前端加密标志
2.privileges 子节点
<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>
对用户的 schema及下级的 table 进行精细化的 DML 权限控制:
- privileges 节点中的 check 属性是用于标识是否开启 DML 权限检查, 默认 false 标识不检查,当然 privileges 节点不配置,等同 check=false;
-
由于Mycat 一个用户的 schemas 属性可配置多个schema,所以privileges 的下级节点 schema 节点同样可配置多个,对多库多表进行细粒度的 DML 权限控制。
Schema/Table 上的 DML 属性描述,如果只设置了schema个别 table 或 未设置 table 的 DML,自动继承 schema 的 DML 属性:
privileges.png
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>
3.system标签
这个标签内嵌套的所有 property 标签都与系统配置有关,请注意,下面省去标签 property 直接使用这个标签的 name 属性内的值来介绍这个属性的作用。
charset 属性
字符集设置:
<system> <property name="charset">utf8</property> </system>
配置字符集的时候一定要坚持 mycat 的字符集与数据库端的字符集是一致的,可以通过变量来查询:
show variables like 'collation_%';
show variables like 'character_set_%';
defaultSqlParser 属性
由于 mycat 最初是时候 Foundation DB 的 sql 解析器,而后才添加的 Druid 的解析器。所以这个属性用来指定默认的解析器。目前的可用的取值有: druidparser 和 fdbparser。使用的时候可以选择其中的一种,目前一般都使用 druidparser。
1.3 解析器默认为 fdbparser, 1.4 默认为 druidparser, 1.4 以后 fdbparser 作废。
processors 属性
这个属性主要用于指定系统可用的线程数,默认值为机器 CPU 核心线程数。
processorBufferChunk 属性
这个属性指定每次分配 Socket Direct Buffer 的大小,默认是 4096 个字节。这个属性也影响 buffer pool 的长度。如果一次性获取的数过大 buffer 不够用 经常出现警告,则可以适当调大。
processorBufferPool 属性
这个属性指定 bufferPool 计算 比例值。由于每次执行 NIO 读、写操作都需要使用到 buffer,系统初始化的时候会建立一定长度的 buffer 池来加快读、写的效率,减少建立 buffer 的时间。
Mycat 中有两个主要的 buffer 池:
- BufferPool
- ThreadLocalPool
processorBufferLocalPercent 属性
这个属性就是用来控制分配这个ThreadLocalPool的大小用的,但其也并不是一个准确的值,也是一个比例值。 这个属性默认值为 100。
processorExecutor 属性
这个属性主要用于指定 NIOProcessor 上共享的 businessExecutor 固定线程池大小。 mycat 在需要处理一
些异步逻辑的时候会把任务提交到这个线程池中。新版本中这个连接池的使用频率不是很大了,可以设置一个较
小的值。
sequnceHandlerType 属性
指定使用 Mycat 全局序列的类型。 0 为本地文件方式, 1 为数据库方式, 2 为时间戳序列方式, 3 为分布式ZK ID 生成器, 4 为 zk 递增 id 生成。
从 1.6 增加 两种 ZK 的全局 ID 生成算法。
TCP 连接相关属性
- StandardSocketOptions.SO_RCVBUF
- StandardSocketOptions.SO_SNDBUF
- StandardSocketOptions.TCP_NODELAY
各自设置前后端 TCP 连接参数。 Mycat 在每次建立前、后端连接的时候都会使用这些参数初始化连接。 可以按系统要求适当的调整这些 buffer 的大小。 TCP 连接参数的定义,可以查看 Javadoc。
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 秒,单位秒。
心跳属性
mycat 中有几个周期性的任务来异步的处理一些我需要的工作。这些属性就在系统调优的过程中也是比不可少的。
processorCheckPeriod : 清理 NIOProcessor 上前后端空闲、超时和关闭连接的间隔时间。默认是 1 秒,单位毫秒。
dataNodeIdleCheckPeriod : 对后端连接进行空闲、超时检查的时间间隔,默认是 300 秒,单位毫秒。
dataNodeHeartbeatPeriod : 对后端所有读、写库发起心跳的间隔时间,默认是 10 秒,单位毫秒。
服务相关属性
这里介绍一个与服务相关的属性,主要会影响外部系统对 mycat 的感知。
bindIp : mycat 服务监听的 IP 地址,默认值为 0.0.0.0。
serverPort : 定义 mycat 的使用端口,默认值为 8066。
managerPort : 定义 mycat 的管理端口,默认值为 9066。
fakeMySQLVersion
mycat 模拟的 mysql 版本号,默认值为 5.6 版本,如非特需,不要修改这个值,目前支持设置 5.5,5.6 版本,其他版本可能会有问题。
此特性从 1.6 版本开始支持。
全局表一致性检测
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、 0为关闭-->
原理通过在全局表增加_MYCAT_OP_TIME 字段来进行一致性检测,类型为 bigint, create 语句通过 mycat执行会自动加上这个字段,其他情况请自己手工添加。
此特性从 1.6 版本开始支持。
全局表一致性定时检测主要分为两个部分:
- 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语句,不进行任何修改操作。保证该拦截不会影响系统原有的健壮性。
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语句也一样,只是无需拦截。下面的检查机制就是根据这个原理来操作的。
一致性的定时检测
在MycatServer的startup中引入一个定时检查任务。
检测全局表的内部列是否存在
检测的实现是通过一个SQLJob来异步操作的。
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。在第一项检测完成之后,才能进行第二
项检测。
检测全局表的记录总数
检查过程是类似的,都是通过SQLjob来完成的。
select count(*) as record_count from user; (假设user表为全局表)
检测全局表的时间戳的最大值
select max(_mycat_op_time) as max_timestamp from user (假设user表为全局表)
SQL的拦截实现记录全局表被修改时的时间戳;定时任务实现对全局表记录总数和时间戳最大值的获取。
如何使用全局表一致性检测
- 在所有全局表中增加一个 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掉。
分布式事务
主要应用场景,主要为了控制是否允许跨库事务。
<!--分布式事务开关, 0为不过滤分布式事务, 1 为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤), 2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
此特性从 1.6 版本开始支持。
Off Heap forMycat
<!--
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, HORT,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。
网友评论