MyCAT配置文件详解

前言

MyCat 是目前最流行的基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信,其核心功能是分库分表。配合数据库的主从模式还可实现读写分离。

对于开发者来说,可以把MyCAT看成是一个数据库,因为它屏蔽了程序对数据库的具体访问过程。可以让开发者把更多的精力放在功能的实现上,但这并不是说开发者不需要了解MyCAT。相反,开发者更应该深入的了解MyCAT,以便在遇到相关的问题时,能够快速的定位问题并修复

本文主要讲解MyCAT的安装和MyCAT的核心配置文件

什么是MyCAT

  • MyCAT是一个数据库中间件
  • MyCAT可以实现对数据库的读写分离分库分表
  • MyCAT对前端应用隐藏了后端数据库的存储逻辑

MyCAT作用

  • 作为分布式数据库中间层使用,不但可以访问MySQL,还可以访问SQL server、MongoDB
  • 对数据库实现读写分离以及负载均衡、分库分表
  • MyCAT可以实现master节点的高可用,主节点故障后自动切换到从节点,但是只适用一主一从模式,因为其他的slave节点不会从新的master节点同步数据(真正的主节点高可用需要用MHA或者MMM,这时需要关闭MyCAT主节点高可用配置)
  • 对于主库进行垂直切分(用户、商品、订单分别存储在不同的库中)
  • 控制数据库的连接数量

MyCAT基本概念

逻辑库

MyCAT逻辑库有以下特点

  • 对应用来说相当于MySQL中的数据库(database)
  • 逻辑库可以对应后端多个物理数据库
  • 逻辑库中并不保存数据

简单来说,逻辑库可以把一个或者多个MySQL物理数据库抽象成一个逻辑的数据库,且这个逻辑库中不保存任何数据,真正的数据还是保存在物理库中。

逻辑表

MyCAT逻辑表有以下特点

  • 对应用来说相当于MySQL中的数据表(table)
  • 逻辑表可以对应后端多个物理数据库中的表
  • 逻辑表中并不保存数据

同理,逻辑表可以把一个或者多个MySQL物理表抽象成一个逻辑表,且这个逻辑表中不保存任何数据,真正的数据还是保存在物理表中,相当于视图(view)。

逻辑表的类型

逻辑表可以分为以下三种

  • 分片表与非分片表:按是否被分片划分,分片表就是进行了水平切分的表,是一组具有相同表结构,但是数据存在不同数据库中的表,只有把所有分片表中的数据汇总起来才是完整的数据。而垂直切分的表,表中所有数据都存储在同一个数据库中。
  • 全局表:一般是字典表,在所有分片中都存在的表
  • ER关系表:按照ER关系进行分片的表。可以把子表的数据与其关联的父表数据放在同一个分片上,子表依赖父表进行存储。可以保证相同一组数据的join查询不会跨库。

实验环境

  • CentOS 7
  • JDK1.8
  • MyCAT1.6.5
  • MySQL5.7

安装步骤

  • 安装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启动有两种方式,分别是执行mycatstartup_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.xmlrule.xmlscheam.xml。下面一一介绍这几个配置文件中常用的标签。

server.xml

server.xml配置文件主要用来配置如下内容

  • 配置系统相关参数
  • 配置用户访问权限
  • 配置SQL防火墙以及SQL拦截功能

具体的配置如下,为了方便一一对应,博主直接把标签的含义以注释的形式

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

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

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

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。

参考

  • 《MyCAT权威指南》

你可能感兴趣的:(MySQL,中间件,MyCAT,rule.xml,schema.xml,server.xml,分库分表)