概念
数据切分
垂直切分
根据业务系统,不同业务分配到不同的库,从而减少库的负载。
优点:
- 业务清晰,拆分规则明确
- 数据维护简单
当某个业务很大,压力还是集中到一个库上,并发不够。这时就需要水平切分
缺点:
- 单库性能瓶颈,不易扩展
水平切分
将表根据策略将表分到多个库中,每个表包含一部分数据。这样多个库就可以到达提高负载量
优点:
- 不存在单库大数据,高并发的性能瓶颈
缺点:
- 分片事物一致性难解决
数据源管理
-
客户端模式:每个业务线有不同数据库配置管理
-
中间代理:由代理来完成一系列的数据拆分(分库分表)
功能
应用场景
- 读写分离
- 分库分表
- 多租户化
- 报表系统,借助mycat的分表能力,处理大规模报表的统计
- 替代hbase,分析大数据
MYCAT中的概念
数据库中间件
当数据量达到一定的量或者请求连接过高时(需要削峰),就需要把数据分配到多个节点中。如果没有中间件,每次操作数据时就应用将直接面对分片集群,数据源切换、事物处理、数据聚合都要由业务层处理,这将导致代码不易读并产生bug.
逻辑库schema
应用不需要知道中间件的存在,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库
逻辑表
逻辑表
分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表可以是数据切分后,分布在一个或多个分片库中
分片表
分片表是逻辑表的一种,因为数据大,所以数据被切分到多个库的各个表中。
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1,dn2" rule="rule1" />
t_node就是分片表,dn1,dn2就是分片存储的不同mysql节点
非分片表
非分片表是逻辑表的一种,数据量不大,数据没必要切分,所以直接放在一个库中
<table name="t_node" primaryKey="vid" autoIncrement="true" dataNode="dn1" />
t_node就是非分片表,放在一个节点上
ER表
关系型表,子表的记录与父表记录存放在同一个数据分片上,通过表分组保证数据join不会节点操作
全局表
字典表,变动不频繁、数据体量不大(类似于地区信息)。这种表因为会跟很大表关联查询,所以在分布式的情况下会很难联表查询。mycat使用数据冗余来解决这个问题,及每个分片下都备份一份数据。
分片节点
切分后的数据库server
节点主机
分片节点的主机
分片规则
按照某种规则(策略),将数据切分。
全局序列号
因为数据被切分后,分配到不同的节点中,原来的主键唯一性无法使用了,mycat的全局序列号保证数据唯一性标识。
mycat配置
server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="ignoreUnknownCommand">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。
在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->
<property name="useHandshakeV10">1</property>
<property name="removeGraveAccent">1</property>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时 单位:秒-->
<property name="sequenceHandlerType">1</property>
<!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx");
-->
<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
<property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</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="sequenceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1开启 0关闭
-->
<property name="useOffHeapForMerge">0</property>
<!--
单位为m
-->
<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>-->
<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
<property name="strictTxIsolation">false</property>
<!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行-->
<property name="parallExecute">0</property>
<property name="usingAIO">1</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="true">
<property name="selelctAllow">false</property> 允许查询权限
</blacklist>
</firewall>
-->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="defaultSchema">TESTDB</property>
<!--No MyCAT Database selected 错误前会尝试使用该schema作为schema,不设置则为null,报错 -->
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<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,TESTDB2</property>
<property name="readOnly">true</property>
<property name="defaultSchema">TESTDB</property>
</user>
</mycat:server>
user
定义登录mycat的用户和权限
属性:
- name:定义登陆mycat的用户名
user.property标签
property标签是迎来申明user标签的具体属性的
<property name="password">user</property>
name值枚举:
- password:连接mycat密码
- readOnly:限制用户是否只读,默认值false
- schema:限制用户可访问的schema标签
- Benchmark:mycat的最大连接数,到达这个值时,拒绝访问
- usingDecrype:密码加密
user.privileges
对用户schema及下级的table进行精华的DML权限控制
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
dml:0禁止 1非禁止
1110 : 1:insert,1:update,1:select,0:delete
schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<!-- auto sharding by id (long) -->
<!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置-->
<!--fetchStoreNodeByJdbc 启用ER表使用JDBC方式获取DataNode-->
<table name="hobby" primaryKey="id" dataNode="dn1" autoIncrement="true" fetchStoreNodeByJdbc="true">
<childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
</table>
<table name="student" primaryKey="id" autoIncrement="true" dataNode="dn1_test,dn2,dn3" />
<!-- <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"
/> -->
<dataNode name="dn1" dataHost="db1" database="mycat_test" />
<dataNode name="dn1_test" dataHost="db1" database="test" />
<dataNode name="dn2" dataHost="db2" database="test" />
<dataNode name="dn3" dataHost="db3" database="test" />
<!--<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" /> -->
<dataHost name="db1" 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="db1" url="db1:3306" user="root"
password="root">
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
<dataHost name="db2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- 淦,这里dbDriver要用native,用JDBC log会提示not found应该是这个组件没有 -->
<!-- can have multi write hosts -->
<writeHost host="db2" url="db2:3306" user="root"
password="root">
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
<dataHost name="db3" 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="db3" url="db3:3306" user="root"
password="root">
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
</mycat:schema>
schema标签
逻辑库标签,mycat可以有多个逻辑库,逻辑库相当于mysql中的database。
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="hobby" primaryKey="id" dataNode="dn1" autoIncrement="true" fetchStoreNodeByJdbc="true">
<childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
</table>
<table name="student" primaryKey="id" autoIncrement="true" dataNode="dn1_test,dn2,dn3" />
</schema>
属性
- name:使用server.xml定义的逻辑库名
- checkSQLschema:检查sql结构,看不懂说的是什么东西,反正最好是true
- sqlMaxLimit:如果schema设置了这个值,mycat会判断select是否有limit关键字,如果没有加会默认加上limit
dataNode标签
此属性用于绑定逻辑库到某个固定的mysql-database上。
<dataNode name="dn1" dataHost="db1" database="mycat_test" />
属性:
- name:数据节点的名字,
- datahost:使用datahost标签的名字
- database:datahost的mysql-server里的库名
table标签
用于定义逻辑库上的逻辑表,需要拆分database都在这里定义
<table name="student" primaryKey="id" autoIncrement="true" dataNode="dn1_test,dn2,dn3" rule='' />
将student逻辑表数据,拆分到dn1_test、dn2、dn3对应的datahost上的student表上
属性:
-
name:逻辑表名
-
dataNode:dataNode标签上的name,需要拆分到那些库上
-
rule:拆分规则,必须与rule.xml山的tableRule标签对应
-
rule_required:boolean,此表是否一定需要rule
-
primaryKey:该逻辑表对应真实表的主键,例如:分片的规则是使用非主键进行分片的,那么在使用主键查询的时候,就
会发送查询语句到所有配置的 DN 上,如果使用该属性配置真实表的主键。难么 MyCat 会缓存主键与具体 DN 的
信息,那么再次使用非主键进行查询的时候就不会进行广播式的查询,就会直接发送语句给具体的 DN,但是尽管
配置该属性,如果缓存并没有命中的话,还是会发送语句给具体的 DN,来获得数据。
-
type:全局表global和普通表(不定义这个属性)。全局表就是每个分片都保存,用数据冗余来解决跨库join
-
autoIncrement:定义的主键是否自增
-
subTables:子表
-
neddAddLimit:boolean,mycat的sql是否会自动添加limit
childTable
定义ER分片的子表,通过标签上的属性与父表进行关联
属性:
- name:子表名
- joinKey:插入子表的时候会使用这个列的值来查找父表存储的数据节点
- parentKey:属性指定的值一般与父表建立关联关系的列名。程序首先获取joinkey的值,在通过parentKey属性指定的列明产生查询语句,通过执行该语句得到父表存储在哪个分片上。从而确定子表存储的位置
- primaryKey:同table标签
- needAddLimit:同table标签
dataNode
数据节点,也是数据分片。
<dataNode name="dn2" dataHost="db2" database="test" />
属性:
- name:定义数据节点名字
- dataHost:dataHost标签上的name
- database:datahost标签上指定的mysql-server实例对应的数据库名
dataHost
mysql-server的具体的数据库实例定义
<dataHost name="db3" 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="db3" url="db3:3306" user="root"
password="root">
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
属性:
- name:定义mysql-server实例名
- maxCon:读写实例连接池的最大连接数
- minCon:读写实例连接池的最小连接数,初始化连接数的大小
- balance:负载均衡类型
- balance = 0 不开启读写分离,所有操作都发送到当前的writeHost上
- balance = 1,全部的readHost与stand by writeHost参与select语句的负载均衡。
- balance = 2,所有读操作都随机的在readHost与writeHost上分发
- balance = 3,所有读请求随机的分到writeHosts对应的readHost,writeHost不负担读压力
- write_type:负载均衡类型
- writeType = 0,所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个writeHost,
- writeType = 1,所有写操作都随机的发送到配置的writeHost
- switch_type:
- -1 表示不自动切换
- 1自动切换
- 2基于mysql主从同步的状态决定是否切换
- dbType:指定后端连接的数据库类型,目前支持二进制的mysql协议,还有其他使用JDBC连接的数据库
- dbDriver:
- native:mysql二进制协议
- JDBC:mongodb,oracle,spark等其他的数据库使用JDBC驱动来支持
- tempReadHostAvailable:如果配置了这个属性writeHost下面的readHost仍旧可用。缺省值是0
heartbeat
这个标签内指明用于和后端数据库进行心跳检查的语句。mysql可以使用select user()
<heartbeat>select user()</heartbeat>
writeHost readHost
两个标签指定后端mysql-server的相关配置
writehost是写实例, readHost是读实例。
table标签是数据分片,wrhost标签是这些分片哪些可读 哪些可写
<writeHost host="db2" url="db2:3306" user="root" password="root"></writeHost>
属性:
- host:定义host名
- url:连接的socket
- user:实例连接名
- password:实例连接密码
- weight:readhost的读权重
- usingDecrypt:是否使用加密程序加密密码 默认是0
rule.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!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>
<tableRule name="sharding-by-date">
<rule>
<columns>createTime</columns>
<algorithm>partbyday</algorithm>
</rule>
</tableRule>
<tableRule name="rule2">
<rule>
<columns>user_id</columns>
<algorithm>func1</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_id</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<tableRule name="mod-long">
<rule>
<columns>id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-murmur">
<rule>
<columns>id</columns>
<algorithm>murmur</algorithm>
</rule>
</tableRule>
<tableRule name="crc32slot">
<rule>
<columns>id</columns>
<algorithm>crc32slot</algorithm>
</rule>
</tableRule>
<tableRule name="sharding-by-month">
<rule>
<columns>create_time</columns>
<algorithm>partbymonth</algorithm>
</rule>
</tableRule>
<tableRule name="latest-month-calldate">
<rule>
<columns>calldate</columns>
<algorithm>latestMonth</algorithm>
</rule>
</tableRule>
<tableRule name="auto-sharding-rang-mod">
<rule>
<columns>id</columns>
<algorithm>rang-mod</algorithm>
</rule>
</tableRule>
<tableRule name="jch">
<rule>
<columns>id</columns>
<algorithm>jump-consistent-hash</algorithm>
</rule>
</tableRule>
<function name="murmur"
class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0</property><!-- 默认是0 -->
<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property>
用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
</function>
<function name="crc32slot"
class="io.mycat.route.function.PartitionByCRC32PreSlot">
<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
</function>
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
</function>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
<function name="latestMonth"
class="io.mycat.route.function.LatestMonthPartion">
<property name="splitOneDay">24</property>
</function>
<function name="partbymonth"
class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2015-01-01</property>
</function>
<function name="partbyday"
class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sNaturalDay">0</property>
<property name="sBeginDate">2014-01-01</property>
<property name="sEndDate">2014-01-31</property>
<property name="sPartionDay">10</property>
</function>
<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
<property name="mapFile">partition-range-mod.txt</property>
</function>
<function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
<property name="totalBuckets">3</property>
</function>
</mycat:rule>
rule里面定义了我们对表进行拆分所涉及到的规则定义。我们对表使用不同的分片算法,或者使用相同的算法但具体的参数不同
tableRule
<tableRule name="latest-month-calldate">
<rule>
<columns>calldate</columns>
<algorithm>latestMonth</algorithm>
</rule>
</tableRule>
属性:
- name:唯一的名字,用户标识不同的表规则,schema.xml使用到
table.columns
要拆分的列明
table.algorithm
使用func标签的name属性,连接表规则和具体路由算法
function
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">8</property>
<property name="partitionLength">128</property>
</function>
属性
- name:指定算法名字
- class:指定路由算法具体的类名字
function.property
具体算法需要用到的属性
MYCAT分片规则
切分原则
- 能不分片尽量不分片
- 如果要分片,就需要提前考虑清楚
- 数据切分尽量通过数据冗余(多分片copy一份数据)或表分组(Table Group)来避免join联表时跨库
- 由于数据库中间件对数据join实现的优劣难以把握,而且实现性能难度高,业务读取尽量少使用join
全局表
数据冗余,每个mysql-server实例都保存了一份同样的数据
ER表 er join
关联表,字表尽量分到同一个分片下,这样关联join查询就不会跨实例join。例如订单表和订单详情表,他们都根据app_id和用户信息分表,这样的策略让他们都分配到同一个分片下
<table name="customer" dataNode="dn1,dn2" rule="sharding-by-intfile">
<childTable name="orders" joinKey="customer_id" parentKey="id"/>
</table>
share join
share join是一个简单的跨分片join,基于HBT的方式实现
目前支持2个表的join,原理就是解析SQL语句,拆分成单标表的SQL语句执行,然后把各个节点的数据汇集
mycat常用分片规则
根据 function标签的class来定义分片方法
分片枚举
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下:
<tableRule name="sharding-by-intfile">
<rule>
<columns>user_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>
<property name="type">0</property>
<property name="defaultNode">0</property>
</function>
标签:
- columns标签:用于分片的字段
- algorithm标签:分片函数
property属性标签值:
- mapFile:标识配置文件名称
- type:default=0,0表示int,~0表示string
- defaultNode:0表示设置默认节点。当columns字段里面的值不在枚举列表内,如果设置了默认节点数据行就会分配到默认分片,如果没有设置就会系统expection
partition-hash-int.txt 配置:
10000=0
10010=1
DEFAULT_NODE=1 //默认节点
固定分片hash
本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取 id 的二进制低 10 位,即 id 二进制 &1111111111。
此算法的优点在于如果按照 10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。
<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>
property属性标签值:
- partitionCount:分片个数列表
- partitionLength: 分片范围列表
约束:
分区长度:默认为最大 2^n=1024 ,即最大支持 1024 分区
count,length 两个数组的长度必须是一致的。
1024 = sum((count[i]*length[i])). count 和 length 两个向量的点积恒等于 1024
如本例 2 * 256 + 1 * 512 == 1024,这个例子就不是平均分配,而是3个分片,他们的量大小不一样
范围分片
此分片适用于,提前规划好分片字段的范围属于哪个分片
<tableRule name="auto-sharding-long">
<rule>
<columns>user_id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
<property name="defaultNode">0</property>
</function>
property属性标签值:
- map:范围定义文件
- defaultNode:超过范围后的默认节点
map.txt文件
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
或
0-10000000=0
10000001-20000000=1
取模分片
根据字段十进制取模,相比固定分片 hash,此种在批量插入时可能存在批量插入单事务插入多数据分片,增大事务一致性难度。推荐使用固定分片
<tableRule name="mod-long">
<rule>
<columns>user_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
property属性标签值:
- count:分片数量
按日期(天)分片
此规则为按天分片。
<tableRule name="sharding-by-date">
<rule>
<columns>create_time</columns>
<algorithm>sharding-by-date</algorithm>
</rule>
</tableRule>
<function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2014-01-01</property>
<property name="sEndDate">2014-01-02</property>
<property name="sPartionDay">10</property>
</function>
标签:
- columns:日期字段
property属性标签值:
- dateFormat:日期格式
- sBeginDate:开始日期
- sEndDate:结束日期
- sPartionDay:划分间隔天数
全局序列号
介绍
分库分表的情况下,数据库自增主键已无法保证自增主键的唯一。因为mycat提供了全局的sequence。并且提供了包含本地配置和数据库配置等实现方法
server.xml
<property name="sequnceHandlerType">1</property>
sequnceHandlerType属性值:
- 0:本地文件方式
- 1:数据库方式
- 2:本地时间戳方式
- 3:zookeeper ID生成器
- 4:zookeeper自增序列号
本地文件方式
schema.xml
<schema name="DB1" checkSQLschema="false" sqlMaxLimit="100">
<table name="sys_user" primaryKey="id" autoIncrement="true" dataNode="dn$1-2" rule="rule1" splitTableNames ="true"/>
</schema>
sequence_conf.properties
GLOBAL_SEQ.HISIDS=
GLOBAL_SEQ.MINID=1001
GLOBAL_SEQ.MAXID=1000000000
GLOBAL_SEQ.CURID=1000
优点:
- 因为是mycat本地的配置,不需要去多个分片去获取id,所以速度稍快一些
缺点:
- mycat重新发布后,会恢复到初始值
数据库方式
不推荐,一堆的表创建和存储过程,而且分布式的都不知道他跑进的是哪个表
本地时间戳方式(推荐使用)
ID= 64位二进制 (42(毫秒) + 5(机器ID) + 5(业务编码) + 12(重复累加))
换算成十进制的18位数的long类型,没毫秒可以并发12位二进制累加
sequence_time_conf.properties
WORKID=0-31任意整数 机器ID
DATACENTERID=0-31任意整数 业务编码
多个个 mycat 节点下每个 mycat 配置的 WORKID,DATAACENTERID 不同,组成唯一标识,总共支持 32*32=1024 种组合。
自增长主键
读写分离
主从复制
- mysql每次提交事务前,将sql的更新的事件记录到binlog中。记录完成后,存储引擎提交事务
- 从服务将主服务的binlog复制过来,然后启动一个IO线程,执行一次log中的sql
文档
github: https://github.com/MyCATApache/Mycat-Server
官网:http://www.mycat.org.cn/
documents
- version2.0:https://www.yuque.com/books/share/6606b3b6-3365-4187-94c4-e51116894695/fb2285b811138a442eb850f0127d7ea3