Mycat学习入门

第一章 入门概述

Mycat概述

1、数据库中间件

Mycat 是数据库中间件。
数据库中间件:连接java应用程序和数据库

2、为什么要用Mycat?

  • Java与数据库紧耦合。
  • 高访问量高并发对数据库的压力。
  • 读写请求数据不一致

3、数据库中间件对比

在这里插入图片描述
① Cobar属于阿里B2B事业群,始于2008年,在阿里服役3年多,接管3000+个MySQL数据库的schema,集群日处理在线SQL请求50亿次以上。由于Cobar发起人的离职,Cobar停止维护。
② Mycat是开源社区在阿里cobar基础上进行二次开发解决了cobar存在的问题,并且加入了许多新的功能在其中。青出于蓝而胜于蓝。
③ OneProxy基于MySQL官方的proxy思想利用c进行开发的,OneProxy是一款商业收费的中间件。舍弃了一些功能,专注在性能和稳定性上。
④ kingshard由小团队用go语言开发,还需要发展,需要不断完善。
⑤ Vitess是Youtube生产在使用,架构很复杂。不支持MySQL原生协议,使用需要大量改造成本。
⑥ Atlas是360团队基于mysql proxy改写,功能还需完善,高并发下不稳定。
⑦ MaxScale是mariadb(MySQL原作者维护的一个版本)研发的中间件
⑧ MySQLRoute是MySQL官方Oracle公司发布的中间件

Mycat作用

1. 读写分离

在这里插入图片描述

2. 数据分片

垂直拆分(分库)、水平拆分(分表)、垂直+水平拆分(分库分表)
在这里插入图片描述

3. 多数据源整合

在这里插入图片描述

Mycat原理

Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用 Mycat 还是MySQL。

在这里插入图片描述

第二章 安装启动

2.1 安装

官网下载Linux版本的Mycat
1、解压后即可使用
解压缩文件拷贝到 linux 下 /usr/local/目录
2、三个配置文件
① schema.xml:定义逻辑库,表、分片节点等内容
② rule.xml:定义分片规则
③ server.xml:定义用户以及系统相关变量,如端口等

2.2 基于 docker 搭建 mysql 主从复制

参考https://blog.csdn.net/weixin_45631876/article/details/104048362

第三章 搭建读写分离

我们通过 Mycat 和 MySQL 的主从复制配合搭建数据库的读写分离,实现 MySQL 的高可用性。
我们将搭建:一主一从、双主双从两种读写分离模式。

3.1 搭建一主一从

一个主机用于处理所有写请求,一台从机负责所有读请求,架构图如下
在这里插入图片描述
搭建读写分离需要先搭建主从复制

3.1.1 修改配置文件server.xml

修改用户信息,与MySQL区分

<user name="mycat" defaultAccount="true">
       <property name="password">123456</property>
       <property name="schemas">TESTDB</property>
       <property name="defaultSchema">TESTDB</property>
       <!--No MyCAT Database selected 错误前会尝试使用该schema作为schema,不设置则为null,报错 -->
</user>

在这里插入图片描述

3.1.2 修改配置文件 schema.xml

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<!-- 数据库配置,此数据库为逻辑数据库,与server.xml中的数据库对应 -->
	<!-- 
		name: 逻辑数据库名,与server.xml中的schema对应
		checkSQLschema: 数据库前缀相关设置,建议看文档,这里暂时设为 false
		sqlMaxLimit: select 时默认的limit,避免查询全表
	-->
	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
		<!-- 
			name: 表名,物理数据库中表名
			dataNode: 表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name
			primaryKey: 主键字段名,自动生成主键时需要设置
			autoIncrement: 是否自增
			rule: 分片规则名,具体规则下文rule详细介绍
			splitTableNames: 启用table name 属性使用逗号分割配置多个表,即多个表使用这个配置
		-->
		<table name="jpa_user" dataNode="dn1"  primaryKey="uid" autoIncrement="true" splitTableNames ="false"/>
	</schema>
	
	<!-- 分片信息,也就是分库相关配置 -->
	<!-- 
		name: 节点名,与table中dataNode对应
		dataHost: 物理数据库名,与datahost中name对应
		database: 物理数据库中数据库名
	-->
	<dataNode name="dn1" dataHost="host1" database="spring_boot" />
	
	<!-- 物理数据库,真正存储数据的数据库 -->
	<!-- 
		name: 物理数据库名,与dataNode中dataHost对应
		balance: 均衡负载的方式
			0:不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
			1:全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
			2:所有读操作都随机的在 writeHost、readhost 上分发。
			3:所有读请求随机的分发到 readhost 执行,writeHost 不负担读压力
		writeType: 写入方式
		dbType: 数据库类型
		heartbeat: 心跳检测语句
	-->
	<dataHost name="host1" maxCon="1000" minCon="10" balance="2" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<!-- 可以有多个 write hosts -->
		<writeHost host="hostM1" url="192.168.2.128:23306" user="root" password="123456">
			<readHost host="hostS1" url="192.168.2.128:23307" user="root" password="123456"/>
		</writeHost>
	</dataHost>
</mycat:schema>

为了能看到读写分离的效果,把balance设置成2,会在两个主机间切换查询
/usr/local/mycat/bin目录使用./mycat start启动Mycat 或使用./mycat console启动
在这里插入图片描述
至此搭建成功 使用命令mysql -umycat -p123456 -h192.168.2.128 -P8066进行登录,或Navicat登录

3.2 搭建双主双从

一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责写请求,m1、m2 互为备机。架构图如下
在这里插入图片描述

3.2.1 搭建双主双从服务器

参考https://blog.csdn.net/weixin_45631876/article/details/104048362

3.2.2 修改 Mycat 的配置文件 schema.xml

修改<dataHost>balance属性,通过此属性配置读写分离的类型
为了双主双从读写分离balance设置为1

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<!-- 数据库配置,此数据库为逻辑数据库,与server.xml中的数据库对应 -->
	<!-- 
		name: 逻辑数据库名,与server.xml中的schema对应
		checkSQLschema: 数据库前缀相关设置,建议看文档,这里暂时设为 false
		sqlMaxLimit: select 时默认的limit,避免查询全表
	-->
	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
		<!-- 
			name: 表名,物理数据库中表名
			dataNode: 表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name
			primaryKey: 主键字段名,自动生成主键时需要设置
			autoIncrement: 是否自增
			rule: 分片规则名,具体规则下文rule详细介绍
			splitTableNames: 启用table name 属性使用逗号分割配置多个表,即多个表使用这个配置
		-->
		<table name="jpa_user" dataNode="dn1"  primaryKey="uid" autoIncrement="true" splitTableNames ="false"/>
	</schema>
	
	<!-- 分片信息,也就是分库相关配置 -->
	<!-- 
		name: 节点名,与table中dataNode对应
		dataHost: 物理数据库名,与datahost中name对应
		database: 物理数据库中数据库名
	-->
	<dataNode name="dn1" dataHost="host1" database="spring_boot" />
	
	<!-- 物理数据库,真正存储数据的数据库 -->
	<!-- 
		name: 物理数据库名,与dataNode中dataHost对应
		balance: 均衡负载的方式
			0:不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
			1:全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
			2:所有读操作都随机的在 writeHost、readhost 上分发。
			3:所有读请求随机的分发到 readhost 执行,writeHost 不负担读压力
		writeType: 写入方式
		dbType: 数据库类型
		heartbeat: 心跳检测语句
		switchType: 1 默认值,自动切换。
			-1:表示不自动切换
			2:基于 MySQL 主从同步的状态决定是否切换。
	-->
	<dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<!-- 可以有多个 write hosts -->
		<writeHost host="hostM1" url="192.168.2.128:23306" user="root" password="123456">
			<readHost host="hostS1" url="192.168.2.128:23307" user="root" password="123456"/>
		</writeHost>
		<writeHost host="hostM2" url="192.168.2.128:23308" user="root" password="123456">
			<readHost host="hostS2" url="192.168.2.128:23309" user="root" password="123456"/>
		</writeHost>
	</dataHost>
</mycat:schema>

至此搭建成功

Master1、Master2 互做备机,负责写的主机宕机,备机切换负责写操作,保证数据库读写分离高
可用性。

第四章 垂直拆分——分库

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表
进行分类,分布到不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面

在这里插入图片描述
系统被切分成了,用户,订单交易,支付几个模块。

4.1 如何划分表

一个问题:在两台主机上的两个数据库中的表,能否关联查询?
答案:不可以关联查询。
分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里。

#客户表 rows:20万
CREATE TABLE customer(
 id INT AUTO_INCREMENT,
 NAME VARCHAR(200),
 PRIMARY KEY(id)
);
#订单表 rows:600万
CREATE TABLE orders(
 id INT AUTO_INCREMENT,
 order_type INT,
 customer_id INT,
 amount DECIMAL(10,2),
 PRIMARY KEY(id)
 ); 
#订单详细表 rows:600万
CREATE TABLE orders_detail(
 id INT AUTO_INCREMENT,
 detail VARCHAR(2000),
 order_id INT,
 PRIMARY KEY(id)
);
#订单状态字典表 rows:20
CREATE TABLE dict_order_type(
 id INT AUTO_INCREMENT,
 order_type VARCHAR(200),
 PRIMARY KEY(id)
);

以上四个表如何分库?客户表分在一个数据库,另外三张都需要关联查询,分在另外一个数据库。

4.2 实现分库

4.2.1 修改 schema 配置文件

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

	<!-- 数据库配置,此数据库为逻辑数据库,与server.xml中的数据库对应 -->
	<!-- 
		name: 逻辑数据库名,与server.xml中的schema对应
		checkSQLschema: 数据库前缀相关设置,建议看文档,这里暂时设为 false
		sqlMaxLimit: select 时默认的limit,避免查询全表
	-->
	<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
		<!-- 
			name: 表名,物理数据库中表名
			dataNode: 表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name
			primaryKey: 主键字段名,自动生成主键时需要设置
			autoIncrement: 是否自增
			rule: 分片规则名,具体规则下文rule详细介绍
			splitTableNames: 启用table name 属性使用逗号分割配置多个表,即多个表使用这个配置
		-->
		<table name="customer" dataNode="dn2"/>
	</schema>
	
	<!-- 分片信息,也就是分库相关配置 -->
	<!-- 
		name: 节点名,与table中dataNode对应
		dataHost: 物理数据库名,与datahost中name对应
		database: 物理数据库中数据库名
	-->
	<dataNode name="dn1" dataHost="host1" database="orders" />
	<dataNode name="dn2" dataHost="host2" database="orders" />
	
	<!-- 物理数据库,真正存储数据的数据库 -->
	<!-- 
		name: 物理数据库名,与dataNode中dataHost对应
		balance: 均衡负载的方式
			0:不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
			1:全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。
			2:所有读操作都随机的在 writeHost、readhost 上分发。
			3:所有读请求随机的分发到 readhost 执行,writeHost 不负担读压力
		writeType: 写入方式
		dbType: 数据库类型
		heartbeat: 心跳检测语句
		switchType: 1 默认值,自动切换。
			-1:表示不自动切换
			2:基于 MySQL 主从同步的状态决定是否切换。
	-->
	<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<writeHost host="hostM1" url="192.168.2.128:23306" user="root" password="123456">
		</writeHost>
	</dataHost>
	<dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<writeHost host="hostM2" url="192.168.2.128:23308" user="root" password="123456">
		</writeHost>
	</dataHost>
</mycat:schema>

4.2.2 新增两个空白库

分库操作不是在原来的老数据库上进行操作,需要准备两台机器分别安装新的数据库
在数据节点 dn1、dn2 上分别创建数据库 orders

4.2.3 启动 Mycat

./mycat console

4.2.4 访问 Mycat 进行分库

  1. 访问 Mycat 数据库
  2. 创建 4 张表(建表信息如上)
  3. 查看表信息
    在这里插入图片描述

第五章 水平拆分——分表

相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,
每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就
是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中

在这里插入图片描述

5.1 实现分表

5.1.1 选择要拆分的表

MySQL 单表存储数据条数是有瓶颈的,单表达到 1000 万条数据就达到了瓶颈,会影响查询效率,
需要进行水平拆分(分表)进行优化。

5.1.2 分表字段

以 orders 表为例,可以根据不同自字段进行分表

编号 分表字段 效果
1 id(主键、或创建时间) 查询订单注重时效,历史订单被查询的次数少,
如此分片会造成一个节点访问多,一个访问少,不平均。
2 customer_id(客户 id) 根据客户 id 去分,两个节点访问平均,一个客户的所有订单都在同一个节点

5.1.3 修改配置文件 schema.xml

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
	<table name="customer" dataNode="dn2"/>
	<!-- 为 orders 表设置数据节点为 dn1、dn2,并指定分片规则为 mod_rule(自定义的名字)-->
	<table name="orders" dataNode="dn1,dn2" rule="mod_rule"/>
</schema>

5.1.4 修改配置文件 rule.xml

添加mod_rule 修改mod-long

<!-- 在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id -->
<!-- 还有选择分片算法 mod-long(对字段求模运算),customer_id 对两个节点求模,根据结果分片 -->
<tableRule name="mod_rule">
	<rule>
		<columns>customer_id</columns>
		<algorithm>mod-long</algorithm>
	</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 配置算法 mod-long 参数 count 为 2,两个节点 -->
	<property name="count">2</property>
</function>

5.1.5 在数据节点 dn2 上建 orders 表

5.1.6 重启 Mycat,让配置生效

5.1.7 访问 Mycat 实现分片

#在 mycat 里向 orders 表插入数据,INSERT 字段不能省略
INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);

查看测试结果

1.mycat中查询
在这里插入图片描述
2.dn1中
在这里插入图片描述
3.dn2中
在这里插入图片描述

5.2 Mycat 的分片 “join”

Orders 订单表已经进行分表操作了,和它关联的 orders_detail 订单详情表如何进行 join 查询。
我们要对 orders_detail 也要进行分片操作。Join 的原理如下图:
在这里插入图片描述

5.2.1 ER 表

Mycat 借鉴了 NewSQL 领域的新秀 Foundation DB 的设计思路,Foundation DB 创新性的提出了 Table Group 的概念,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了JION 的效率和性能问题,根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。

  1. 修改配置文件 schema.xml
<table name="orders" dataNode="dn1,dn2" rule="mod_rule">
	<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
  1. 在 dn2 创建 orders_detail 表
  2. 重启 Mycat
  3. 访问 Mycat 向 orders_detail 表插入数据
INSERT INTO orders_detail(id,detail,order_id) values(1,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(2,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(3,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(4,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(5,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(6,'detail1',6);
  1. 在mycat、dn1、dn2中运行两个表join语句
select o.*,od.detail from orders o inner join orders_detail od on o.id=od.order_id;

在这里插入图片描述

5.2.2 全局表

在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较 棘手的问题,考虑到字典表具有以下几个特性:

  • 变动不频繁
  • 数据量总体变化不大
  • 数据规模不大,很少有超过数十万条记录

鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:

  • 全局表的插入、更新操作会实时在所有节点上执行,保持各个分片的数据一致性
  • 全局表的查询操作,只从一个节点获取
  • 全局表可以跟任何一个表进行 JOIN 操作

将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据
JOIN 的难题。通过全局表+基于 E-R 关系的分片策略,Mycat 可以满足 80%以上的企业应用开发

  1. 修改 schema.xml 配置文件
<table name="orders" dataNode="dn1,dn2" rule="mod_rule">
	<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
<table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
  1. 在 dn2 创建 dict_order_type 表
  2. 重启 Mycat
  3. 访问 Mycat 向 dict_order_type 表插入数据
INSERT INTO dict_order_type(id,order_type) VALUES(101,'type1');
INSERT INTO dict_order_type(id,order_type) VALUES(102,'type2');
  1. 在Mycat、dn1、dn2中查询表数据
    在这里插入图片描述

5.3 常用分片规则

5.3.1 取模

此规则为对分片字段求摸运算。也是水平分表最常用规则。5.1 配置分表中,orders 表采用了此规则。

5.3.2 分片枚举

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

  1. 修改schema.xml配置文件
<table name="orders_ware_info" dataNode="dn1,dn2" rule="sharding-by-intfile" ></table>
  1. 修改rule.xml配置文件
<!-- 
	columns: 分片字段
	algorithm: 分片函数
 -->
<tableRule name="sharding-by-intfile">
	<rule>
		<columns>areacode</columns>
		<algorithm>hash-int</algorithm>
	</rule>
</tableRule>
<!-- 
	mapFile: 标识配置文件名称
	type: 0为int型、非0为String
	defaultNode: 默认节点:小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点
		设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错
 -->
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
	<property name="mapFile">partition-hash-int.txt</property>
	<property name="type">1</property>
	<property name="defaultNode">0</property>
</function>
  1. 修改partition-hash-int.txt配置文件
110=0
120=1
  1. 重启 Mycat
  2. 访问Mycat创建表
#订单归属区域信息表
CREATE TABLE orders_ware_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `order_id` INT comment '订单编号',
 `address` VARCHAR(200) comment '地址',
`areacode` VARCHAR(20) comment '区域编号',
PRIMARY KEY(id)
);
  1. 插入数据
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (1,1,'北京','110');
INSERT INTO orders_ware_info(id, order_id,address,areacode) VALUES (2,2,'天津','120');
  1. 查询Mycat、dn1、dn2可以看到数据分片效果
    在这里插入图片描述

5.3 范围约定

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

  1. 修改schema.xml配置文件
<table name="payment_info" dataNode="dn1,dn2" rule="auto-sharding-long" ></table>
  1. 修改rule.xml配置文件
<tableRule name="auto-sharding-long">
	<rule>
		<columns>order_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>
  1. 修改autopartition-long.txt配置文件
0-102=0
103-200=1
  1. 重启 Mycat
  2. 访问Mycat创建表
#支付信息表 
CREATE TABLE payment_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `order_id` INT comment '订单编号',
 `payment_status` INT comment '支付状态',
 PRIMARY KEY(id)
);
  1. 插入数据
INSERT INTO payment_info (id,order_id,payment_status) VALUES (1,101,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (2,102,1);
INSERT INTO payment_info (id,order_id ,payment_status) VALUES (3,103,0);
INSERT INTO payment_info (id,order_id,payment_status) VALUES (4,104,1);
  1. 查询Mycat、dn1、dn2可以看到数据分片效果
    在这里插入图片描述

5.4 按日期(天)分片

此规则为按天分片。设定时间格式、范围

  1. 修改schema.xml配置文件
<table name="login_info" dataNode="dn1,dn2" rule="sharding-by-date" ></table>
  1. 修改rule.xml配置文件
<tableRule name="sharding-by-date">
	<rule>
		<columns>login_date</columns>
		<algorithm>partbyday</algorithm>
	</rule>
</tableRule>
<!-- 
	dateFormat: 日期格式
	sNaturalDay:
	sBeginDate: 开始日期
	sEndDate: 结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
	sPartionDay: 分区天数,即默认从开始日期算起,分隔 2 天一个分区
 -->
<function name="partbyday" class="io.mycat.route.function.PartitionByDate">
	<property name="dateFormat">yyyy-MM-dd</property>
	<property name="sNaturalDay">0</property>
	<property name="sBeginDate">2019-01-01</property>
	<property name="sEndDate">2019-01-4</property>
	<property name="sPartionDay">2</property>
</function>
  1. 重启 Mycat
  2. 访问Mycat创建表
#用户信息表 
CREATE TABLE login_info
(
 `id` INT AUTO_INCREMENT comment '编号',
 `user_id` INT comment '用户编号',
 `login_date` date comment '登录日期',
 PRIMARY KEY(id)
);
  1. 插入数据
INSERT INTO login_info(id,user_id,login_date) VALUES (1,101,'2019-01-01');
INSERT INTO login_info(id,user_id,login_date) VALUES (2,102,'2019-01-02');
INSERT INTO login_info(id,user_id,login_date) VALUES (3,103,'2019-01-03');
INSERT INTO login_info(id,user_id,login_date) VALUES (4,104,'2019-01-04');
INSERT INTO login_info(id,user_id,login_date) VALUES (5,103,'2019-01-05');
INSERT INTO login_info(id,user_id,login_date) VALUES (6,104,'2019-01-06');
  1. 查询Mycat、dn1、dn2可以看到数据分片效果
    在这里插入图片描述

5.4 全局序列

在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,Mycat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。

5.4.1 本地文件

此方式 Mycat 将 sequence 配置到文件中,当使用到 sequence 中的配置后,Mycat 会更新 classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。

  • 优点:本地加载,读取速度较快
  • 缺点:抗风险能力差,Mycat 所在主机宕机后,无法读取本地文件。

5.4.2 数据库方式

利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。如果内存中的号段用完了 Mycat 会再向数据库要一次。

问:那如果 Mycat 崩溃了 ,那内存中的序列岂不是都没了?
是的。如果是这样,那么 Mycat 启动后会向数据库申请新的号段,原有号段会弃用。
也就是说如果 Mycat 重启,那么损失是当前的号段没用完的号码,但是不会因此出现主键重复
  1. 建库序列脚本
#在 dn1 上创建全局序列表
CREATE TABLE MYCAT_SEQUENCE (NAME VARCHAR(50) NOT NULL,current_value INT NOT NULL,
increment INT NOT NULL DEFAULT 100, PRIMARY KEY(NAME)) ENGINE=INNODB;
#创建全局序列所需函数
DELIMITER $$ 
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
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 ;

DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),VALUE INTEGER) RETURNS 
VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = VALUE
WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64) 
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

#初始化序列表记录
INSERT INTO MYCAT_SEQUENCE(NAME,current_value,increment) VALUES ('ORDERS', 400000,100);
  1. 修改 Mycat 配置

修改sequence_db_conf.properties

#sequence stored in datanode
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
#意思是 ORDERS这个序列在dn1这个节点上
ORDERS=dn1

修改server.xml

<!-- 全局序列类型:0-本地文件,1-数据库方式,2-时间戳方式。 -->
<property name="sequnceHandlerType">1</property>   

重启Mycat

  1. 验证全局序列

登录 Mycat,插入数据

insert into orders(id,amount,customer_id,order_type) 
values(next value for MYCATSEQ_ORDERS,1000,101,102);

查询数据(插入了两条)
在这里插入图片描述
重启Mycat后,再次插入数据,再查询
在这里插入图片描述

5.4.3 时间戳方式

全局序列ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 换算成十进制为 18 位数的long 类型,每毫秒可以并发 12 位二进制的累加。

  • 优点:配置简单
  • 缺点:18 位 ID 过长

5.4.4 自主生成全局序列

可在 java 项目里自己生成全局序列,如下:

  • 根据业务逻辑组合
  • 可以利用 redis 的单线程原子性 incr 来生成序列,但自主生成需要单独在工程中用 java 代码实现,还是推荐使用 Mycat 自带全局序列。

第六章 Mycat 安全设置

6.1 权限配置

6.1.1 user 标签权限控制

目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制。是通过 server.xml 的 user 标签进行配置。

<user name="mycat">
	<property name="password">123456</property>
	<property name="schemas">TESTDB</property>
	<property name="defaultSchema">TESTDB</property>
</user>
<user name="user">
	<property name="password">user</property>
	<property name="schemas">TESTDB</property>
	<!-- readerOnly: 应用连接中间件逻辑库所具有的权限。true 为只读,false 为读写都有,默认为 false -->
	<property name="readOnly">true</property>
	<property name="defaultSchema">TESTDB</property>
</user>

6.1.2 privileges 标签权限控制

在 user 标签下的 privileges 标签可以对逻辑库(schema)、表(table)进行精细化的 DML 权限控制。
privileges 标签下的 check 属性,如为 true 开启权限检查,为 false 不开启,默认为 false。
由于 Mycat 一个用户的 schemas 属性可配置多个逻辑库(schema) ,所以 privileges 的下级节点 schema 节点同样可配置多个,对多库多表进行细粒度的 DML 权限控制。

<user name="mycat" defaultAccount="true">
	<property name="password">123456</property>
	<property name="schemas">TESTDB</property>
	<property name="defaultSchema">TESTDB</property>
	<!-- 表级 DML 权限设置 -->
	<privileges check="false">
		<schema name="TESTDB" dml="1111" >
			<!-- 配置orders表没有增删改查权限 -->
			<table name="orders" dml="0000"></table>
			<table name="tb02" dml="1111"></table>
		</schema>
	</privileges>		
</user>

dml 权限顺序为:insert(新增),update(修改),select(查询),delete(删除), 0000–> 1111,0 为禁止权限,1 为开启权限。

6.2 SQL 拦截

firewall 标签用来定义防火墙;firewall 下 whitehost 标签用来定义 IP 白名单 ,blacklist 用来定义SQL 黑名单。

6.2.1 白名单

可以通过设置白名单,实现某主机某用户可以访问 Mycat,而其他主机用户禁止访问。

<firewall>
   <whitehost>
   	  <!-- 配置只有192.168.2.128主机可以通过mycat用户访问 -->
      <host host="192.168.2.128" user="mycat"/>
   </whitehost>
</firewall>

6.2.2 黑名单

可以通过设置黑名单,实现 Mycat 对具体 SQL 操作的拦截,如增删改查等操作的拦截。

<firewall>
	<whitehost>
		<!-- 配置只有192.168.2.128主机可以通过mycat用户访问 -->
		<host host="1*7.0.0.*" user="root"/>
	</whitehost>
	<blacklist check="true">
		<!-- 配置禁止mycat用户进行删除操作 -->
		<property name="deleteAllow">false</property>
	</blacklist>
</firewall>
配置项 缺省值 描述
selelctAllow true 是否允许执行 SELECT 语句
deleteAllow true 是否允许执行 DELETE 语句
updateAllow true 是否允许执行 UPDATE 语句
insertAllow true 是否允许执行 INSERT 语句
createTableAllow true 是否允许创建表
setAllow true 是否允许使用 SET 语法
alterTableAllow true 是否允许执行 Alter Table 语句
dropTableAllow true 是否允许修改表
commitAllow true 是否允许执行 commit 操作
rollbackAllow true 是否允许执行 roll back 操作

第七章 Mycat 监控工具

7.1 Mycat-web 简介

Mycat-web 是 Mycat 可视化运维的管理和监控平台,弥补了 Mycat 在监控上的空白。帮 Mycat 分担统计任务和配置管理任务。Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计 SQL 并分析慢 SQL 和高频 SQL 等。为优化 SQL 提供依据。
在这里插入图片描述

7.2 Mycat-web 配置使用

发布了25 篇原创文章 · 获赞 1 · 访问量 1979

猜你喜欢

转载自blog.csdn.net/weixin_45631876/article/details/104040237