MyCat 学习及使用 -- 如何配置分库分表

什么是MyCat

Mycat 是什么?从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了MySQL 协议的的Server,前端用户可以把它看作是一个数据库代理,用MySQL 客户端工具和命令行访问,而其后端可以用MySQL 原生(Native)协议与多个MySQL 服务器通信,也可以用JDBC 协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N 个小表,存储在后端MySQL 服务器里或者其他数据库里。

Mycat 发展到目前的版本,已经不是一个单纯的MySQL 代理了,它的后端可以支持MySQL、SQLServer、Oracle、DB2、PostgreSQL 等主流数据库,也支持MongoDB 这种新型NoSQL 方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在Mycat 里,都是一个传统的数据库表,支持标准的SQL 语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。

核心实现原理

Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的SQL 语句,首先对SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。当Mycat 收到一个SQL 时,会先解析这个SQL,查找涉及到的表,然后看此表的定义,如果有分片规则,则获取到SQL 里分片字段的值,并匹配分片函数,得到该SQL 对应的分片列表,然后将SQL 发往这些分片去执行,最后收集和处理所有分片返回的结果数据,并输出到客户端。

主体架构

Mycat在逻率持上由几个模块组成:通信协议、路由解析、结果集处理、数据库连接、监控等模块。

  1. 通信协议模块:底层的收发数据、线程回调处理工作,主要采用 Reactor、 proactor模式来提高效率。目前, Mycat通信模块默认釆用 Reactor模式,在协议层采用 MySQL协议。
  2. 路由解析模块:对传入的SL语句进行语法解析,解析从 MySQL协议中解析出来并进入该模块的SQL语句的条件、语句类型、携带的关键字等,对符合要求的SQL语句进行相关优化,最后根据这些路由计算单元进行路由计算。
  3. 结果集处理模块:对跨分片的结果进行汇聚、排序、截取等。由于数据存储在不同的数据库中,所以对跨分片的数据需要进行汇聚。
  4. 数据库连接模块:创建、管理、维护后端的连接池。为了诚少每次建立数据库连接的开销,数据库使用连接池机制对连接生命周期进行管理
  5. 监控管理模块:对 Mycat中的连接、内存等资源进行监控和管理。监控主要是通过管理命令实时地展现一些监控数据,例如连接数、缓存命中数等;管理则主要通过轮询事件来检测和释放不使用的资源。
  6. SQL执行模块:从连接池中获取相应的目标连接,对目标连接进行信息同步后,再根据路由解析的结果,把SQL语句分发到相应的节点执行。

执行流程

主要流程:由通信协议模块的读写事件通知发起。读写事件通知具体的回调代码进行这次读写事件的处理。管理模块的执行流程由定时器事件进行资源检查和资源释放时发起,由客户端发送过来的数据通过协议解析、路由解析等流程进入执行组件。通过执行组把数据发送到通信协议模块,最终数据被写入目标数据库。由后端数据库返回数据,通过协议解析后发送至回调模块。如果是涉及多节点的数据,则执行流程将会先进入结果集汇聚、排序等模块中,然后将处理后的数据通过通信协议模块返回到客户端。如果有排序则是用的将结果集在mycat中进行了堆排序。

从原理上来看,可以把mycat 看成一个sql 转发器。mycat 接收到前端发来的sql,然后转发到后台的mysql 服务器上去执行。但是后面有很多台mysql 节点(如dn1,dn2,dn3),该转发到哪些节点呢?这就是路由解析该做的事情了。路由能保证sql 转发到正确的节点。转发的范围是刚刚好,不多发也不少发。在选型上,mycat使用的是druidparser。

核心概念

  1. 逻辑库(schema):通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。
  2. 逻辑表(table):既然有逻辑库,那么就会有逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。
  3. 分片表:分片表,是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。
  4. 非分片表:一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。
  5. ER 表:关系型数据库是基于实体关系模型(Entity-Relationship Model)之上,通过其描述了真实世界中事物与关系,Mycat 中的ER 表即是来源于此。根据这一思路,提出了基于E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据Join 不会跨库操作。表分组(Table Group)是解决跨分片数据join 的一种很好的思路,也是数据切分规划的重要一条规则。
  6. 全局表:一个真实的业务系统中,往往存在大量的类似字典表的表,这些表基本上很少变动, 对于这类的表,在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,所以Mycat 中通过数据冗余来解决这类表的join,即所有的分片都有一份数据的拷贝,所有将字典表或者符合字典表特性的一些表定义为全局表。数据冗余是解决跨分片数据join 的一种很好的思路,也是数据切分规划的另外一条重要规则。
  7. 分片节点(dataNode):数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)。
  8. 节点主机(dataHost):数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。
  9. 分片规则(rule):前面讲了数据切分,一个大表被分成若干个分片表,就需要一定的规则,这样按照某种业务规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。
  10. 全局序列号(sequence):数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence)。

安装与部署

MyCAT 是使用JAVA 语言进行编写开发,使用前需要先安装JAVA 运行环境(JRE),由于MyCAT 中使用了JDK7 中的一些特性,所以要求必须在JDK7 以上的版本上运行。同时Mysql建议使用5.5以上版本,5.6为宜。进入https://github.com/MyCATApache/Mycat-download,在列表中选择一个合适自己操作系统的包下载,然后解压在某个目录下。Mycat是个标准的Java程序,解压后即可运行。Linux下执行 mycat ,windows下执行 startup_nowrap.bat。

其中 bin 程序目录,存放了启动脚本,除了提供封装成服务的版本之外,也提供了nowrap 的脚本命令。conf 目录下存放配置文件,server.xml 是Mycat 服务器参数调整和用户授权的配置文件,schema.xml 是逻辑库定义和表以及分片定义的配置文件,rule.xml 是分片规则的配置文件,分片规则的具体一些参数信息单独存放为文件,也在这个目录下,配置文件修改,需要重启Mycat 或者通过9066 端口reload。lib 目录下主要存放mycat 依赖的一些jar 文件。日志存放在logs/mycat.log 中,每天一个文件,日志的配置是在conf/log4j.xml 中,根据自己的需要,可以调整输出级别为debug,debug 级别下,会输出更多的信息,方便排查问题。

配置详解

主要配置文件为server.xml,schema.xml,rule.xml以及相应的分片规则。

server.xml

server.xml用于配置系统参数、用户信息、访问权限及SQL防火墙和SQL拦截功能等。

system 标签

配置 Mycat 的系统参数。

	<system>
		<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
		<property name="useHandshakeV10">1</property>
		<property name="useSqlStat">0</property>  <!-- 1为开启实时统计、0为关闭 -->
		<property name="useGlobleTableCheck">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->

		<property name="sequnceHandlerType">2</property>
		<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->

        <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
		<property name="processorBufferPoolType">0</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>

		<property name="spillsFileBufferSize">1k</property>
		<property name="useStreamOutput">0</property>
		<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>

user 标签

配置Mycat的访问用户及权限。

	<user name="root" defaultAccount="true">
		<property name="password">123456</property>
		<property name="schemas">db1,db2</property>
		
		<!-- 表级 DML 权限设置 -->
		<!-- 		
		<privileges check="false">
			<schema name="db1" dml="0110" >
				<table name="demotb" dml="0000"></table>
			</schema>
		</privileges>		
		 -->
	</user>

schema.xml

schema.xml用于配置逻辑库及逻辑表,配置逻辑表所存储的数据节点,配置数据节点所对应的物理数据库服务器信息。

dataHost 标签

定义了具体的数据库实例、读写分离配置和心跳语句。

	<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
			  writeType="0" dbType="mysql" dbDriver="native" switchType="0"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<!-- can have multi write hosts -->
		<writeHost host="hostM1" url="localhost:3306" user="root"
				   password="123456">
				   <!-- can have multi read hosts -->
				   <readHost host="hostS1" url="localhost:3307" user="root" password="123456" />
		</writeHost>
	</dataHost>
  1. heartbeat 标签:内指明用于和后端数据库进行心跳检查的语句。例如,MySQL可以使用select user(),Oracle可以使用select 1 from dual 等。
  2. writehost 、readHost标签:这两个标签都用于配置一组主从数据库的相关信息,Mycat用这两个标签配置的连接信息实例化后端连接池。唯一不同的是,writeHost 配置写实例(master)、readHost 配置读实例(salve),并且readHost 为writeHost 的子标签。通过这两个标签可以组合读/写实例以满足系统的要求。在一个dataHost内可以定义多个writeHostreadHost。但是,如果writeHost指定的后端数据库宕机,那么这个writeHost绑定的所有readHost都将不可用。另一方面,当一个writeHost宕机时系统会自动检测到,并切换到备用的writeHost 上去。

dataNode 标签

定义了 MyCat 中的数据节点,也就是我们通常说所的数据分片。一个 dataNode 标签就是一个独立的数据分片。

	<dataNode name="dnUser1" dataHost="localhost1" database="user" />
	<dataNode name="dnUser2" dataHost="localhost1" database="user01" />
	<dataNode name="dnUser3" dataHost="localhost1" database="user02" />

schema 标签

定义 MyCat 实例中的逻辑库,MyCat 可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用 schema 标签来划分这些不同的逻辑库。

	<schema name="schema_db1"  checkSQLschema="false"  sqlMaxLimit="100"></schema>
	<schema name="schema_db2"  checkSQLschema="false"  sqlMaxLimit="100"></schema>

table 标签

定义了 MyCat 中的逻辑表,所有需要拆分的表都需要在这个标签中定义。其中 childTable 标签用于定义 E-R 分片的子表。通过标签上的属性与父表进行关联。

	<schema name="USERDB" checkSQLschema="false" sqlMaxLimit="100">
		<table name="user" primaryKey="id"  dataNode="dnUser1,dnUser2,dnUser3" rule="user-mod-long">
			<childTable name="user_log" primaryKey="id" joinKey="user_id"
						parentKey="id">
			</childTable>
		</table>
		<table name="area" primaryKey="area_code" type="global" dataNode="dnUser1,dnUser2,dnUser3" />
	</schema>
属性 说明
name 该属性定义逻辑表的表名
dataNode 该属性定义这个逻辑表所属的 dataNode, 该属性的值需要和 dataNode 标签中 name 属性的值相互对应。
rule 该属性用于指定逻辑表要使用的规则名字,规则名字在 rule.xml 中定义,必须与 tableRule 标签中 name 属性属性值一一对应
ruleRequired 该属性用于指定表是否绑定分片规则,如果配置为 true,但没有配置具体 rule 的话 ,程序会报错。
primaryKey 该逻辑表对应真实表的主键
type 该属性定义了逻辑表的类型,目前逻辑表只有“全局表”和”普通表”两种类型。全局表定义type=”global”,不定义的就是普通表。
autoIncrement 主键是否自增长。
subTables 分表,分表目前不支持Join。
needAddLimit 是否自动添加limit,默认是开启状态。

这里定义了一个逻辑表user表及其子表user_log,分片规则为user-mod-long,一个全局表area。

rule.xml

定义了我们对表进行拆分所涉及到的规则定义。

tableRule标签

属性 name指定唯一的名字,用于标识不同的表规则。内嵌的 rule 标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。

	<tableRule name="user-mod-long">
		<rule>
            <!-- 指定使用表中的哪个列进行分片 -->
			<columns>id</columns>
            <!-- 指定表的分片算法,取值为<function>标签的name属性 -->
			<algorithm>mod-long</algorithm>
		</rule>
	</tableRule>

function标签

定义了tableRule中使用的分片算法。

	<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
		<!-- how many data nodes -->
		<property name="count">3</property>
	</function>	

常用的几个分片算法:

  • PartitionByMod:简单取模,直接通过列值进行取模得出分片位置
  • PartitionByHashMod:哈希取模,先将列值进行hash运算之后再取模得出分片位置
  • PartitionByFileMap:分片枚举,根据枚举值对数据进行分片,例如在异地多活的场景中通过地区id进行数据分片的场景
  • PartitionByPrefixPattern:字符串范围取模,根据长字符串的前面几位进行取模分片

猜你喜欢

转载自blog.csdn.net/zwt122755527/article/details/111946362