课程介绍

第一天 第二天 第三天 第四天
MyCat简介 MyCat分片规则 MyCat高可用集群搭建 MyCat综合案例
MyCat入门 MyCat高级 MyCat架构剖析
MyCat配置文件详解

1. MyCat简介

1.1 MyCat 引入

  1. 如今随着互联网的发展,数据的量级也是成指数式的增长,从GBTBPB。对数据的各种操作也是愈加的困难,传统的关系性数据库已经无法满足快速查询与插入数据的需求,这个时候NoSQL的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。但是,在有些场合NoSQL一些折衷是无法满足使用场景的,就比如有些使用场景是绝对要有事务与安全指标的。这个时候NoSQL肯定是无法满足的,所以还是需要使用关系性数据库。如何使用关系型数据库解决海量存储的问题呢?此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储,为应对此问题就出现了——MyCat 。<br />MyCAT的目标是:低成本的将现有的单机数据库和应用平滑迁移到"云"端,解决海量数据存储和业务规模迅速增长情况下的数据存储和访问的瓶颈问题 。<br />![](https://cdn.nlark.com/yuque/0/2021/png/596101/1629271027385-aad1117d-0205-401b-8c52-349251052a78.png#)

1.2 MyCat 历史

1). Mycat 背后是阿里曾经开源的知名产品——Cobar。Cobar 的核心功能和优势是 MySQL 数据库分片,此产品曾经广为流传,据说最早的发起者对 Mysql 很精通,后来从阿里跳槽了,阿里随后开源的 Cobar,并维持到 2013 年年初,然后,就没有然后了。 Cobar 的思路和实现路径的确不错。基于 Java 开发的,实现了 MySQL 公开的二进制传输协议,巧妙地将自己伪装成一个 MySQL Server,目前市面上绝大多数 MySQL 客户端工具和应用都能兼容。比自己实现一个新的数据库协议要明智的多,因为生态环境在哪里摆着。
2). Mycat 是基于 cobar 演变而来,相对于cobar来说 , 有两个显著优势 : ①. 对 cobar 的代码进行了彻底的重构,Mycat在I/O方面进行了重大改进,将原来的BIO改成了NIO, 并发量有大幅提高 ; ②. 增加了对Order By、Group By、limit等聚合功能的支持,同时兼容绝大多数数据库成为通用的数据库中间件 。
3). 简单的说,MyCAT就是:一个新颖的数据库中间件产品支持mysql集群,或者 mariadb cluster,提供高可用性数据分片集群。你可以像使用mysql一样使用 mycat 。对于开发人员来说根本感觉不到mycat的存在。
MyCat-数据库中间件 - 图1

1.3 MyCat 优势

MyCat 是一个彻底开源的,面向企业应用数据库中间件 , 支持事务, 可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群, 在MyCat 中融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server , 并结合传统数据库和新型分布式数据仓库的新一代企业级数据库中间件产品 。
并具有优势:
1). 性能可靠稳定
基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能以及众多成熟的使用案例使得MYCAT一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。业界优秀的开源项目和创新思路被广泛融入到MYCAT的基因中,使得MYCAT在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。
2). 强大的技术团队
MyCat 现在由一支强大的技术团队维护 , 吸引和聚集了一大批业内大数据和云计算方面的资深工程师、架构师、DBA,优秀的团队保障了MyCat的稳定高效运行。而且MyCat不依托于任何商业公司,而且得到大批开源爱好者的支持。
3). 体系完善
MyCat已经形成了一系列的周边产品,比较有名的是 Mycat-web、Mycat-NIO、Mycat-Balance等,已经形成了一个比较完整的解决方案,而不仅仅是一个中间件。
4). 社区活跃
与MyCat数据库中间件类似的产品还有 TDDL、Amoeba、Cobar 。
①. TDDL(Taobao Distributed Data Layer)不同于其它几款产品,并非独立的中间件,只能算作中间层,是以Jar包方式提供给应用调用 ,属于JDBC Shard的思想 。
②. Amoeba是作为一个真正的独立中间件提供服务,应用去连接Amoeba操作MySQL集群,就像操作单个MySQL一样。Amoeba算中间件中的早期产品,后端还在使用JDBC Driver。
③. Cobar是在Amoeba基础上进化的版本,一个显著变化是把后端JDBC Driver改为原生的MySQL通信协议层。
④. MyCat又是在Cobar基础上发展的版本, 性能优良, 功能强大, 社区活跃 。

1.4 MyCat 使用场合

要想用好MyCat,就需要了解其适用场景,以下几个场景适合适用MyCat。
1). 高可用性与MySQL读写分离
高可用:利用MyCat可以轻松实现热备份,当一台服务器停机时,可以由集群中的另一台服务器自动接管业务,无需人工干预,从而保证高可用。
读写分离:通过MySQL数据库的binlog日志完成主从复制,并可以通过MyCat轻松实现读写分离,实现insert、update、delete走主库,而在select时走从库,从而缓解单台服务器的访问压力。
2). 业务数据分级存储保障
企业的数据量总是无休止的增长,这些数据的格式不一样,访问效率不一样,重要性也不一样。可以针对不同级别的数据,采用不同的存储设备,通过分级存储管理软件实现数据客体在存储设备之间自动迁移及自动访问切换。
3). 大表水平拆分,集群并行计算
数据切分是MyCat的核心功能,是指通过某种特定的条件,将存放在同一个数据库的数据,分散存储在多个数据库中,以达到分散单台设备负载的效果。当数据库量超过800万行且需要做分片时,就可以考虑使用MyCat实现数据切分。
4). 数据库路由器
MyCat基于MySQL实例的连接池复用机制,可以让每个应用最大程度共享一个MySQL实例的所有连接池,让数据库的并发访问能力大大提升。
5). 整合多种数据源
当一个项目中使用了多个数据库(Oracle,MySQL,SQL Server,PostgreSQL),并配置了多个数据源,操作起来就比较烦锁,这时就可以使用MyCat进行整合,最终我们的应用程序只需要访问一个数据源即可。

1.5 MyCat 下载

下载地址: https://github.com/MyCATApache/Mycat-download
MyCat-数据库中间件 - 图2
最新下载地址: http://dl.mycat.io/
MyCat-数据库中间件 - 图3

2. MyCat入门

本章节主要讲解MyCat的在Linux中的安装、基本概念和基本原理,这些是学习MyCat的基础。

2.1 环境搭建

Mycat是采用java语言开发的开源的数据库中间件,支持Windows和Linux运行环境,下面介绍MyCat的Linux中的环境搭建。
A. MySQL
B. JDK
C. MyCat

2.1.1 安装配置MySQL

2.1.1.1 安装MySQL
  1. A). 卸载 centos 中预安装的 mysql
  2. rpm -qa | grep -i mysql
  3. rpm -e mysql-libs-5.1.71-1.el6.x86_64 --nodeps
  4. B). 上传 mysql 的安装包
  5. alt + p -------> put E:/test/MySQL-5.6.22-1.el6.i686.rpm-bundle.tar
  6. C). 解压 mysql 的安装包
  7. mkdir mysql
  8. tar -xvf MySQL-5.6.22-1.el6.i686.rpm-bundle.tar -C /root/mysql
  9. D). 安装依赖包
  10. yum -y install libaio.so.1 libgcc_s.so.1 libstdc++.so.6 libncurses.so.5 --setopt=protected_multilib=false
  11. yum update libstdc++-4.4.7-4.el6.x86_64
  12. E). 安装 mysql-client
  13. rpm -ivh MySQL-client-5.6.22-1.el6.i686.rpm
  14. F). 安装 mysql-server
  15. rpm -ivh MySQL-server-5.6.22-1.el6.i686.rpm

2.1.1.2 启动停止MySQL
  1. service mysql start
  2. service mysql stop
  3. service mysql status
  4. service mysql restart

2.1.1.3 登录MySQL
  1. mysql 安装完成之后, 会自动生成一个随机的密码, 并且保存在一个密码文件中 : /root/.mysql_secret
  2. mysql -u root -p
  3. 登录之后, 修改密码 :
  4. set password = password('itcast');
  5. 授权远程访问 :
  6. grant all privileges on *.* to 'root' @'%' identified by 'itcast';
  7. flush privileges;

授权远程访问之后 , 就可以通过sqlYog来连接Linux上的MySQL , 但是记得关闭Linux上的防火墙(或者配置防火墙):
MyCat-数据库中间件 - 图4

2.1.2 安装JDK1.8

  1. A. 上传JDK的安装包到Linuxroot目录下
  2. alt + p -----------> put D:/jdk-8u181-linux-x64.tar.gz
  3. B. 解压压缩包 , /usr/share 目录下
  4. tar -zxvf jdk-8u181-linux-x64.tar.gz -C /usr/share/
  5. C. 配置PATH环境变量 , 在该配置文件(/etc/profile)的最后加入如下配置
  6. export JAVA_HOME=/usr/share/jdk1.8.0_181
  7. export PATH=$PATH:$JAVA_HOME/bin

2.1.3 安装MyCat

1). 上传MyCat的压缩包
alt + p ————> put D:/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz
2). 解压MyCat的压缩包
tar -zxvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz -C /usr/local
3). MyCat的目录结构介绍
MyCat-数据库中间件 - 图5

2.2 MyCat核心概念

2.2.1 分片

简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。
1). 一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分。
MyCat-数据库中间件 - 图6
2). 另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
MyCat-数据库中间件 - 图7
MyCat 分片策略 :
MyCat-数据库中间件 - 图8
虚线以上是逻辑结构图, 虚线以下是物理结构图 ;

2.2.2 逻辑库(schema)

MyCat是一个数据库中间件,通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。

2.2.3 逻辑表(table)

既然有逻辑库,那么就会有逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成。
1). 分片表
是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。 总而言之就是需要进行分片的表。如 :tb_order 表是一个分片表, 数据按照规则被切分到dn1、dn2两个节点。
2). 非分片表
一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。如: tb_city是非分片表 , 数据只存于其中的一个节点 dn1 上。
3). ER表
关系型数据库是基于实体关系模型(Entity Relationship Model)的, MyCat中的ER表便来源于此。 MyCat提出了基于ER关系的数据分片策略 , 字表的记录与其所关联的父表的记录存放在同一个数据分片中, 通过表分组(Table Group)保证数据关联查询不会跨库操作。
4). 全局表
在一个大型的项目中,会存在一部分字典表(码表) , 在其中存储的是项目中的一些基础的数据 , 而这些基础的数据 , 数据量都不大 , 在各个业务表中可能都存在关联 。当业务表由于数据量大而分片后 , 业务表与附属的数据字典表之间的关联查询就变成了比较棘手的问题 , 在MyCat中可以通过数据冗余来解决这类表的关联查询 , 即所有分片都复制这一份数据(数据字典表),因此可以把这些冗余数据的表定义为全局表。

2.2.4 分片节点(dataNode)

数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)。

2.2.5 节点主机(dataHost)

数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。

2.2.6 分片规则(rule)

前面讲了数据切分,一个大表被分成若干个分片表,就需要一定的规则,这样按照某种业务规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。

2.3 分片配置测试

2.3.1 需求

由于 TB_TEST 表中数据量很大, 现在需要对 TB_TEST 表进行数据分片, 分为三个数据节点 , 每一个节点主机位于不同的服务器上, 具体的结构 ,参考下图 :
MyCat-数据库中间件 - 图9

2.3.2 环境准备

准备三台虚拟机 , 且安装好MySQL , 并配置好 :

  1. IP 地址列表 :
  2. 192.168.192.157
  3. 192.168.192.158
  4. 192.168.192.159

2.3.3 配置 schema.xml

schema.xml 作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、逻辑表以及对应的分片规则、DataNode以及DataSource。弄懂这些配置,是正确使用MyCat的前提。这里就一层层对该文件进行解析。

属性 含义
schema 标签用于定义MyCat实例中的逻辑库
table 标签定义了MyCat中的逻辑表, rule用于指定分片规则,auto-sharding-long的分片规则是按ID值的范围进行分片 1-5000000 为第1片 5000001-10000000 为第2片…. 具体设置我们会在第四节中讲解。
dataNode 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。
dataHost 标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。

在服务器上创建3个数据库,命名为 db1
修改schema.xml如下:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <!-- 逻辑库配置 -->
  5. <schema name="ITCAST" checkSQLschema="false" sqlMaxLimit="100">
  6. <!-- 逻辑表配置 -->
  7. <table name="TB_TEST" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
  8. </schema>
  9. <!-- 数据节点配置 -->
  10. <dataNode name="dn1" dataHost="host1" database="db1" />
  11. <dataNode name="dn2" dataHost="host2" database="db1" />
  12. <dataNode name="dn3" dataHost="host3" database="db1" />
  13. <!-- 节点主机配置 -->
  14. <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
  15. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  16. <heartbeat>select user()</heartbeat>
  17. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast"></writeHost>
  18. </dataHost>
  19. <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
  20. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  21. <heartbeat>select user()</heartbeat>
  22. <writeHost host="hostM1" url="192.168.192.158:3306" user="root" password="itcast"></writeHost>
  23. </dataHost>
  24. <dataHost name="host3" maxCon="1000" minCon="10" balance="0"
  25. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  26. <heartbeat>select user()</heartbeat>
  27. <writeHost host="hostM1" url="192.168.192.159:3306" user="root" password="itcast"></writeHost>
  28. </dataHost>
  29. </mycat:schema>

2.3.4 配置 server.xml

server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。在system中添加UTF-8字符集设置,否则存储中文会出现问号

  1. <property name="charset">utf8</property>

修改user的设置 , 我们这里为 ITCAST 设置了两个用户 :

  1. <user name="root">
  2. <property name="password">123456</property>
  3. <property name="schemas">ITCAST</property>
  4. </user>
  5. <user name="test">
  6. <property name="password">123456</property>
  7. <property name="schemas">ITCAST</property>
  8. </user>

并且需要将原来的逻辑库的配置 , 替换为 ITCAST逻辑库 ;

2.3.5 启动MyCat

启动:

  1. bin/mycat start
  2. bin/mycat stop
  3. bin/mycat status

查看MyCat:
连接端口号 8066
1). 通过命令行

  1. mysql -h 127.0.0.1 -P 8066 -u root -p

MyCat-数据库中间件 - 图10
2). 通过sqlyog连接
MyCat-数据库中间件 - 图11

2.3.6 MyCat分片测试

进入mycat ,执行下列语句创建一个表

  1. CREATE TABLE TB_TEST (
  2. id BIGINT(20) NOT NULL,
  3. title VARCHAR(100) NOT NULL ,
  4. PRIMARY KEY (id)
  5. ) ENGINE=INNODB DEFAULT CHARSET=utf8 ;

我们再查看MySQL的3个库,发现表都自动创建好啦。好神奇。
接下来是插入表数据,注意,在写 INSERT 语句时一定要写把字段列表写出来,否则会出现下列错误提示:
错误代码: 1064
partition table, insert must provide ColumnList
我们试着插入一些数据:

  1. INSERT INTO TB_TEST(ID,TITLE) VALUES(1,'goods1');
  2. INSERT INTO TB_TEST(ID,TITLE) VALUES(2,'goods2');
  3. INSERT INTO TB_TEST(ID,TITLE) VALUES(3,'goods3');

我们会发现这些数据被写入到第一个节点中了,那什么时候数据会写到第二个节点中呢?
我们插入下面的数据就可以插入第二个节点了

  1. INSERT INTO TB_TEST(ID,TITLE) VALUES(5000001,'goods5000001');

因为我们采用的分片规则是每节点存储500万条数据,所以当ID大于5000000则会存储到第二个节点上。
目前只设置了两个节点,如果数据大于1000万条,会怎么样呢?执行下列语句测试一下

  1. INSERT INTO TB_TEST(ID,TITLE) VALUES(10000001,'goods10000001');

2.4 MyCat原理介绍

MyCat原理中最重要的一个动词就是 “拦截”, 它拦截了用户发送过来的SQL语句, 首先对SQL语句做一些特定的分析,如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL语句发往后端的真实数据库,并将返回的结果做适当处理,最终再返回给用户,如图所示。
MyCat-数据库中间件 - 图12
在图中,user表被分为三个分片节点dn1、dn2、dn3, 他们分布式在三个MySQL Server(dataHost)上,因此可以使用1-N台服务器来分片,分片规则(sharding rule)为典型的字符串枚举分片规则, 一个规则的定义是分片字段+分片函数。这里的分片字段为 status,分片函数则为字符串枚举方式。
MyCat收到一条SQL语句时,首先解析SQL语句涉及到的表,接着查看此表的定义,如果该表存在分片规则,则获取SQL语句里分片字段的值,并匹配分片函数,得到该SQL语句对应的分片列表,然后将SQL语句发送到相应的分片去执行,最后处理所有分片返回的数据并返回给客户端。以”select from user where status=’0’” 为例, 查找 status=’0’ ,按照分片函数, ‘0’ 值存放在dn1,于是SQL语句被发送到第一个节点中执行, 然后再将查询的结果返回给用户。
如果发送的SQL语句为 “select
from user where status in (‘0’,’1’)” , 那么SQL语句会被发送到dn1,dn2对应的主机上执行, 然后将结果集合并后输出给用户。

3. MyCat配置文件详解

3.1 server.xml

3.1.1 system 标签

属性 取值 含义
charset utf8 设置Mycat的字符集, 字符集需要与MySQL的字符集保持一致
nonePasswordLogin 0,1 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户
useHandshakeV10 0,1 使用该选项主要的目的是为了能够兼容高版本的jdbc驱动, 是否采用HandshakeV10Packet来与client进行通信, 1:是, 0:否
useSqlStat 0,1 开启SQL实时统计, 1 为开启 , 0 为关闭 ;
开启之后, MyCat会自动统计SQL语句的执行情况 ;
mysql -h 127.0.0.1 -P 9066 -u root -p
查看MyCat执行的SQL, 执行效率比较低的SQL , SQL的整体执行情况、读写比例等 ;
show @_@_sql ; show @_@_sql.slow ; show @_@_sql.sum ;
useGlobleTableCheck 0,1 是否开启全局表的一致性检测。1为开启 ,0为关闭 。
sqlExecuteTimeout 1000 SQL语句执行的超时时间 , 单位为 s ;
sequnceHandlerType 0,1,2 用来指定Mycat全局序列类型,0 为本地文件,1 为数据库方式,2 为时间戳列方式,默认使用本地文件方式,文件方式主要用于测试
sequnceHandlerPattern 正则表达式 必须带有MYCATSEQ或者 mycatseq进入序列匹配流程 注意MYCATSEQ_有空格的情况
subqueryRelationshipCheck true,false 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false
useCompression 0,1 开启mysql压缩协议 , 0 : 关闭, 1 : 开启
fakeMySQLVersion 5.5,5.6 设置模拟的MySQL版本号
defaultSqlParser 由于MyCat的最初版本使用了FoundationDB的SQL解析器, 在MyCat1.3后增加了Druid解析器, 所以要设置defaultSqlParser属性来指定默认的解析器; 解析器有两个 : druidparser 和 fdbparser, 在MyCat1.4之后,默认是druidparser, fdbparser已经废除了
processors 1,2…. 指定系统可用的线程数量, 默认值为CPU核心 x 每个核心运行线程数量; processors 会影响processorBufferPool, processorBufferLocalPercent, processorExecutor属性, 所有, 在性能调优时, 可以适当地修改processors值
processorBufferChunk 指定每次分配Socket Direct Buffer默认值为4096字节, 也会影响BufferPool长度, 如果一次性获取字节过多而导致buffer不够用, 则会出现警告, 可以调大该值
processorExecutor 指定NIOProcessor上共享 businessExecutor固定线程池的大小; MyCat把异步任务交给 businessExecutor线程池中, 在新版本的MyCat中这个连接池使用频次不高, 可以适当地把该值调小
packetHeaderSize 指定MySQL协议中的报文头长度, 默认4个字节
maxPacketSize 指定MySQL协议可以携带的数据最大大小, 默认值为16M
idleTimeout 30 指定连接的空闲时间的超时长度;如果超时,将关闭资源并回收, 默认30分钟
txIsolation 1,2,3,4 初始化前端连接的事务隔离级别,默认为 REPEATED_READ , 对应数字为3
READ_UNCOMMITED=1;
READ_COMMITTED=2;
REPEATED_READ=3;
SERIALIZABLE=4;
sqlExecuteTimeout 300 执行SQL的超时时间, 如果SQL语句执行超时,将关闭连接; 默认300秒;
serverPort 8066 定义MyCat的使用端口, 默认8066
managerPort 9066 定义MyCat的管理端口, 默认9066

3.1.2 user 标签

  1. <user name="root" defaultAccount="true">
  2. <property name="password">123456</property>
  3. <property name="schemas">ITCAST</property>
  4. <property name="readOnly">true</property>
  5. <property name="benchmark">1000</property>
  6. <property name="usingDecrypt">0</property>
  7. <!-- 表级 DML 权限设置 -->
  8. <!--
  9. <privileges check="false">
  10. <schema name="TESTDB" dml="0110" >
  11. <table name="tb01" dml="0000"></table>
  12. <table name="tb02" dml="1111"></table>
  13. </schema>
  14. </privileges>
  15. -->
  16. </user>

user标签主要用于定义登录MyCat的用户和权限 :
1). : name 属性用于声明用户名 ;
2). 123456 : 指定该用户名访问MyCat的密码 ;
3). ITCAST : 能够访问的逻辑库, 多个的话, 使用 “,” 分割
4). true : 是否只读
5). 11111 : 指定前端的整体连接数量 , 0 或不设置表示不限制
6). 0 : 是否对密码加密默认 0 否 , 1 是

  1. java -cp Mycat-server-1.6.7.3-release.jar io.mycat.util.DecryptUtil 0:root:123456

7).
A. 对用户的 schema 及 下级的 table 进行精细化的 DML 权限控制;
B. privileges 节点中的 check 属性是用 于标识是否开启 DML 权限检查, 默认 false 标识不检查,当然 privileges 节点不配置,等同 check=false, 由于 Mycat 一个用户的 schemas 属性可配置多个 schema ,所以 privileges 的下级节点 schema 节点同样 可配置多个,对多库多表进行细粒度的 DML 权限控制;
C. 权限修饰符四位数字(0000 - 1111),对应的操作是 IUSD ( 增,改,查,删 )。同时配置了库跟表的权限,就近原则。以表权限为准。

3.1.3 firewall 标签

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

  1. <firewall>
  2. <!-- 白名单配置 -->
  3. <whitehost>
  4. <host user="root" host="127.0.0.1"></host>
  5. </whitehost>
  6. <!-- 黑名单配置 -->
  7. <blacklist check="true">
  8. <property name="selelctAllow">false</property>
  9. </blacklist>
  10. </firewall>

黑名单拦截明细配置:

配置项 缺省值 描述
selelctAllow true 是否允许执行 SELECT 语句
selectAllColumnAllow true 是否允许执行 SELECT FROM T 这样的语句。如果设置为 false,不允许执行 select from t,但可以select from (select id, name from t) a。这个选项是防御程序通过调用 select 获得数据表的结构信息。
selectIntoAllow true SELECT 查询中是否允许 INTO 字句
deleteAllow true 是否允许执行 DELETE 语句
updateAllow true 是否允许执行 UPDATE 语句
insertAllow true 是否允许执行 INSERT 语句
replaceAllow true 是否允许执行 REPLACE 语句
mergeAllow true 是否允许执行 MERGE 语句,这个只在 Oracle 中有用
callAllow true 是否允许通过 jdbc 的 call 语法调用存储过程
setAllow true 是否允许使用 SET 语法
truncateAllow true truncate 语句是危险,缺省打开,若需要自行关闭
createTableAllow true 是否允许创建表
alterTableAllow true 是否允许执行 Alter Table 语句
dropTableAllow true 是否允许修改表
commentAllow false 是否允许语句中存在注释,Oracle 的用户不用担心,Wall 能够识别 hints和注释的区别
noneBaseStatementAllow false 是否允许非以上基本语句的其他语句,缺省关闭,通过这个选项就能够屏蔽 DDL。
multiStatementAllow false 是否允许一次执行多条语句,缺省关闭
useAllow true 是否允许执行 mysql 的 use 语句,缺省打开
describeAllow true 是否允许执行 mysql 的 describe 语句,缺省打开
showAllow true 是否允许执行 mysql 的 show 语句,缺省打开
commitAllow true 是否允许执行 commit 操作
rollbackAllow true 是否允许执行 roll back 操作
拦截配置-永真条件
selectWhereAlwayTrueCheck true 检查 SELECT 语句的 WHERE 子句是否是一个永真条件
selectHavingAlwayTrueCheck true 检查 SELECT 语句的 HAVING 子句是否是一个永真条件
deleteWhereAlwayTrueCheck true 检查 DELETE 语句的 WHERE 子句是否是一个永真条件
deleteWhereNoneCheck false 检查 DELETE 语句是否无 where 条件,这是有风险的,但不是 SQL 注入类型的风险
updateWhereAlayTrueCheck true 检查 UPDATE 语句的 WHERE 子句是否是一个永真条件
updateWhereNoneCheck false 检查 UPDATE 语句是否无 where 条件,这是有风险的,但不是SQL 注入类型的风险
conditionAndAlwayTrueAllow false 检查查询条件(WHERE/HAVING 子句)中是否包含 AND 永真条件
conditionAndAlwayFalseAllow false 检查查询条件(WHERE/HAVING 子句)中是否包含 AND 永假条件
conditionLikeTrueAllow true 检查查询条件(WHERE/HAVING 子句)中是否包含 LIKE 永真条件
其他拦截配置
selectIntoOutfileAllow false SELECT … INTO OUTFILE 是否允许,这个是 mysql 注入攻击的常见手段,缺省是禁止的
selectUnionCheck true 检测 SELECT UNION
selectMinusCheck true 检测 SELECT MINUS
selectExceptCheck true 检测 SELECT EXCEPT
selectIntersectCheck true 检测 SELECT INTERSECT
mustParameterized false 是否必须参数化,如果为 True,则不允许类似 WHERE ID = 1 这种不参数化的 SQL
strictSyntaxCheck true 是否进行严格的语法检测,Druid SQL Parser 在某些场景不能覆盖所有的SQL 语法,出现解析 SQL 出错,可以临时把这个选项设置为 false,同时把 SQL 反馈给 Druid 的开发者。
conditionOpXorAllow false 查询条件中是否允许有 XOR 条件。XOR 不常用,很难判断永真或者永假,缺省不允许。
conditionOpBitwseAllow true 查询条件中是否允许有”&”、”~”、”|”、”^”运算符。
conditionDoubleConstAllow false 查询条件中是否允许连续两个常量运算表达式
minusAllow true 是否允许 SELECT FROM A MINUS SELECT FROM B 这样的语句
intersectAllow true 是否允许 SELECT FROM A INTERSECT SELECT FROM B 这样的语句
constArithmeticAllow true 拦截常量运算的条件,比如说 WHERE FID = 3 - 1,其中”3 - 1”是常量运算表达式。
limitZeroAllow false 是否允许 limit 0 这样的语句
禁用对象检测配置
tableCheck true 检测是否使用了禁用的表
schemaCheck true 检测是否使用了禁用的 Schema
functionCheck true 检测是否使用了禁用的函数
objectCheck true 检测是否使用了“禁用对对象”
variantCheck true 检测是否使用了“禁用的变量”
readOnlyTables 指定的表只读,不能够在 SELECT INTO、DELETE、UPDATE、INSERT、MERGE 中作为”被修改表”出现

3.2 schema.xml

schema.xml 作为MyCat中最重要的配置文件之一 , 涵盖了MyCat的逻辑库 、 表 、 分片规则、分片节点及数据源的配置。

3.2.1 schema 标签

  1. <schema name="ITCAST" checkSQLschema="false" sqlMaxLimit="100">
  2. <table name="TB_TEST" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
  3. </schema>

schema 标签用于定义 MyCat实例中的逻辑库 , 一个MyCat实例中, 可以有多个逻辑库 , 可以通过 schema 标签来划分不同的逻辑库。MyCat中的逻辑库的概念 , 等同于MySQL中的database概念 , 需要操作某个逻辑库下的表时, 也需要切换逻辑库:

  1. use ITCAST;

3.2.1.1 属性

schema 标签的属性如下 :
1). name
指定逻辑库的库名 , 可以自己定义任何字符串 ;
2). checkSQLschema
取值为 true / false ;
如果设置为true时 , 如果我们执行的语句为 “select from ITCAST.TB_TEST;” , 则MyCat会自动把schema字符去掉, 把SQL语句修改为 “select from TBTEST;” 可以避免SQL发送到后端数据库执行时, 报table不存在的异常 。
不过当我们在编写SQL语句时, 指定了一个不存在schema, MyCat是不会帮我们自动去除的 ,这个时候数据库就会报错, 所以在编写SQL语句时,最好不要加逻辑库的库名, 直接查询表即可。
3). sqlMaxLimit
当该属性设置为某个数值时,每次执行的SQL语句如果没有加上limit语句, MyCat也会自动在limit语句后面加上对应的数值 。也就是说, 如果设置了该值为100,则执行 select
from TBTEST 与 select from TB_TEST limit 100 是相同的效果 。
所以在正常的使用中, 建立设置该值 , 这样就可以避免每次有过多的数据返回。

3.2.1.2 子标签table

table 标签定义了MyCat中逻辑库schema下的逻辑表 , 所有需要拆分的表都需要在table标签中定义 。

  1. <table name="TB_TEST" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />

属性如下 :
MyCat-数据库中间件 - 图13
1). name
定义逻辑表的表名 , 在该逻辑库下必须唯一。
2). dataNode
定义的逻辑表所属的dataNode , 该属性需要与dataNode标签中的name属性的值对应。 如果一张表拆分的数据,存储在多个数据节点上,多个节点的名称使用”,”分隔 。
MyCat-数据库中间件 - 图14
3). rule
该属性用于指定逻辑表的分片规则的名字, 规则的名字是在rule.xml文件中定义的, 必须与tableRule标签中name属性对应。
MyCat-数据库中间件 - 图15
4). ruleRequired
该属性用于指定表是否绑定分片规则, 如果配置为true, 但是没有具体的rule, 程序会报错。
5). primaryKey
逻辑表对应真实表的主键
如: 分片规则是使用主键进行分片, 使用主键进行查询时, 就会发送查询语句到配置的所有的datanode上; 如果使用该属性配置真实表的主键, 那么MyCat会缓存主键与具体datanode的信息, 再次使用主键查询就不会进行广播式查询了, 而是直接将SQL发送给具体的datanode。
6). type
该属性定义了逻辑表的类型,目前逻辑表只有全局表和普通表。
全局表:type的值是 global , 代表 全局表 。
普通表:无
7). autoIncrement
mysql对非自增长主键,使用last_insert_id() 是不会返回结果的,只会返回0。所以,只有定义了自增长主键的表,才可以用last_insert_id()返回主键值。
mycat提供了自增长主键功能,但是对应的mysql节点上数据表,没有auto_increment,那么在mycat层调用last_insert_id()也是不会返回结果的。
如果使用这个功能, 则最好配合数据库模式的全局序列。使用 autoIncrement=”true” 指定该表使用自增长主键,这样MyCat才不会抛出 “分片键找不到” 的异常。 autoIncrement的默认值为 false。
8). needAddLimit
指定表是否需要自动在每个语句的后面加上limit限制, 默认为true。

3.2.2 dataNode 标签

  1. <dataNode name="dn1" dataHost="host1" database="db1" />

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

属性 含义 描述
name 数据节点的名称 需要唯一 ; 在table标签中会引用这个名字, 标识表与分片的对应关系
dataHost 数据库实例主机名称 引用自 dataHost 标签中name属性
database 定义分片所属的数据库

3.2.3 dataHost 标签

  1. <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
  2. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  3. <heartbeat>select user()</heartbeat>
  4. <writeHost host="hostM1" url="192.168.192.147:3306" user="root" password="itcast"></writeHost>
  5. </dataHost>

该标签在MyCat逻辑库中作为底层标签存在, 直接定义了具体的数据库实例、读写分离、心跳语句。

3.2.3.1 属性
属性 含义 描述
name 数据节点名称 唯一标识, 供上层标签使用
maxCon 最大连接数 内部的writeHost、readHost都会使用这个属性
minCon 最小连接数 内部的writeHost、readHost初始化连接池的大小
balance 负载均衡类型 取值0,1,2,3 ; 后面章节会详细介绍;
writeType 写操作分发方式 0 : 写操作都转发到第1台writeHost, writeHost1挂了, 会切换到writeHost2上;
1 : 所有的写操作都随机地发送到配置的writeHost上 ;
dbType 后端数据库类型 mysql, mongodb , oracle
dbDriver 数据库驱动 指定连接后端数据库的驱动,目前可选值有 native和JDBC。native执行的是二进制的MySQL协议,可以使用MySQL和MariaDB。其他类型数据库需要使用JDBC(需要在MyCat/lib目录下加入驱动jar)
switchType 数据库切换策略 取值 -1,1,2,3 ; 后面章节会详细介绍;

3.2.3.2 子标签heartbeat

配置MyCat与后端数据库的心跳,用于检测后端数据库的状态。heartbeat用于配置心跳检查语句。例如 : MySQL中可以使用 select user(), Oracle中可以使用 select 1 from dual等。

3.2.3.3 子标签writeHost、readHost

指定后端数据库的相关配置, 用于实例化后端连接池。 writeHost指定写实例, readHost指定读实例。
在一个dataHost中可以定义多个writeHost和readHost。但是,如果writeHost指定的后端数据库宕机, 那么这个writeHost绑定的所有readHost也将不可用。
属性:

属性名 含义 取值
host 实例主机标识 对于writeHost一般使用 _M1;对于readHost,一般使用 _S1;
url 后端数据库连接地址 如果是native,一般为 ip:port ; 如果是JDBC, 一般为jdbc:mysql://ip:port/
user 数据库用户名 root
password 数据库密码 itcast
weight 权重 在readHost中作为读节点权重
usingDecrypt 密码加密 默认 0 否 , 1 是

3.3 rule.xml

rule.xml中定义所有拆分表的规则, 在使用过程中可以灵活的使用分片算法, 或者对同一个分片算法使用不同的参数, 它让分片过程可配置化。

3.3.1 tableRule标签

  1. <tableRule name="auto-sharding-long">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>rang-long</algorithm>
  5. </rule>
  6. </tableRule>

A. name : 指定分片算法的名称
B. rule : 定义分片算法的具体内容
C. columns : 指定对应的表中用于分片的列名
D. algorithm : 对应function中指定的算法名称

3.3.2 Function标签

  1. <function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
  2. <property name="mapFile">autopartition-long.txt</property>
  3. </function>

A. name : 指定算法名称, 该文件中唯一
B. class : 指定算法的具体类
C. property : 根据算法的要求执行

3.4 sequence 配置文件

在分库分表的情况下 , 原有的自增主键已无法满足在集群中全局唯一的主键 ,因此, MyCat中提供了全局sequence来实现主键 , 并保证全局唯一。那么在MyCat的配置文件 sequence_conf.properties 中就配置的是序列的相关配置。
主要包含以下几种形式:
1). 本地文件方式
2). 数据库方式
3). 本地时间戳方式
4). 其他方式
5). 自增长主键

4. MyCat分片

4.1 垂直拆分

4.1.1 概述

MyCat-数据库中间件 - 图16
一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分。

4.1.2 案例场景

MyCat-数据库中间件 - 图17
在业务系统中, 有以下表结构 ,但是由于用户与订单每天都会产生大量的数据, 单台服务器的数据存储及处理能力是有限的, 可以对数据库表进行拆分, 原有的数据库表:
MyCat-数据库中间件 - 图18

4.1.3 准备工作

1). 准备三台数据库实例

  1. 192.168.192.157
  2. 192.168.192.158
  3. 192.168.192.159

2). 在三台数据库实例中建库建表
将准备好的三个SQL脚本, 分别导入到三台MySQL实例中 ;
MyCat-数据库中间件 - 图19
登录MySQL数据库之后, 使用source命令导入 ;

4.1.4 schema.xml的配置

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="ITCAST_DB" checkSQLschema="false" sqlMaxLimit="100">
  5. <table name="tb_areas_city" dataNode="dn1" primaryKey="id" />
  6. <table name="tb_areas_provinces" dataNode="dn1" primaryKey="id" />
  7. <table name="tb_areas_region" dataNode="dn1" primaryKey="id" />
  8. <table name="tb_user" dataNode="dn1" primaryKey="id" />
  9. <table name="tb_user_address" dataNode="dn1" primaryKey="id" />
  10. <table name="tb_goods_base" dataNode="dn2" primaryKey="id" />
  11. <table name="tb_goods_desc" dataNode="dn2" primaryKey="goods_id" />
  12. <table name="tb_goods_item_cat" dataNode="dn2" primaryKey="id" />
  13. <table name="tb_order_item" dataNode="dn3" primaryKey="id" />
  14. <table name="tb_order_master" dataNode="dn3" primaryKey="order_id" />
  15. <table name="tb_order_pay_log" dataNode="dn3" primaryKey="out_trade_no" />
  16. </schema>
  17. <dataNode name="dn1" dataHost="host1" database="user_db" />
  18. <dataNode name="dn2" dataHost="host2" database="goods_db" />
  19. <dataNode name="dn3" dataHost="host3" database="order_db" />
  20. <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
  21. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  22. <heartbeat>select user()</heartbeat>
  23. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast"></writeHost>
  24. </dataHost>
  25. <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
  26. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  27. <heartbeat>select user()</heartbeat>
  28. <writeHost host="hostM2" url="192.168.192.158:3306" user="root" password="itcast"></writeHost>
  29. </dataHost>
  30. <dataHost name="host3" maxCon="1000" minCon="10" balance="0"
  31. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  32. <heartbeat>select user()</heartbeat>
  33. <writeHost host="hostM3" url="192.168.192.159:3306" user="root" password="itcast"></writeHost>
  34. </dataHost>
  35. </mycat:schema>

4.1.5 server.xml的配置

  1. <user name="root" defaultAccount="true">
  2. <property name="password">123456</property>
  3. <property name="schemas">ITCAST_DB</property>
  4. </user>
  5. <user name="test">
  6. <property name="password">123456</property>
  7. <property name="schemas">ITCAST_DB</property>
  8. </user>
  9. <user name="user">
  10. <property name="password">123456</property>
  11. <property name="schemas">ITCAST_DB</property>
  12. <property name="readOnly">true</property>
  13. </user>

4.1.6 测试

1). 查询数据

  1. select * from tb_goods_base;
  2. select * from tb_user;
  3. select * from tb_order_master;

2). 插入数据

  1. insert into tb_user_address(id,user_id,province_id,city_id,town_id,mobile,address,contact,is_default,notes,create_date,alias) values (null,'java00001',NULL,NULL,NULL,'13900112222','钟楼','张三','0',NULL,NULL,NULL)
  1. insert into tb_order_item(id,item_id,goods_id,order_id,title,price,num,total_fee,pic_path,seller_id) values (null,19,149187842867954,3,'3G 6','1.00',5,'5.00',NULL,'qiandu')

3). 测试跨分片的查询

  1. SELECT order_id , payment ,receiver, province , city , area FROM tb_order_master o , tb_areas_provinces p , tb_areas_city c , tb_areas_region r
  2. WHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;

当运行上述的SQL语句时, MyCat会报错, 原因是因为当前SQL语句涉及到跨域的join操作 ;

4.1.7 全局表配置

1). 将数据节点user_db中的关联的字典表 tb_areas_provinces , tb_areas_city , tb_areas_region中的数据备份 ;

  1. mysqldump -uroot -pitcast user_db tb_areas_provinces > provinces;
  2. mysqldump -uroot -pitcast user_db tb_areas_city > city;
  3. mysqldump -uroot -pitcast user_db tb_areas_region > region;

2). 将备份的表结构及数据信息, 远程同步到其他两个数据节点的数据库中;

  1. scp city root@192.168.192.158:/root
  2. scp city root@192.168.192.159:/root
  3. scp provinces root@192.168.192.158:/root
  4. scp provinces root@192.168.192.159:/root
  5. scp region root@192.168.192.158:/root
  6. scp region root@192.168.192.159:/root

3). 导入到对应的数据库中

  1. mysql -uroot -p goods_db < city
  2. mysql -uroot -p goods_db < provinces
  3. mysql -uroot -p goods_db < region

4). MyCat逻辑表中的配置

  1. <table name="tb_areas_city" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
  2. <table name="tb_areas_provinces" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
  3. <table name="tb_areas_region" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>

5). 重启MyCat

  1. bin/mycat restart

6). 测试
再次执行相同的连接查询 , 是可以正常查询出对应的数据的 ;

  1. SELECT order_id , payment ,receiver, province , city , area FROM tb_order_master o , tb_areas_provinces p , tb_areas_city c , tb_areas_region r
  2. WHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;

当我们对Mycat全局表进行增删改的操作时, 其他节点主机上的后端MySQL数据库中的数据时会同步变化的;

  1. update tb_areas_city set city = '石家庄' where id = 5;

4.2 水平拆分

4.2.1 概述

根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
MyCat-数据库中间件 - 图20

4.2.2 案例场景

MyCat-数据库中间件 - 图21
在业务系统中, 有一张表(日志表), 业务系统每天都会产生大量的日志数据 , 单台服务器的数据存储及处理能力是有限的, 可以对数据库表进行拆分, 原有的数据库表拆分成以下表 :
MyCat-数据库中间件 - 图22

4.2.3 准备工作

1). 准备三台数据库实例

  1. 192.168.192.157
  2. 192.168.192.158
  3. 192.168.192.159

2). 在三台数据库实例中创建数据库

  1. create database log_db DEFAULT CHARACTER SET utf8mb4;

4.2.4 schema.xml的配置

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="LOG_DB" checkSQLschema="false" sqlMaxLimit="100">
  5. <table name="tb_log" dataNode="dn1,dn2,dn3" primaryKey="id" rule="mod-long" />
  6. </schema>
  7. <dataNode name="dn1" dataHost="host1" database="log_db" />
  8. <dataNode name="dn2" dataHost="host2" database="log_db" />
  9. <dataNode name="dn3" dataHost="host3" database="log_db" />
  10. <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  11. <heartbeat>select user()</heartbeat>
  12. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast"></writeHost>
  13. </dataHost>
  14. <dataHost name="host2" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  15. <heartbeat>select user()</heartbeat>
  16. <writeHost host="hostM2" url="192.168.192.158:3306" user="root" password="itcast"></writeHost>
  17. </dataHost>
  18. <dataHost name="host3" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  19. <heartbeat>select user()</heartbeat>
  20. <writeHost host="hostM3" url="192.168.192.159:3306" user="root" password="itcast"></writeHost>
  21. </dataHost>
  22. </mycat:schema>

4.2.5 server.xml的配置

  1. <user name="root" defaultAccount="true">
  2. <property name="password">123456</property>
  3. <property name="schemas">LOG_DB</property>
  4. </user>
  5. <user name="test">
  6. <property name="password">123456</property>
  7. <property name="schemas">LOG_DB</property>
  8. </user>
  9. <user name="user">
  10. <property name="password">123456</property>
  11. <property name="schemas">LOG_DB</property>
  12. <property name="readOnly">true</property>
  13. </user>

4.2.6 测试

1). 在MyCat数据库中执行建表语句

  1. CREATE TABLE `tb_log` (
  2. `id` bigint(20) NOT NULL COMMENT 'ID',
  3. `model_name` varchar(200) DEFAULT NULL COMMENT '模块名',
  4. `model_value` varchar(200) DEFAULT NULL COMMENT '模块值',
  5. `return_value` varchar(200) DEFAULT NULL COMMENT '返回值',
  6. `return_class` varchar(200) DEFAULT NULL COMMENT '返回值类型',
  7. `operate_user` varchar(20) DEFAULT NULL COMMENT '操作用户',
  8. `operate_time` varchar(20) DEFAULT NULL COMMENT '操作时间',
  9. `param_and_value` varchar(500) DEFAULT NULL COMMENT '请求参数名及参数值',
  10. `operate_class` varchar(200) DEFAULT NULL COMMENT '操作类',
  11. `operate_method` varchar(200) DEFAULT NULL COMMENT '操作方法',
  12. `cost_time` bigint(20) DEFAULT NULL COMMENT '执行方法耗时, 单位 ms',
  13. `source` int(1) DEFAULT NULL COMMENT '来源 : 1 PC , 2 Android , 3 IOS',
  14. PRIMARY KEY (`id`)
  15. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2). 插入数据

  1. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('1','user','insert','success','java.lang.String','10001','2020-02-26 18:12:28','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','insert','10',1);
  2. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('2','user','insert','success','java.lang.String','10001','2020-02-26 18:12:27','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','insert','23',1);
  3. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('3','user','update','success','java.lang.String','10001','2020-02-26 18:16:45','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','update','34',1);
  4. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('4','user','update','success','java.lang.String','10001','2020-02-26 18:16:45','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','update','13',2);
  5. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('5','user','insert','success','java.lang.String','10001','2020-02-26 18:30:31','{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}','cn.itcast.controller.UserController','insert','29',3);
  6. INSERT INTO `tb_log` (`id`, `model_name`, `model_value`, `return_value`, `return_class`, `operate_user`, `operate_time`, `param_and_value`, `operate_class`, `operate_method`, `cost_time``source`) VALUES('6','user','find','success','java.lang.String','10001','2020-02-26 18:30:31','{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}','cn.itcast.controller.UserController','find','29',2);

4.3 分片规则

MyCat的分片规则配置在conf目录下的rule.xml文件中定义 ;
环境准备 :
1). schema.xml中的内容做好备份 , 并配置逻辑库;

  1. <schema name="PARTITION_DB" checkSQLschema="false" sqlMaxLimit="100">
  2. <table name="" dataNode="dn1,dn2,dn3" rule=""/>
  3. </schema>
  4. <dataNode name="dn1" dataHost="host1" database="partition_db" />
  5. <dataNode name="dn2" dataHost="host2" database="partition_db" />
  6. <dataNode name="dn3" dataHost="host3" database="partition_db" />
  7. <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
  8. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  9. <heartbeat>select user()</heartbeat>
  10. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast"></writeHost>
  11. </dataHost>
  12. <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
  13. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  14. <heartbeat>select user()</heartbeat>
  15. <writeHost host="hostM2" url="192.168.192.158:3306" user="root" password="itcast"></writeHost>
  16. </dataHost>
  17. <dataHost name="host3" maxCon="1000" minCon="10" balance="0"
  18. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  19. <heartbeat>select user()</heartbeat>
  20. <writeHost host="hostM3" url="192.168.192.159:3306" user="root" password="itcast"></writeHost>
  21. </dataHost>

2). 在MySQL的三个节点的数据库中 , 创建数据库partition_db

  1. create database partition_db DEFAULT CHARACTER SET utf8mb4;

4.3.1 取模分片

  1. <tableRule name="mod-long">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>mod-long</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
  8. <property name="count">3</property>
  9. </function>

配置说明 :

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
count 数据节点的数量

4.3.2 范围分片

根据指定的字段及其配置的范围与数据节点的对应情况, 来决定该数据属于哪一个分片 , 配置如下:

  1. <tableRule name="auto-sharding-long">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>rang-long</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
  8. <property name="mapFile">autopartition-long.txt</property>
  9. <property name="defaultNode">0</property>
  10. </function>

autopartition-long.txt 配置如下:

  1. # range start-end ,data node index
  2. # K=1000,M=10000.
  3. 0-500M=0
  4. 500M-1000M=1
  5. 1000M-1500M=2

含义为 : 0 - 500 万之间的值 , 存储在0号数据节点 ; 500万 - 1000万之间的数据存储在1号数据节点 ; 1000万 - 1500 万的数据节点存储在2号节点 ;
配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
mapFile 对应的外部配置文件
type 默认值为0 ; 0 表示Integer , 1 表示String
defaultNode 默认节点
默认节点的所用:枚举分片时,如果碰到不识别的枚举值, 就让它路由到默认节点 ; 如果没有默认值,碰到不识别的则报错 。

测试:
配置

  1. <table name="tb_log" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_log` (
  3. id bigint(20) NOT NULL COMMENT 'ID',
  4. operateuser varchar(200) DEFAULT NULL COMMENT '姓名',
  5. operation int(2) DEFAULT NULL COMMENT '1: insert, 2: delete, 3: update , 4: select',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  8. 2). 插入数据
  9. insert into tb_log (id,operateuser ,operation) values(1,'Tom',1);
  10. insert into tb_log (id,operateuser ,operation) values(2,'Cat',2);
  11. insert into tb_log (id,operateuser ,operation) values(3,'Rose',3);
  12. insert into tb_log (id,operateuser ,operation) values(4,'Coco',2);
  13. insert into tb_log (id,operateuser ,operation) values(5,'Lily',1);

4.3.3 枚举分片

通过在配置文件中配置可能的枚举值, 指定数据分布到不同数据节点上, 本规则适用于按照省份或状态拆分数据等业务 , 配置如下:

  1. <tableRule name="sharding-by-intfile">
  2. <rule>
  3. <columns>status</columns>
  4. <algorithm>hash-int</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
  8. <property name="mapFile">partition-hash-int.txt</property>
  9. <property name="type">0</property>
  10. <property name="defaultNode">0</property>
  11. </function>

partition-hash-int.txt ,内容如下 :

  1. 1=0
  2. 2=1
  3. 3=2

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
mapFile 对应的外部配置文件
type 默认值为0 ; 0 表示Integer , 1 表示String
defaultNode 默认节点 ; 小于0 标识不设置默认节点 , 大于等于0代表设置默认节点 ;
默认节点的所用:枚举分片时,如果碰到不识别的枚举值, 就让它路由到默认节点 ; 如果没有默认值,碰到不识别的则报错 。

测试:
配置

  1. <table name="tb_user" dataNode="dn1,dn2,dn3" rule="sharding-by-enum-status"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_user` (
  3. id bigint(20) NOT NULL COMMENT 'ID',
  4. username varchar(200) DEFAULT NULL COMMENT '姓名',
  5. status int(2) DEFAULT '1' COMMENT '1: 未启用, 2: 已启用, 3: 已关闭',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  8. 2). 插入数据
  9. insert into tb_user (id,username ,status) values(1,'Tom',1);
  10. insert into tb_user (id,username ,status) values(2,'Cat',2);
  11. insert into tb_user (id,username ,status) values(3,'Rose',3);
  12. insert into tb_user (id,username ,status) values(4,'Coco',2);
  13. insert into tb_user (id,username ,status) values(5,'Lily',1);

4.3.4 范围求模算法

该算法为先进行范围分片, 计算出分片组 , 再进行组内求模。
优点: 综合了范围分片和求模分片的优点。 分片组内使用求模可以保证组内的数据分布比较均匀, 分片组之间采用范围分片可以兼顾范围分片的特点。
缺点: 在数据范围时固定值(非递增值)时,存在不方便扩展的情况,例如将 dataNode Group size 从 2 扩展为 4 时,需要进行数据迁移才能完成 ; 如图所示:
MyCat-数据库中间件 - 图23
配置如下:

  1. <tableRule name="auto-sharding-rang-mod">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>rang-mod</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
  8. <property name="mapFile">autopartition-range-mod.txt</property>
  9. <property name="defaultNode">0</property>
  10. </function>

autopartition-range-mod.txt 配置格式 :

  1. #range start-end , data node group size
  2. 0-500M=1
  3. 500M1-2000M=2

在上述配置文件中, 等号前面的范围代表一个分片组 , 等号后面的数字代表该分片组所拥有的分片数量;
配置说明:

属性 描述
columns 标识将要分片的表字段名
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
mapFile 对应的外部配置文件
defaultNode 默认节点 ; 未包含以上规则的数据存储在defaultNode节点中, 节点从0开始

测试:
配置

  1. <table name="tb_stu" dataNode="dn1,dn2,dn3" rule="auto-sharding-rang-mod"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_stu` (
  3. id bigint(20) NOT NULL COMMENT 'ID',
  4. username varchar(200) DEFAULT NULL COMMENT '姓名',
  5. status int(2) DEFAULT '1' COMMENT '1: 未启用, 2: 已启用, 3: 已关闭',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  8. 2). 插入数据
  9. insert into tb_stu (id,username ,status) values(1,'Tom',1);
  10. insert into tb_stu (id,username ,status) values(2,'Cat',2);
  11. insert into tb_stu (id,username ,status) values(3,'Rose',3);
  12. insert into tb_stu (id,username ,status) values(4,'Coco',2);
  13. insert into tb_stu (id,username ,status) values(5,'Lily',1);
  14. insert into tb_stu (id,username ,status) values(5000001,'Roce',1);
  15. insert into tb_stu (id,username ,status) values(5000002,'Jexi',2);
  16. insert into tb_stu (id,username ,status) values(5000003,'Mini',1);

4.3.5 固定分片hash算法

该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位 与 1111111111 进行位 & 运算。
最小值:
MyCat-数据库中间件 - 图24
最大值:
MyCat-数据库中间件 - 图25
优点: 这种策略比较灵活,可以均匀分配也可以非均匀分配,各节点的分配比例和容量大小由partitionCount和partitionLength两个参数决定
缺点:和取模分片类似。
配置如下 :

  1. <tableRule name="sharding-by-long-hash">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>func1</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="func1" class="org.opencloudb.route.function.PartitionByLong">
  8. <property name="partitionCount">2,1</property>
  9. <property name="partitionLength">256,512</property>
  10. </function>

在示例中配置的分片策略,希望将数据水平分成3份,前两份各占 25%,第三份占 50%。
配置说明:

属性 描述
columns 标识将要分片的表字段名
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
partitionCount 分片个数列表
partitionLength 分片范围列表

约束 :
1). 分片长度 : 默认最大2^10 , 为 1024 ;
2). count, length的数组长度必须是一致的 ;
3). 两组数据的对应情况: (partitionCount[0]partitionLength[0])=(partitionCount[1]partitionLength[1])
以上分为三个分区:0-255,256-511,512-1023
测试:
配置

  1. <table name="tb_brand" dataNode="dn1,dn2,dn3" rule="sharding-by-long-hash"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_brand` (
  3. id int(11) NOT NULL COMMENT 'ID',
  4. name varchar(200) DEFAULT NULL COMMENT '名称',
  5. firstChar char(1) COMMENT '首字母',
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  8. 2). 插入数据
  9. insert into tb_brand (id,name ,firstChar) values(1,'七匹狼','Q');
  10. insert into tb_brand (id,name ,firstChar) values(529,'八匹狼','B');
  11. insert into tb_brand (id,name ,firstChar) values(1203,'九匹狼','J');
  12. insert into tb_brand (id,name ,firstChar) values(1205,'十匹狼','S');
  13. insert into tb_brand (id,name ,firstChar) values(1719,'六匹狼','L');

4.3.6 取模范围算法

该算法先进行取模,然后根据取模值所属范围进行分片。
优点:可以自主决定取模后数据的节点分布
缺点:dataNode 划分节点是事先建好的,需要扩展时比较麻烦。
配置如下:

  1. <tableRule name="sharding-by-pattern">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>sharding-by-pattern</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPattern">
  8. <property name="mapFile">partition-pattern.txt</property>
  9. <property name="defaultNode">0</property>
  10. <property name="patternValue">96</property>
  11. </function>

partition-pattern.txt 配置如下:

  1. 0-32=0
  2. 33-64=1
  3. 65-96=2

在mapFile配置文件中, 1-32即代表id%96后的分布情况。如果在1-32, 则在分片0上 ; 如果在33-64, 则在分片1上 ; 如果在65-96, 则在分片2上。
配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
mapFile 对应的外部配置文件
defaultNode 默认节点 ; 如果id不是数字, 无法求模, 将分配在defaultNode上
patternValue 求模基数

测试:
配置

  1. <table name="tb_mod_range" dataNode="dn1,dn2,dn3" rule="sharding-by-pattern"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_mod_range` (
  3. id int(11) NOT NULL COMMENT 'ID',
  4. name varchar(200) DEFAULT NULL COMMENT '名称',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. 2). 插入数据
  8. insert into tb_mod_range (id,name) values(1,'Test1');
  9. insert into tb_mod_range (id,name) values(2,'Test2');
  10. insert into tb_mod_range (id,name) values(3,'Test3');
  11. insert into tb_mod_range (id,name) values(4,'Test4');
  12. insert into tb_mod_range (id,name) values(5,'Test5');

注意 : 取模范围算法只能针对于数字类型进行取模运算 ; 如果是字符串则无法进行取模分片 ;

4.3.7 字符串hash求模范围算法

与取模范围算法类似, 该算法支持数值、符号、字母取模,首先截取长度为 prefixLength 的子串,在对子串中每一个字符的 ASCII 码求和,然后对求和值进行取模运算(sum%patternValue),就可以计算出子串的分片数。
优点:可以自主决定取模后数据的节点分布
缺点:dataNode 划分节点是事先建好的,需要扩展时比较麻烦。
配置如下:

  1. <tableRule name="sharding-by-prefixpattern">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>sharding-by-prefixpattern</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-prefixpattern" class="io.mycat.route.function.PartitionByPrefixPattern">
  8. <property name="mapFile">partition-prefixpattern.txt</property>
  9. <property name="prefixLength">5</property>
  10. <property name="patternValue">96</property>
  11. </function>

partition-prefixpattern.txt 配置如下:

  1. # range start-end ,data node index
  2. # ASCII
  3. # 48-57=0-9
  4. # 64、65-90=@、A-Z
  5. # 97-122=a-z
  6. ###### first host configuration
  7. 0-32=0
  8. 33-64=1
  9. 65-96=2

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
mapFile 对应的外部配置文件
prefixLength 截取的位数; 将该字段获取前prefixLength位所有ASCII码的和, 进行求模sum%patternValue ,获取的值,在通配范围内的即分片数 ;
patternValue 求模基数

如 :

  1. 字符串 :
  2. gf89f9a
  3. 截取字符串的前5位进行ASCII的累加运算 :
  4. g - 103
  5. f - 102
  6. 8 - 56
  7. 9 - 57
  8. f - 102
  9. sum求和 : 103 + 102 + + 56 + 57 + 102 = 420
  10. 求模 : 420 % 96 = 36

附录 ASCII码表 :
MyCat-数据库中间件 - 图26
测试:
配置

  1. <table name="tb_u" dataNode="dn1,dn2,dn3" rule="sharding-by-prefixpattern"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_u` (
  3. username varchar(50) NOT NULL COMMENT '用户名',
  4. age int(11) default 0 COMMENT '年龄',
  5. PRIMARY KEY (`username`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. 2). 插入数据
  8. insert into tb_u (username,age) values('Test100001',18);
  9. insert into tb_u (username,age) values('Test200001',20);
  10. insert into tb_u (username,age) values('Test300001',19);
  11. insert into tb_u (username,age) values('Test400001',25);
  12. insert into tb_u (username,age) values('Test500001',22);

4.3.8 应用指定算法

由运行阶段由应用自主决定路由到那个分片 , 直接根据字符子串(必须是数字)计算分片号 , 配置如下 :

  1. <tableRule name="sharding-by-substring">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>sharding-by-substring</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
  8. <property name="startIndex">0</property> <!-- zero-based -->
  9. <property name="size">2</property>
  10. <property name="partitionCount">3</property>
  11. <property name="defaultPartition">0</property>
  12. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
startIndex 字符子串起始索引
size 字符长度
partitionCount 分区(分片)数量
defaultPartition 默认分片(在分片数量定义时, 字符标示的分片编号不在分片数量内时,使用默认分片)

示例说明 :
id=05-100000002 , 在此配置中代表根据id中从 startIndex=0,开始,截取siz=2位数字即05,05就是获取的分区,如果没传默认分配到defaultPartition 。
测试:
配置

  1. <table name="tb_app" dataNode="dn1,dn2,dn3" rule="sharding-by-substring"/>

数据

  1. 1). 创建表
  2. CREATE TABLE `tb_app` (
  3. id varchar(10) NOT NULL COMMENT 'ID',
  4. name varchar(200) DEFAULT NULL COMMENT '名称',
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. 2). 插入数据
  8. insert into tb_app (id,name) values('00-00001','Testx00001');
  9. insert into tb_app (id,name) values('01-00001','Test100001');
  10. insert into tb_app (id,name) values('01-00002','Test200001');
  11. insert into tb_app (id,name) values('02-00001','Test300001');
  12. insert into tb_app (id,name) values('02-00002','TesT400001');

4.3.9 字符串hash解析算法

截取字符串中的指定位置的子字符串, 进行hash算法, 算出分片 , 配置如下:

  1. <tableRule name="sharding-by-stringhash">
  2. <rule>
  3. <columns>user_id</columns>
  4. <algorithm>sharding-by-stringhash</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-stringhash" class="io.mycat.route.function.PartitionByString">
  8. <property name="partitionLength">512</property> <!-- zero-based -->
  9. <property name="partitionCount">2</property>
  10. <property name="hashSlice">0:2</property>
  11. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
partitionLength hash求模基数 ; length*count=1024 (出于性能考虑)
partitionCount 分区数
hashSlice hash运算位 , 根据子字符串的hash运算 ; 0 代表 str.length() , -1 代表 str.length()-1 , 大于0只代表数字自身 ; 可以理解为substring(start,end),start为0则只表示0

测试:
配置

  1. <table name="tb_strhash" dataNode="dn1,dn2,dn3" rule="sharding-by-stringhash"/>

数据

  1. 1). 创建表
  2. create table tb_strhash(
  3. name varchar(20) primary key,
  4. content varchar(100)
  5. )engine=InnoDB DEFAULT CHARSET=utf8mb4;
  6. 2). 插入数据
  7. INSERT INTO tb_strhash (name,content) VALUES('T1001', UUID());
  8. INSERT INTO tb_strhash (name,content) VALUES('ROSE', UUID());
  9. INSERT INTO tb_strhash (name,content) VALUES('JERRY', UUID());
  10. INSERT INTO tb_strhash (name,content) VALUES('CRISTINA', UUID());
  11. INSERT INTO tb_strhash (name,content) VALUES('TOMCAT', UUID());

原理:
MyCat-数据库中间件 - 图27

4.3.10 一致性hash算法

一致性Hash算法有效的解决了分布式数据的拓容问题 , 配置如下:

  1. <tableRule name="sharding-by-murmur">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>murmur</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
  8. <property name="seed">0</property>
  9. <property name="count">3</property><!-- -->
  10. <property name="virtualBucketTimes">160</property>
  11. <!-- <property name="weightMapFile">weightMapFile</property> -->
  12. <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> -->
  13. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
seed 创建murmur_hash对象的种子,默认0
count 要分片的数据库节点数量,必须指定,否则没法分片
virtualBucketTimes 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍;virtualBucketTimes*count就是虚拟结点数量 ;
weightMapFile 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替
bucketMapPath 用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西

测试:
配置

  1. <table name="tb_order" dataNode="dn1,dn2,dn3" rule="sharding-by-murmur"/>

数据

  1. 1). 创建表
  2. create table tb_order(
  3. id int(11) primary key,
  4. money int(11),
  5. content varchar(200)
  6. )engine=InnoDB ;
  7. 2). 插入数据
  8. INSERT INTO tb_order (id,money,content) VALUES(1, 100 , UUID());
  9. INSERT INTO tb_order (id,money,content) VALUES(212, 100 , UUID());
  10. INSERT INTO tb_order (id,money,content) VALUES(312, 100 , UUID());
  11. INSERT INTO tb_order (id,money,content) VALUES(412, 100 , UUID());
  12. INSERT INTO tb_order (id,money,content) VALUES(534, 100 , UUID());
  13. INSERT INTO tb_order (id,money,content) VALUES(621, 100 , UUID());
  14. INSERT INTO tb_order (id,money,content) VALUES(754563, 100 , UUID());
  15. INSERT INTO tb_order (id,money,content) VALUES(8123, 100 , UUID());
  16. INSERT INTO tb_order (id,money,content) VALUES(91213, 100 , UUID());
  17. INSERT INTO tb_order (id,money,content) VALUES(23232, 100 , UUID());
  18. INSERT INTO tb_order (id,money,content) VALUES(112321, 100 , UUID());
  19. INSERT INTO tb_order (id,money,content) VALUES(21221, 100 , UUID());
  20. INSERT INTO tb_order (id,money,content) VALUES(112132, 100 , UUID());
  21. INSERT INTO tb_order (id,money,content) VALUES(12132, 100 , UUID());
  22. INSERT INTO tb_order (id,money,content) VALUES(124321, 100 , UUID());
  23. INSERT INTO tb_order (id,money,content) VALUES(212132, 100 , UUID());

4.3.11 日期分片算法

按照日期来分片

  1. <tableRule name="sharding-by-date">
  2. <rule>
  3. <columns>create_time</columns>
  4. <algorithm>sharding-by-date</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate">
  8. <property name="dateFormat">yyyy-MM-dd</property>
  9. <property name="sBeginDate">2020-01-01</property>
  10. <property name="sEndDate">2020-12-31</property>
  11. <property name="sPartionDay">10</property>
  12. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
dateFormat 日期格式
sBeginDate 开始日期
sEndDate 结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入
sPartionDay 分区天数,默认值 10 ,从开始日期算起,每个10天一个分区

注意:配置规则的表的 dataNode 的分片,必须和分片规则数量一致,例如 2020-01-01 到 2020-12-31 ,每10天一个分片,一共需要37个分片。

4.3.12 单月小时算法

单月内按照小时拆分, 最小粒度是小时 , 一天最多可以有24个分片, 最小1个分片, 下个月从头开始循环, 每个月末需要手动清理数据。
配置如下 :

  1. <tableRule name="sharding-by-hour">
  2. <rule>
  3. <columns>create_time</columns>
  4. <algorithm>sharding-by-hour</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-hour" class="io.mycat.route.function.LatestMonthPartion">
  8. <property name="splitOneDay">24</property>
  9. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段 ; 字符串类型(yyyymmddHH), 需要符合JAVA标准
algorithm 指定分片函数与function的对应关系
splitOneDay 一天切分的分片数

4.3.13 自然月分片算法

使用场景为按照月份列分区, 每个自然月为一个分片, 配置如下:

  1. <tableRule name="sharding-by-month">
  2. <rule>
  3. <columns>create_time</columns>
  4. <algorithm>sharding-by-month</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="sharding-by-month" class="io.mycat.route.function.PartitionByMonth">
  8. <property name="dateFormat">yyyy-MM-dd</property>
  9. <property name="sBeginDate">2020-01-01</property>
  10. <property name="sEndDate">2020-12-31</property>
  11. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
dateFormat 日期格式
sBeginDate 开始日期
sEndDate 结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入

4.3.14 日期范围hash算法

其思想和范围取模分片一样,先根据日期进行范围分片求出分片组,再根据时间hash使得短期内数据分布的更均匀 ;
优点 : 可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题
注意 : 要求日期格式尽量精确些,不然达不到局部均匀的目的

  1. <tableRule name="range-date-hash">
  2. <rule>
  3. <columns>create_time</columns>
  4. <algorithm>range-date-hash</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="range-date-hash" class="io.mycat.route.function.PartitionByRangeDateHash">
  8. <property name="dateFormat">yyyy-MM-dd HH:mm:ss</property>
  9. <property name="sBeginDate">2020-01-01 00:00:00</property>
  10. <property name="groupPartionSize">6</property>
  11. <property name="sPartionDay">10</property>
  12. </function>

配置说明:

属性 描述
columns 标识将要分片的表字段
algorithm 指定分片函数与function的对应关系
class 指定该分片算法对应的类
dateFormat 日期格式 , 符合Java标准
sBeginDate 开始日期 , 与 dateFormat指定的格式一致
groupPartionSize 每组的分片数量
sPartionDay 代表多少天为一组

5. MyCat高级

5.1 MyCat 性能监控

5.1.1 MyCat-web简介

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

5.1.2 MyCat-web下载

下载地址 : http://dl.mycat.io/
MyCat-数据库中间件 - 图29

5.1.3 Mycat-web安装配置

5.1.3.1 安装

1). 安装Zookeeper

  1. A. 上传安装包
  2. alt + p -----> put D:\tmp\zookeeper-3.4.11.tar.gz
  3. B. 解压
  4. tar -zxvf zookeeper-3.4.11.tar.gz -C /usr/local/
  5. C. 创建数据存放目录
  6. mkdir data
  7. D. 修改配置文件名称并配置
  8. mv zoo_sample.cfg zoo.cfg
  9. E. 配置数据存放目录
  10. dataDir=/usr/local/zookeeper-3.4.11/data
  11. F. 启动Zookeeper
  12. bin/zkServer.sh start

2). 安装Mycat-web

  1. A. 上传安装包
  2. alt + p --------> put D:\tmp\Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz
  3. B. 解压
  4. tar -zxvf Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz -C /usr/local/
  5. C. 目录介绍
  6. drwxr-xr-x. 2 root root 4096 Oct 19 2015 etc ----> jetty配置文件
  7. drwxr-xr-x. 3 root root 4096 Oct 19 2015 lib ----> 依赖jar
  8. drwxr-xr-x. 7 root root 4096 Jan 1 2017 mycat-web ----> mycat-web项目
  9. -rwxr-xr-x. 1 root root 116 Oct 19 2015 readme.txt
  10. -rwxr-xr-x. 1 root root 17125 Oct 19 2015 start.jar ----> 启动jar
  11. -rwxr-xr-x. 1 root root 381 Oct 19 2015 start.sh ----> linux启动脚本
  12. D. 启动
  13. sh start.sh
  14. E. 访问
  15. http://192.168.192.147:8082/mycat

如果Zookeeper与Mycat-web不在同一台服务器上 , 需要设置Zookeeper的地址 ; 在/usr/local/mycat-web/mycat-web/WEB-INF/classes/mycat.properties文件中配置 :
MyCat-数据库中间件 - 图30

5.1.3.2 配置

MyCat-数据库中间件 - 图31
MyCat-数据库中间件 - 图32

5.1.4 Mycat-web之MyCat性能监控

在 Mycat-web 上可以进行 Mycat 性能监控,例如:内存分享、流量分析、连接分析、活动线程分析等等。 如下图:
A. MyCat内存分析:
MyCat-数据库中间件 - 图33
MyCat的内存分析 , 反映了当前的内存使用情况与历史时间段的峰值、平均值。
B. MyCat流量分析:
MyCat-数据库中间件 - 图34
MyCat流量分析统计了历史时间段的流量峰值、当前值、平均值,是MyCat数据传输的重要指标, In代表输入, Out代表输出。
C. MyCat连接分析
MyCat-数据库中间件 - 图35
MyCat连接分析, 反映了MyCat的连接数
D. MyCat TPS分析
MyCat-数据库中间件 - 图36
MyCat TPS 是并发性能的重要参数指标, 指系统在每秒内能够处理的请求数量。 MyCat TPS的值越高 , 代表MyCat单位时间内能够处理的请求就越多, 并发能力也就越高。
E. MyCat活动线程分析反映了MyCat线程的活动情况。
F. MyCat缓存队列分析, 反映了当前在缓存队列中的任务数量。

5.1.5 Mycat-web之MySQL性能监控指标

1). MySQL配置
MyCat-数据库中间件 - 图37
2). MySQL监控指标
MyCat-数据库中间件 - 图38
可以通过MySQL服务监控, 检测每一个MySQL节点的运行状态, 包含缓存命中率 、增删改查比例、流量统计、慢查询比例、线程、临时表等相关性能数据。

5.1.6 Mycat-web之SQL监控

1). SQL 统计
MyCat-数据库中间件 - 图39
2). SQL表分析
MyCat-数据库中间件 - 图40
3). SQL监控
MyCat-数据库中间件 - 图41
4). 高频SQL
MyCat-数据库中间件 - 图42
5). 慢SQL统计
MyCat-数据库中间件 - 图43
6). SQL解析
MyCat-数据库中间件 - 图44

5.2 MyCat 读写分离

5.2.1 MySQL主从复制原理

复制是指将主数据库的DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
MySQL支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。
MySQL主从复制的原理如下 :
MyCat-数据库中间件 - 图45
从上层来看,复制分成三步:

  • Master 主库在事务提交时,会把数据变更作为时间 Events 记录在二进制日志文件 Binlog 中。
  • 主库推送二进制日志文件 Binlog 中的日志事件到从库的中继日志 Relay Log 。
  • slave重做中继日志中的事件,将改变反映它自己的数据。

MySQL 复制的优点:

  • 主库出现问题,可以快速切换到从库提供服务。
  • 可以在从库上执行查询操作,从主库中更新,实现读写分离,降低主库的访问压力。
  • 可以在从库中执行备份,以避免备份期间影响主库的服务。

    5.2.2 MySQL一主一从搭建

    准备的两台机器:
MySQL IP 端口号
Master 192.168.192.157 3306
Slave 192.168.192.158 3306

5.2.2.1 master

1) 在master 的配置文件(/usr/my.cnf)中,配置如下内容:

  1. #mysql 服务ID,保证整个集群环境中唯一
  2. server-id=1
  3. #mysql binlog 日志的存储路径和文件名
  4. log-bin=/var/lib/mysql/mysqlbin
  5. #设置logbin格式
  6. binlog_format=STATEMENT
  7. #是否只读,1 代表只读, 0 代表读写
  8. read-only=0
  9. #忽略的数据, 指不需要同步的数据库
  10. #binlog-ignore-db=mysql
  11. #指定同步的数据库
  12. binlog-do-db=db01

2) 执行完毕之后,需要重启Mysql:

  1. service mysql restart ;

3) 创建同步数据的账户,并且进行授权操作:

  1. grant replication slave on *.* to 'itcast'@'192.168.192.158' identified by 'itcast';
  2. flush privileges;

4) 查看master状态:

  1. show master status;

MyCat-数据库中间件 - 图46
字段含义:

  1. File : 从哪个日志文件开始推送日志文件
  2. Position 从哪个位置开始推送日志
  3. Binlog_Ignore_DB : 指定不需要同步的数据库

5.2.2.2 slave

1) 在 slave 端配置文件/usr/my.cnf中,配置如下内容:

  1. #mysql服务端ID,唯一
  2. server-id=2
  3. #指定binlog日志
  4. log-bin=/var/lib/mysql/mysqlbin
  5. #启用中继日志
  6. relay-log=mysql-relay

2) 执行完毕之后,需要重启Mysql:

  1. service mysql restart;

3) 执行如下指令 :

  1. change master to master_host= '192.168.192.157', master_user='itcast', master_password='itcast', master_log_file='mysqlbin.000001', master_log_pos=413;

指定当前从库对应的主库的IP地址,用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志。
4) 开启同步操作

  1. start slave;
  2. show slave status;

MyCat-数据库中间件 - 图47
5) 停止同步操作

  1. stop slave;

5.2.2.3 验证主从同步

1) 在主库中创建数据库,创建表,并插入数据 :

  1. create database db01;
  2. user db01;
  3. create table user(
  4. id int(11) not null auto_increment,
  5. name varchar(50) not null,
  6. sex varchar(1),
  7. primary key (id)
  8. )engine=innodb default charset=utf8;
  9. insert into user(id,name,sex) values(null,'Tom','1');
  10. insert into user(id,name,sex) values(null,'Trigger','0');
  11. insert into user(id,name,sex) values(null,'Dawn','1');

2) 在从库中查询数据,进行验证 :
在从库中,可以查看到刚才创建的数据库:
MyCat-数据库中间件 - 图48
在该数据库中,查询user表中的数据:
MyCat-数据库中间件 - 图49

5.2.3 MyCat一主一从读写分离

5.2.3.1 读写分离原理

MyCat-数据库中间件 - 图50
读写分离,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。
通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle和SQL Server。
MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制。

5.2.3.2 读写分离配置

配置如下:
1). 检查MySQL的主从复制是否运行正常 .
2). 修改MyCat 的conf/schema.xml 配置如下:

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100">
  5. <table name="user" dataNode="dn1" primaryKey="id"/>
  6. </schema>
  7. <dataNode name="dn1" dataHost="localhost1" database="db01" />
  8. <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql"
  9. dbDriver="native" switchType="1" slaveThreshold="100">
  10. <heartbeat>select user()</heartbeat>
  11. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast">
  12. <readHost host="hostS1" url="192.168.192.158:3306" user="root" password="itcast" />
  13. </writeHost>
  14. </dataHost>
  15. </mycat:schema>

3). 修改conf/server.xml

  1. <user name="root" defaultAccount="true">
  2. <property name="password">123456</property>
  3. <property name="schemas">ITCAST</property>
  4. </user>
  5. <user name="test">
  6. <property name="password">123456</property>
  7. <property name="schemas">ITCAST</property>
  8. </user>
  9. <user name="user">
  10. <property name="password">123456</property>
  11. <property name="schemas">ITCAST</property>
  12. <property name="readOnly">true</property>
  13. </user>

4). 配置完毕之后, 重启MyCat服务;
属性含义说明:

  1. checkSQLschema
  2. 当该值设置为true时, 如果我们执行语句"select * from test01.user ;" 语句时, MyCat则会把schema字符去掉 , 可以避免后端数据库执行时报错 ;
  3. balance
  4. 负载均衡类型, 目前取值有4种:
  5. balance="0" : 不开启读写分离机制 , 所有读操作都发送到当前可用的writeHost上.
  6. balance="1" : 全部的readHost stand by writeHost (备用的writeHost) 都参与select 语句的负载均衡,简而言之,就是采用双主双从模式(M1 --> S1 , M2 --> S2, 正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。);
  7. balance="2" : 所有的读写操作都随机在writeHost , readHost上分发
  8. balance="3" : 所有的读请求随机分发到writeHost对应的readHost上执行, writeHost不负担读压力 ;balance=3 只在MyCat1.4 之后生效 .

5.2.3.3 验证读写分离

修改balance的值, 查询MyCat中的逻辑表中的数据变化;

5.2.4 MySQL双主双从搭建

5.2.4.1 架构

一个主机 Master1 用于处理所有写请求,它的从机 Slave1 和另一台主机 Master2 还有它的从机 Slave2 负责所有读请求。当 Master1 主机宕机后,Master2 主机负责写请求,Master1 、Master2 互为备机。架构图如下:
MyCat-数据库中间件 - 图51

5.2.4.2 双主双从配置

准备的机器如下:

编号 角色 IP地址 端口号
1 Master1 192.168.192.157 3306
2 Slave1 192.168.192.158 3306
3 Master2 192.168.192.159 3306
4 Slave2 192.168.192.160 3306

1). 双主机配置
Master1配置:

  1. #主服务器唯一ID
  2. server-id=1
  3. #启用二进制日志
  4. log-bin=mysql-bin
  5. # 设置不要复制的数据库(可设置多个)
  6. # binlog-ignore-db=mysql
  7. # binlog-ignore-db=information_schema
  8. #设置需要复制的数据库
  9. binlog-do-db=db02
  10. binlog-do-db=db03
  11. binlog-do-db=db04
  12. #设置logbin格式
  13. binlog_format=STATEMENT
  14. # 在作为从数据库的时候,有写入操作也要更新二进制日志文件
  15. log-slave-updates

Master2配置:

  1. #主服务器唯一ID
  2. server-id=3
  3. #启用二进制日志
  4. log-bin=mysql-bin
  5. # 设置不要复制的数据库(可设置多个)
  6. #binlog-ignore-db=mysql
  7. #binlog-ignore-db=information_schema
  8. #设置需要复制的数据库
  9. binlog-do-db=db02
  10. binlog-do-db=db03
  11. binlog-do-db=db04
  12. #设置logbin格式
  13. binlog_format=STATEMENT
  14. # 在作为从数据库的时候,有写入操作也要更新二进制日志文件
  15. log-slave-updates

2). 双从机配置
Slave1配置:

  1. #从服务器唯一ID
  2. server-id=2
  3. #启用中继日志
  4. relay-log=mysql-relay

Salve2配置:

  1. #从服务器唯一ID
  2. server-id=4
  3. #启用中继日志
  4. relay-log=mysql-relay

3). 双主机、双从机重启 mysql 服务
4). 主机从机都关闭防火墙
5). 在两台主机上建立帐户并授权 slave

  1. #在主机MySQL里执行授权命令
  2. GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%' IDENTIFIED BY 'itcast';
  3. flush privileges;

查询Master1的状态 :
MyCat-数据库中间件 - 图52
查询Master2的状态 :
MyCat-数据库中间件 - 图53
6). 在从机上配置需要复制的主机
Slave1 复制 Master1,Slave2 复制 Master2
slave1 指令:

  1. CHANGE MASTER TO MASTER_HOST='192.168.192.157',
  2. MASTER_USER='itcast',
  3. MASTER_PASSWORD='itcast',
  4. MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=409;

slave2 指令:

  1. CHANGE MASTER TO MASTER_HOST='192.168.192.159',
  2. MASTER_USER='itcast',
  3. MASTER_PASSWORD='itcast',
  4. MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=409;

7). 启动两台从服务器复制功能 , 查看主从复制的运行状态

  1. start slave;
  2. show slave status\G;

MyCat-数据库中间件 - 图54
MyCat-数据库中间件 - 图55
8). 两个主机互相复制
Master2 复制 Master1,Master1 复制 Master2
Master1 执行指令:

  1. CHANGE MASTER TO MASTER_HOST='192.168.192.159',
  2. MASTER_USER='itcast',
  3. MASTER_PASSWORD='itcast',
  4. MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=409;

Master2 执行指令:

  1. CHANGE MASTER TO MASTER_HOST='192.168.192.157',
  2. MASTER_USER='itcast',
  3. MASTER_PASSWORD='itcast',
  4. MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=409;

9). 启动两台主服务器复制功能 , 查看主从复制的运行状态

  1. start slave;
  2. show slave status\G;

MyCat-数据库中间件 - 图56
MyCat-数据库中间件 - 图57
10). 验证

  1. create database db03;
  2. use db03;
  3. create table user(
  4. id int(11) not null auto_increment,
  5. name varchar(50) not null,
  6. sex varchar(1),
  7. primary key (id)
  8. )engine=innodb default charset=utf8;
  9. insert into user(id,name,sex) values(null,'Tom','1');
  10. insert into user(id,name,sex) values(null,'Trigger','0');
  11. insert into user(id,name,sex) values(null,'Dawn','1');
  12. insert into user(id,name,sex) values(null,'Jack Ma','1');
  13. insert into user(id,name,sex) values(null,'Coco','0');
  14. insert into user(id,name,sex) values(null,'Jerry','1');

在Master1上创建数据库:
MyCat-数据库中间件 - 图58
在Master1上创建表 :
MyCat-数据库中间件 - 图59
11). 停止从服务复制功能

  1. stop slave;

12). 重新配置主从关系

  1. stop slave;
  2. reset master;

5.2.5 MyCat双主双从读写分离

5.2.5.1 配置

修改的 balance属性,通过此属性配置读写分离的类型 ;

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100">
  5. <table name="user" dataNode="dn1" primaryKey="id"/>
  6. </schema>
  7. <dataNode name="dn1" dataHost="localhost1" database="db03" />
  8. <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql"
  9. dbDriver="native" switchType="1" slaveThreshold="100">
  10. <heartbeat>select user()</heartbeat>
  11. <writeHost host="hostM1" url="192.168.192.147:3306" user="root" password="itcast">
  12. <readHost host="hostS1" url="192.168.192.149:3306" user="root" password="itcast" />
  13. </writeHost>
  14. <writeHost host="hostM2" url="192.168.192.150:3306" user="root" password="itcast">
  15. <readHost host="hostS2" url="192.168.192.151:3306" user="root" password="itcast" />
  16. </writeHost>
  17. </dataHost>
  18. </mycat:schema>

1). balance
1 : 代表 全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡 ;
2). writeType
0 : 写操作都转发到第1台writeHost, writeHost1挂了, 会切换到writeHost2上;
1 : 所有的写操作都随机地发送到配置的writeHost上 ;
3). switchType
-1 : 不自动切换
1 : 默认值, 自动切换
2 : 表示基于MySQL的主从同步状态决定是否切换, 心跳语句 : show slave status

5.2.5.2 读写分离验证

查询数据 : select * from user;
MyCat-数据库中间件 - 图60
插入数据 : insert into user(id,name,sex) values(null,’Dawn’,’1’);
MyCat-数据库中间件 - 图61

5.2.5.3 可用性验证

关闭Master1 , 然后再执行写入的SQL语句 , 通过日志查询当前写入操作, 操作的是那台服务器 ;

6. MyCat高可用集群搭建

6.1 集群架构

6.1.1 MyCat实现读写分离架构

在上面的章节, 我们已经讲解过了通过MyCat来实现MySQL的读写分离, 从而完成MySQL集群的负载均衡 , 如下面的结构图:
MyCat-数据库中间件 - 图62
但是以上架构存在问题 , 由于MyCat中间件是单节点的服务, 前端客户端所有的压力过来都直接请求这一台MyCat , 存在单点故障。所以这个时候, 我们就需要考虑MyCat的集群 ;

6.1.2 MyCat集群架构

通过MyCat来实现后端MySQL的负载均衡 , 通过HAProxy再实现MyCat集群的负载均衡 ;
MyCat-数据库中间件 - 图63
HAProxy 负责将请求分发到 MyCat 上,起到负载均衡的作用,同时 HAProxy 也能检测到 MyCat 是否存活,HAProxy 只会将请求转发到存活的 MyCat 上。如果一台 MyCat 服务器宕机,HAPorxy 转发请求时不会转发到宕机的 MyCat 上,所以 MyCat 依然可用。
HAProxy介绍:
HAProxy 是一个开源的、高性能的基于TCP(第四层)和HTTP(第七层)应用的负载均衡软件。 使用HAProxy可以快速、可靠地实现基于TCP与HTTP应用的负载均衡解决方案。
具有以下优点:
①. 可靠性和稳定性好, 可以与硬件级的F5负载均衡服务器媲美 ;
②. 处理能力强, 最高可以通过维护4w-5w个并发连接, 单位时间处理的最大请求数达到2w个 ;
③. 支持多种负载均衡算法 ;
④. 有功能强大的监控界面, 通过此页面可以实时了解系统的运行情况 ;
但是, 上述的架构也是存在问题的, 因为所以的客户端请求都是先到达HAProxy, 由HAProxy再将请求再向下分发, 如果HAProxy宕机的话, 就会造成整个MyCat集群不能正常运行, 依然存在单点故障。

6.1.3 MyCat的高可用集群

MyCat-数据库中间件 - 图64
图解说明:
1). HAProxy 实现了 MyCat 多节点的集群高可用和负载均衡,而 HAProxy 自身的高可用则可以通过Keepalived 来实现。因此,HAProxy 主机上要同时安装 HAProxy 和 Keepalived,Keepalived 负责为该服务器抢占 vip(虚拟 ip),抢占到 vip 后,对该主机的访问可以通过原来的 ip访问,也可以直接通过 vip访问。
2). Keepalived 抢占 vip 有优先级,在 keepalived.conf 配置中的 priority 属性决定。但是一般哪台主机上的Keepalived服务先启动就会抢占到vip,即使是slave,只要先启动也能抢到(要注意避免Keepalived的资源抢占问题)。
3). HAProxy 负责将对 vip 的请求分发到 MyCat 集群节点上,起到负载均衡的作用。同时 HAProxy 也能检测到 MyCat 是否存活,HAProxy 只会将请求转发到存活的 MyCat 上。
4). 如果 Keepalived+HAProxy 高可用集群中的一台服务器宕机,集群中另外一台服务器上的 Keepalived会立刻抢占 vip 并接管服务,此时抢占了 vip 的 HAProxy 节点可以继续提供服务。
5). 如果一台 MyCat 服务器宕机,HAPorxy 转发请求时不会转发到宕机的 MyCat 上,所以 MyCat 依然可用。
综上:MyCat 的高可用及负载均衡由 HAProxy 来实现,而 HAProxy 的高可用,由 Keepalived 来实现。
keepalived介绍:
Keepalived是一种基于VRRP协议来实现的高可用方案,可以利用其来避免单点故障。 通常有两台甚至多台服务器运行Keepalived,一台为主服务器(Master), 其他为备份服务器, 但是对外表现为一个虚拟IP(VIP), 主服务器会发送特定的消息给备份服务器, 当备份服务器接收不到这个消息时, 即认为主服务器宕机, 备份服务器就会接管虚拟IP, 继续提供服务, 从而保证了整个集群的高可用。
VRRP(虚拟路由冗余协议-Virtual Router Redundancy Protocol)协议是用于实现路由器冗余的协议,VRRP 协议将两台或多台路由器设备虚拟成一个设备,对外提供虚拟路由器 IP(一个或多个),而在路由器组内部,如果实际拥有这个对外 IP 的路由器如果工作正常的话就是 MASTER,或者是通过算法选举产生。MASTER 实现针对虚拟路由器 IP 的各种网络功能,如 ARP 请求,ICMP,以及数据的转发等;其他设备不拥有该虚拟 IP,状态是 BACKUP,除了接收 MASTER 的VRRP 状态通告信息外,不执行对外的网络功能。当主机失效时,BACKUP 将接管原先 MASTER 的网络功能。VRRP 协议使用多播数据来传输 VRRP 数据,VRRP 数据使用特殊的虚拟源 MAC 地址发送数据而不是自身网卡的 MAC 地址,VRRP 运行时只有 MASTER 路由器定时发送 VRRP 通告信息,表示 MASTER 工作正常以及虚拟路由器 IP(组),BACKUP 只接收 VRRP 数据,不发送数据,如果一定时间内没有接收到 MASTER 的通告信息,各 BACKUP 将宣告自己成为 MASTER,发送通告信息,重新进行 MASTER 选举状态。

6.2 高可用集群搭建

6.2.1 部署环境规划

名称 IP 端口 用户名/密码
MySQL Master 192.168.192.157 3306 root/itcast
MySQL Slave 192.168.192.158 3306 root/itcast
MyCat节点1 192.168.192.157 8066 root/123456
MyCat节点2 192.168.192.158 8066 root/123456
HAProxy节点1/keepalived主 192.168.192.159
HAProxy节点2/keepalived备 192.168.192.160

MyCat-数据库中间件 - 图65

6.2.2 MySQL主从复制搭建

6.2.2.1 master

1) 在master 的配置文件(/usr/my.cnf)中,配置如下内容:

  1. #mysql 服务ID,保证整个集群环境中唯一
  2. server-id=1
  3. #mysql binlog 日志的存储路径和文件名
  4. log-bin=/var/lib/mysql/mysqlbin
  5. #设置logbin格式
  6. binlog_format=STATEMENT
  7. #是否只读,1 代表只读, 0 代表读写
  8. read-only=0
  9. #指定同步的数据库
  10. binlog-do-db=db01
  11. binlog-do-db=db02
  12. binlog-do-db=db03

2) 执行完毕之后,需要重启Mysql:

  1. service mysql restart ;

3) 创建同步数据的账户,并且进行授权操作:

  1. grant replication slave on *.* to 'itcast'@'%' identified by 'itcast';
  2. flush privileges;

4) 查看master状态:

  1. show master status;

MyCat-数据库中间件 - 图66
字段含义:

  1. File : 从哪个日志文件开始推送日志文件
  2. Position 从哪个位置开始推送日志
  3. Binlog_Do_DB : 指定需要同步的数据库

6.2.2.2 slave

1) 在 slave 端配置文件中,配置如下内容:

  1. #mysql服务端ID,唯一
  2. server-id=2
  3. #指定binlog日志
  4. log-bin=/var/lib/mysql/mysqlbin
  5. #启用中继日志
  6. relay-log=mysql-relay

2) 执行完毕之后,需要重启Mysql:

  1. service mysql restart;

3) 执行如下指令 :

  1. change master to master_host= '192.168.192.157', master_user='itcast', master_password='itcast', master_log_file='mysqlbin.000002', master_log_pos=120;

指定当前从库对应的主库的IP地址,用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志。
4) 开启同步操作

  1. start slave;
  2. show slave status;

MyCat-数据库中间件 - 图67
5) 停止同步操作

  1. stop slave;

6.2.2.3 测试验证
  1. create database db01;
  2. user db01;
  3. create table user(
  4. id int(11) not null auto_increment,
  5. name varchar(50) not null,
  6. sex varchar(1),
  7. primary key (id)
  8. )engine=innodb default charset=utf8;
  9. insert into user(id,name,sex) values(null,'Tom','1');
  10. insert into user(id,name,sex) values(null,'Trigger','0');
  11. insert into user(id,name,sex) values(null,'Dawn','1');

6.2.3 MyCat安装配置

6.2.3.1 schema.xml
  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100">
  5. <table name="user" dataNode="dn1" primaryKey="id"/>
  6. </schema>
  7. <dataNode name="dn1" dataHost="localhost1" database="db01" />
  8. <dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql"
  9. dbDriver="native" switchType="1" slaveThreshold="100">
  10. <heartbeat>select user()</heartbeat>
  11. <writeHost host="hostM1" url="192.168.192.157:3306" user="root" password="itcast">
  12. <readHost host="hostS1" url="192.168.192.158:3306" user="root" password="itcast" />
  13. </writeHost>
  14. </dataHost>
  15. </mycat:schema>

6.2.3.2 server.xml
  1. <user name="root" defaultAccount="true">
  2. <property name="password">123456</property>
  3. <property name="schemas">ITCAST</property>
  4. </user>
  5. <user name="test">
  6. <property name="password">123456</property>
  7. <property name="schemas">ITCAST</property>
  8. </user>

两台MyCat服务, 做相同的配置 ;

6.2.4 HAProxy安装配置

6.2.4.1 安装

1). 准备好HAProxy安装包,传到/root目录下

  1. haproxy-1.5.16.tar.gz

2). 解压到/usr/local/src目录下

  1. tar -zxvf haproxy-1.5.16.tar.gz -C /usr/local/src

3). 进入解压后的目录,查看内核版本,进行编译

  1. cd /usr/local/src/haproxy-1.5.16
  2. uname -r
  3. make TARGET=linux2632 PREFIX=/usr/local/haproxy ARCH=x86_64
  4. # TARGET=linux310,内核版本,使用uname -r查看内核,如:2.6.32-431.el6.x86_64,此时该参数就为linux2632;
  5. # ARCH=x86_64,系统位数;
  6. # PREFIX=/usr/local/haprpxy #/usr/local/haprpxy,为haprpxy安装路径。

4). 编译完成后,进行安装

  1. make install PREFIX=/usr/local/haproxy

5). 安装完成后,创建目录

  1. mkdir -p /usr/data/haproxy/

6). 创建HAProxy配置文件
vim /usr/local/haproxy/haproxy.conf

  1. global
  2. log 127.0.0.1 local0
  3. maxconn 4096
  4. chroot /usr/local/haproxy
  5. pidfile /usr/data/haproxy/haproxy.pid
  6. uid 99
  7. gid 99
  8. daemon
  9. node mysql-haproxy-01
  10. description mysql-haproxy-01
  11. defaults
  12. log global
  13. mode tcp
  14. option abortonclose
  15. option redispatch
  16. retries 3
  17. maxconn 2000
  18. timeout connect 50000ms
  19. timeout client 50000ms
  20. timeout server 50000ms
  21. listen proxy_status
  22. bind 0.0.0.0:48066
  23. mode tcp
  24. balance roundrobin
  25. server mycat_1 192.168.192.157:8066 check
  26. server mycat_2 192.168.192.158:8066 check
  27. frontend admin_stats
  28. bind 0.0.0.0:8888
  29. mode http
  30. stats enable
  31. option httplog
  32. maxconn 10
  33. stats refresh 30s
  34. stats uri /admin
  35. stats auth admin:123123
  36. stats hide-version
  37. stats admin if TRUE

内容解析如下 :

  1. #global 配置中的参数为进程级别的参数,通常与其运行的操作系统有关
  2. global
  3. #定义全局的syslog服务器, 最多可定义2个; local0 是日志设备, 对应于/etc/rsyslog.conf中的配置 , 默认收集info级别日志
  4. log 127.0.0.1 local0
  5. #log 127.0.0.1 local1 notice
  6. #log loghost local0 info
  7. #设定每个haproxy进程所接受的最大并发连接数 ;
  8. maxconn 4096
  9. #修改HAproxy工作目录至指定的目录并在放弃权限之前执行chroot操作, 可以提升haproxy的安全级别
  10. chroot /usr/local/haproxy
  11. #进程ID保存文件
  12. pidfile /usr/data/haproxy/haproxy.pid
  13. #指定用户ID
  14. uid 99
  15. #指定组ID
  16. gid 99
  17. #设置HAproxy以守护进程方式运行
  18. daemon
  19. #debug
  20. #quiet
  21. node mysql-haproxy-01 ## 定义当前节点的名称,用于 HA 场景中多 haproxy 进程共享同一个 IP 地址时
  22. description mysql-haproxy-01 ## 当前实例的描述信息
  23. #defaults:用于为所有其他配置段提供默认参数,这默认配置参数可由下一个"defaults"所重新设定
  24. defaults
  25. #继承global中的log定义
  26. log global
  27. #所使用的处理模式(tcp:四层 , http:七层, health:状态检查,只返回OK)
  28. ### tcp: 实例运行于纯 tcp 模式,在客户端和服务器端之间将建立一个全双工的连接,且不会对 7 层报文做任何类型的检查,此为默认模式
  29. ### http:实例运行于 http 模式,客户端请求在转发至后端服务器之前将被深度分析,所有不与 RFC 模式兼容的请求都会被拒绝
  30. ### health:实例运行于 health 模式,其对入站请求仅响应“OK”信息并关闭连接,且不会记录任何日志信息 ,此模式将用于相应外部组件的监控状态检测请求
  31. mode tcp
  32. #当服务器负载很高的时候,自动结束掉当前队列处理时间比较长的连接
  33. option abortonclose
  34. #当使用了cookie时,haproxy将会将请求的后端服务器的serverID插入到cookie中,以保证会话的session持久性,而此时,后端服务器宕机,但是客户端的cookie不会刷新,设置此参数,将会将客户请求强制定向到另外一个后端server上,以保证服务的正常。
  35. option redispatch
  36. retries 3
  37. # 前端的最大并发连接数(默认为 2000)
  38. maxconn 2000
  39. # 连接超时(默认是毫秒,单位可以设置 us,ms,s,m,h,d)
  40. timeout connect 5000
  41. # 客户端超时时间
  42. timeout client 50000
  43. # 服务器超时时间
  44. timeout server 50000
  45. #listen: 用于定义通过关联“前端”和“后端”一个完整的代理,通常只对 TCP 流量有用
  46. listen proxy_status
  47. bind 0.0.0.0:48066 # 绑定端口
  48. mode tcp
  49. balance roundrobin # 定义负载均衡算法,可用于"defaults"、"listen"和"backend"中,默认为轮询
  50. #格式: server <name> <address> [:[port]] [param*]
  51. # weight : 权重,默认为 1,最大值为 256,0 表示不参与负载均衡
  52. # backup : 设定为备用服务器,仅在负载均衡场景中的其他 server 均不可以启用此 server
  53. # check : 启动对此 server 执行监控状态检查,其可以借助于额外的其他参数完成更精细的设定
  54. # inter : 设定监控状态检查的时间间隔,单位为毫秒,默认为 2000,也可以使用 fastinter 和 downinter 来根据服务器端专题优化此事件延迟
  55. # rise : 设置 server 从离线状态转换至正常状态需要检查的次数(不设置的情况下,默认值为 2)
  56. # fall : 设置 server 从正常状态转换至离线状态需要检查的次数(不设置的情况下,默认值为 3)
  57. # cookie : 为指定 server 设定 cookie 值,此处指定的值将会在请求入站时被检查,第一次为此值挑选的 server 将会被后续的请求所选中,其目的在于实现持久连接的功能
  58. # maxconn: 指定此服务器接受的最大并发连接数,如果发往此服务器的连接数目高于此处指定的值,其将被放置于请求队列,以等待其他连接被释放
  59. server mycat_1 192.168.192.157:8066 check inter 10s
  60. server mycat_2 192.168.192.158:8066 check inter 10s
  61. # 用来匹配接收客户所请求的域名,uri等,并针对不同的匹配,做不同的请求处理
  62. # HAProxy 的状态信息统计页面
  63. frontend admin_stats
  64. bind 0.0.0.0:8888
  65. mode http
  66. stats enable
  67. option httplog
  68. maxconn 10
  69. stats refresh 30s
  70. stats uri /admin
  71. stats auth admin:123123
  72. stats hide-version
  73. stats admin if TRUE

HAProxy的负载均衡策略:

策略 含义
roundrobin 表示简单的轮循,即客户端每访问一次,请求轮循跳转到后端不同的节点机器上
static-rr 基于权重轮循,根据权重轮循调度到后端不同节点
leastconn 加权最少连接,表示最少连接者优先处理
source 表示根据请求源IP,这个跟Nginx的IP_hash机制类似,使用其作为解决session问题的一种方法
uri 表示根据请求的URL,调度到后端不同的服务器
url_param 表示根据请求的URL参数来进行调度
hdr(name) 表示根据HTTP请求头来锁定每一次HTTP请求
rdp-cookie(name) 表示根据cookie(name)来锁定并哈希每一次TCP请求

6.2.4.2 启动访问

1). 启动HAProxy

  1. /usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.conf

2). 查看HAProxy进程

  1. ps -ef|grep haproxy

3). 访问
http://192.168.192.162:8888/admin
界面:
MyCat-数据库中间件 - 图68

6.2.5 Keepalived安装配置

MyCat-数据库中间件 - 图69

6.2.5.1 安装配置

1). 上传安装包到Linux

  1. alt + p --------> put D:/tmp/keepalived-1.4.5.tar.gz

2). 解压安装包到目录 /usr/local/src

  1. tar -zxvf keepalived-1.4.5.tar.gz -C /usr/local/src

3). 安装依赖插件

  1. yum install -y gcc openssl-devel popt-devel

4). 进入解压后的目录,进行配置,进行编译

  1. cd /usr/local/src/keepalived-1.4.5
  2. ./configure --prefix=/usr/local/keepalived

5). 进行编译,完成后进行安装

  1. make && make install

6). 运行前配置

  1. cp /usr/local/src/keepalived-1.4.5/keepalived/etc/init.d/keepalived /etc/init.d/
  2. mkdir /etc/keepalived
  3. cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
  4. cp /usr/local/src/keepalived-1.4.5/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
  5. cp /usr/local/keepalived/sbin/keepalived /usr/sbin/

7). 修改配置文件 /etc/keepalived/keepalived.conf
Master:

  1. global_defs {
  2. notification_email {
  3. javadct@163.com
  4. }
  5. notification_email_from keepalived@showjoy.com
  6. smtp_server 127.0.0.1
  7. smtp_connect_timeout 30
  8. router_id haproxy01
  9. vrrp_skip_check_adv_addr
  10. vrrp_garp_interval 0
  11. vrrp_gna_interval 0
  12. }
  13. vrrp_script chk_haproxy {
  14. script "/etc/keepalived/haproxy_check.sh"
  15. interval 2
  16. weight 2
  17. }
  18. vrrp_instance VI_1 {
  19. #主机配MASTER,备机配BACKUP
  20. state MASTER
  21. #所在机器网卡
  22. interface eth1
  23. virtual_router_id 51
  24. #数值越大优先级越高
  25. priority 120
  26. advert_int 1
  27. authentication {
  28. auth_type PASS
  29. auth_pass 1111
  30. }
  31. ## 将 track_script 块加入 instance 配置块
  32. track_script {
  33. chk_haproxy ## 检查 HAProxy 服务是否存活
  34. }
  35. virtual_ipaddress {
  36. #虚拟IP
  37. 192.168.192.200
  38. }
  39. }

BackUP:

  1. global_defs {
  2. notification_email {
  3. javadct@163.com
  4. }
  5. notification_email_from keepalived@showjoy.com
  6. smtp_server 127.0.0.1
  7. smtp_connect_timeout 30
  8. #标识本节点
  9. router_id haproxy02
  10. vrrp_skip_check_adv_addr
  11. vrrp_garp_interval 0
  12. vrrp_gna_interval 0
  13. }
  14. # keepalived 会定时执行脚本并对脚本执行的结果进行分析,动态调整 vrrp_instance 的优先级
  15. vrrp_script chk_haproxy {
  16. # 检测 haproxy 状态的脚本路径
  17. script "/etc/keepalived/haproxy_check.sh"
  18. #检测时间间隔
  19. interval 2
  20. #如果条件成立,权重+2
  21. weight 2
  22. }
  23. vrrp_instance VI_1 {
  24. #主机配MASTER,备机配BACKUP
  25. state BACKUP
  26. #所在机器网卡
  27. interface eth1
  28. virtual_router_id 51
  29. #数值越大优先级越高
  30. priority 100
  31. advert_int 1
  32. authentication {
  33. auth_type PASS
  34. auth_pass 1111
  35. }
  36. ## 将 track_script 块加入 instance 配置块
  37. track_script {
  38. chk_haproxy ## 检查 HAProxy 服务是否存活
  39. }
  40. virtual_ipaddress {
  41. #虚拟IP
  42. 192.168.192.200
  43. }
  44. }

8). 编写检测haproxy的shell脚本 haproxy_check.sh

  1. #!/bin/bash
  2. A=`ps -C haproxy --no-header | wc -l`
  3. if [ $A -eq 0 ];then
  4. /usr/local/haproxy/sbin/haproxy -f /usr/local/haproxy/haproxy.conf
  5. echo "haproxy restart ..." &> /dev/null
  6. sleep 1
  7. if [ `ps -C haproxy --no-header | wc -l` -eq 0 ];then
  8. /etc/init.d/keepalived stop
  9. echo "stop keepalived" &> /dev/null
  10. fi
  11. fi

6.2.5.2 启动测试

1). 启动Keepalived

  1. service keepalived start

2). 登录验证

  1. mysql -uroot -p123456 -h 192.168.192.200 -P 48066

MyCat-数据库中间件 - 图70

7. MyCat架构剖析

7.1 MyCat总体架构介绍

7.1.1 源码下载及导入

MyCat-数据库中间件 - 图71
导入Idea
MyCat-数据库中间件 - 图72

7.1.2 总体架构

MyCat在逻辑上由几个模块组成: 通信协议、路由解析、结果集处理、数据库连接、监控等模块。如图所示:
MyCat-数据库中间件 - 图73
1). 通信协议模块: 通信协议模块承担底层的收发数据、线程回调处理工作, MyCat通信协议默认采用Reactor模式,在协议层采用MySQL协议;
2). 路由解析模块: 负责对传入的SQL语句进行语法解析, 解析语句的条件、类型、关键字等,并进行优化;
3). SQL执行模块: 负责从连接池中获取连接, 再根据路由解析的结果, 把SQL语句分发到相应的节点执行;
4). 数据库连接模块: 负责创建、管理、维护后端的连接池。为减少每次建立数据库连接的开销,数据库使用连接池机制对连接声明周期进行管理;
5). 结果集处理模块: 负责对跨分片的查询结果进行汇聚、排序、截取等;
6). 监控管理模块: 负责MyCat的连接、内存等资源进行监控和管理。监控主要通过管理指令及监控服务展现一些监控数据; 管理则主要通过轮询事件来检测和释放不适用的资源;

7.1.3 总体执行流程

MyCat-数据库中间件 - 图74

7.2 MyCat网络I/O架构及实现

7.2.1 BIO、NIO与AIO

1). BIO
BIO(同步阻塞I/O) 通常由一个单独的Acceptor线程负责监听客户端的连接, 接收到客户端的连接请求后, 会为每个客户端创建一个新的线程进行处理, 处理完成之后, 再给客户端返回结果, 销毁线程 。
每个客户端请求接入时, 都需要开启一个线程进行处理, 一个线程只能处理一个客户端连接。 当客户端变多时,会创建大量的处理线程, 每个线程都需要分配栈空间和CPU, 并且频繁的线程上下文切换也会造成性能的浪费。所以该模式, 无法满足高性能、高并发接入的需求。
2). NIO
NIO(同步非阻塞I/O)基于Reactor模式作为底层通信模型,Reactor模式可以将事件驱动的应用进行事件分派, 将客户端发送过来的服务请求分派给合适的处理类(handler)。当Socket有流可读或可写入Socket时, 操作系统会通知相应的应用程序进行处理, 应用程序再将流读取到缓冲区或写入操作系统。 这时已经不是一个连接对应一个处理线程了, 而是一个有效的请求对应一个线程, 当没有数据时, 就没有工作线程来处理。
NIO 的最大优点体现在线程轮询访问Selector, 当read或write到达时则处理, 未到达时则继续轮询。
3). AIO
AIO,全程 Asynchronous IO(异步非阻塞的IO), 是一种非阻塞异步的通信模式。在NIO的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。AIO中客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
AIO与NIO的主要区别在于回调与轮询, 客户端不需要关注服务处理事件是否完成, 也不需要轮询, 只需要关注自己的回调函数。

7.2.2 通信架构

在MyCat中实现了NIO与AIO两种I/O模式, 可以通过配置文件server.xml进行指定 :

  1. <property name="usingAIO">1</property>

usingAIO为1代表使用AIO模型 , 为0表示使用NIO模型;
MyCat的AIO架构
MyCat-数据库中间件 - 图75
1). MyCatStartUp是整个MyCat服务启动的入口;
2). 在获取到MyCat的home目录后, 把主要的任务交给MyCatServer , 并调用其startup方法;
3). 初始化系统配置, 获取配置文件中的usingAIO的配置, 如果配置为1, 说明使用AIO模型 , 进入到AIO的分支, 并创建两个连接, 一个是管理后台连接(9066), 一个server的连接(8066);
4). 进入AIO分支 , 主要有AIOAcceptor接收客户端请求, 绑定端口, 创建服务端的异步Socket ;在accept方法中完成两件事: ①. FrontedConnection的创建, 这是前段连接的关键; ②. register注册事件, MySQL协议握手包就在此时发送;
MyCat-数据库中间件 - 图76
MyCat的NIO架构
如果设置的usingAIO为0 ,那么将走NIOAcceptor通道 , 流程如下:
MyCat-数据库中间件 - 图77
1). 如果走NIO分支 , 将首先创建NIOAcceptor对象, 并调用其start方法;
2). NIOAcceptor 负责处理Accept事件, 服务端接收客户端的连接事件, 就是MyCat作为服务端去处理前端业务程序发过来的连接请求, 建立链接后, 调用NIOAcceptor的 NIOReactor.postRegister方法进行注册(并没有注解注册, 而是放入缓冲队列, 避免加锁的竞争)。
NIOAcceptor的accept方法 :
MyCat-数据库中间件 - 图78
NIOReactor的postRegister方法:
MyCat-数据库中间件 - 图79

7.3 Mycat实现MySQL协议

7.3.1 MySQL协议简介

7.3.1.1 概述

MySQL协议处于应用层之下、TCP/IP之上, 在MySQL客户端和服务端之间使用。包含了链接器、MySQL代理、主从复制服务器之间通信,并支持SSL加密、传输数据的压缩、连接和身份验证及数据交互等。其中,握手认证阶段和命令执行阶段是MySQL协议中的两个重要阶段。

7.3.1.2 握手认证阶段

MyCat-数据库中间件 - 图80
A. 握手认证阶段是客户端连接服务器的必经之路, 客户端与服务端完成TCP的三次握手以后, 服务端会向客户端发送一个初始化握手包, 握手包中包含了协议版本、MySQLServer版本、线程ID、服务器的权能标识和字符集等信息。
B. 客户端在接收到服务端的初始化握手包之后, 会发送身份验证包给服务端(AuthPacket), 该包中包含用户名、密码等信息。
C. 服务端接收到客户端的登录验证包之后,需要进行逻辑校验,校验该登录信息是否正确。如果信息都符合,则返回一个OKPacket,表示登录成功,否则返回ERR_Packet,表示拒绝。
Wireshark抓包如下:
MyCat-数据库中间件 - 图81
报文分析如下:
1). 初始化握手包
MyCat-数据库中间件 - 图82
通过抓包工具Wireshark抓取到的握手包信息如下, 握手包格式:
MyCat-数据库中间件 - 图83
说明:
Packet Length : 包的长度;
Packet Number : 包的序号;
Server Greeting : 消息体, 包含了协议版本、MySQLServer版本、线程ID和字符集等信息。
2). 登录认证包
客户端在接收到服务端发来的初始握手包之后, 向服务端发出认证请求, 该请求包含以下信息(由Wireshark抓获) :
MyCat-数据库中间件 - 图84
3). OK包或ERROR包
服务端接收到客户端的登录认证包之后,如果通过认证,则返回一个OKPacket,如果未通过认证,则返回一个ERROR包。
OK报文如下:
MyCat-数据库中间件 - 图85
ERROR报文如下 :
MyCat-数据库中间件 - 图86

7.3.1.3 命令执行阶段

在握手认证阶段通过并完成以后, 客户端可以向服务端发送各种命令来请求数据, 此阶段的流程是: 命令请求->返回结果集。
Wireshark 捕获的数据包如下:
MyCat-数据库中间件 - 图87
1). 命令包
MyCat-数据库中间件 - 图88
2). 结果集包
MyCat-数据库中间件 - 图89

7.3.2 MySQL协议在MyCat中实现

7.3.2.1 握手认证实现

在MyCat中同时实现了NIO和AIO, 通过配置可以选择NIO和AIO。MyCat Server在启动阶段已经选择好采用NIO还是AIO,因此建立I/O通道后,MyCat服务端一直等待客户端端的连接,当有连接到来的时候,MyCat首先发送握手包。
1). 握手包源码实现
MyCat中的源码中io.mycat.net.FrontendConnection类的实现如下:
MyCat-数据库中间件 - 图90
握手包信息组装完毕后, 通过FrontedConnection写回客户端。
2). 认证包源码实现
客户端接收到握手包后, 紧接着向服务端发起一个认证包, MyCat封装为类 AuthPacket:
MyCat-数据库中间件 - 图91
客户端发送的认证包转由 FrontendAuthenticator 的Handler来处理, 主要操作就是 拆包, 检查用户名、密码合法性, 检查连接数是够超出限制。源码实现如下:
MyCat-数据库中间件 - 图92
认证失败, 调用failure方法, 认证成功调用success方法。
failure方法源码:
MyCat-数据库中间件 - 图93
success方法源码:
MyCat-数据库中间件 - 图94

7.3.2.2 命令执行实现

命令执行阶段就是SQL命令和SQL语句执行阶段, 在该阶段MyCat主要需要做的事情, 就是对客户端发来的数据包进行拆包, 并判断命令的类型, 并解析SQL语句, 执行响应的SQL语句, 最后把执行结果封装在结果集包中, 返回给客户端。
从客户端发来的命令交给 FrontendCommandHandler 中的handle方法处理:
MyCat-数据库中间件 - 图95
处理具体的请求, 返回客户端结果集数据包:
MyCat-数据库中间件 - 图96

7.4 MyCat线程架构与实现

7.4.1 MyCat线程池实现

在MyCat中大量用到了线程池, 通过线程池来避免频繁的创建和销毁线程而造成的系统性能的浪费。在MyCat中使用的线程池是JDK中提供的线程池 ThreadPoolExecutor 的子类 NameableExecutor , 构造方法如下:
MyCat-数据库中间件 - 图97
父类构造为:
MyCat-数据库中间件 - 图98
构造参数含义:
corePoolSize : 核心池大小
maximumPoolSize : 最大线程数
keepAliveTime: 线程没有任务执行时, 最多能够存活多久
timeUnit: 时间单位
workQueue: 阻塞任务队列
threadFactory: 线程工厂, 用来创建线程

7.4.2 MyCat线程架构

MyCat-数据库中间件 - 图99
在MyCat中主要有两大线程池: timerExecutor 和 businessExecutor。
1). timerExecutor 线程池主要完成系统时间定时更新、处理器定时检查、数据节点定时连接空闲超时检查、数据节点定时心跳检测等任务。
2). businessExecutor是MyCat最重要的线程资源池, 该资源池的线程使用的范围非常广, 涵盖以下方面:
A. 后端用原生协议连接数据
B. JDBC执行SQL语句
C. SQL拦截
D. 数据合并服务
E. 批量SQL作业
F. 查询结果的异步分发
G. 基于guava实现异步回调
MyCat-数据库中间件 - 图100

7.5 MyCat内存管理及缓存框架与实现

这里所提到的内存管理指的是MyCat缓冲区管理, 众所周知设置缓冲区的唯一目的是提高系统的性能, 缓冲区通常是部分常用的数据存放在缓冲池中以便系统直接访问, 避免使用磁盘IO访问磁盘数据, 从而提高性能。

7.5.1 内存管理

1). 缓冲池组成
缓冲池的最小单位为chunk, 默认的chunk大小为4096字节(DEFAULTBUFFER_CHUNK_SIZE), BufferPool的总大小为4096 x processors x 1000(其中processors为处理器数量)。对I/O进程而言, 他们共享一个缓冲池。缓冲池有两种类型: 本地缓存线程(以$开头的线程)缓冲区和其他缓冲区, 分配buffer时, 优先获取ThreadLocalPool中的buffer, 没有命中时会获取BufferPool中的buffer。
2). 分配MyCat缓冲池
分配缓冲池时, 可以指定大小, 也可以用默认值。
A. allocate(): 先检测是否为本地线程, 当执行线程为本地缓存线程时, localBufferPool取出一个可用的buffer。如果不是, 则从ConcurrentLinkedQueue队列中取出一个buffer进行分配, 如果队列没有可用的buffer, 则创建一个直接缓冲区。
B. allocate(size): 如果用户指定的size不大于chunkSize, 则调用allocate()进行分配; 反之则调用createTempBuffer(size)创建临时非直接缓冲区。
3). MyCat缓冲池的回收
回收时先判断buffer是否有效, 有如下情况时缓冲池不回收。
A. 不是直接缓冲区
B. buffer是空的
C. buffer的容量大于chunkSize

7.5.2 MyCat缓存架构

1). 缓存框架选择
MyCat支持ehcache、mapdb、leveldb缓存, 可通过配置文件cacheserver.properties来进行配置;
MyCat-数据库中间件 - 图101
2). 缓存内容
MyCat有路由缓存、表主键到datanode缓存、ER关系缓存。
A. 路由缓存: 即SQLRouteCache, 根据SQL语句查找路由信息的缓存, 该缓存只是针对select语句, 如果执行了之前已经执行过的某个SQL语句(缓存命中), 那么路由信息就不需要重复计算了, 直接从缓存中获取。
B. 表主键到datanode缓存: 当分片字段与主键字段不一致时, 直接通过主键值查询时无法定位具体分片的(只能全分片下发), 所以设置该缓存之后, 就可以利用主键值查找到分片名, 缓存的key是ID值, value是节点名。
C. ER关系缓存: 在ER分片时使用, 而且在insert查询中才会使用缓存, 当字表插入数据时, 根据父子关联字段确定子表分片, 下次可以直接从缓存中获取所在的分片。
查看缓存指令: show @@cache;
MyCat-数据库中间件 - 图102

7.6 MyCat连接池架构与实现

这里我们所讨论的连接池是MyCat的后端连接池, 也就是MyCat后端与各个数据库节点之间的连接架构。
1). 连接池创建
MyCat按照每个dataHost创建一个连接池, 根据schema.xml文件的配置取得最小的连接数minCon, 并初始化minCon个连接。在初始化连接时, 还需要判定用户选择的是JDBC还是原生的MySQL协议, 以便于创建对应的连接。
2). 连接池分配
分配连接就是从连接池队列中取出一个连接, 在取出一个连接时, MyCat需要根据负载均衡(balance属性)的类型选择不同的数据源, 因为连接和数据源绑在一起,所以需要知道MyCat读写的是那些数据源, 才能分配响应的连接。
3). 架构
MyCat-数据库中间件 - 图103

7.7 MyCat主从切换架构与实现

7.7.1 MyCat主从切换概述

MyCat实现MySQL读写分离的目的在于降低单节点数据库的访问压力, 原理就是让主数据库执行增删改操作, 从数据库执行查询操作, 利用MySQL数据库的复制机制将Master的数据同步到slave上。
当master宕机后,slave承载的业务如何切换到master继续提供服务,以及slave宕机后如何将master切换到slave上。手动切换数据源很简单, 但不是运维工作的首选,本节重点就是讲解如何实现自动切换。
MyCat的读写分离依赖于MySQL的主从同步, 也就是说MyCat没有实现数据的主从同步功能, 但是实现了自动切换功能。
1). 自动切换
自动切换是MyCat主从复制的默认配置 , 当主机或从机宕机后, MyCat自动切换到可用的服务器上。 假设写服务器为M, 读服务器为S, 则:
正常时, 写M读S;
当M宕机后, 读写S ; 恢复M后, 写S, 读M ;
当S宕机后, 读写M ; 恢复S后, 写M, 读S ;
2). 基于MySQL主从同步状态的切换
这种切换方式与自动切换不同, MyCat检测到主从数据同步延迟时, 会自动切换到拥有最新数据的MySQL服务器上, 防止读到很久以前的数据。
原理就是通过检查MySQL的主从同步状态(show slave status)中的Seconds_Behind_Master、Slave_IO_Running、Slave_SQL_Running三个字段,来确定当前主从同步的状态以及主从之间的数据延迟。 Seconds_Behind_Master为0表示没有延迟,数值越大,则说明延迟越高。

7.7.2 MyCat主从切换实现

基于延迟的切换, 则判断结果集中的Slave_IO_Running、Slave_SQL_Running两个个字段是否都为yes,以及Seconds_Behind_Master 是否小于配置文件中配置的 slaveThreshold的值, 如果有其中任何一个条件不满足, 则切换。
主要流程如下:
MyCat-数据库中间件 - 图104

7.8 MyCat核心技术

5.2.1 MyCat分布式事务实现

MyCat在1.6版本以后已经支持XA分布式事务类型了。具体的使用流程如下:
1). 在应用层需要设置事务不能自动提交

  1. set autocommit=0;

2). 在SQL中设置XA为开启状态

  1. set xa = on;

3). 执行SQL

  1. insert into user(id,name,sex) values(1,'Tom','1'),(2,'Rose','2'),(3,'Leo','1'),(4,'Lee','1');

4). 对事务进行提交或回滚

  1. commit/rollback

完整流程如下:
MyCat-数据库中间件 - 图105

5.2.2 MyCat SQL路由实现

MyCat的路由是和SQL解析组件息息相关的, SQL路由模块是MyCat数据库中间件最重要的模块之一, 使用MyCat主要是为了分库分表, 而分库分表的核心就是路由。

5.2.2.1 路由的作用

MyCat-数据库中间件 - 图106
如图所示, MyCat接收到应用系统发来的查询语句, 要将其发送到后端连接的MySQL数据库去执行, 但是后端有三个数据库服务器,具体要查询那一台数据库服务器呢, 这就是路由需要实现的功能。
SQL的路由既要保证数据的完整 , 也不能造成资源的浪费, 还要保证路由的效率。

5.2.2.2 SQL解析器

Mycat1.3版本之前模式使用的是Fdbparser的foundationdb的开源SQL解析器,在2015年被apple收购后,从开源变为闭源了。
目前版本的MyCat采用的是Druid的SQL解析器, 性能比采用Fdbparser整体性能提高20%以上。

5.2.3 MyCat跨库Join

5.2.3.1 全局表

每个企业级的系统中, 都会存在一些系统的基础信息表, 类似于字典表、省份、城市、区域、语言表等, 这些表与业务表之间存在关系, 但不是业务主从关系,而是一种属性关系。
当我们对业务表进行分片处理时, 可以将这些基础信息表设置为全局表, 也就是在每个节点中都存在该表。
全局表的特性如下:
A. 全局表的insert、update、delete操作会实时地在所有节点同步执行, 保持各个节点数据的一致性
B. 全局表的查询操作会从任意节点执行,因为所有节点的数据都一致
C. 全局表可以和任意表进行join操作
MyCat-数据库中间件 - 图107

5.2.3.2 ER表

关系型数据库是基于实体关系模型(Entity Relationship Model)的, MyCat中的ER表便来源于此。 MyCat提出了基于ER关系的数据分片策略 , 子表的记录与其所关联的父表的记录存放在同一个数据分片中, 通过表分组(Table Group)保证数据关联查询不会跨库操作。
MyCat-数据库中间件 - 图108

5.2.3.3 catlet

catlet是MyCat为了解决跨分片Join提出的一种创新思路, 也叫做人工智能(HBT)。MyCat参考了数据库中存储过程的实现方式,提出类似的跨库解决方案,用户可以根据系统提供的API接口实现跨分片Join。
采用这种方案开发时,必须要实现Catlet接口的两个方法 :
MyCat-数据库中间件 - 图109
route 方法: 路由的方法, 传递系统配置和schema配置等 ;
processSQL方法: EngineCtx执行SQL并给客户端返回结果集 ;
当我们自定义Catlet完成之后, 需要将Catlet的实现类进行编译,并将其字节码文件 XXXCatlet.class存放在mycat_home/catlet目录下, 系统会加载相关Class, 而且每隔1分钟扫描一次文件是否更新, 若更新则自动重新加载,因此无需重启服务。
ShareJoin
ShareJoin 是Catlet的实现, 是一个简单的跨分片Join, 目前支持两个表的Join,原理就是解析SQL语句, 拆分成单表的语句执行, 单后把各个节点的数据进行汇集。
要想使用Catlet完成join, 还需要借助于MyCat中的注解, 在执行SQL语句时,使用catlet注解:

  1. /*!mycat:catlet=demo.catlets.ShareJoin */ select a.id as aid , a.id , b.id as bid , b.name as name from customer a, company b where a.company_id=b.id and a.id = 1;

5.2.4 MyCat数据汇聚与排序

通过MyCat实现数据汇聚和排序,不仅可以减少各分片与客户端之间的数据传输IO, 也可以帮助开发者总复杂的数据处理中解放出来,从而专注于开发业务代码。
在MySQL中存在两种排序方式: 一种利用有序索引获取有序数据, 另一种通过相应的排序算法将获取到的数据在内存中进行排序。 而MyCat中数据排序采用堆排序法对多个分片返回有序数据,并在合并、排序后再返回给客户端。
MyCat-数据库中间件 - 图110

8. MyCat综合案例

8.1 案例概述

8.1.1 案例介绍

本案例将模拟电商项目中的商品管理、订单管理、基础信息管理、日志管理模块,对整个系统中的数据表进行分片操作,将根据不同的业务需求,采用不同的分片方式 。

8.1.2 系统架构

MyCat-数据库中间件 - 图111
本案例涉及到的模块:
1). 商品微服务
2). 订单微服务
3). 日志微服务

8.1.3 技术选型

  • SpringBoot
  • SpringCloud
  • SpringMVC
  • Mybatis
  • SpringDataRedis
  • MySQL
  • Redis
  • Lombok

    8.2 案例需求

    1). 商品管理
    A. 添加商品
    B. 查询商品
    MyCat-数据库中间件 - 图112
    2). 订单管理
    A. 下订单
    B. 查询订单
    MyCat-数据库中间件 - 图113
    3). 日志管理
    A. 日志记录
    B. 日志查询
    MyCat-数据库中间件 - 图114

    8.3 案例环境搭建

    8.3.1 数据库

    1). 省份表 tb_provinces
Field Type Comment
provinceid varchar(20) 省份ID
province varchar(50) 省份名称

2). 市表 tb_cities

Field Type Comment
cityid varchar(20) 城市ID
city varchar(50) 城市名称
provinceid varchar(20) 省份ID

3). 区县表 tb_areas

Field Type Comment
areaid varchar(20) 区域ID
area varchar(50) 区域名称
cityid varchar(20) 城市ID

4). 商品分类表 tb_category

Field Type Comment
id int(20) 分类ID
name varchar(50) 分类名称
goods_num int(11) 商品数量
is_show char(1) 是否显示
is_menu char(1) 是否导航
seq int(11) 排序
parent_id int(20) 上级ID

5). 品牌表 tb_brand

Field Type Comment
id int(11) 品牌id
name varchar(100) 品牌名称
image varchar(1000) 品牌图片地址
letter char(1) 品牌的首字母
seq int(11) 排序

6). 商品SPU表 tb_spu

Field Type Comment
id varchar(20) 主键
sn varchar(60) 货号
name varchar(100) SPU名
caption varchar(100) 副标题
brand_id int(11) 品牌ID
category1_id int(20) 一级分类
category2_id int(10) 二级分类
category3_id int(10) 三级分类
template_id int(20) 模板ID
freight_id int(11) 运费模板id
image varchar(200) 图片
images varchar(2000) 图片列表
sale_service varchar(50) 售后服务
introduction text 介绍
spec_items varchar(3000) 规格列表
para_items varchar(3000) 参数列表
sale_num int(11) 销量
comment_num int(11) 评论数
is_marketable char(1) 是否上架
is_enable_spec char(1) 是否启用规格
is_delete char(1) 是否删除
status char(1) 审核状态

7). 商品SKU表 tb_sku

Field Type Comment
id varchar(20) 商品id
sn varchar(100) 商品条码
name varchar(200) SKU名称
price int(20) 价格(分)
num int(10) 库存数量
alert_num int(11) 库存预警数量
image varchar(200) 商品图片
images varchar(2000) 商品图片列表
weight int(11) 重量(克)
create_time datetime 创建时间
update_time datetime 更新时间
spu_id varchar(20) SPUID
category_id int(10) 类目ID
category_name varchar(200) 类目名称
brand_name varchar(100) 品牌名称
spec varchar(200) 规格
sale_num int(11) 销量
comment_num int(11) 评论数
status char(1) 商品状态 1-正常,2-下架,3-删除
version int(255)

8). 订单表 tb_order

Field Type Comment
id varchar(200) 订单id
total_num int(11) 数量合计
total_money int(11) 金额合计
pre_money int(11) 优惠金额
post_fee int(11) 邮费
pay_money int(11) 实付金额
pay_type varchar(1) 支付类型,1、在线支付、0 货到付款
create_time datetime 订单创建时间
update_time datetime 订单更新时间
pay_time datetime 付款时间
consign_time datetime 发货时间
end_time datetime 交易完成时间
close_time datetime 交易关闭时间
shipping_name varchar(20) 物流名称
shipping_code varchar(20) 物流单号
username varchar(50) 用户名称
buyer_message varchar(1000) 买家留言
buyer_rate char(1) 是否评价
receiver_contact varchar(50) 收货人
receiver_mobile varchar(12) 收货人手机
receiver_province varchar(200) 收货人省份
receiver_city varchar(200) 收货人市
receiver_area varchar(200) 收货人区/县
receiver_address varchar(200) 收货人具体街道地址
source_type char(1) 订单来源:1:web,2:app,3:微信公众号,4:微信小程序 5 H5手机页面
transaction_id varchar(30) 交易流水号
order_status char(1) 订单状态
pay_status char(1) 支付状态 0:未支付 1:已支付
consign_status char(1) 发货状态 0:未发货 1:已发货 2:已送达
is_delete char(1) 是否删除

9). 订单明细表 tb_order_item

Field Type Comment
id varchar(200) ID
category_id1 int(11) 1级分类
category_id2 int(11) 2级分类
category_id3 int(11) 3级分类
spu_id varchar(200) SPU_ID
sku_id varchar(200) SKU_ID
order_id varchar(200) 订单ID
name varchar(200) 商品名称
price int(20) 单价
num int(10) 数量
money int(20) 总金额
pay_money int(11) 实付金额
image varchar(200) 图片地址
weight int(11) 重量
post_fee int(11) 运费
is_return char(1) 是否退货

10). 订单日志表 tb_order_log

Field Type Comment
id varchar(20) ID
operater varchar(50) 操作员
operate_time datetime 操作时间
order_id bigint(20) 订单ID
order_status char(1) 订单状态
pay_status char(1) 付款状态
consign_status char(1) 发货状态
remarks varchar(100) 备注

11). 操作日志表 tb_operatelog

Field Type Comment
id bigint(20) ID
model_name varchar(200) 模块名
model_value varchar(200) 模块值
return_value varchar(200) 返回值
return_class varchar(200) 返回值类型
operate_user varchar(20) 操作用户
operate_time varchar(20) 操作时间
param_and_value varchar(500) 请求参数名及参数值
operate_class varchar(200) 操作类
operate_method varchar(200) 操作方法
cost_time bigint(20) 执行方法耗时, 单位 ms

12). 字典表 tb_dictionary

Field Type Comment
id int(11) 主键ID , 自增
codeid int(11) 码表ID
codetype varchar(2) 码值类型
codename varchar(50) 名称
codevalue varchar(50) 码值
description varchar(100) 描述
createtime datetime 创建时间
updatetime datetime 修改时间
createuser int(11) 创建人
updateuser int(11) 修改人

8.3.2 工程预览

MyCat-数据库中间件 - 图115

  1. spring-boot-starter-parent
  2. |- v_parent --------------------> 父工程, 统一管理依赖版本
  3. |- v_common ----------------> 通用工程, 存放通用的工具类及组件
  4. |- v_model -----------------> 实体类
  5. |- v_eureka ----------------> 注册中心
  6. |- v_feign_api -------------> feign远程调用的客户端接口
  7. |- v_gateway ---------------> 网关工程
  8. |- v_manage_web ------------> 模拟前端工程
  9. |- v_service_goods ---------> 商品微服务
  10. |- v_service_log -----------> 日志微服务
  11. |- v_service_order ---------> 订单微服务

8.3.3 工程层级关系

MyCat-数据库中间件 - 图116

8.3.4 父工程搭建

工程名: v_parent
pom.xml

  1. <!-- springBoot项目需要集成自父工程 -->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.1.4.RELEASE</version>
  6. </parent>
  7. <properties>
  8. <skipTests>true</skipTests>
  9. </properties>
  10. <!--依赖包-->
  11. <dependencies>
  12. <!--测试包-->
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-test</artifactId>
  16. <scope>test</scope>
  17. </dependency>
  18. </dependencies>
  19. <dependencyManagement>
  20. <dependencies>
  21. <dependency>
  22. <groupId>org.springframework.cloud</groupId>
  23. <artifactId>spring-cloud-dependencies</artifactId>
  24. <version>Greenwich.SR1</version>
  25. <type>pom</type>
  26. <scope>import</scope>
  27. </dependency>
  28. <!--MySQL数据库驱动-->
  29. <dependency>
  30. <groupId>mysql</groupId>
  31. <artifactId>mysql-connector-java</artifactId>
  32. <version>5.1.47</version>
  33. </dependency>
  34. <!--mybatis分页插件-->
  35. <dependency>
  36. <groupId>com.github.pagehelper</groupId>
  37. <artifactId>pagehelper-spring-boot-starter</artifactId>
  38. <version>1.2.3</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>com.alibaba</groupId>
  42. <artifactId>fastjson</artifactId>
  43. <version>1.2.51</version>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.projectlombok</groupId>
  47. <artifactId>lombok</artifactId>
  48. <version>1.18.6</version>
  49. </dependency>
  50. </dependencies>
  51. </dependencyManagement>

8.3.5 基础工程搭建

1). v_model
该基础工程中存放的是与数据库对应的实体类 ;
A. pom.xml

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.projectlombok</groupId>
  4. <artifactId>lombok</artifactId>
  5. </dependency>
  6. </dependencies>

B. 导入实体类
2). v_common
该基础工程中存放的是通用的组件及工具类 , 比如 分页实体类, 结果实体类, 状态码 等
直接导入资料中提供的基础组件和工具类 ;
3). v_feign_api
该工程中, 主要存放的是Feign远程调用的客户端接口;
pom.xml

  1. <dependencies>
  2. <!--web起步依赖-->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-web</artifactId>
  6. </dependency>
  7. <!-- Feign起步依赖 -->
  8. <dependency>
  9. <groupId>org.springframework.cloud</groupId>
  10. <artifactId>spring-cloud-starter-openfeign</artifactId>
  11. </dependency>
  12. <dependency>
  13. <groupId>cn.itcast</groupId>
  14. <artifactId>v_common</artifactId>
  15. <version>1.0-SNAPSHOT</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>cn.itcast</groupId>
  19. <artifactId>v_model</artifactId>
  20. <version>1.0-SNAPSHOT</version>
  21. </dependency>
  22. </dependencies>

8.3.6 Eureka Server搭建

1). pom.xml

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5. </dependency>
  6. </dependencies>

2). 引导类

  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class EurekaApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaApplication.class,args);
  6. }
  7. }

3). application.yml

  1. spring:
  2. application:
  3. name: eureka
  4. server:
  5. port: 8161
  6. eureka:
  7. client:
  8. register-with-eureka: false #是否将自己注册到eureka中
  9. fetch-registry: false #是否从eureka中获取信息
  10. service-url:
  11. defaultZone: http://127.0.0.1:${server.port}/eureka/
  12. server:
  13. enable-self-preservation: true

8.3.7 GateWay 网关搭建

1). pom.xml

  1. <!--网关依赖-->
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.cloud</groupId>
  5. <artifactId>spring-cloud-starter-gateway</artifactId>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework.cloud</groupId>
  9. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  10. </dependency>
  11. </dependencies>

2). 引导类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. public class GateWayApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(GateWayApplication.class,args);
  6. }
  7. }

3). application.yml

  1. server:
  2. port: 8001
  3. eureka:
  4. client:
  5. service-url:
  6. defaultZone: http://127.0.0.1:8161/eureka
  7. instance:
  8. prefer-ip-address: true
  9. spring:
  10. application:
  11. name: gateway
  12. cloud:
  13. gateway:
  14. routes:
  15. - id: v_goods_route
  16. uri: lb://goods
  17. predicates:
  18. - Path=/goods/**
  19. filters:
  20. - StripPrefix=1
  21. - id: v_order_route
  22. uri: lb://order
  23. predicates:
  24. - Path=/order/**
  25. filters:
  26. - StripPrefix=1

4). Cors配置类

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.cors.CorsConfiguration;
  4. import org.springframework.web.cors.reactive.CorsWebFilter;
  5. import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
  6. import org.springframework.web.util.pattern.PathPatternParser;
  7. @Configuration
  8. public class CorsConfig {
  9. @Bean
  10. public CorsWebFilter corsFilter(){
  11. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
  12. source.registerCorsConfiguration("/**", buildConfig());
  13. return new CorsWebFilter(source);
  14. }
  15. private CorsConfiguration buildConfig(){
  16. CorsConfiguration corsConfiguration = new CorsConfiguration();
  17. //在生产环境上最好指定域名,以免产生跨域安全问题
  18. corsConfiguration.addAllowedOrigin("*");
  19. corsConfiguration.addAllowedHeader("*");
  20. corsConfiguration.addAllowedMethod("*");
  21. return corsConfiguration;
  22. }
  23. }

8.4 功能开发

8.4.1 商品管理模块

需求 :
1). 根据ID查询商品SPU信息;
2). 根据条件查询商品SPU列表;
3). 根据ID查询商品SKU信息;
MyCat-数据库中间件 - 图117
概念:
1). SPU = Standard Product Unit (标准产品单位)
概念 : SPU 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
通俗点讲,属性值、特性相同的货品就可以称为一个 SPU
例如:华为P30 就是一个 SPU
2). SKU=stock keeping unit( 库存量单位)
SKU 即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。在服装、鞋类商品中使用最多最普遍。
例如:红色 64G 全网通 的华为P30 就是一个 SKU

8.4.1.1 创建工程

pom.xml

  1. <dependencies>
  2. <!-- Eureka客户端依赖 -->
  3. <dependency>
  4. <groupId>org.springframework.cloud</groupId>
  5. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  6. </dependency>
  7. <!--MySQL数据库驱动-->
  8. <dependency>
  9. <groupId>mysql</groupId>
  10. <artifactId>mysql-connector-java</artifactId>
  11. </dependency>
  12. <!--mybatis分页插件-->
  13. <dependency>
  14. <groupId>com.github.pagehelper</groupId>
  15. <artifactId>pagehelper-spring-boot-starter</artifactId>
  16. </dependency>
  17. <!--web起步依赖-->
  18. <dependency>
  19. <groupId>org.springframework.boot</groupId>
  20. <artifactId>spring-boot-starter-web</artifactId>
  21. </dependency>
  22. <!-- redis 使用-->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-data-redis</artifactId>
  26. </dependency>
  27. <!-- fastJson依赖 -->
  28. <dependency>
  29. <groupId>com.alibaba</groupId>
  30. <artifactId>fastjson</artifactId>
  31. </dependency>
  32. <!-- Feign依赖 -->
  33. <dependency>
  34. <groupId>org.springframework.cloud</groupId>
  35. <artifactId>spring-cloud-starter-openfeign</artifactId>
  36. </dependency>
  37. <dependency>
  38. <groupId>cn.itcast</groupId>
  39. <artifactId>v_common</artifactId>
  40. <version>1.0-SNAPSHOT</version>
  41. </dependency>
  42. <dependency>
  43. <groupId>cn.itcast</groupId>
  44. <artifactId>v_model</artifactId>
  45. <version>1.0-SNAPSHOT</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>cn.itcast</groupId>
  49. <artifactId>v_feign_api</artifactId>
  50. <version>1.0-SNAPSHOT</version>
  51. </dependency>
  52. </dependencies>

application.yml

  1. server:
  2. port: 9001
  3. spring:
  4. application:
  5. name: goods
  6. datasource:
  7. driver-class-name: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/v_shop?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  9. username: root
  10. password: 2143
  11. main:
  12. allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
  13. eureka:
  14. client:
  15. fetch-registry: true
  16. register-with-eureka: true
  17. service-url:
  18. defaultZone: http://127.0.0.1:8161/eureka
  19. instance:
  20. prefer-ip-address: true

引导类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @EnableFeignClients(basePackages = "cn.itcast.feign")
  4. @MapperScan("cn.itcast.goods.mapper")
  5. public class GoodsApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(GoodsApplication.class,args);
  8. }
  9. }

8.4.1.2 Mapper

1). mapper接口定义

  1. public interface SpuMapper {
  2. public TbSpu findById(String spuId);
  3. public List<TbSpu> search(Map<String,Object> searchMap);
  4. }
  1. public interface SkuMapper {
  2. //根据ID查询SKU
  3. public TbSku findById(String skuId);
  4. }

2). mapper映射配置文件
SpuMapper.xml

  1. <mapper namespace="cn.itcast.goods.mapper.SpuMapper" >
  2. <resultMap id="spuResultMap" type="cn.itcast.model.TbSpu">
  3. <id column="id" jdbcType="VARCHAR" property="id" />
  4. <result column="sn" jdbcType="VARCHAR" property="sn" />
  5. <result column="name" jdbcType="VARCHAR" property="name" />
  6. <result column="caption" jdbcType="VARCHAR" property="caption" />
  7. <result column="brand_id" jdbcType="INTEGER" property="brandId" />
  8. <result column="category1_id" jdbcType="INTEGER" property="category1Id" />
  9. <result column="category2_id" jdbcType="INTEGER" property="category2Id" />
  10. <result column="category3_id" jdbcType="INTEGER" property="category3Id" />
  11. <result column="template_id" jdbcType="INTEGER" property="templateId" />
  12. <result column="freight_id" jdbcType="INTEGER" property="freightId" />
  13. <result column="image" jdbcType="VARCHAR" property="image" />
  14. <result column="images" jdbcType="VARCHAR" property="images" />
  15. <result column="sale_service" jdbcType="VARCHAR" property="saleService" />
  16. <result column="spec_items" jdbcType="VARCHAR" property="specItems" />
  17. <result column="para_items" jdbcType="VARCHAR" property="paraItems" />
  18. <result column="sale_num" jdbcType="INTEGER" property="saleNum" />
  19. <result column="comment_num" jdbcType="INTEGER" property="commentNum" />
  20. <result column="is_marketable" jdbcType="CHAR" property="isMarketable" />
  21. <result column="is_enable_spec" jdbcType="CHAR" property="isEnableSpec" />
  22. <result column="is_delete" jdbcType="CHAR" property="isDelete" />
  23. <result column="status" jdbcType="CHAR" property="status" />
  24. </resultMap>
  25. <select id="findById" parameterType="java.lang.String" resultMap="spuResultMap">
  26. select
  27. *
  28. from
  29. tb_spu
  30. where
  31. id = #{spuId}
  32. </select>
  33. <select id="search" resultMap="spuResultMap">
  34. select * from tb_spu
  35. <where>
  36. <if test="name != null and name != ''">
  37. and name like '%${name}%'
  38. </if>
  39. <if test="caption != null and caption != ''" >
  40. and caption like '%${caption}%'
  41. </if>
  42. <if test="brandId != null">
  43. and brand_id = #{brandId}
  44. </if>
  45. <if test="status != null and status != ''">
  46. and status = #{status}
  47. </if>
  48. </where>
  49. </select>
  50. </mapper>

SkuMapper.xml

  1. <mapper namespace="cn.itcast.goods.mapper.SkuMapper" >
  2. <resultMap id="skuResultMap" type="cn.itcast.model.TbSku">
  3. <id column="id" jdbcType="VARCHAR" property="id" />
  4. <result column="sn" jdbcType="VARCHAR" property="sn" />
  5. <result column="name" jdbcType="VARCHAR" property="name" />
  6. <result column="price" jdbcType="INTEGER" property="price" />
  7. <result column="num" jdbcType="INTEGER" property="num" />
  8. <result column="alert_num" jdbcType="INTEGER" property="alertNum" />
  9. <result column="image" jdbcType="VARCHAR" property="image" />
  10. <result column="images" jdbcType="VARCHAR" property="images" />
  11. <result column="weight" jdbcType="INTEGER" property="weight" />
  12. <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
  13. <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  14. <result column="spu_id" jdbcType="VARCHAR" property="spuId" />
  15. <result column="category_id" jdbcType="INTEGER" property="categoryId" />
  16. <result column="category_name" jdbcType="VARCHAR" property="categoryName" />
  17. <result column="brand_name" jdbcType="VARCHAR" property="brandName" />
  18. <result column="spec" jdbcType="VARCHAR" property="spec" />
  19. <result column="sale_num" jdbcType="INTEGER" property="saleNum" />
  20. <result column="comment_num" jdbcType="INTEGER" property="commentNum" />
  21. <result column="status" jdbcType="CHAR" property="status" />
  22. <result column="version" jdbcType="INTEGER" property="version" />
  23. </resultMap>
  24. <select id="findById" parameterType="java.lang.String" resultMap="skuResultMap">
  25. select * from tb_sku where id = #{skuId}
  26. </select>
  27. </mapper>

8.4.1.3 Service

1). 接口定义

  1. public interface SkuService {
  2. /**
  3. * 根据ID查询SKU
  4. * @param skuId
  5. * @return
  6. */
  7. public TbSku findById(String skuId);
  8. }
  1. public interface SpuService {
  2. /**
  3. * 根据ID查询
  4. * @param id
  5. * @return
  6. */
  7. TbSpu findById(String id);
  8. /***
  9. * 多条件分页查询
  10. * @param searchMap
  11. * @param page
  12. * @param size
  13. * @return
  14. */
  15. Page<TbSpu> findPage(Map<String, Object> searchMap, int page, int size);
  16. }

2).接口实现

  1. @Service
  2. public class SkuServiceImpl implements SkuService {
  3. @Autowired
  4. private SkuMapper skuMapper;
  5. @Autowired
  6. private RedisTemplate redisTemplate;
  7. @Override
  8. public TbSku findById(String skuId) {
  9. return skuMapper.findById(skuId);
  10. }
  11. }
  1. @Service
  2. public class SpuServiceImpl implements SpuService {
  3. @Autowired
  4. private SpuMapper spuMapper;
  5. /**
  6. * 根据ID查询
  7. * @param id
  8. * @return
  9. */
  10. @Override
  11. public TbSpu findById(String id){
  12. return spuMapper.findById(id);
  13. }
  14. /**
  15. * 条件+分页查询
  16. * @param searchMap 查询条件
  17. * @param page 页码
  18. * @param size 页大小
  19. * @return 分页结果
  20. */
  21. @Override
  22. public Page<TbSpu> findPage(Map<String,Object> searchMap, int page, int size){
  23. PageHelper.startPage(page,size);
  24. return (Page<TbSpu>) spuMapper.search(searchMap);
  25. }
  26. }

8.4.1.4 Controller
  1. @RestController
  2. @CrossOrigin(origins = "*")
  3. @RequestMapping("/sku")
  4. public class SkuController {
  5. @Autowired
  6. private SkuService skuService;
  7. /***
  8. * 根据ID查询数据
  9. * @param id
  10. * @return
  11. */
  12. @GetMapping("/{id}")
  13. public Result<TbSku> findById(@PathVariable("id") String id){
  14. TbSku sku = skuService.findById(id);
  15. return new Result(true,StatusCode.OK,"查询成功",sku);
  16. }
  17. }
  1. @RestController
  2. @CrossOrigin(origins = "*")
  3. @RequestMapping("/spu")
  4. public class SpuController {
  5. @Autowired
  6. private SpuService spuService;
  7. /***
  8. * 根据ID查询数据
  9. * @param id
  10. * @return
  11. */
  12. @GetMapping("/{id}")
  13. public Result<TbSpu> findById(@PathVariable("id") String id){
  14. TbSpu spu = spuService.findById(id);
  15. return new Result(true,StatusCode.OK,"查询成功",spu);
  16. }
  17. /***
  18. * 分页搜索实现
  19. * @param searchMap
  20. * @param page
  21. * @param size
  22. * @return
  23. */
  24. @PostMapping(value = "/search/{page}/{size}" )
  25. public Result<TbSpu> findPage(@RequestBody Map searchMap, @PathVariable Integer page, @PathVariable Integer size){
  26. com.github.pagehelper.Page<TbSpu> pageList = spuService.findPage(searchMap, page, size);
  27. PageResult pageResult=new PageResult<TbSpu>(pageList.getTotal(),pageList.getResult());
  28. return new Result<TbSpu>(true,StatusCode.OK,"查询成功",pageResult);
  29. }
  30. }

8.4.2 订单模块

需求:
1). 下单业务分析
2). 根据条件分页查询订单
表结构:
tb_order , tb_order_item , tb_order_log

8.4.2.1 创建工程

1).pom.xml

  1. <dependencies
  2. <dependency>
  3. <groupId>cn.itcast</groupId>
  4. <artifactId>v_common</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>cn.itcast</groupId>
  9. <artifactId>v_model</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>cn.itcast</groupId>
  14. <artifactId>v_feign_api</artifactId>
  15. <version>1.0-SNAPSHOT</version>
  16. </dependency>
  17. <!-- Eureka客户端依赖 -->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  21. </dependency>
  22. <!-- springboot - Mybatis 起步依赖 -->
  23. <dependency>
  24. <groupId>org.mybatis.spring.boot</groupId>
  25. <artifactId>mybatis-spring-boot-starter</artifactId>
  26. <version>2.1.0</version>
  27. </dependency>
  28. <!--MySQL数据库驱动-->
  29. <dependency>
  30. <groupId>mysql</groupId>
  31. <artifactId>mysql-connector-java</artifactId>
  32. </dependency>
  33. <!--mybatis分页插件-->
  34. <dependency>
  35. <groupId>com.github.pagehelper</groupId>
  36. <artifactId>pagehelper-spring-boot-starter</artifactId>
  37. </dependency>
  38. <!--web起步依赖-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-web</artifactId>
  42. </dependency>
  43. <!-- redis 使用-->
  44. <dependency>
  45. <groupId>org.springframework.boot</groupId>
  46. <artifactId>spring-boot-starter-data-redis</artifactId>
  47. </dependency>
  48. <!-- fastJson依赖 -->
  49. <dependency>
  50. <groupId>com.alibaba</groupId>
  51. <artifactId>fastjson</artifactId>
  52. </dependency>
  53. <!-- Feign依赖 -->
  54. <dependency>
  55. <groupId>org.springframework.cloud</groupId>
  56. <artifactId>spring-cloud-starter-openfeign</artifactId>
  57. </dependency>
  58. </dependencies>

2). application.yml

  1. server:
  2. port: 9002
  3. spring:
  4. application:
  5. name: order
  6. datasource:
  7. driver-class-name: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/v_shop?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  9. username: root
  10. password: 2143
  11. main:
  12. allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
  13. eureka:
  14. client:
  15. fetch-registry: true
  16. register-with-eureka: true
  17. service-url:
  18. defaultZone: http://127.0.0.1:8161/eureka
  19. instance:
  20. prefer-ip-address: true
  21. feign:
  22. client:
  23. config:
  24. default: #配置全局的feign的调用超时时间 如果 有指定的服务配置 默认的配置不会生效
  25. connectTimeout: 60000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接 单位是毫秒
  26. readTimeout: 20000 # 指定的是调用服务提供者的 服务 的超时时间() 单位是毫秒

3). 引导类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @MapperScan(basePackages = "cn.itcast.order.mapper")
  4. public class OrderApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(OrderApplication.class,args);
  7. }
  8. }

8.4.2.2 下单业务分析

MyCat-数据库中间件 - 图118

8.4.2.3 查询订单

MyCat-数据库中间件 - 图119

8.4.2.3.1 Mapper

1). mapper接口

  1. public interface OrderMapper {
  2. public List<TbOrder> search(Map<String,Object> searchMap);
  3. }

2). mapper映射配置文件
OrderMapper.xml

  1. <mapper namespace="cn.itcast.order.mapper.OrderMapper" >
  2. <resultMap id="orderResultMap" type="cn.itcast.model.TbOrder">
  3. <id column="id" jdbcType="VARCHAR" property="id" />
  4. <result column="total_num" jdbcType="INTEGER" property="totalNum" />
  5. <result column="total_money" jdbcType="INTEGER" property="totalMoney" />
  6. <result column="pre_money" jdbcType="INTEGER" property="preMoney" />
  7. <result column="post_fee" jdbcType="INTEGER" property="postFee" />
  8. <result column="pay_money" jdbcType="INTEGER" property="payMoney" />
  9. <result column="pay_type" jdbcType="VARCHAR" property="payType" />
  10. <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
  11. <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  12. <result column="pay_time" jdbcType="TIMESTAMP" property="payTime" />
  13. <result column="consign_time" jdbcType="TIMESTAMP" property="consignTime" />
  14. <result column="end_time" jdbcType="TIMESTAMP" property="endTime" />
  15. <result column="close_time" jdbcType="TIMESTAMP" property="closeTime" />
  16. <result column="shipping_name" jdbcType="VARCHAR" property="shippingName" />
  17. <result column="shipping_code" jdbcType="VARCHAR" property="shippingCode" />
  18. <result column="username" jdbcType="VARCHAR" property="username" />
  19. <result column="buyer_message" jdbcType="VARCHAR" property="buyerMessage" />
  20. <result column="buyer_rate" jdbcType="CHAR" property="buyerRate" />
  21. <result column="receiver_contact" jdbcType="VARCHAR" property="receiverContact" />
  22. <result column="receiver_mobile" jdbcType="VARCHAR" property="receiverMobile" />
  23. <result column="receiver_province" jdbcType="VARCHAR" property="receiverProvince" />
  24. <result column="receiver_city" jdbcType="VARCHAR" property="receiverCity" />
  25. <result column="receiver_area" jdbcType="VARCHAR" property="receiverArea" />
  26. <result column="receiver_address" jdbcType="VARCHAR" property="receiverAddress" />
  27. <result column="source_type" jdbcType="CHAR" property="sourceType" />
  28. <result column="transaction_id" jdbcType="VARCHAR" property="transactionId" />
  29. <result column="order_status" jdbcType="CHAR" property="orderStatus" />
  30. <result column="pay_status" jdbcType="CHAR" property="payStatus" />
  31. <result column="consign_status" jdbcType="CHAR" property="consignStatus" />
  32. <result column="is_delete" jdbcType="CHAR" property="isDelete" />
  33. </resultMap>
  34. <select id="search" resultType="cn.itcast.model.TbOrder">
  35. SELECT
  36. o.id ,
  37. o.`create_time` createTime,
  38. o.username ,
  39. o.`total_money` totalMoney,
  40. o.`total_num` totalNum,
  41. o.`pay_type` payType,
  42. o.`pay_status` payStatus,
  43. p.`province` receiverProvince
  44. FROM
  45. tb_order o , tb_provinces p
  46. WHERE
  47. o.receiver_province = p.provinceid
  48. <if test="orderId != null and orderId != ''">
  49. and o.id = #{orderId}
  50. </if>
  51. <if test="payType != null and payType != ''">
  52. and o.pay_type = #{payType}
  53. </if>
  54. <if test="username != null and username != ''">
  55. and o.username = #{username}
  56. </if>
  57. <if test="payStatus != null and payStatus != ''">
  58. and o.order_status = #{payStatus}
  59. </if>
  60. </select>
  61. </mapper>

8.4.2.3.2 Service

service接口

  1. public interface OrderService {
  2. /***
  3. * 新增
  4. * @param order
  5. */
  6. void add(TbOrder order);
  7. /***
  8. * 多条件分页查询
  9. * @param searchMap
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. Page<TbOrder> findPage(Map<String, Object> searchMap, int page, int size);
  15. }

service实现

  1. @Service
  2. public class OrderServiceImpl implements OrderService {
  3. @Autowired
  4. private OrderMapper orderMapper;
  5. @Autowired
  6. private OrderItemMapper orderItemMapper;
  7. @Autowired
  8. private IdWorker idWorker;
  9. /**
  10. * 增加
  11. * @param order
  12. */
  13. @Override
  14. public void add(TbOrder order){
  15. //1.获取购物车的相关数据(redis)
  16. //2.统计计算:总金额,总数量
  17. //3.填充订单数据并保存到tb_order
  18. //4.填充订单项数据并保存到tb_order_item
  19. //5.记录订单日志
  20. //6.扣减库存并增加销量
  21. //7.删除购物车数据(redis)
  22. }
  23. /**
  24. * 条件+分页查询
  25. * @param searchMap 查询条件
  26. * @param page 页码
  27. * @param size 页大小
  28. * @return 分页结果
  29. */
  30. @Override
  31. public Page<TbOrder> findPage(Map<String,Object> searchMap, int page, int size){
  32. PageHelper.startPage(page,size);
  33. return (Page<TbOrder>)orderMapper.search(searchMap);
  34. }
  35. }

8.4.2.3.3 Controller
  1. @RestController
  2. @CrossOrigin(value = {"*"})
  3. @RequestMapping("/order")
  4. public class OrderController {
  5. @Autowired
  6. private OrderService orderService;
  7. @PostMapping
  8. @OperateLog
  9. public Result add(@RequestBody TbOrder order){
  10. //获取登录人名称
  11. orderService.add(order);
  12. return new Result(true,StatusCode.OK,"提交成功");
  13. }
  14. /***
  15. * 分页搜索实现
  16. * @param searchMap
  17. * @param page
  18. * @param size
  19. * @return
  20. */
  21. @PostMapping(value = "/search/{page}/{size}" )
  22. @OperateLog
  23. public Result findPage(@RequestBody Map searchMap, @PathVariable Integer page, @PathVariable Integer size){
  24. Page<TbOrder> pageList = orderService.findPage(searchMap, page, size);
  25. PageResult pageResult=new PageResult(pageList.getTotal(),pageList.getResult());
  26. return new Result(true,StatusCode.OK,"查询成功",pageResult);
  27. }
  28. }

8.4.3 日志模块

表结构:
tb_operatelog
需求:
1). 记录日志
2). 查询日志
MyCat-数据库中间件 - 图120
MyCat-数据库中间件 - 图121

8.4.3.1 创建工程

1). pom.xml

  1. <dependencies>
  2. <dependency>
  3. <groupId>cn.itcast</groupId>
  4. <artifactId>v_common</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>cn.itcast</groupId>
  9. <artifactId>v_model</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>cn.itcast</groupId>
  14. <artifactId>v_feign_api</artifactId>
  15. <version>1.0-SNAPSHOT</version>
  16. </dependency>
  17. <!-- Eureka客户端依赖 -->
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.mybatis.spring.boot</groupId>
  24. <artifactId>mybatis-spring-boot-starter</artifactId>
  25. <version>2.1.0</version>
  26. </dependency>
  27. <!--MySQL数据库驱动-->
  28. <dependency>
  29. <groupId>mysql</groupId>
  30. <artifactId>mysql-connector-java</artifactId>
  31. </dependency>
  32. <!--mybatis分页插件-->
  33. <dependency>
  34. <groupId>com.github.pagehelper</groupId>
  35. <artifactId>pagehelper-spring-boot-starter</artifactId>
  36. </dependency>
  37. <!--web起步依赖-->
  38. <dependency>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-starter-web</artifactId>
  41. </dependency>
  42. <!-- fastJson依赖 -->
  43. <dependency>
  44. <groupId>com.alibaba</groupId>
  45. <artifactId>fastjson</artifactId>
  46. </dependency>
  47. <!-- Feign依赖 -->
  48. <dependency>
  49. <groupId>org.springframework.cloud</groupId>
  50. <artifactId>spring-cloud-starter-openfeign</artifactId>
  51. </dependency>
  52. </dependencies>

2). application.yml

  1. server:
  2. port: 9003
  3. spring:
  4. application:
  5. name: log
  6. datasource:
  7. driver-class-name: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/v_shop?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  9. username: root
  10. password: 2143
  11. main:
  12. allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
  13. eureka:
  14. client:
  15. fetch-registry: true
  16. register-with-eureka: true
  17. service-url:
  18. defaultZone: http://127.0.0.1:8161/eureka
  19. instance:
  20. prefer-ip-address: true

3). 引导类

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. @MapperScan(basePackages = "cn.itcast.log.mapper")
  4. public class LogApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(LogApplication.class,args);
  7. }
  8. @Bean
  9. public IdWorker idworker(){
  10. return new IdWorker(0,0);
  11. }
  12. }

分布式ID生成
snowflake是 Twitter 开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0 ;
MyCat-数据库中间件 - 图122
使用方式:

  1. IdWorker idWorker=new IdWorker(1,1);//0-31 , 0-31
  2. for(int i=0;i<10000;i++){
  3. long id = idWorker.nextId();
  4. System.out.println(id);
  5. }

8.4.3.2 Mapper

mapper接口

  1. public interface OperateLogMapper {
  2. public void insert(TbOperatelog operationLog);
  3. public List<TbOperatelog> search(Map searchMap);
  4. }

OperateLogMapper.xml

  1. <mapper namespace="cn.itcast.log.mapper.OperateLogMapper" >
  2. <resultMap id="operateLogResultMap" type="cn.itcast.model.TbOperatelog">
  3. <id column="id" jdbcType="BIGINT" property="id" />
  4. <result column="model_name" jdbcType="VARCHAR" property="modelName" />
  5. <result column="model_value" jdbcType="VARCHAR" property="modelValue" />
  6. <result column="return_value" jdbcType="VARCHAR" property="returnValue" />
  7. <result column="return_class" jdbcType="VARCHAR" property="returnClass" />
  8. <result column="operate_user" jdbcType="VARCHAR" property="operateUser" />
  9. <result column="operate_time" jdbcType="VARCHAR" property="operateTime" />
  10. <result column="param_and_value" jdbcType="VARCHAR" property="paramAndValue" />
  11. <result column="operate_class" jdbcType="VARCHAR" property="operateClass" />
  12. <result column="operate_method" jdbcType="VARCHAR" property="operateMethod" />
  13. <result column="cost_time" jdbcType="BIGINT" property="costTime" />
  14. </resultMap>
  15. <insert id="insert" parameterType="cn.itcast.model.TbOperatelog">
  16. insert into tb_operatelog (id, model_name, model_value,
  17. return_value, return_class, operate_user,
  18. operate_time, param_and_value, operate_class,
  19. operate_method, cost_time)
  20. values (#{id}, #{modelName}, #{modelValue},
  21. #{returnValue}, #{returnClass}, #{operateUser},
  22. #{operateTime}, #{paramAndValue}, #{operateClass},
  23. #{operateMethod}, #{costTime})
  24. </insert>
  25. <select id="search" resultMap="operateLogResultMap">
  26. select * from tb_operatelog
  27. <where>
  28. <if test="operateUser != null and operateUser != ''">
  29. and operate_user = #{operateUser}
  30. </if>
  31. <if test="operateMethod != null and operateMethod != ''">
  32. and operate_method = #{operateMethod}
  33. </if>
  34. <if test="returnClass != null and returnClass != ''">
  35. and return_class = #{returnClass}
  36. </if>
  37. <if test="costTime != null and costTime != '' ">
  38. and cost_time = #{costTime}
  39. </if>
  40. </where>
  41. </select>
  42. </mapper>

8.4.3.3 Service

接口

  1. public interface OperateLogService {
  2. public void insert(TbOperatelog operationLog);
  3. public Page<TbOperatelog> findPage(Map searchMap, Integer pageNum , Integer pageSize);
  4. }

实现

  1. @Service
  2. @Transactional
  3. public class OperateLogServiceImpl implements OperateLogService {
  4. @Autowired
  5. private OperateLogMapper operateLogMapper;
  6. public void insert(TbOperatelog operationLog){
  7. long id = idworker.nextId();
  8. operationLog.setId(id);
  9. operateLogMapper.insert(operationLog);
  10. }
  11. public Page<TbOperatelog> findPage(Map searchMap, Integer pageNum , Integer pageSize){
  12. System.out.println(searchMap);
  13. PageHelper.startPage(pageNum,pageSize);
  14. List<TbOperatelog> list = operateLogMapper.search(searchMap);
  15. return (Page<TbOperatelog>) list;
  16. }
  17. }

8.4.3.4 Controller
  1. @RestController
  2. @RequestMapping("/operateLog")
  3. public class OperateLogController {
  4. @Autowired
  5. private OperateLogService operateLogService;
  6. @RequestMapping("/search/{page}/{size}")
  7. public Result findList(@RequestBody Map dataMap, @PathVariable Integer page, @PathVariable Integer size){
  8. Page<TbOperatelog> pageList = operateLogService.findPage(dataMap, page, size);
  9. PageResult pageResult=new PageResult(pageList.getTotal(),pageList.getResult());
  10. return new Result(true, StatusCode.OK,"查询成功",pageResult);
  11. }
  12. @RequestMapping("/add")
  13. public Result add(@RequestBody TbOperatelog operatelog){
  14. operateLogService.insert(operatelog);
  15. return new Result(true,StatusCode.OK,"添加成功");
  16. }
  17. }

8.4.3.5 AOP记录日志

在需要记录操作日志的微服务中, 引入AOP记录日志的类 :
1). 自定义注解

  1. @Inherited
  2. @Documented
  3. @Target(ElementType.METHOD)
  4. @Retention(RetentionPolicy.RUNTIME)
  5. public @interface OperateLog {
  6. }

2). AOP通知类

  1. @Component
  2. @Aspect
  3. public class OperateAdvice {
  4. @Autowired
  5. private OperateLogFeign operateLogFeign;
  6. @Around("execution(* cn.itcast.goods.controller.*.*(..)) && @annotation(operateLog)")
  7. public Object insertLogAround(ProceedingJoinPoint pjp , OperateLog operateLog) throws Throwable{
  8. System.out.println(" *********************************** 记录日志 [start] ****************************** ");
  9. TbOperatelog op = new TbOperatelog();
  10. DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  11. op.setOperateTime(sdf.format(new Date()));
  12. op.setOperateUser("10000");
  13. op.setOperateClass(pjp.getTarget().getClass().getName());
  14. op.setOperateMethod(pjp.getSignature().getName());
  15. String paramAndValue = "";
  16. Object[] args = pjp.getArgs();
  17. if(args != null){
  18. for (Object arg : args) {
  19. if(arg instanceof String || arg instanceof Integer || arg instanceof Long){
  20. paramAndValue += arg +",";
  21. }else{
  22. paramAndValue += JSON.toJSONString(arg)+",";
  23. }
  24. }
  25. op.setParamAndValue(paramAndValue);
  26. }
  27. long start_time = System.currentTimeMillis();
  28. //放行
  29. Object object = pjp.proceed();
  30. long end_time = System.currentTimeMillis();
  31. op.setCostTime(end_time - start_time);
  32. if(object != null){
  33. op.setReturnClass(object.getClass().getName());
  34. op.setReturnValue(object.toString());
  35. }else{
  36. op.setReturnClass("java.lang.Object");
  37. op.setParamAndValue("void");
  38. }
  39. operateLogFeign.add(op);
  40. System.out.println(" *********************************** 记录日志 [end] ****************************** ");
  41. return object;
  42. }
  43. }

8.5 MyCat分片

当前数据库的情况 :
MyCat-数据库中间件 - 图123
由于当前项目是一个电商项目,项目上线后,随着项目的运营,业务系统的数据库中的数据与日俱增,特别是订单、日志等数据,如果数据量过大,这个时候就需要考虑通过MyCat分库分表。

8.5.1 分片分析

1). 垂直拆分
数据量过大,需要考虑扩容,可以通过MyCat来实现数据库表的垂直拆分,将同一块业务的数据库表拆分到同一个数据库服务中。拆分方式如下:
MyCat-数据库中间件 - 图124
2). 全局表
按照上述的方式进行表结构的拆分,可以解决扩容的问题,但是存在另一个问题:由于省、市、区县、数据字典表,在订单及商品等模块中都需要用到,还会涉及到多表连接查询,那么这个时候涉及到跨库的join操作,可以使用全局表来解决。结构图如下:
MyCat-数据库中间件 - 图125
3). 水平拆分
即使我们在上述的方案中使用垂直拆分,将系统中的表结构拆分到了三个数据库服务器中,但是对于当前这个比较繁忙的业务系统来说,每天都会产生大量的用户操作日志,长年累月,这张表的数据在单台服务器中已经存储不下了,这个时候,我们就可以使用MyCat的水平拆分来解决这个问题。
MyCat-数据库中间件 - 图126

8.5.2 服务器配置

名称 IP 端口 用户名/密码
MyCat-Server 192.168.192.157 8066 root/123456
MySQL-1 192.168.192.158 3306 root/itcast
MySQL-2 192.168.192.159 3306 root/itcast
MySQL-3 192.168.192.160 3306 root/itcast
MySQL-4 192.168.192.161 3306 root/itcast

8.5.3 schema.xml

  1. <?xml version="1.0"?>
  2. <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
  3. <mycat:schema xmlns:mycat="http://io.mycat/">
  4. <schema name="V_SHOP" checkSQLschema="false" sqlMaxLimit="100">
  5. <table name="tb_areas" dataNode="dn1,dn2,dn3,dn4" primaryKey="areaid" type="global"/>
  6. <table name="tb_provinces" dataNode="dn1,dn2,dn3,dn4" primaryKey="provinceid" type="global"/>
  7. <table name="tb_cities" dataNode="dn1,dn2,dn3,dn4" primaryKey="cityid" type="global"/>
  8. <table name="tb_dictionary" dataNode="dn1,dn2,dn3,dn4" primaryKey="id" type="global"/>
  9. <table name="tb_brand" dataNode="dn1" primaryKey="id" />
  10. <table name="tb_category" dataNode="dn1" primaryKey="id" />
  11. <table name="tb_sku" dataNode="dn1" primaryKey="id" />
  12. <table name="tb_spu" dataNode="dn1" primaryKey="id" />
  13. <table name="tb_order" dataNode="dn2" primaryKey="id" />
  14. <table name="tb_order_item" dataNode="dn2" primaryKey="id" />
  15. <table name="tb_order_log" dataNode="dn2" primaryKey="id" />
  16. <table name="tb_operatelog" dataNode="dn3,dn4" primaryKey="id" rule="log-sharding-by-murmur"/>
  17. </schema>
  18. <dataNode name="dn1" dataHost="host1" database="v_goods" />
  19. <dataNode name="dn2" dataHost="host2" database="v_order" />
  20. <dataNode name="dn3" dataHost="host3" database="v_log" />
  21. <dataNode name="dn4" dataHost="host4" database="v_log" />
  22. <dataHost name="host1" maxCon="1000" minCon="10" balance="0"
  23. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  24. <heartbeat>select user()</heartbeat>
  25. <writeHost host="hostM1" url="192.168.192.158:3306" user="root" password="itcast"> </writeHost>
  26. </dataHost>
  27. <dataHost name="host2" maxCon="1000" minCon="10" balance="0"
  28. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  29. <heartbeat>select user()</heartbeat>
  30. <writeHost host="hostM2" url="192.168.192.159:3306" user="root" password="itcast"></writeHost>
  31. </dataHost>
  32. <dataHost name="host3" maxCon="1000" minCon="10" balance="0"
  33. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  34. <heartbeat>select user()</heartbeat>
  35. <writeHost host="hostM3" url="192.168.192.160:3306" user="root" password="itcast"></writeHost>
  36. </dataHost>
  37. <dataHost name="host4" maxCon="1000" minCon="10" balance="0"
  38. writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
  39. <heartbeat>select user()</heartbeat>
  40. <writeHost host="hostM4" url="192.168.192.161:3306" user="root" password="itcast"></writeHost>
  41. </dataHost>
  42. </mycat:schema>

8.5.4 分片配置

1). 配置Mycat的schema.xml
2). 配置rule.xml

  1. <tableRule name="log-sharding-by-murmur">
  2. <rule>
  3. <columns>id</columns>
  4. <algorithm>log-murmur</algorithm>
  5. </rule>
  6. </tableRule>
  7. <function name="log-murmur" class="io.mycat.route.function.PartitionByMurmurHash">
  8. <property name="seed">0</property>
  9. <property name="count">2</property>
  10. <property name="virtualBucketTimes">160</property>
  11. </function>

3). 配置server.xml

  1. <user name="root" defaultAccount="true">
  2. <property name="password">GO0bnFVWrAuFgr1JMuMZkvfDNyTpoiGU7n/Wlsa151CirHQnANVk3NzE3FErx8v6pAcO0ctX3xFecmSr+976QA==</property>
  3. <property name="schemas">V_SHOP</property>
  4. <property name="readOnly">false</property>
  5. <property name="benchmark">1000</property>
  6. <property name="usingDecrypt">1</property>
  7. <!-- 表级 DML 权限设置
  8. <privileges check="true">
  9. <schema name="ITCAST" dml="1111" >
  10. <table name="TB_TEST" dml="1110"></table>
  11. </schema>
  12. </privileges>
  13. -->
  14. </user>

4). 在各个MySQL数据库实例中创建数据库

  1. MySQL-1 : v_goods
  2. MySQL-2 : v_order
  3. MySQL-3 : v_log
  4. MySQL-4 : v_log

5). 导出本地的SQL脚本 , 在MyCat中执行SQL脚本 , 创建数据表 ,并导入数据
6). 连接测试

8.5.5 微服务连接MyCat

  1. datasource:
  2. driver-class-name: com.mysql.jdbc.Driver
  3. url: jdbc:mysql://192.168.192.157:8066/V_SHOP?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  4. username: root
  5. password: 123456

8.5.6 配置MyCat-Web监控

1). 启动Zookeeper
2). 启动MyCat-Web
3). 访问
http://192.168.192.157:8082/mycat
界面:
MyCat-数据库中间件 - 图127