MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信,其核心功能是分库分表。配合数据库的主从模式还可实现读写分离。
对于开发者来说,可以把MyCAT看成是一个数据库,因为它屏蔽了程序对数据库的具体访问过程。可以让开发者把更多的精力放在功能的实现上,但这并不是说开发者不需要了解MyCAT。相反,开发者更应该深入的了解MyCAT,以便在遇到相关的问题时,能够快速的定位问题并修复
本文主要讲解MyCAT的安装和MyCAT的核心配置文件
master
节点的高可用,主节点故障后自动切换到从节点,但是只适用一主一从模式,因为其他的slave
节点不会从新的master
节点同步数据(真正的主节点高可用需要用MHA
或者MMM
,这时需要关闭MyCAT主节点高可用配置)MyCAT逻辑库有以下特点
简单来说,逻辑库可以把一个或者多个MySQL物理数据库抽象成一个逻辑的数据库,且这个逻辑库中不保存任何数据,真正的数据还是保存在物理库中。
MyCAT逻辑表有以下特点
同理,逻辑表可以把一个或者多个MySQL物理表抽象成一个逻辑表,且这个逻辑表中不保存任何数据,真正的数据还是保存在物理表中,相当于视图(view)。
逻辑表可以分为以下三种
join
查询不会跨库。安装Oracle JDK并配置环境变量JAVA_HOME
下载
wget http://dl.mycat.org.cn/1.6.5-RELEASE/Mycat-server-1.6-RELEASE-20180122220033-linux.tar.gz
解压
tar -zxvf Mycat-server-1.6.5-release-20180122220033-linux.tar.gz -C /usr/local/
创建用户
adduser mycat
修改MyCAT的所有者
chown mycat:mycat -R /usr/local/mycat/
配置环境变量
vim /etc/profile
添加如下内容
export MYCAT_HOME=/usr/local/mycat
export PATH=$PATH:$MYCAT_HOME/bin
修改配置文件
切换到MyCAT的bin
目录
cd /usr/local/mycat/bin
MyCAT启动有两种方式,分别是执行mycat
和startup_nowrap.sh
。如果执行mycat
,默认读取的是conf
目录下的wrapper.conf
文件,所以先修改一下相关的配置文件
# Java Additional Parameters
#wrapper.java.additional.1=
wrapper.java.additional.5=-XX:MaxDirectMemorySize=256M
wrapper.java.additional.10=-Xmx512M
wrapper.java.additional.11=-Xms512M
因为博主采用的是虚拟机,只给每个CentOS实例分配了1G内存,所以此处的参考配置如下,大家可以根据自己的实际情况进行配置。
启动
mycat start
运行后查看日志文件
[root@localhost bin]# tail -20f ../logs/wrapper.log
STATUS | wrapper | 2020/05/23 11:29:02 | --> Wrapper Started as Daemon
STATUS | wrapper | 2020/05/23 11:29:02 | Launching a JVM...
INFO | jvm 1 | 2020/05/23 11:29:02 | Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=64M; support was removed in 8.0
INFO | jvm 1 | 2020/05/23 11:29:04 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
INFO | jvm 1 | 2020/05/23 11:29:04 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.
INFO | jvm 1 | 2020/05/23 11:29:04 |
INFO | jvm 1 | 2020/05/23 11:29:07 | log4j:WARN No appenders could be found for logger (io.mycat.memory.MyCatMemory).
INFO | jvm 1 | 2020/05/23 11:29:07 | log4j:WARN Please initialize the log4j system properly.
INFO | jvm 1 | 2020/05/23 11:29:07 | log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
INFO | jvm 1 | 2020/05/23 11:29:07 | MyCAT Server startup successfully. see logs in logs/mycat.log
根据日志可以看到MyCAT已经成功启动。
细心的同学可能会注意到这个VM warning
。
ignoring option MaxPermSize=64M; support was removed in 8.0
MaxPermSize
参数被忽略了,这个参数是指定永久代的最大容量,JDK1.8之后已经换成了metaspace
mycat/conf
目录下,MyCAT
核心配置文件主要有三个,分别是server.xml
、rule.xml
、scheam.xml
。下面一一介绍这几个配置文件中常用的标签。
server.xml
配置文件主要用来配置如下内容
具体的配置如下,为了方便一一对应,博主直接把标签的含义以注释的形式
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<!-- mycat对外提供服务的端口-->
<property name="serverPort">3306</property>
<!-- mycat管理端口-->
<property name="managerPort">9066</property>
<!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="nonePasswordLogin">0</property>
<property name="useHandshakeV10">1</property>
<!-- 服务器有多个网卡,多个ip,指定要绑定的ip,0.0.0.0表示绑定所有-->
<property name="bindIp">0.0.0.0</property>
<!-- 前端写队列的大小 -->
<property name="frontWriteQueueSize">4096</property>
<!-- mycat默认字符集 -->
<property name="charset">utf8</property>
<!-- 连接mysql使用的隔离级别1、2、3、4,分别代表读未提交、读已提交、可重复读、串行化 -->
<property name="txIsolation">3</property>
<!-- mycat进程的数量,通常等于服务器CPU核数 -->
<property name="processors">8</property>
<!-- 前台应用访问mycat最大空闲时间,单位是毫秒 -->
<property name="idleTimeout">300000</property>
<!-- SQL执行超时时间,单位是秒 -->
<property name="sqlExecuteTimeout">300</property>
<!-- 如果SQL中没有指定limit,则会使用该参数来限制返回的结果集行数 -->
<property name="defaultMaxLimit">100</property>
<!-- mycat允许的最大packet大小 -->
<property name="maxPacketSize">104857600</property>
<!-- 1为开启实时统计、0为关闭 -->
<property name="useSqlStat">0</property>
<!-- 1为开启全局表一致性检测、0为关闭 -->
<property name="useGlobleTableCheck">0</property>
<!-- 全局ID类型
0、本地文件方式
1、数据库方式
2、时间戳序列方式
3、分布式ZK ID生成器
4、ZK递增ID生成
-->
<property name="sequnceHandlerType">2</property>
<!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
<property name="subqueryRelationshipCheck">false</property>
<!--1为开启mysql压缩协议-->
<property name="useCompression">1</property>
<!--设置模拟的MySQL版本号-->
<property name="fakeMySQLVersion">5.7.30</property>
<property name="processorBufferChunk">40960</property>
<property name="processorExecutor">32</property>
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<property name="maxStringLiteralLength">65535</property>
<property name="sequnceHandlerType">0</property>
<property name="backSocketNoDelay">1</property>
<property name="frontSocketNoDelay">1</property>
<property name="processorExecutor">16</property>
<!--分布式事务开关
0、为不过滤分布式事务
1、为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤)
2、为不过滤分布式事务,但是记录分布式事务日志
-->
<property name="handleDistributedTransactions">0</property>
<!-- off heap for merge/order/group/limit 1开启 0关闭-->
<property name="useOffHeapForMerge">1</property>
<property name="memoryPageSize">64k</property>
<!-- 单位为k -->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!-- 单位为m-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">false</property>
<!-- XA Recovery Log日志路径 -->
<property name="XARecoveryLogBaseDir">./</property>
<!-- XA Recovery Log日志名称 -->
<property name="XARecoveryLogBaseName">tmlog</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--白名单可以使用通配符%或着*-->
<!--例如<host host="127.0.0.*" user="root"/>-->
<!--例如<host host="127.0.*" user="root"/>-->
<!--例如<host host="127.*" user="root"/>-->
<!--例如<host host="1*7.*" user="root"/>-->
<!--这些配置情况下对于127.0.0.1都能以root账户登录-->
<!--
<firewall>
<whitehost>
<host host="1*7.0.0.*" user="root"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<!-- 可以配置多个,逗号分割-->
<property name="schemas">TESTDB,TESTDB1,TESTD2</property>
<!-- 表级 DML 权限设置 -->
<!-- check表示是否启用权限控制-->
<privileges check="false">
<!-- dml四个bit位分别代表insert、update、select、delete-->
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
</user>
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
rule.xml
配置文件主要用来配置如下内容
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<!-- 定义分片规则-->
<tableRule name="rule1">
<rule>
<!-- 分片列-->
<columns>id</columns>
<!-- 分片函数-->
<algorithm>func1</algorithm>
</rule>
</tableRule>
<!-- 配置分片函数 class表示分片算法实现类-->
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<!-- 分片所需的参数-->
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
</mycat:rule>
想要对数据进行分片,必须要指定一组分片规则,即指定数据根据什么来进行分片,常用的分片算法有以下四种
PartitionByMod
:简单取模(最常用)。对指定的分片列进行简单的取模,把数据平均分配在多个分片中。优点是数据分片均匀,适用于分片列为整数类型的表。
配置方式
<tableRule name="mod-long">
<rule>
<!-- 分片列:被分片的列 -->
<columns>id</columns>
<!-- 分片算法,和function标签的name相对应 -->
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<!-- 定义分片算法,name表示名称,class表示算法实现类 -->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 数据要被分成多少个分片 -->
<property name="count">3</property>
</function>
PartitionByHashMod
:哈希取模。因为简单取模只适用于整数列,对于字符串类型,可以先进行hash运算,再进行取模。
配置方式
<tableRule name="mod-hash">
<rule>
<columns>name</columns>
<algorithm>mod-hash</algorithm>
</rule>
</tableRule>
<function name="mod-hash" class="io.mycat.route.function.PartitionByHashMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
PartitionByFileMap
:枚举分片。因为简单取模和哈希取模都是由算法本身决定数据所在的分片。如果想要指定数据所在的分片,就需要使用枚举分片。例如:根据用户的地址把同一区域的用户分配在同一分片。
配置方式
<tableRule name="sharding-by-filemap">
<rule>
<columns>sharding_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<!-- 指定枚举和分片的映射关系-->
<property name="mapFile">partition-hash-int.txt</property>
<!-- mapFile指定文件中key的类型,0表示整数,非0表示字符串-->
<property name="type">0</property>
<!-- 是否启用默认分片,<0表不启用,>=0表示启用,这里只是配置是否启用,具体默认节点的映射在mapFile中配置-->
<property name="defaultNode">0</property>
</function>
PartitionByPrefixPattern
:字符串范围取模分片。工作原理就是对分片列的指定前缀长度的字符取ascii
码的和再对取模基数进行取模。
配置方式
<tableRule name="sharding-by-prefix-pattern">
<rule>
<columns>sharding_id</columns>
<algorithm>prefix-pattern</algorithm>
</rule>
</tableRule>
<function name="prefix-pattern" class="io.mycat.route.function.PartitionByPrefixPattern">
<!-- 取模基数-->
<property name="paternValue">128</property>
<!-- 字符串前缀的长度-->
<property name="prefixLength">2</property>
<!-- 指定枚举和分片的映射关系-->
<property name="mapFile">prefix-partition-pattern.txt</property>
</function>
log4j2.xml
相信大家都很熟悉了,用来配置如下类型
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%-5p][%t] %m %throwable{full} (%C:%F:%L) %n"/>
</Console>
<RollingFile name="RollingFile" fileName="${sys:MYCAT_HOME}/logs/mycat.log"
filePattern="${sys:MYCAT_HOME}/logs/$${date:yyyy-MM}/mycat-%d{MM-dd}-%i.log.gz">
<PatternLayout>
<!-- yyyy-MM-dd HH:mm:ss.SSS表示输出时间的年月日,时分秒、毫秒。%5p表示输出日志级别,不足5个字符则右对齐,%t表示线程的名称,%m表示程序中输出的信息,%n输出一个回车换行符,windows平台为"/r/n",linux平台为"/n"-->
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p [%t] (%l) - %m%n</Pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<!-- level表示日志输出的级别,mycat支持的级别从低到高ALL、Trace、Debug、Info、Warn、Error、Fatal、OFF-->
<AsyncLogger name="io.mycat" level="info" includeLocation="true" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</AsyncLogger>
<asyncRoot level="info" includeLocation="true">
<AppenderRef ref="Console" />
<AppenderRef ref="RollingFile"/>
</asyncRoot>
</Loggers>
</Configuration>
schema.xml
文件主要用来配置如下内容
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!-- 定义逻辑库
name属性定义逻辑库的名称;
checkSQLschema设置是否检查SQL语句中是否包含库名,如果设置true,且SQL中包含库名(即select ... from db_name.table_name的形式)会自动去掉库名;
sqlMaxLimit用于SQL语句未使用Limit时,限制返回的结果集,优先级高于server.xml中的defaultMaxLimit,如果不想限制,可以设置成-1
-->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="1000">
<!-- 定义逻辑表
name属性定义逻辑表的名称,名称必须和物理数据库中的表名一致;
primaryKey定义逻辑表的主键,一般就是物理表的主键,作用是:当分片列不是主键时,MyCAT会缓存主键和分片列的映射关系,这样可以把对分片列的查询转换成对主键的查询,以提高查询性能;
dataNode定义表数据所存储的物理节点的名称,由标签<dataNode>定义,有多个分片时需要逗号分割
rule定义使用哪种分片规则,对应rule.xml中<tableRule>节点的name
-->
<table name="travel_record" primaryKey="table_id" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2" />
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
<table name="dual" primaryKey="ID" dataNode="dnx,dnoracle2" type="global" needAddLimit="false"/>
<table name="worker" primaryKey="ID" dataNode="jdbc_dn1,jdbc_dn2,jdbc_dn3" rule="mod-long" />
<table name="employee" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-intfile" />
<table name="customer" primaryKey="ID" dataNode="dn1,dn2" rule="sharding-by-intfile">
<childTable name="orders" primaryKey="ID" joinKey="customer_id" parentKey="id">
<childTable name="order_items" joinKey="order_id" parentKey="id" />
</childTable>
<childTable name="customer_addr" primaryKey="ID" joinKey="customer_id" parentKey="id" />
</table>
<table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate" />
</schema>
<dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743" />
<!-- name属性定义数据节点的名称,必须唯一
dataHost属性定义分片所在的物理主机(可能是一组集群)
database定义物理数据库的名称,物理数据库必须真实存在
-->
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />
<dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" />
<dataNode name="jdbc_dn2" dataHost="jdbchost" database="db2" />
<dataNode name="jdbc_dn3" dataHost="jdbchost" database="db3" />
<!-- 定义后端数据库主机信息,name属性定义一组数据库服务器的名称(主从复制集群或者PXC集群),必须唯一
maxCon、minCon定义MyCAT连接数的最大值和最小值
balance定义负载均衡策略,可选值为0、1、2、3
* 0表示不读写分离机制(适合单机)
* 1表示所有的readHost与stand by writeHost参与select语句的负载均衡(适用于多主多从模式)
* 2表示所有的readHost和writeHost都参与select语句的负载均衡(适用于写请求压力不大时,让写节点参与读负载)
* 3表示所有的readHost参与select语句的负载均衡(适用于一主多从模式)
writeType定义写操作的执行方式,可选值为0、1
* 0表示所有的写请求都由第一个writeHost来执行,只有当它不可用时,才会由下一个可用的writeHost来执行(适用于主从复制集群)
* 1表示写请求随机分发到writeHost来执行(适用于PXC集群)
dbType定于数据库类型
dbDriver可选值为native、jdbc
* native表示mysql原生的通信协议
* jdbc表示连接其他的关系型数据库或者非关系型数据库
switchType定义写数据库如何进行高可用的切换,可选值为1、-1
* 1表示当写数据库不可用时,会自动切换到下一个writeHost(仅适用一主一从模式)
* -1表示关闭自动切换
-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!-- mycat对后端数据库进行心跳检测-->
<heartbeat>select user()</heartbeat>
<!-- 主从复制中的master节点-->
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456">
<!-- 主从复制中的slave节点,必须定义在writeHost内部-->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
<writeHost host="hostS1" url="localhost:3316" user="root" password="123456" />
<writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/>
</dataHost>
</mycat:schema>
本篇主要从入门的角度讲解了什么是MyCAT、MyCAT的安装,以及MyCAT的几个核心配置文件,只有了解了这些内容,才能更好的开始学习和使用MyCAT。