简介

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

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 属性描述

参数 说明 事例(禁止增删改查)
dml insert,update,select,delete 0000

注: 设置了 schema , 但只设置了个别 table 或 未设置 table 的 DML,自动继承 schema 的 DML 属性

<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>

system 标签

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

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_%';

defaultSqlParser 属性

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

processors 属性

这个属性主要用于指定系统可用的线程数,默认值为机器 CPU 核心线程数。
主要影响 processorBufferPool、processorBufferLocalPercent、processorExecutor 属性。
NIOProcessor 的个数也是由这个属性定义的,所以调优的时候可以适当的调高这个属性。

processorBufferChunk属性

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

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 属性 * 1000
BufferPool 的总长度 = bufferPool / bufferChunk。
若bufferPool不是bufferChunk的整数倍,则总长度为前面计算得出的商 + 1
假设系统线程数为4,其他都为属性的默认值,则:
bufferPool = 4096 * 4 * 1000
BufferPool的总长度 : 4000 = 16384000 / 4096

processorBufferLocalPercent属性

前面提到了ThreadLocalPool。这个属性就是用来控制分配这个pool的大小用的,但其也并不是一个准确
的值,也是一个比例值。这个属性默认值为100。
线程缓存百分比 = bufferLocalPercent / processors 属性。
例如,系统可以同时运行4个线程,使用默认值,则根据公式每个线程的百分比为25。最后根据这个百分比
来计算出具体的ThreadLocalPool的长度公式如下:
ThreadLocalPool的长度 = 线程缓存百分比 * BufferPool长度 / 100
假设BufferPool的长度为 4000,其他保持默认值。
那么最后每个线程建立上的ThreadLocalPool的长度为: 1000 = 25 * 4000 / 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

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

frontSocketSoRcvbuf 默认值: 1024 * 1024

frontSocketSoSndbuf 默认值: 4 * 1024 * 1024

frontSocketNoDelay 默认值: 1

backSocketSoRcvbuf 默认值: 4 * 1024 * 1024

backSocketSoSndbuf 默认值: 1024 * 1024

backSocketNoDelay 默认值: 1

各自设置前后端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。

f a k e M y S Q L V e r s i o n

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 版本开始支持。

“增加 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语句也一样,只是无需拦截。下面的检查机制就是根 据这个原理来操作的。

2.一致性的定时检测

在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的拦截实现记录全局表被修改时的时间戳;定时任务实现对全局表记录总数和时间戳最大值的获 取。

3.如何使用全局表一致性检测

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)) 2> 在对全局表进行crud时,最好将内部列当作不存在一样,也就是最好不要对内部列 update,insert等操作,不然会在Log中进行警告:不用操作内部列; 3> 因为全局表多了一个内部列,所以在对全局表进行insert时,必须携带列名,也就是insert into t(id,name) values(xx,xx),不能使用insert into t values(xx,xx); 因为会报错:列数 不对。这是唯一的一个小问题。未来可能会fix掉。
7.9.15 分 布 式 事 务 开 关
< ! - - 分 布 式 事 务 开 关 , 0 为 不 过 滤 分 布 式 事 务 , 1 为 过 滤 分 布 式 事 务 ( 如 果 分 布 式 事 务 内 只 涉 及 全 局 表 , 则 不 过 滤 ) , 2 为 不 过 滤 分 布 式 事 务,但 是 记 录 分 布 式 事 务 日 志 - - > 0
主要应用场景,主要为了控制是否允许跨库事务。
此特性从 1.6 版本开始支持。

Off Heap for Mycat

此特性从 1.6 版本开始支持。
< ! - o f f h e a p f o r m e r g e / o r d e r / g r o u p / li m it 1 开 启 0 关 闭 - - > 1

  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响应速度。

  1. 使用TimeSort 和RadixSort,跨分片结果集合并排序使用PriorityQueue,其中经测试RadixSort适合
    LONG,INT,SHORT,Float,Double,String数据类型排序,性能优越。

  2. Java obj连续内存存取,二进制序列化和反序列化,使用缓存友好的数据结构Map和Row。

  3. 支持内存和外存并存的排序方式,结果集排序可以达上亿规模。此时应注意:

    a. 此时前端和后端空闲连接超时检测时间应该设置大些,避免空闲检测关闭front或者backend
    connection,造成Mysqlclient连接丢失时结果集无法正确。

    b. 设置-Xmn值尽可能大些,新生代使用UseParallelGC 垃圾回收器,-Xss设置512K 比较合适,物理内
    存足够时,MaxDirectMemorySize 尽可能设置大些,可以加快结果集处理时间,
    例如:-Xmx1024m -Xmn512m -XX:MaxDirectMemorySize=2048m -Xss256k -XX:+UseParallelGC。

http://www.mycat.io/document/mycat-definitive-guide.pdf

更多相关文章

  1. linux内核段属性机制(以subsys_initcall和module_init为例)
  2. 查询表中的某一行,表中没有行号相关的属性字段,SQL语句怎么写啊?50
  3. odbc驱动程序不支持请求的属性VB6
  4. Recordset记录集对象的属性
  5. 要在更新属性上执行的TRIGGER?
  6. Android动画精讲一:从setTranslationX谈属性动画和view动画的区别
  7. googlesamples/android-topeka学习笔记(一)-----一些不知道的属
  8. Android 基本控件的常用属性
  9. Android 属性动画(Property Animation)

随机推荐

  1. Android系列教程之前言
  2. The difference between '?attr' between
  3. Android基础(一)
  4. Android(安卓)安全机制概述
  5. Android(安卓)Jni代码示例讲解
  6. VS2019中用xamarin开发Android,显示需要an
  7. 快速进入Android世界
  8. Android控件编辑时键盘弹起与关闭处理
  9. View控件中android:drawablePadding不起
  10. Android单元测试 - 验证函数参数、返回值