文章目录
一、概述
官方文档,是中文的:http://www.mycat.io/document/mycat-definitive-guide.pdf
Mycat是一个开源数据库中间件,是一个实现了MySQL协议的的数据库中间件服务器,我们可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问Mycat,而Mycat再使用用MySQL原生(Native)协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,包括SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储;
一般地,Mycat主要用于代理MySQL数据库,虽然它也支持去访问其他类型的数据库;Mycat的默认端口是8066
,一般地,我们可以使用常见的对象映射框架比如MyBatis操作Mycat。
简单来说:
- 一个彻底开源的,面向企业应用开发的大数据库集群
- 支持事务、ACID、可以替代MySQL的加强版数据库
- 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群
- 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server
- 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品
- 一个新颖的数据库中间件产品
Mycat主要能做什么
(1)数据库的读写分离
通过Mycat可以自动实现写数据时操作主数据库,读数据时操作从数据库,这样能有效地减轻数据库压力,也能减轻IO压力。
实现读写分离,当主出现故障后,Mycat自动切换到另一个主上,进而提供高可用的数据库服务,当然我需要部署多主多从的模式
如果有了Mycat,客户端直接连接Mycat,可以实现读写分离,如果主出现问题,会自动切换到从服务器上
(2)数据库分库分表
分库分表指的是对数据库数据的拆分,分为两种:水平拆分和垂直拆分
- 水平切分(横向切分)
根据表中数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库服务器上面 - 垂直切分(纵向切分)
按照不同的表来切分到不同的数据库服务器之上
MyCAT通过定义表的分片规则来实现分片,每个表格可以捆绑一个分片规则,每个分片规则指定一个分片字段并绑定一个函数,来实现动态分片算法(后面会讲到)
-
Schema:逻辑库,与MySQL中的Database(数据库)对应,一个逻辑库中定义了所包括的Table。
-
Table:逻辑表,即物理数据库中存储的某一张表,与传统数据库不同,这里的表格需要声明其所存储的逻辑数据节点DataNode。在此可以指定表的分片规则。
-
DataNode:MyCAT的逻辑数据节点,是存放table的具体物理节点,也称之为分片节点,通过DataSource来关联到后端某个具体数据库上
-
DataSource:定义某个物理库的访问地址,用于捆绑到Datanode上
性能有瓶颈了,可以读写分离
数据库容量有瓶颈了,可以分库分表
二、环境搭建
下载地址:http://dl.mycat.io/1.6-RELEASE/
将下载好的Mycat直接上传到Linux上
1. 解压
Java语言开发的,直接解压即可使用
tar -zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local
2. 常用命令
-
Mycat启动
切换到mycat的bin路径下,执行./mycat start
-
Mycat关闭
切换到mycat的bin路径下,执行./mycat stop
-
Mycat命令行
登录mycat命令行,使用mysql的命令行工具来操作的,在mysql的bin路径下执行:- mycat默认数据访问端口是8066
./mysql -umycat -p -P8066 -h127.0.0.1
三、配置文件详解
1. server.xml
主要用于配置mycat需要的服务器信息,常用的配置有:
- 配置序列生成方式
- 配置mycat逻辑数据库
- 配置mycat的访问账户和密码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sequnceHandlerType">2</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-->
<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>-->
<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="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">1</property>
<!--单位为m-->
<property name="memoryPageSize">1m</property>
<!--单位为k-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--单位为m-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">true</property>
</system>
<!-- 全局SQL防火墙设置 -->
<firewall>
<whitehost><!--白名单-->
<host host="127.0.0.1" user="mycat"/><!--允许ip为host的主机使用user进行访问-->
</whitehost>
<blacklist check="false"><!--黑名单-->
</blacklist>
</firewall>
<user name="root"><!--用户名-->
<property name="password">123456</property><!--密码-->
<property name="schemas">mycatdb</property><!--逻辑库的名字-->
<!-- 表级 DML 权限设置 -->
<privileges check="false">
<!--每位分别代表:增、删、改、查,1表示可用,0表示不可用-->
<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">mycatdb</property>
<property name="readOnly">true</property><!--只读-->
</user>
</mycat:server>
(1)sequnceHandlerType
指定使用Mycat全局序列的类型:
- 0为本地文件方式
- 1为数据库方式
- 2为时间戳序列方式
2. schema.xml
用于配置的逻辑数据库的映射、表、分片规则、数据结点及真实的数据库信息;常用的配置有:
- 配置逻辑库映射
- 配置垂直切分的表
- 配置真实的数据库
- 配置读写结点
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<!--配置逻辑库
name:逻辑库的名字,一个schema是一个逻辑库
checkSQLschema:是否检查sql语句语法规则
sqlMaxLimit:最大分页数量,每个查询语句最多返回的条数(100条已经很多了)
-->
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- 逻辑表,将三个分片节点dn1,dn2,dn3整合到同一个逻辑表中
name:逻辑表的名字
dataNode:该逻辑表下的分片节点
rule:水平分表的主键规则
-->
<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
<!-- global table is auto cloned to all defined data nodes ,so can join
with any table whose sharding node is in the same data node -->
<table name="company" primaryKey="ID" type="global" dataNode="dn1,dn2,dn3" />
<table name="goods" primaryKey="ID" type="global" dataNode="dn1,dn2" />
<!-- random sharding using mod sharind rule -->
<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>
<!--定义Mycat中的数据节点/分片,一个dataNode标签就是一个独立的数据分片。通俗理解,一个分片就是一个物理数据库
name:定义数据节点的名字,这个名字需要是唯一的
dataHost:用于定义该分片属于哪个数据库实例的,属性值是引用dataHost标签上定义的name属性
database:用于对应真实的数据库名,必须是真实存在的
-->
<!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743"/> -->
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<!--定义具体的数据库实例、读写分离配置和心跳语句;
name:物理库的名字
maxCon/minCon:最大/最小连接数量
balance:负载均衡策略,详见后面
writeType:已过时,1.6版本就不用了
dbType:数据库类型
dbDriver:数据库驱动类型,只有dbType为mysql时,才可以使用native,表示原生驱动;
其它数据库类型这里只能写jdbc
switchType:故障切换类型,详见后面
slaveThreshold:最多拥有多少个子节点
-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!--心跳机制,判断服务器是否正常运行,需要配合switchType一起进行配置-->
<heartbeat>select user()</heartbeat>
<!--写节点,主从复制中的主服务器-->
<writeHost host="hostM1" url="localhost:3306" user="root"
password="123456">
<!--从节点,主从复制中的从服务器-->
<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />
</writeHost>
<writeHost host="hostS1" url="localhost:3316" user="root"
password="123456" />
</dataHost>
</mycat:schema>
(1)dataHost配置详解
balance
(dataHost 标签中)属性是指负载均衡类型,目前的取值有4种(推荐设置为1):
balance="0"
:不开启读写分离机制,所有读操作都发送到当前可用的writeHost上;balance="1"
:全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2、S1、S2都参与select语句的负载均衡。balance="2"
:所有读操作都随机的在writeHost、readhost上分发balance="3"
:所有读请求随机的分发到wiriterHost对应的readhost执行,writerHost不负担读压力
switchType
属性,用于指定主服务器发生故障后的切换类型
- -1:表示不自动切换
- 1:默认值,自动切换(推荐)
- 2:基于MySQL主从同步的状态决定是否切换
- 3:基于MySQL galary cluster的切换机制(适合集群)(1.4.1)
通常情况下,我们MySQL采用双主双从的模式下,switchType为1即可。因为双主从模式下,主从同步关系很复杂,不能根据MySQL的状态来切换。只需要在一个主出问题后,切换到另外的主。
heartbeat
标签
用于和后端数据库进行心跳检查的语句,检测MySQL数据库是否正常运行
- 当switchType为
1
时,mysql心跳检查语句是select user()
- 当switchType为
2
时,mysql心跳检查语句是show slave status
- 当switchType为
3
时,mysql心跳检查语句是show status like 'wsrep%'
writeHost与readHost标签
这两个标签都指定后端数据库的相关配置给mycat,用于实例化后端连接池。唯一不同的是,writeHost指定写实例、readHost指定读实例,组合这些读写实例来满足系统的要求。
在一个dataHost内可以定义多个writeHost和readHost。但是,如果writeHost指定的后端数据库宕机,那么这个writeHost绑定的所有readHost都将不可用。另一方面,由于这个writeHost宕机系统会自动的检测到,并切换到备用的writeHost上去。
3. rule.xml
用于定义分片规则; 文件中有两种标签:
<tableRule>
:定义分片规则<columns>
:根据哪个列(表字段)进行分片计算<algorithm>
:function标签中定义的分片函数
<function>
:指定相应的类和参数- name属性:指定分片函数的名字
- class属性:完成该功能对应的类
(1)分片枚举
通过在配置文件中配置可能的枚举 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>
partition-hash-int.txt 配置:
10000=0
10010=1
DEFAULT_NODE=1
其中分片函数配置中:
- mapFile:标识配置文件名称,配置文件使用的是键值对(类似properties文件)
- type:默认值为 0,0 表示 Integer,非零表示 String
- defaultNode:默认节点,小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点
默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点,如果不配置默认节点(defaultNode 值小于 0 表示不配置默认节点),碰到不识别的枚举值就会报错,can’t find datanode for sharding column
- 配置文件作用:
- 枚举各个id,并指定对应的节点,所有的节点配置都是从 0 开始,及 0 代表节点 1
(2)固定分片 hash 算法
本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取 id 的二进制低 10 位,即 id 二进制&1111111111。
此算法的优点在于如果按照 10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。
<tableRule name="rule1">
<rule>
<columns>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:分片范围列表
-
上面的配置表示:共分三个区,两个分区为25%(256/1024),一个分区为50%(512/1024),
- 表字段id的hash值的二进制低10位,如果在0-255的范围内,被分到第一个分区,如果在256-511的范围内,被分到第二个分区,如果在252-1023的范围内,被分到第三个分区
-
约 束:
- 分区长度:默认为最大 2^n=1024,即最大支持 1024 分区
- count,length 两个数组的长度必须是一致的。
1024 = sum((count[i]*length[i]))
(3)范围约定
此分片适用于,提前规划好分片字段某个范围属于哪个分片
<tableRule name="auto-sharding-long">
<rule>
<columns>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>
-
mapFile:代表配置文件路径
-
defaultNode:超过范围后的默认节点。
-
配置文件:所有的节点配置都是从 0 开始,及 0 代表节点 1,此配置非常简单,即预先制定可能的 id 范围到某个分片
0-500M=0 500M-1000M=1 1000M-1500M=2
或
0-10000000=0 10000001-20000000=1
(4)求模法
此规则为对分片字段求摸运算。
<tableRule name="mod-long">
<rule>
<columns>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>
此种配置非常明确,即根据id与count(你的结点数)进行十进制求模运算,相比固定分片 hash,此种在批量插入时需要切换数据源,增大事务一致性难度。
(5)按日期(天)分片
<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="sPartionDay">10</property>
</function>
- dateFormat:日期格式
- sBeginDate:开始日期
- sPartionDay:分区天数(步长),上面的配置表示:从开始日期算起,分隔 10 天一个分区
例如:2014-01-01分区为0,2014-01-02分区为0,2014-01-10分区为0,2014-01-11分区为1……依此类推
(6)取模范围约束
此种规则是取模运算与范围约束的结合,主要为了后续数据迁移做准备,即可以自主决定取模后数据的节点分布。
<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:
###### first host configuration
1-32=0
33-64=1
65-96=2
97-128=3
######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7
- patternValue:即求模基数
- defaoultNode:默认节点,如果没有配置,则默认为第0个节点
- mapFile 配置文件路径
- 配置文件中,1-32 即代表 id%256 后分布的范围,如果在 1-32 则在分区 1,其他类推,如果 id 非数据,则会分配在 defaoultNode 默认节点
(7) 应用指定
此规则是在运行阶段有应用自主决定路由到那个分片。
<tableRule name="sharding-by-substring">
<rule>
<columns>user_id</columns>
<algorithm>sharding-by-substring</algorithm>
</rule>
</tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
<property name="startIndex">0</property><!-- zero-based -->
<property name="size">2</property>
<property name="partitionCount">8</property>
<property name="defaultPartition">0</property>
</function>
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。
例如:id为05-100000002
在上面的配置中,根据 id ,从 startIndex=0 开始,截取 size=2 位字符串数字即 05,05 就是获取的分区,如果没传默认分配到 defaultPartition;如果截取出的字符超过了partitionCount,则分配到defaultPartition
(8)一致性 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">
<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>
其它分片规则详见官方文档10.5节:http://www.mycat.io/document/mycat-definitive-guide.pdf
四、读写分离
先只做读写分离,不做分库分表,Mycat只是帮我们转发一下请求,读转发到从库,写转发到主库。
1. 配置server.xml
-
对于读写分离而言,是不需要考虑主键生成方式的,也就是不需要配置全局序列号的,故sequnceHandlerType参数设置为1
<property name="sequnceHandlerType">1</property>
-
配置连接mycat的用户名和密码,以及对应的逻辑库
<user name="mycat"> <property name="password">123456</property> <property name="schemas">mycatdb</property> </user>
2. 配置schema.xml
-
由于只做读写分离,则schema标签里面不用配置table
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"/>
-
配置dataNode
<dataNode name="dn1" dataHost="localhost1" database="test" />
-
配置dataHost,指定writeHost和readHost
-
一主三从dataHost配置:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!--配置写数据库(主库) 一主三从的读写分离配置 --> <writeHost host="hostM3307" url="localhost:3307" user="root" password="123456"> <!--配置写数据库下的读数据库(从库)--> <readHost host="hostS3308" url="localhost:3308" user="root" password="123456" /> <readHost host="hostS3309" url="localhost:3309" user="root" password="123456" /> <readHost host="hostS3310" url="localhost:3310" user="root" password="123456" /> </writeHost> </dataHost>
-
双主双从dataHost配置:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <!--配置心跳语句 --> <heartbeat>select user()</heartbeat> <!--配置写数据库(主库) 双主双从的读写分离配置 --> <writeHost host="hostM3307" url="localhost:3307" user="root" password="123456"> <!--配置写数据库下的读数据库(从库)--> <readHost host="hostS3308" url="localhost:3308" user="root" password="123456" /> <readHost host="hostS3309" url="localhost:3309" user="root" password="123456" /> </writeHost> <writeHost host="hostM3308" url="localhost:3308" user="root" password="123456"> <!--配置写数据库下的读数据库(从库)--> <readHost host="hostS3307" url="localhost:3307" user="root" password="123456" /> <readHost host="hostS3310" url="localhost:3310" user="root" password="123456" /> </writeHost> </dataHost>
五、分库分表
不管是何种方式的切分,主键生成必须交给MyCat实现
1. 水平
(1)配置server.xml
指定主键的生成策略
<!--配置数据库的主键怎么生成,0为本地文件方式,1为数据库方式,2为时间戳序列方式-->
<property name="sequnceHandlerType">0</property>
(2)配置schema.xml
指定逻辑库,分片结点,结点主机等
- 要实现分库分表,那么就需要在
<schema>
标签下配置表了,现在是水平切分,表示要对哪张表进行切分
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100">
<!-- 要实现分库分表,那么就需要在<schema>标签下配置表了,现在是水平切分,表示要对哪张表进行切分 -->
<table name="orders" primaryKey="id" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>
<!--配置真实的数据库名称 test -->
<dataNode name="dn1" dataHost="localhost1" database="test01" />
<dataNode name="dn2" dataHost="localhost1" database="test02" />
<dataNode name="dn3" dataHost="localhost1" database="test03" />
<!--读写分离前面已经说过了,这里就只写了双主双从的情况-->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
</writeHost>
<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
(2)配置rule.xml
根据实际情况选择水平拆分的规则,这里使用的是求模法:
<tableRule name="mod-long">
<rule>
<columns>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>
2. 垂直
垂直切分带来的价值:可以屏蔽掉多数据源的问题,只需要一个统一入口mycat就可以操作下面的多个数据库
(1)配置server.xml
指定主键生成策略
<!--配置数据库的主键怎么生成,0为本地文件方式,1为数据库方式,2为时间戳序列方式-->
<property name="sequnceHandlerType">0</property>
(2)配置schema.xml
也同样需要配置table标签,水平切分是要对哪张表进行切分就配置那张表;垂直切分需要将数据库中的所有表都配置上,指定不同的datanode
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="mycatdb" checkSQLschema="false" sqlMaxLimit="100">
<!-- 要实现分库分表,那么就需要在<schema>标签下配置表了,现在是垂直切分 -->
<!--需求:整个P2P平台的数据库(p2p)进行垂直切分,分为前台数据库(p2p-web)、后台数据库(p2p-admin)、红包数据库(p2p-red)-->
<!--P2P前台数据库-->
<table name="orders" primaryKey="id" autoIncrement="true" dataNode="dn1"/>
<table name="users" primaryKey="id" autoIncrement="true" dataNode="dn1"/>
<!--P2P后台数据库-->
<table name="products" primaryKey="id" autoIncrement="true" dataNode="dn2"/>
<table name="news" primaryKey="id" autoIncrement="true" dataNode="dn2"/>
<!--P2P红包数据库-->
<table name="reds" primaryKey="id" autoIncrement="true" dataNode="dn3"/>
<table name="redType" primaryKey="id" autoIncrement="true" dataNode="dn3"/>
</schema>
<!--配置真实的数据库名称 testdb01 -->
<dataNode name="dn1" dataHost="localhost1" database="p2p-web" />
<dataNode name="dn2" dataHost="localhost1" database="p2p-admin" />
<dataNode name="dn3" dataHost="localhost1" database="p2p-red" />
<!--配置具体的数据库连接信息、读写分离、心跳语句 -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<!--配置心跳语句 -->
<heartbeat>select user()</heartbeat>
<!--配置写数据库(主库) 双主双从的读写分离配置 -->
<writeHost host="hostM3307" url="localhost:3307" user="root" password="123456">
<!--配置写数据库下的读数据库(从库)-->
<readHost host="hostS3308" url="localhost:3308" user="root" password="123456" />
<readHost host="hostS3309" url="localhost:3309" user="root" password="123456" />
</writeHost>
<writeHost host="hostM3308" url="localhost:3308" user="root" password="123456">
<!--配置写数据库下的读数据库(从库)-->
<readHost host="hostS3307" url="localhost:3307" user="root" password="123456" />
<readHost host="hostS3310" url="localhost:3310" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
六、Mycat全局序列号
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
1. 本地文件方式
在servler.xml文件中配置<property name="sequnceHandlerType">0</property>
,即为本地文件方式,在conf/sequence_conf.properties
中维护主键信息,每当插入数据时,从该文件中取出主键信息,并修改该文件的值。
conf/sequence_conf.properties
文件默认提供的主键信息:
- HISIDS:表示使用过的历史分段(一般无特殊需要可不配置)
- MINID:表示最小 ID 值
- MAXID:表示最大ID 值
- CURID(currentId):当前主键id,下回插入时,插入的主键为:10001
#default global sequence
GLOBAL.HISIDS=
GLOBAL.MINID=10001
GLOBAL.MAXID=200000
GLOBAL.CURID=10000
-
如果想要每个表生成的主键连续,可以在sequence_conf.properties配置当前表的生成值,一般将Global替换为自己对应表的前缀即可。
-
取主键值的时候通过
next value for MYCATSEQ_XXXX
获取
例如插入语句:
insert into tbl_user (id, name) values (next value for MYCATSEQ_Global, '张三');
- 缺点:当 MyCAT 重新发布后,配置文件中的 sequence 会恢复到初始值。
- 优点:本地加载,读取速度较快。
2. 时间戳方式
在servler.xml文件中配置<property name="sequnceHandlerType">2</property>
,即为时间戳方式
ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
换算成十进制为 18 位数的 long 类型,每毫秒可以并发 12 位二进制的累加。
这种方式,需要将主键设置为bigint
或者varchar
(长度一般20)类型,插入时,不指定主键,MyCat会自动生成主键
3. 数据库方式
在servler.xml文件中配置<property name="sequnceHandlerType">1</property>
,表示使用数据库方式生成sequence
(1)原理
在数据库中建立一张表,存放 sequence 名称(name),sequence 当前值(current_value),步长(increment int 类型每次读取多少个 sequence,假设为 K)等信息;
这里是数据库方式生成主键ID,不是采用数据库的主键自增,而是mycat利用mysql数据库生成一个主键,使用的是数据库的函数,函数需要自定义
- 当初次使用该 sequence 时,根据传入的 sequence 名称,从数据库这张表中读取 current_value,和 increment 到 MyCat 中,并将数据库中的 current_value 设置为原 current_value 值+increment 值。
- MyCat 将读取到 current_value+increment 作为本次要使用的 sequence 值,下次使用时,自动加 1,当使用 increment 次后,执行步骤 1 相同的操作。
- MyCat 负责维护这张表,用到哪些 sequence,只需要在这张表中插入一条记录即可。若某次读取的 sequence 没有用完,系统就停掉了,则这次读取的 sequence 剩余值不会再使用。
(2)创建表与函数
在数据库中创建一张表,插入一条数据,创建三个函数:
# 创建存放 sequence 的表
DROP TABLE IF EXISTS MYCAT_SEQUENCE;
# name sequence 名称
# current_value 当前 value
# increment 增长步长。 可理解为 mycat 在数据库中一次读取多少个 sequence放到内存中。 当这些用完后, 下次再从数据库中读取。
CREATE TABLE MYCAT_SEQUENCE (name VARCHAR(50) NOT NULL,current_value INT NOT NULL,increment INT NOT NULL DEFAULT 1,
PRIMARY KEY(name)) ENGINE=InnoDB default charset=utf8;
# 插入一条 sequence
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ("GLOBAL", 0, 100);
# 获取当前 sequence 的值 (返回当前值,增量)
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
DECLARE retval VARCHAR(64);
SET retval="-999999999,null";
SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR)) INTO retval FROM MYCAT_SEQUENCE WHERE name = seq_name;
RETURN retval;
END
DELIMITER;
# 设置 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;
# 获取下一个 sequence 值
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET utf8
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END
DELIMITER;
(3)配置在哪个节点上
MYCAT_SEQUENCE 表和以上的 3 个 function,需要放在同一个节点上。在 conf/sequence_db_conf.propertie
文件中 GLOBAL 参数指定存放的节点:
#sequence stored in datanode
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1
例如:上面的MYCAT_SEQUENCE 表和以上的 3 个 function 存放的数据库对应的节点为dn3,则 conf/sequence_db_conf.propertie 文件中的GLOBAL参数应改为dn3
如果执行的时候报:
sql 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 数据库中执行上面的函数。
使用示例:
insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
八、java连接MyCat
-
SpringBoot中,连接MyCat的参数指定(和连接MySQL类似,端口改为8066,用户名和密码改为MyCat中的):
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.29.128:8066/mycat?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CTT spring.datasource.username=mycat spring.datasource.password=123
-
在数据库的插入语句中,根据指定的主键生成规则,获取相应的主键进行插入
来源:CSDN
作者:一名小码农
链接:https://blog.csdn.net/zyx1260168395/article/details/104027058