快乐学习
前程无忧、中华英才非你莫属!

MyCAT入门到精通-Day4

一、Server.xml 详解
    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 切换到这个其他的数据库。
2、system 标签
  
 
1、 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_%';
  
2、 defaultSqlParser 属性
 
由于 mycat 最初是时候 Foundation DB 的 sql 解析器,而后才添加的 Druid 的解析器。所以这个属性用来 指定默认的解析器。目前的可用的取值有:druidparser 和 fdbparser。使用的时候可以选择其中的一种,目前一般都使用 druidparser。1.3 解析器默认为 fdbparser,1.4 默认为 druidparser,1.4 以后 fdbparser 作废。
 
3、 processors 属性
这个属性主要用于指定系统可用的线程数,默认值为机器 CPU 核心线程数。
主要影响 processorBufferPool、processorBufferLocalPercent、processorExecutor 属性。 NIOProcessor 的个数也是由这个属性定义的,所以调优的时候可以适当的调高这个属性。
 
4、 processorBufferChunk 属性
这个属性指定每次分配 Socket Direct Buffer 的大小,默认是 4096 个字节。这个属性也影响 buffer pool 的 长度。如果一次性获取的数过大 buffer 不够用 经常出现警告,则可以适当调大。    
 
 
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 属性 * 1000
BufferPool 的总长度 = bufferPool / bufferChunk。
 
若 bufferPool 不是 bufferChunk 的整数倍,则总长度为前面计算得出的商 + 1
 
假设系统线程数为 4,其他都为属性的默认值,则:
    bufferPool = 4096 * 4 * 1000
    BufferPool 的总长度 : 4000 = 16384000 / 4096
 
 
6、processorBufferLocalPercent 属性
 
前面提到了 ThreadLocalPool。这个属性就是用来控制分配这个 pool 的大小用的,但其也并不是一个准确 的值,也是一个比例值。这个属性默认值为 100。
线程缓存百分比 = bufferLocalPercent / processors 属性。
 
例如,系统可以同时运行 4 个线程,使用默认值,则根据公式每个线程的百分比为 25。
最后根据这个百分比来计算出具体的 ThreadLocalPool 的长度公式如下:
ThreadLocalPool 的长度 = 线程缓存百分比 * BufferPool 长度 / 100
 
假设 BufferPool 的长度为 4000,其他保持默认值。
那么最后每个线程建立上的 ThreadLocalPool 的长度为: 1000 = 25 * 4000 / 100
 
 
7、 processorExecutor 属性
这个属性主要用于指定 NIOProcessor 上共享的 businessExecutor 固定线程池大小。mycat 在需要处理一 些异步逻辑的时候会把任务提交到这个线程池中。新版本中这个连接池的使用频率不是很大了,可以设置一个较 小的值。
8、 sequnceHandlerType 属性
指定使用 Mycat 全局序列的类型。0 为本地文件方式,1 为数据库方式,2 为时间戳序列方式,3 为分布式 ZK ID 生成器,4 为 zk 递增 id 生成。
从 1.6 增加 两种 ZK 的全局 ID 生成算法。
9、 TCP 连接相关属性
        StandardSocketOptions.SO_RCVBUF
        StandardSocketOptions.SO_SNDBUF
        StandardSocketOptions.TCP_NODELAY
        以上这三个属性,分别由:
                frontSocketSoRcvbuf 默认值: 1024 * 1024
                frontSocketSoSndbuf 默认值: 4 * 1024 * 1024
                frontSocketNoDelay 默认值: 1
                backSocketSoRcvbuf 默认值: 4 * 1024 * 1024
                backSocketNoDelay 默认值: 1
各自设置前后端 TCP 连接参数。Mycat 在每次建立前、后端连接的时候都会使用这些参数初始化连接。
可以 按系统要求适当的调整这些 buffer 的大小。TCP 连接参数的定义,可以查看 Javadoc。
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 秒,单位秒。
11、 心跳属性
 
mycat 中有几个周期性的任务来异步的处理一些我需要的工作。这些属性就在系统调优的过程中也是比不可少的。
processorCheckPeriod : 清理 NIOProcessor 上前后端空闲、超时和关闭连接的间隔时间。默认是 1 秒,单 位毫秒。
dataNodeIdleCheckPeriod : 对后端连接进行空闲、超时检查的时间间隔,默认是 300 秒,单位毫秒。
dataNodeHeartbeatPeriod : 对后端所有读、写库发起心跳的间隔时间,默认是 10 秒,单位毫秒。
 
12 服务相关属性
 
这里介绍一个与服务相关的属性,主要会影响外部系统对 mycat 的感知。
bindIp : mycat 服务监听的 IP 地址,默认值为 0.0.0.0。
serverPort : 定义 mycat 的使用端口,默认值为 8066。
managerPort : 定义 mycat 的管理端口,默认值为 9066。
13、fakeMySQLVersion
mycat 模拟的 mysql 版本号,默认值为 5.6 版本,如非特需,不要修改这个值,目前支持设置 5.5,5.6 版本,其 他版本可能会有问题。
此特性从 1.6 版本开始支持。
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语句也一样,只是无需拦截。下面的检查机制就是根
据这个原理来操作的。
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掉。

 
15、分布式事务开关
<!--分布式事务开关,0 为不过滤分布式事务,1 为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2 为不过滤分布 式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
主要应用场景,主要为了控制是否允许跨库事务。
此特性从 1.6 版本开始支持。
16、 Off Heap for Mycat
此特性从 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 或者 backendconnection,造成 Mysqlclient 连接丢失时结果集无法正确。
    b.设置-Xmn 值尽可能大些,新生代使用 UseParallelGC 垃圾回收器,-Xss 设置 512K 比较合适,物理内存足够时,MaxDirectMemorySize 尽可能设置大些,可以加快结果集处理时间,
        例如:-Xmx1024m -Xmn512m -XX:MaxDirectMemorySize=2048m -Xss256k -XX:+UseParallelGC。
 参考:MyCAT 权威指南
打赏
赞(0) 打赏
未经允许不得转载:同乐学堂 » MyCAT入门到精通-Day4

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

error: Sorry,暂时内容不可复制!