Mycat核心概念工作原理及高级特性分析

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mycat核心概念工作原理及高级特性分析相关的知识,希望对你有一定的参考价值。

海量数据存储解决方案之分库分表原理解析及mycat安装及使用_踩踩踩从踩的博客-CSDN博客

前言

上篇文章主要对海量数据的分片进行介绍,并且包括mycat的安装和简单使用,做了一个简单的配置,也对分片中会出现的问题做分析;这篇文章会接着继续进行分析数据库中间件中重要的mycat,在使用上有一些高级特性及核心概念、分片解决的办法做了哪些优化和处理大的介绍

核心概念理解和工作原理

mycat的版本迭代

  • Mycat-mini-monitor项目开源了,又一款Mycat监控!
  • Mycat-mini-monitor-1.0.0 版本发布
  • Mycat-server-1.6.6-release 版本发布
  • Mycat-server-1.6.6-test 版本发布
  • Mycat-server-1.6-release 版本发布
  • Mycat-server-1.5-release 版本发布
  • Mycat-server-1.4-release 版本发布
  • Mycat-server-1.3-release 版本发布
  • Mycat-web(eye) 版本发布

Mycat工作原理

  • 将订单表 orders 数据按省份分片;
  • 应用连接mycat,提交SQL;
  • Mycat ‚拦截‛
Mycat 的原理 : ‚拦截‛,它拦截了用户发送过来的SQL 语句,首先解析SQL 语句,做一
些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL 发往
后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户;

架构

连接mycat 利用mysql的连接包,基于mysql协议包,sql执行组件。 对于mysql 不适用jdbc进行连接,如果其他的数据库,使用的jdbc的数据库,并发能力也好,还是其他的特殊的支持等都不是很好。

核心概念

数据库中间件 :对应用,mycat就是数据库服务,mycat是后端数据库集群的代理

应用层访问数据库,依次会找到下面的 对应的配置

  • 逻辑库:mycat数据库服务中定义、管理的数据库

  • 逻辑表:逻辑库中包含的需分库分表存储的表  

 

  • dataNode:数据节点(分片节点),逻辑表分片的存放节点。这里的datanode 对应的数据节点。

  • dataHost: 数据主机(节点主机),数据节点所在的主机。  

 

 

  • writeHost:写主机,真实的数据库服务主机描述

 

  • readHost:读主机,真实的数据库服务主机描述

schema配置-schema

逻辑库 schema
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
 <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
 </schema>
</mycat:schema>
MyCat 可以有多个逻辑库,每个逻辑库都有自己的相关配置。
如果不配置schema 标签,所有的表配置,会属于同一个默认的逻辑库。

schema元素属性说明

checkSQLschema  需要设置为true ,当库名与逻辑库名相同时,会去掉语句中的库名前缀。

这个在实际应用中一般都加上的。

schema配置-table

逻辑表 table
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
 <table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
 <table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2" />
 <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>
 </table>
</schema>
Table标签定义了MyCat中的逻辑表,所有需要拆分的表都需要通过这个标签定义。、
table元素属性说明

 这其中,

  • name :表名,同个schema 标签中定义的名字必须唯一,存放表数据的数据节点名。
  • dataNode
很多难以罗列时,可以这样配置:dataNode=‚multipleDn$0-99,multipleDn2$100-
199‛,$0-99表示0-99
<dataNode name="multipleDn$0-99" dataHost="localhost1" database="db$0-99"
></dataNode>
  • primaryKey  用来做缓存的。

 

 schema配置-childTable标签

<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>
</table>
childTable 标签用于定义E-R 分片的子表,通过标签上的属性与父表进行关联。
 两张表有关联关系,定义出父子关系。防止分片过后,join的问题。

schema配置-dataHost  

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
 writeType="0" dbType="mysql" dbDriver="native" switchType="1" 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="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
</dataHost>
dataHost定义Mycat中的数据主机,数据主机定义中定义了具体的数据库服务、读写分离配置
和心跳语句。这是与物理数据库服务关联的地方。
真正关联的地方

 这些对应不配置都有默认的。

 schema配置-heartbeat

<heartbeat>select user()</heartbeat>
  •    这个标签内指明用于和后端数据库进行心跳检查的语句  如:MYSQL 可以使用select user(),Oracle 可以使用select 1 from dual 等。
  • 这个标签还有一个connectionInitSql 属性,主要是当使用Oracle数据库时,需要执行的初始化SQL 语句就放到这里。 如:alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'
  • 主从切换的语句必须是:show slave status

schema配置-writeHost readHost

<!-- can have multi write hosts -->
<writeHost host="hostM1" url="localhost:3306" user="root" password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
这两个标签都指定后端数据库的相关配置给mycat,用于实例化后端连接池。writeHost 指定写实例、readHost 指定读实例。
在一个dataHost 内可以定义多个writeHost 和readHost。但是,如果writeHost 指定的后端数据库宕机,那么这个writeHost 绑定的所有readHost 都将不可用。另一方面,由于这个writeHost 宕机系统会自动的检测到,并切换到备用的writeHost 上去。
加密密码,执行mycat jar 程序(1.4.1 以后)
java -cp Mycat-server-1.4.1-dev.jar io.mycat.util.DecryptUtil 1:host:user:password
Mycat-server-1.4.1-dev.jar 为mycat download 下载目录的jar
1:host:user:password 中1 为db 端加密标志,host 为dataHost 的host 名称

Mycat如何解决分库分表带来的挑战

解决原则

第一原则:能不切分尽量不要切分
第二原则:如果要切分一定要选择合适的切分规则,提前规划好。
第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库Join 的可能。
第四原则:由于数据库中间件对数据Join实现的优劣难以把握,而且实现高性能难度极大,
业务读取尽量少使用多表Join。

Mycat中表分类

分片表 

分片表,是指那些有很大数据,需要切分到多个数据库的表,这样每个分片都有一部分
数据,所有分片构成了完整的数据。
<table name="t_goods" primaryKey="vid" autoIncrement="true" dataNode="dn1,dn2" rule="rule1" />

非分片表 

一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分
片表来说的,就是那些不需要进行数据切分的表。
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1" />

ER表

Mycat 中的ER 表是基于E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放
在同一个数据分片上,保证数据Join 不会跨库操作。
ER分片是解决跨分片数据join 的一种很好的思路,也是数据切分规划的一条重要规则。
做一个关联起来,  规则是一样的就可以,将数据进行分割开      
<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>
</table>

全局表

一个真实的业务系统中,往往存在大量的类似字典表的表,数据量不大,这些表基本上很少变动。
问题: 业务表往往需要和字典表Join查询,当业务表因为规模而进行分片以后,业务表与字典表之
间的关联跨库了。
解决 :Mycat中通过表冗余来解决这类表的join,即它的定义中指定的dataNode上都有一份该表的
拷贝。(将字典表或者符合字典表特性的表定义为全局表。 )
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />

也是解决效率慢,一定要数据量小,不然会导致占用空间大。

分片规则

在conf/rule.xml中定义分片规则

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

 

function 标签说明:
  • name 指定算法的名字。
  • class 制定路由算法具体的类名字。
  • property 为具体算法需要用到的一些属性。

Mycat内置的常用分片规则 

Mycat分库分表 提取码:hahh

分片枚举(列表分片)

通过在配置文件中配置可能的枚举 id ,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下:

function 分片函数中配置说明:
  • 算法实现类为:io.mycat.route.function.PartitionByFileMap
  • mapFile 标识配置文件名称;
  • type 默认值为00 表示Integer,非零表示String
  • defaultNode defaultNode 默认节点:小于0 表示不设置默认节点,大于等于0 表示设置默认节点为第几个数 据节点。
  • 默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点 如果不配置默认节点 (defaultNode 值小于0 表示不配置默认节点),碰到不识别的枚举值就会报错。
like this:can’t find datanode for sharding column:column_name val:ffffffff
  • sharding-by-enum.txt 放置在conf/下,配置内容示例:
10000=0 # 字段值为 10000 的放到 0 号数据节点
10010=1

范围分片

此分片适用于,提前规划好分片字段某个范围属于哪个分片

配置说明:
  • mapFile 代表配置文件路径
  • defaultNode 超过范围后的默认节点。
所有的节点配置都是从 0 开始,及 0 代表节点 1
mapFile 中的定义规则:
start <= range <= end. 
range start-end=data node index 
K=1000,M=10000.
配置示例:
0-500M=0 
500M-1000M=1 
1000M-1500M=2

按日期范围分片

此规则为按日期段进行分片。

 

配置说明:
  • columns :标识将要分片的表字段
  • algorithm :分片函数
  • dateFormat :日期格式
  • sBeginDate :开始日期
  • sEndDate:结束日期
  • sPartionDay :分区天数,即默认从开始日期算起,分隔10 天一个分区
sBeginDate,sEndDate 配置情况说明:
  • sBeginDate,sEndDate 都有指定 此时表的dataNode 数量的>=这个时间段算出的分片数,否则启动时会异常:

如果配置了 sEndDate 则代表数据达到了这个日期的分片后循环从开始分片插入。
  • 没有指定 sEndDate 的情况 数据分片将依次存储到dataNode上,数据分片随时间增长,所需的dataNode数也随之增长,当超出了为该表配置的dataNode数时,将得到如下异常信息:

 

自然月分片 

按月份列分区,每个自然月一个分片。
<tableRule name="sharding-by-month"> 
    <rule>
        <columns>create_time</columns> 
        <algorithm>sharding-by-month</algorithm> 
    </rule> 
</tableRule> 
<function name="sharding-by-month" class="io.mycat.route.function.PartitionByMonth">             
      <property name="dateFormat">yyyy-MM-dd</property> 
      <property name="sBeginDate">2014-01-01</property> 
</function>
配置说明:
  • columns: 分片字段,字符串类型
  • dateFormat :  日期字符串格式,默认为yyyy-MM-dd
  • sBeginDate : 开始日期,无默认值
  • sEndDate:结束日期,无默认值
  • 节点从0 开始分片
使用场景:
场景 1 :默认设置(不指定 sBeginDate sEndDate

节点数量必须是12 个,对应1 ~12

  • "2017-01-01" = 节点0
  • "2018-01-01" = 节点0
  • "2018-05-01" = 节点4
  • "2019-12-01" = 节点11
场景 2 :仅指定 sBeginDate
  • sBeginDate = "2017-01-01" 该配置表示"2017-01 "是第0 个节点,从该时间按月递增,无最大节点
  • "2014-01-01" = 未找到节点
  • "2017-01-01" = 节点0
  • "2017-12-01" = 节点11
  • "2018-01-01" = 节点12
  • "2018-12-01" = 节点23
场景 3 指定 sBeginDate=1 月、 sEndDate=12
  • sBeginDate = "2015-01-01" sEndDate = "2015-12-01" 该配置可看成与场景1 一致。
  • "2014-01-01" = 节点0
  • "2014-02-01" = 节点1
  • "2015-02-01" = 节点1
  • "2017-01-01" = 节点0
  • "2017-12-01" = 节点11
  • "2018-12-01" = 节点11
场景 4
sBeginDate = "2015-01-01"sEndDate = "2015-03-01" 该配置表示只有 3 个节点;很难与月份对应上;平均分散到3 个节点上

取模

此规则为对分片字段进行十进制运算,来分片数据。
<tableRule name="mod-sharding"> 
    <rule>
        <columns>user_id</columns> 
        <algorithm>mod-fun</algorithm> 
    </rule> 
 </tableRule>
 <function name="mod-fun" class="io.mycat.route.function.PartitionByMod">
     <!-- how many data nodes --> 
        <property name="count">3</property> 
</function>
配置说明:
count 指明 dataNode 的数量,是求模的基数
此种在批量插入时可能存在批量插入单事务插入多数据分片,增大事务一致性难度。

取模范围分片

此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点 分布。
<tableRule name="sharding-by-pattern">
     <rule>
        <columns>user_id</columns> 
        <algorithm>sharding-by-pattern</algorithm> 
    </rule> 
</tableRule> 
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPattern"> 
        <property name="patternValue">256</property> 
        <property name="defaultNode">2</property>
        <property name="mapFile">partition-pattern.txt</property> 
</function>
partition-pattern.txt
1-32=0 #余数为1-32的放到数据节点0上 
33-64=1 
65-96=2 
97-128=3 
129-160=4 
161-192=5 
193-224=6 
225-256=7 
0-0=7
配置说明:
  • patternValue 即求模基数
  • defaoultNode 默认节点,如果配置了默认节点,如果id 非数据,则会分配在defaoultNode 默认节点
  • mapFile 指定余数范围分片配置文件

二进制取模范围分片

本条规则类似于十进制的求模范围分片,区别在于是二进制的操作 , 是分片列值的二进制低 10 &1111111111 。 此算法的优点在于如果按照10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务控制难度。
二进制低 10&1111111111 的结果是 0-1023 一共是 1024 个值,按范围分成多个连续的片(最大 1024 个片)
<tableRule name="rule1"> 
    <rule>
        <columns>user_id</columns> 
        <algorithm>func1</algorithm> 
    </rule> 
</tableRule> 
<function name="func1" class="io.mycat.route.function.PartitionByLong"> 
        <property name="partitionCount">2,1</property> 
        <property name="partitionLength">256,512</property> 
</function>
配置说明:
  • partitionCount 分片个数列表。
  • partitionLength 分片范围列表
分区长度 : 默认为最大 2^n=1024 , 即最大支持 1024 分区
约束 :
  • count,length 两个数组的长度必须是一致的。
  • 1024 = sum((count[i] * length[i]))count length 两个向量的点积恒等于1024

范围取模分片

先进行范围分片计算出分片组,组内再求模。 优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题。综合了范围分片和求模分片的优点,分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片可以兼顾范围查询。
最好事先规划好分片的数量,数据扩容时按分片组扩容,则原有分片组的数据不需要迁移。由于分片组内数据比 较均匀,所以分片组内可以避免热点数据问题

配置说明:
  • mapFile 配置文件路径
  • defaultNode 超过范围后的默认节点顺序号,节点从0 开始。
  • partition-range-mod.txt 以下配置一个范围代表一个分片组,=号后面的数字代表该分片组所拥有的分片的数量。

 一致性hash

一致性 hash 算法有效解决了分布式数据的扩容问题。
<tableRule name="sharding-by-murmur"> 
    <rule>
        <columns>user_id</columns> 
        <algorithm>murmur</algorithm> 
    </rule> 
</tableRule> 
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
 <!-- 默认是0-->
     <property name="seed">0</property> 
  <!-- 要分片的数据库节点数量,必须指定,否则没法分片--> 
    <property name="count">2</property>
 <!-- 一个实际的数据库节点被映射为多少个虚拟节点,默认是160 --> 
    <property name="virtualBucketTimes">160</property> 
<!-- <property name="weightMapFile">weightMapFile</property> 
节点的权重,没有指定权重的节点默认是1。以properties 文件的格式填写,以从0 开始到count-1 的整数值也 就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1 代替--> 
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash 值与物理 节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西-->
 </function>

应用指定

此规则是在运行阶段有应用自主决定路由到那个分片。

配置说明:
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。 例如 id=05- 100000002 在此配置中代表根据 id 中从 startIndex=0 ,开始,截取 siz=2 位数字即 05 05 就是获取的分区,如果没传 默认分配到defaultPartition

 截取字符ASCII求和求模范围分片

此种规则类似于取模范围约束,只是计算的数值是取前几个字符的 ASCII 值和,再取模,再对余数范围分片。

 

partition-pattern.tx
range start-end =data node index
配置说明:
  • patternValue 即求模基数,
  • prefifixLength ASCII 截取的位数,求这几位字符的ASCII码值的和,再求余patternValue
  • mapFile 配置文件路径,配置文件中配置余数范围分片规则。

主键值生成

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。

为此,MyCat 提供了全局sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

本地文件方式

原理:此方式 MyCAT sequence 配置到文件中,当使用到 sequence 中的配置后, MyCAT 会更新 conf 中的sequence_conf.properties 文件中 sequence 当前的值。
配置方式:
1 、在 sequence_conf.properties 文件中做如下配置:
GLOBAL.HISIDS= 
GLOBAL.MINID=1001 
GLOBAL.MAXID=1000000000 
GLOBAL.CURID=1000
其中 HISIDS 表示使用过的历史分段 ( 一般无特殊需要可不配置 ) MINID 表示最小 ID 值, MAXID 表示最大 ID 值,
CURID 表示当前 ID 值。
2 server.xml 中配置:
<system><property name="sequnceHandlerType">0</property></system>
注: sequnceHandlerType 需要配置为 0 ,表示使用本地文件方式。
使用示例:
insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
缺点:当 MyCAT 重新发布后,配置文件中的 sequence 会恢复到初始值。 优点:本地加载,读取速度较快。
为表配置主键自增值的序列:
规则:在 sequence_conf.properties 中配置以表名为名的序列
T_COMPANY.CURID=501 
T_COMPANY.MINID=1 
T_COMPANY.MAXID=1000000000

就可以使用了。

<table name="t_company" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="range-sharding-by-members-count" />

数据库方式

在数据库中建立一张表,存放 sequence 名称 (name) sequence 当前值 (current_value) ,步长 (increment int 类型,每次读取多少个sequence) 等信息;
Sequence 获取步骤:
  • 1. 当初次使用该sequence 时,根据传入的sequence 名称,从数据库这张表中读取current_value,和 increment 到MyCat 中,并将数据库中的current_value 设置为原current_value +increment 值。
  • 2. MyCat 将读取到current_value+increment 作为本次要使用的sequence 值,下次使用时,自动加1,当使用increment 次后,执行步骤1)相同的操作。
MyCat 负责维护这张表,用到哪些 sequence ,只需要在这张表中插入一条记录即可。若某次读取的 sequence 没有用完,系统就停掉了,则这次读取的sequence 剩余值不会再使用。
配置方式:
server.xml 配置:
<system><property name="sequnceHandlerType">1</property></system>
注: sequnceHandlerType 需要配置为 1 ,表示使用数据库方式生成 sequence
  • 创建MYCAT_SEQUENCE  
  • 创建相关function
注意: MYCAT_SEQUENCE 表和以上的 3 function ,需要放在同一个节点上。 function 请直接在具体节点的数据库上执行,如果执行的时候报:
you might want to use the less safe log_bin_trust_function_creators variable
需要对数据库做如下设置:
windows my.ini[mysqld] 加上 log_bin_trust_function_creators=1 linux 下/etc/my.cnf my.ini[mysqld] 加上 log_bin_trust_function_creators=1 修改完后,即可在 mysql 数据库中执行
上面的函数。

 

  • sequence_db_conf.properties 相关配置,指定sequence 相关配置在哪个节点上:
例如:
USER_SEQ=test_dn1
使用示例:
insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,'test');
配置表的主键自增使用序列:
在序列定义表中增加名字为表名的序列:
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('T_COMPANY', 1,100);
sequence_db_conf.properties 中增加表的序列配置
T_COMPANY=dn1
主键自增就可以使用了
<table name="t_company" primaryKey

以上是关于Mycat核心概念工作原理及高级特性分析的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Flink核心技术及原理

Flink:特性、概念、组件栈、架构及原理分析

mycat原理及分表分库入门

Webpack底层原理及核心概念实际应用(一篇封神系列)

MyBatis高级特性

Mycat原理应用场景