使用单个数据库存储所有的数据,如果磁盘和内存和内存不足了可以增大磁盘和内存,但是对于一台服务器的磁盘和内存不可能无限制的扩张下去,它是受我们服务器的硬件影响的,如果说数据库所存储的数据还在不断增加,那么会带来数据库的性能问题。而此时前端的程序还在持续频繁的去请求数据库,那么数据库的访问压力会变得非常大。
随着互联网及移动互联网的发展,应用系统的数据量也是成指数式增长,若采用单数据库进行数据存储,存在以下性能瓶颈:
IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较低。 请求数据太多,带宽不够,网络IO瓶颈。
CPU瓶颈:大量用户请求过来,用户在查询时所涉及到的排序、分组、连接查询、聚合统计等,这些SQL会耗费大量的CPU资源,请求数太多,CPU出现瓶颈。
分库分表的中心思想都是将数据分散存储,使得单一数据库/表的数据量变小来缓解单一数据库的性能 问题,从而达到提升数据库性能的目的。
分库分表的形式,主要是两种:垂直拆分和水平拆分。而拆分的粒度,一般又分为分库和分表,所以组成的拆分策略最终如下:
1.垂直分库
垂直分库
:以表为依据,根据业务将不同表拆分到不同库中。
特点:
2.垂直分表
垂直分表
:以字段为依据,根据字段属性将不同字段拆分到不同表中。
特点:
水平分库
:以字段为依据,按照一定策略,将一个库的数据拆分到多个库中。
特点:
水平分表
:以字段为依据,按照一定策略,将一个表的数据拆分到多个表中。
特点:
在业务系统中,为了缓解磁盘IO及CPU的性能瓶颈,到底是垂直拆分,还是水平拆分;具体是分库,还是分表,都需要根据具体的业务需求具体分析。
问题:
解决:
shardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析、改写、路由处理。需要自行编码配置实现,只支持java语言,性能较高。
MyCat:数据库分库分表中间件,不用调整代码即可实现分库分表,支持多种语言,性能不及前者。
什么叫数据库分库分表中间件:
本次课程,我们选择了是MyCat数据库中间件,通过MyCat中间件来完成分库分表操作。
Mycat(阿里的)是开源的、活跃的、基于Java语言编写的MySQL数据库中间件
。可以像使用mysql一样来使用mycat,对于开发人员来说根本感觉不到mycat的存在。
原理:MyCat伪装了mysql的协议,所以就可以把mycat看作成一台mysql。对于我们的应用程序来说,完全可以不关心你用的是mysql还是mycat,只需要把数据库的连接换为mycat就可以了,连驱动都不用换。
开发人员只需要连接MyCat即可,而具体底层用到几台数据库,每一台数据库服务器里面存储了什么数据,都无需关心。 具体的分库分表的策略,只需要在MyCat中配置即可。
优势:
官网: http://www.mycat.org.cn/
2版本出来没多久,所以这里选择比较稳定的版本1.6
Mycat是采用java语言开发的开源的数据库中间件,支持Windows和Linux运行环境,下面介绍MyCat的Linux中的环境搭建。我们需要在准备好的服务器中安装如下软件。
服务器 | 安装软件 | 说明 |
---|---|---|
192.168.10.210 | JDK、Mycat | MyCat中间件服务器 |
192.168.10.210 | MySQL | 分片服务器 |
192.168.10.213 | MySQL | 分片服务器 |
192.168.10.214 | MySQL | 分片服务器 |
占用磁盘太大了,测试完成后删了。
具体的安装步骤: 参考资料中提供的 《MyCat安装文档》即可,里面有详细的安装及配置步骤。mycat安装的博客地址
进入解压后生成的mycat目录:
进入存放jar包的目录,可以看到这个自带的mysql驱动版本的包比较低,我们现在使用的mysql版本是8.0.26,所以需要替换掉这个驱动包
cd lib
ll
在lib目录下删除此驱动包
rm -rf mysql-connector-java-5.1.35.jar
重新上传一份驱动包到lib目录
ll
查看,发现此jar包为红色,因为它的权限不够,所以还要进行授权。
chmod 777 mysql-connector-java-8.0.22.jar
#再次查看就变为绿色了
ll
在MyCat的整体结构中,分为两个部分:上面的逻辑结构、下面的物理结构。
在MyCat的逻辑结构主要负责逻辑库、逻辑表、分片规则、分片节点等逻辑结构的处理,而具体的数据存储还是在物理结构,也就是数据库服务器中存储的。
在后面讲解MyCat入门以及MyCat分片时,还会讲到上面所提到的概念。
由于 tb_order 表中数据量很大,磁盘IO及容量都到达了瓶颈,现在需要对 tb_order 表进行数据分片,分为三个数据节点,每一个节点主机位于不同的服务器上, 具体的结构,参考下图:
准备3台服务器:
并且在上述3台数据库中创建数据库 db01 。
1).schema.xml
在schema.xml中配置逻辑库、逻辑表、数据节点、节点主机等相关信息。具体的配置如下:
#存放在mycat解压后的conf目录
cd /usr/local/src/MyCat/mycat/conf/
#也可以直接在远程工具中使用记事本打开左侧的文件,这样更方便。
vim schema.xml
精简完毕后的内容
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
<table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
schema>
<dataNode name="dn1" dataHost="dhost1" database="db01" />
<dataNode name="dn2" dataHost="dhost2" database="db01" />
<dataNode name="dn3" dataHost="dhost3" database="db01" />
<dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.210:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.213:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.214:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
mycat:schema>
去掉注释后的内容:
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="DB01" checkSQLschema="true" sqlMaxLimit="100">
<table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
schema>
<dataNode name="dn1" dataHost="dhost1" database="db01" />
<dataNode name="dn2" dataHost="dhost2" database="db01" />
<dataNode name="dn3" dataHost="dhost3" database="db01" />
<dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.210:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.213:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.214:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
mycat:schema>
2).server.xml
需要在server.xml中配置用户名、密码,以及用户的访问权限信息,具体的配置如下:
同样在这个目录下:
cd /usr/local/src/MyCat/mycat/conf/
配置内容:其它的配置信息到下面讲解,这里只配置需要修改的部分内容
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">DB01property>
user>
<user name="user">
<property name="password">123456property>
<property name="schemas">DB01property>
<property name="readOnly">trueproperty>
user>
上述的配置表示,定义了两个用户 root 和 user ,这两个用户都可以访问 DB01 这个逻辑库,访问密码都是123456,但是root用户访问DB01逻辑库,既可以读,又可以写,但是 user用户访问DB01逻辑库是只读的。
配置完毕后,先启动涉及到的3台分片服务器,然后启动MyCat服务器。切换到Mycat的安装目录,执行如下指令,启动Mycat:
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#启动
bin/mycat start
#停止
bin/mycat stop
Mycat启动之后,占用端口号 8066。
启动完毕之后,可以查看logs目录下的启动日志,查看Mycat是否启动完成。
#在mycat目录下查看日志
tail -f logs/wrapper.log
1).连接MyCat
通过如下指令,就可以连接并登陆MyCat(用法和mysql相同
)。
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是DB01,他是在schema.xml文件中配置的逻辑库
show databases;
use DB01;
#显示tb_order(配置文件里是大写,这里显示小写),这张表目前只是在mycat中逻辑
# 存在的,在具体的数据库当中是没有的。
show tables;
我们看到我们是通过MySQL的指令来连接的MyCat,因为MyCat在底层实际上是模拟了MySQL的协议。
2).数据测试
然后就可以在MyCat中来创建表
,并往表结构中插入数据,查看数据在MySQL中的分布情况。
CREATE TABLE TB_ORDER (
id BIGINT(20) NOT NULL,
title VARCHAR(100) NOT NULL ,
PRIMARY KEY (id)
) ENGINE=INNODB DEFAULT CHARSET=utf8 ;
一旦在mycat中把这个表结构创建了,那么这个逻辑表对应的数据库表结构中的真实表就已经创建了,此时底层所涉及到的三个数据节点都有这张表了。(水平分表----表结构相同)
插入数据测试:
INSERT INTO TB_ORDER(id,title) VALUES(1,'goods1');
INSERT INTO TB_ORDER(id,title) VALUES(2,'goods2');
INSERT INTO TB_ORDER(id,title) VALUES(3,'goods3');
INSERT INTO TB_ORDER(id,title) VALUES(5000000,'goods5000000');
INSERT INTO TB_ORDER(id,title) VALUES(10000000,'goods10000000');
INSERT INTO TB_ORDER(id,title) VALUES(10000001,'goods10000001');
INSERT INTO TB_ORDER(id,title) VALUES(15000000,'goods15000000');
INSERT INTO TB_ORDER(id,title) VALUES(15000001,'goods15000001');
经过测试,我们发现,在往 TB_ORDER 表中插入数据时:
为什么会出现这种现象,数据到底落在哪一个分片服务器到底是如何决定的呢? 这是由逻辑表配置时的一个参数 rule 决定的,而这个参数配置的就是分片规则,关于分片规则的配置,在后面的课程中会详细讲解。
解释:
第二步上:在rule.xml可以看到这种分片规则是根据id字段进行分片的,具体是通过下面这个rang-long算法决定的
第三步:这个函数又管理了一个物理文件,autopartition-long.txt。
schema.xml、rule.xml、autopartition-long.txt都在/usr/local/src/MyCat/mycat/conf/目录下
schema.xml 作为MyCat中最重要的配置文件之一 , 涵盖了MyCat的逻辑库 、 逻辑表 、 分片规则、分片节点及数据源的配置。
主要包含以下三组标签:
1).schema 定义逻辑库
schema 标签用于定义 MyCat实例中的逻辑库 , 一个MyCat实例中, 可以有多个逻辑库 , 可以通过 schema 标签来划分不同的逻辑库。MyCat中的逻辑库的概念,等同于MySQL中的database概念, 需要操作某个逻辑库下的表时,也需要切换逻辑库(use xxx)。
核心属性:
name:指定自定义的逻辑库库名(注意大小写)
checkSQLschema:在SQL语句操作时指定了数据库名称,执行时是否自动去除;true:自动去 除,false:不自动去除
sqlMaxLimit:如果未指定limit进行查询,列表查询模式最多查询多少条记录
2).schema 中的table定义逻辑表
table 标签定义了MyCat中逻辑库schema下的逻辑表 ,一个逻辑库下面可以配置多个逻辑表,所有需要拆分的表都需要在table标签中定义 。
核心属性:
用来配置数据节点
核心属性:
该标签在MyCat逻辑库中作为底层标签存在, 直接定义了具体的数据库实例、读写分离、心跳语句。
核心属性:
具体到读写分离时讲解
)rule.xml中定义所有拆分表的规则,在使用过程中可以灵活的使用分片算法,或者对同一个分片算法使用不同的参数,它让分片过程可配置化。主要包含两类标签:tableRule、Function。
在后面详解
)
server.xml配置文件包含了MyCat的系统配置信息,主要有两个重要的标签:system、user。
1). system标签
不作为重点关注
主要配置MyCat中的系统配置信息,对应的系统配置项及其含义,如下:
属性 | 取值 | 含义 |
---|---|---|
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 |
2). user标签
配置MyCat中的用户、访问密码,以及用户针对于逻辑库、逻辑表的权限信息,具体的权限描述方式及配置说明如下:
在测试权限操作时,我们只需要将 privileges 标签的注释放开。 在 privileges 下的schema 标签中配置的dml属性配置的是逻辑库的权限。 在privileges的schema下的table标签的dml属性中配置逻辑表的权限。(如果不配置就代表他可以对这个逻辑库当中的虽有的逻辑表进行任何操作。
)
测试完成后注释掉。
<privileges check="true">
<schema name="DB01" dml="1110" >
<table name="TB_ORDER" dml="1110"> table>
schema>
privileges>
在业务系统中,涉及以下表结构,但是由于用户与订单每天都会产生大量的数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分,原有的数据库表如下。
现在考虑将其进行垂直分库操作,将商品相关的表拆分到一个数据库服务器,订单表拆分的一个数据库服务器,用户及省市区表拆分到一个服务器。最终结构如下:
省市区相关的表里面存储的数据本来就不多,几千最多几万条数据,也就没有必要存放在一个单独的分区中,用户表里面有一个用户地址字段,里面存放的有省市区相关的编号,最终我在展示的时候还需要展示省市区的名字。所以考虑把用户和省市区相关的表放在一个分片当中。
准备三台服务器,IP地址如图所示:
并且在192.168.10.210,192.168.10.213,192.168.10.214上面创建数据库shopping
(3台都要创建)。
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_goods_base" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_brand" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_cat" dataNode="dn1" primaryKey="id" />
<table name="tb_goods_desc" dataNode="dn1" primaryKey="goods_id" />
<table name="tb_goods_item" dataNode="dn1" primaryKey="id" />
<table name="tb_order_item" dataNode="dn2" primaryKey="id" />
<table name="tb_order_master" dataNode="dn2" primaryKey="order_id" />
<table name="tb_order_pay_log" dataNode="dn2" primaryKey="out_trade_no" />
<table name="tb_user" dataNode="dn3" primaryKey="id" />
<table name="tb_user_address" dataNode="dn3" primaryKey="id" />
<table name="tb_areas_provinces" dataNode="dn3" primaryKey="id"/>
<table name="tb_areas_city" dataNode="dn3" primaryKey="id"/>
<table name="tb_areas_region" dataNode="dn3" primaryKey="id"/>
schema>
<dataNode name="dn1" dataHost="dhost1" database="shopping" />
<dataNode name="dn2" dataHost="dhost2" database="shopping" />
<dataNode name="dn3" dataHost="dhost3" database="shopping" />
<dataHost name="dhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.210:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.213:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
<dataHost name="dhost3" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.214:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234" />
dataHost>
mycat:schema>
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">SHOPPINGproperty>
user>
<user name="user">
<property name="password">123456property>
<property name="schemas">SHOPPINGproperty>
<property name="readOnly">trueproperty>
user>
1)mycat修改完配置文件后需要重新启动
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先把之前启动的mycat 关闭掉
bin/mycat stop
#启动
bin/mycat start
#在mycat目录下查看日志,是否启动成功
tail -f logs/wrapper.log
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use SHOPPING;
#显示所有的逻辑表,这些逻辑表只是在schema.xml中定义出来了,在逻辑上是有这些表的,
# 但是这些表在具体的物理节点中并不存在,所以接下来要创建具体的表结构
show tables;
3)上传测试SQL脚本到服务器的/root目录
4)执行指令导入测试数据
重新启动MyCat后,在mycat的命令行中,通过source指令导入表结构,以及对应的数据,查看数据分布情况。
#登录mycat并且切换了数据库之后执行
source /root/shopping-table.sql
source /root/shopping-insert.sql
将表结构及对应的测试数据导入之后,可以检查一下各个数据库服务器中的表结构分布情况。 检查是否和我们准备工作中规划的服务器一致,以及表中是否有数据。
5)查询用户的收件人及收件人地址信息(包含省、市、区)。
涉及到的表
在MyCat
的命令行中,当我们执行以下多表联查的SQL语句时,可以正常查询出数据。
SELECT ua.user_id, ua.contact, p.province, c.city, r.area , ua.address
FROM tb_user_address ua ,tb_areas_city c , tb_areas_provinces p ,tb_areas_region r
WHERE ua.province_id = p.provinceid AND ua.city_id = c.cityid AND ua.town_id = r.areaid ;
6)查询每一笔订单及订单的收件地址信息(包含省、市、区)。
涉及到的表
实现该需求对应的SQL语句如下:
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
WHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;
但是现在存在一个问题,订单相关的表结构是在 192.168.10.213 数据库服务器中,而省市区的数据库表是在 192.168.10.214 数据库服务器中。那么在MyCat中执行是否可以成功呢?
经过测试,我们看到,SQL语句执行报错。原因就是因为MyCat在执行该SQL语句时,需要往具体的数据库服务器中路由,而当前没有一个数据库服务器完全包含了订单以及省市区的表结构,造成SQL语句失败,报错。
对于上述的这种现象,我们如何来解决呢? 下面我们介绍的全局表,就可以轻松解决这个问题。
对于省、市、区/县表tb_areas_provinces , tb_areas_city , tb_areas_region,是属于数据字典表,在多个业务模块中都可能会遇到,可以将其设置为全局表,利于业务操作。
字典表:数据不多还不会变。
全局表:在每一个节点中都存在。
修改schema.xml中的逻辑表的配置,修改 tb_areas_provinces、tb_areas_city、tb_areas_region 三个逻辑表,增加 type 属性,配置为global,就代表该表是全局表,就会在所涉及到的dataNode中创建给表。对于当前配置来说,也就意味着所有的节点中都有该表了。
<table name="tb_areas_provinces" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_city" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_region" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
1)配置完毕后,先停止MyCat。
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#停止
bin/mycat stop
2)删除原来每一个数据库服务器中的所有表结构(即:把三个分片数据库shopping中的表都删除)
3)删除表结构之后,重新启动mycat
#启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
4)通过source指令,导入表及数据
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
use SHOPPING;
source /root/shopping-table.sql
source /root/shopping-insert.sql
5)检查每一个数据库服务器中的表及数据分布,看到三个节点中都有这三张全局表
6)然后再次执行上面的多表联查的SQL语句
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
WHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;
是可以正常执行成功的。
7)当在MyCat中更新全局表的时候,我们可以看到,所有分片节点中的数据都发生了变化,每个节点的全局表数据时刻保持一致。
update tb_areas_provinces set province = "北京" where id = 1;
在业务系统中,有一张表(日志表),业务系统每天都会产生大量的日志数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分。
并且,在三台数据库服务器中分表创建一个数据库itcast
。
3个都要创建
1).schema.xml
在schema.xml中增加水平分表的配置,上面垂直分库的配置不用删除。
<schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100">
<table name="tb_log" dataNode="dn4,dn5,dn6" primaryKey="id" rule="mod-long" />
schema>
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
tb_log表最终落在3个节点中,分别是 dn4、dn5、dn6 ,而具体的数据分别存储在 dhost1、dhost2、dhost3的itcast数据库中。
2).server.xml
配置root用户既可以访问 SHOPPING 逻辑库,又可以访问ITCAST逻辑库。
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">SHOPPING,ITCASTproperty>
user>
<user name="user">
<property name="password">123456property>
<property name="schemas">SHOPPINGproperty>
<property name="readOnly">trueproperty>
user>
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_log只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
CREATE TABLE tb_log (
id bigint(20) NOT NULL COMMENT 'ID',
model_name varchar(200) DEFAULT NULL COMMENT '模块名',
model_value varchar(200) DEFAULT NULL COMMENT '模块值',
return_value varchar(200) DEFAULT NULL COMMENT '返回值',
return_class varchar(200) DEFAULT NULL COMMENT '返回值类型',
operate_user varchar(20) DEFAULT NULL COMMENT '操作用户',
operate_time varchar(20) DEFAULT NULL COMMENT '操作时间',
param_and_value varchar(500) DEFAULT NULL COMMENT '请求参数名及参数值',
operate_class varchar(200) DEFAULT NULL COMMENT '操作类',
operate_method varchar(200) DEFAULT NULL COMMENT '操作方法',
cost_time bigint(20) DEFAULT NULL COMMENT '执行方法耗时, 单位 ms',
source int(1) DEFAULT NULL COMMENT '来源 : 1 PC , 2 Android , 3 IOS',
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
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','2022-01-06 18:12:28','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','insert','10',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('2','user','insert','success','java.lang.String','10001','2022-01-06 18:12:27','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','insert','23',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('3','user','update','success','java.lang.String','10001','2022-01-06 18:16:45','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','update','34',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('4','user','update','success','java.lang.String','10001','2022-01-06 18:16:45','{\"age\":\"20\",\"name\":\"Tom\",\"gender\":\"1\"}','cn.itcast.controller.UserController','update','13',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('5','user','insert','success','java.lang.String','10001','2022-01-06 18:30:31','{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}','cn.itcast.controller.UserController','insert','29',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('6','user','find','success','java.lang.String','10001','2022-01-06 18:30:31','{\"age\":\"200\",\"name\":\"TomCat\",\"gender\":\"0\"}','cn.itcast.controller.UserController','find','29',2);
效果:发现是均匀分片,并且满足分片规则(分片1:id%=0,分片2:id%=1,分片3:id%=2)
schema.xml文件中的rule字段指定分片规则,它是个引用
具体是在rule.xml中配置的分片规则:
1).介绍
根据指定的字段及其配置的范围与数据节点的对应情况, 来决定该数据属于哪一个分片。
2).配置
schema.xml逻辑表配置:
<table name="TB_ORDER" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
schema.xml数据节点配置:
<dataNode name="dn1" dataHost="dhost1" database="db01" />
<dataNode name="dn2" dataHost="dhost2" database="db01" />
<dataNode name="dn3" dataHost="dhost3" database="db01" />
rule.xml分片规则配置:
<tableRule name="auto-sharding-long">
<rule>
<columns>idcolumns>
<algorithm>rang-longalgorithm>
rule>
tableRule>
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txtproperty>
<property name="defaultNode">0property>
function>
分片规则配置属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
mapFile | 对应的外部配置文件 |
type | 默认值为0 ; 0 表示Integer , 1 表示String |
defaultNode | 默认节点 默认节点的所用:枚举分片时,如果碰到不识别的枚举值, 就让它路由到默认节点 ; 如果没有默认值,碰到不识别的则报错 。 |
在rule.xml中配置分片规则时,关联了一个映射配置文件 autopartition-long.txt,该配置文件的配置如下:
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
含义:0-500万之间的值,存储在0号数据节点(数据节点的索引从0开始) ; 500万-1000万之间的数据存储在1号数据节点 ; 1000万-1500万的数据节点存储在2号节点 ;
该分片规则,主要是针对于数字类型
的字段适用。 在MyCat的入门程序中,我们使用的就是该分片规则。
1).介绍
根据指定的字段值与节点数量进行求模运算,根据运算结果, 来决定该数据属于哪一个分片。
2).配置
schema.xml逻辑表配置:
<table name="tb_log" dataNode="dn4,dn5,dn6" primaryKey="id" rule="mod-long" />
schema.xml数据节点配置:
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml分片规则配置:
<tableRule name="mod-long">
<rule>
<columns>idcolumns>
<algorithm>mod-longalgorithm>
rule>
tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3property>
function>
分片规则属性说明如下:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
count | 数据节点的数量 |
该分片规则,主要是针对于数字类型
的字段适用。 在前面水平拆分的演示中,我们选择的就是取模分片。
1).介绍
所谓一致性哈希指的是在进行分片操作的时候,它会去计算我们所指定的字段的哈希值,相同的哈希因子计算值总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置,有效的解决了分布式数据的拓容问题。
2).配置
schema.xml中逻辑表配置:
<table name="tb_order" dataNode="dn4,dn5,dn6" rule="sharding-by-murmur" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:
<tableRule name="sharding-by-murmur">
<rule>
<columns>idcolumns>
<algorithm>murmuralgorithm>
rule>
tableRule>
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0property>
<property name="count">3property>
<property name="virtualBucketTimes">160property>
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值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 |
3).测试
配置完毕后,重新启动MyCat,然后在mycat的命令行中,执行如下SQL创建表、并插入数据,查看数 据分布情况。
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_order只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
create table tb_order(
id varchar(100) not null primary key,
money int null,
content varchar(200) null
);
INSERT INTO tb_order (id, money, content) VALUES ('b92fdaaf-6fc4-11ec-b831-482ae33c4a2d', 10, 'b92fdaf8-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b93482b6-6fc4-11ec-b831-482ae33c4a2d', 20, 'b93482d5-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b937e246-6fc4-11ec-b831-482ae33c4a2d', 50, 'b937e25d-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b93be2dd-6fc4-11ec-b831-482ae33c4a2d', 100, 'b93be2f9-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b93f2d68-6fc4-11ec-b831-482ae33c4a2d', 130, 'b93f2d7d-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b9451b98-6fc4-11ec-b831-482ae33c4a2d', 30, 'b9451bcc-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b9488ec1-6fc4-11ec-b831-482ae33c4a2d', 560, 'b9488edb-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b94be6e6-6fc4-11ec-b831-482ae33c4a2d', 10, 'b94be6ff-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b94ee10d-6fc4-11ec-b831-482ae33c4a2d', 123, 'b94ee12c-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b952492a-6fc4-11ec-b831-482ae33c4a2d', 145, 'b9524945-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b95553ac-6fc4-11ec-b831-482ae33c4a2d', 543, 'b95553c8-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b9581cdd-6fc4-11ec-b831-482ae33c4a2d', 17, 'b9581cfa-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b95afc0f-6fc4-11ec-b831-482ae33c4a2d', 18, 'b95afc2a-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b95daa99-6fc4-11ec-b831-482ae33c4a2d', 134, 'b95daab2-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b9667e3c-6fc4-11ec-b831-482ae33c4a2d', 156, 'b9667e60-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b96ab489-6fc4-11ec-b831-482ae33c4a2d', 175, 'b96ab4a5-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b96e2942-6fc4-11ec-b831-482ae33c4a2d', 180, 'b96e295b-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b97092ec-6fc4-11ec-b831-482ae33c4a2d', 123, 'b9709306-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b973727a-6fc4-11ec-b831-482ae33c4a2d', 230, 'b9737293-6fc4-11ec-b831-482ae33c4a2d');
INSERT INTO tb_order (id, money, content) VALUES ('b978840f-6fc4-11ec-b831-482ae33c4a2d', 560, 'b978843c-6fc4-11ec-b831-482ae33c4a2d');
效果:根据字段的hash值来决定到底路由到那个分片
1).介绍
通过在配置文件中配置可能的枚举值, 指定数据分布到不同数据节点上, 本规则适用于按照省份、性别、状态拆分数据等业务 。
2).配置
schema.xml中逻辑表配置:
<table name="tb_user" dataNode="dn4,dn5,dn6" rule="sharding-by-intfile-enumstatus"/>
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:
<tableRule name="sharding-by-intfile">
<rule>
<columns>sharding_idcolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<tableRule name="sharding-by-intfile-enumstatus">
<rule>
<columns>statuscolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<property name="defaultNode">2property>
<property name="mapFile">partition-hash-int.txtproperty>
function>
partition-hash-int.txt ,内容如下 :
1=0
2=1
3=2
分片规则属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
mapFile | 对应的外部配置文件 |
type | 默认值为0 ; 0 表示Integer , 1 表示String |
defaultNode | 默认节点 ; 小于0 标识不设置默认节点 , 大于等于0代表设置默认节点 ; 默认节点的所用:枚举分片时,如果碰到不识别的枚举值, 就让它路由到默认节点 ; 如果没有默认值,碰到不识别的则报错 。 |
3).测试
配置完毕后,重新启动MyCat,然后在mycat的命令行中,执行如下SQL创建表、并插入数据,查看数据分布情况。
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_user只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
CREATE TABLE tb_user (
id bigint(20) NOT NULL COMMENT 'ID',
username varchar(200) DEFAULT NULL COMMENT '姓名',
status int(2) DEFAULT '1' COMMENT '1: 未启用, 2: 已启用, 3: 已关闭',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into tb_user (id,username ,status) values(1,'Tom',1);
insert into tb_user (id,username ,status) values(2,'Cat',2);
insert into tb_user (id,username ,status) values(3,'Rose',3);
insert into tb_user (id,username ,status) values(4,'Coco',2);
insert into tb_user (id,username ,status) values(5,'Lily',1);
insert into tb_user (id,username ,status) values(6,'Tom',1);
insert into tb_user (id,username ,status) values(7,'Cat',2);
insert into tb_user (id,username ,status) values(8,'Rose',3);
insert into tb_user (id,username ,status) values(9,'Coco',2);
insert into tb_user (id,username ,status) values(10,'Lily',1);
效果:第一个节点的status都为1,第二个节点的status都为2,第三个节点的status都为3
4).测试:如果我们往这个数据库表在插入数据的时候,超出了我们所指定的枚举值,那么此时默认往哪一个节点当中进行存储。默认值是2,意味着往第3个节点进行储存。
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_order只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
#status为6,自己在partition-hash-int.txt中设置的是1 2 3,超出最大值3所以会放在第3个节点中。
insert into tb_user (id,username ,status) values(11,'xiaoheihei',6);
1).介绍
运行阶段由应用自主决定当前这条数据会路由到那个分片 , 直接根据字符子串(必须是数字)计算分片号。
即:根据这条数据当中某一个字段的子字符串,来计算出对应的分片。这个求取出来的子字符串必须是数字,比如求取出来的这个子字符串是0 就代表会落在第一个分片上,求取出来的这个子字符串是1 就代表会落在第二个分片上,求取出来的这个子字符串是2 就代表会落在第三个分片上。
截取前2个子字符串
2).配置
schema.xml中逻辑表配置:
<table name="tb_app" dataNode="dn4,dn5,dn6" rule="sharding-by-substring" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:默认没有提供这个规则样例,需要自己增加
<tableRule name="sharding-by-substring">
<rule>
<columns>idcolumns>
<algorithm>sharding-by-substringalgorithm>
rule>
tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
<property name="startIndex">0property>
<property name="size">2property>
<property name="partitionCount">3property>
<property name="defaultPartition">0property>
function>
分片规则属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
startIndex | 字符子串起始索引 |
size | 字符长度 |
partitionCount | 分区(分片)数量 |
defaultPartition | 默认分片(在分片数量定义时, 字符标示的分片编号不在分片数量内时, 使用默认分片) |
示例说明 :
id=05-100000002 , 在此配置中代表根据id中从 startIndex=0,开始,截取siz=2位数字即05,05就是获取的分区,如果没找到对应的分片则默认分配到defaultPartition 。
3).测试
配置完毕后,重新启动MyCat,然后在mycat的命令行中,执行如下SQL创建表、并插入数据,查看数 据分布情况。
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
重新登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_app只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
CREATE TABLE tb_app (
id varchar(10) NOT NULL COMMENT 'ID',
name varchar(200) DEFAULT NULL COMMENT '名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into tb_app (id,name) values('0000001','Testx00001');
insert into tb_app (id,name) values('0100001','Test100001');
insert into tb_app (id,name) values('0100002','Test200001');
insert into tb_app (id,name) values('0200001','Test300001');
insert into tb_app (id,name) values('0200002','TesT400001');
效果:截取id的前2个字符串,截取的数字就代表所属的分片。
如果插入数据的id,截取出来的前2个字符串不在3这个分片范围内,默认放在第一个分片中(因为rule.xml中设置的是0)
insert into tb_app (id,name) values('0400002','TesT500001');
1).介绍
该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位 与1111111111 进行位 & 运算,位与运算最小值为 0000000000,最大值为1111111111,转换为十进制,也就是位于0-1023之间。
同为1则为1,有一个是0则为0
例如:如果计算出来的结果在0~255
之间则落在第一个分片,256~511
之间则落在第二个分片,512~1023
之间则落在第三个分片。
举例:
特点:
2).配置
schema.xml中逻辑表配置:
<table name="tb_longhash" dataNode="dn4,dn5,dn6" rule="sharding-by-long-hash" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:这个也是rule.xml中默认没有案例
<tableRule name="sharding-by-long-hash">
<rule>
<columns>idcolumns>
<algorithm>sharding-by-long-hashalgorithm>
rule>
tableRule>
<function name="sharding-by-long-hash" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">2,1property>
<property name="partitionLength">256,512property>
function>
分片规则属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段名 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
partitionCount | 分片个数列表 |
partitionLength | 分片范围列表 |
约束 :
分片长度 : 默认最大2^10 , 为 1024 ;
count, length的数组长度必须是一致的 ;
以上分为三个分区:0-255,256-511,512-1023
示例说明 :
当配置了这2个属性(分片的数量partitionCount,分片的长度partitionLength)之后,mycat在进行分片的时候,就相当于在底层他要去初始化一个数组,数组的整个长度是1024,其中0到255索引上存放的元素全部是0,256到511索引上存放的元素全部是1,512到1023索引上存放的元素全部是2。当插入的id为515时他会与1023进行位与运算,515转化为二进制就是10 0000 0011,1023转化为二进制就是11 1111 1111,结果为10 0000 0011也就是515,在数组中515索引对应的元素是2,这个2是分片的索引,所以此时会路由到第三个分片中。
3).测试
配置完毕后,重新启动MyCat,然后在mycat的命令行中,执行如下SQL创建表、并插入数据,查看数据分布情况。
配置完毕后,重新启动MyCat,此时会报错
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志,此时会报错
#因为在rule.xml中还有2个引用使用到了func1,而我们在下面函数中把func1修改成了sharding-by-long-hash
#解决:这个个规则测试时候我们没用到,所以直接注释换删除掉即可
tail -f logs/wrapper.log
注释掉后再次重新启动
#先停止
bin/mycat stop
#在启动
bin/mycat start
#查看日志启动成功
tail -f logs/wrapper.log
重新登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_longhash只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
CREATE TABLE tb_longhash (
id int(11) NOT NULL COMMENT 'ID',
name varchar(200) DEFAULT NULL COMMENT '名称',
firstChar char(1) COMMENT '首字母',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
insert into tb_longhash (id,name,firstChar) values(1,'七匹狼','Q');
insert into tb_longhash (id,name,firstChar) values(2,'八匹狼','B');
insert into tb_longhash (id,name,firstChar) values(3,'九匹狼','J');
insert into tb_longhash (id,name,firstChar) values(4,'十匹狼','S');
insert into tb_longhash (id,name,firstChar) values(5,'六匹狼','L');
insert into tb_longhash (id,name,firstChar) values(6,'五匹狼','W');
insert into tb_longhash (id,name,firstChar) values(7,'四匹狼','S');
insert into tb_longhash (id,name,firstChar) values(8,'三匹狼','S');
insert into tb_longhash (id,name,firstChar) values(9,'两匹狼','L');
效果:这9条记录都是在第一个节点
当插入id为1089的数据时,此时超过1024范围,会落在第一个节点上。因为进行位与运算之后,他出来的结果就落在第一个节点上了。
insert into tb_longhash (id,name,firstChar) values(1089,'两匹狼','L');
1).介绍
截取字符串中的指定位置的子字符串,进行hash算法,算出分片。
解释:
举例:截取的子字符串为字段的前2位,之后对截取出来的这2个子字符串进行hash运算,算出的hash值在与1023进行位于运算,最后根据运算的结果来决定到底落在哪一个分片节点上。
2).配置
schema.xml中逻辑表配置:
<table name="tb_strhash" dataNode="dn4,dn5" rule="sharding-by-stringhash" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
rule.xml中分片规则配置:默认没有需要自己定义
<tableRule name="sharding-by-stringhash">
<rule>
<columns>namecolumns>
<algorithm>sharding-by-stringhashalgorithm>
rule>
tableRule>
<function name="sharding-by-stringhash" class="io.mycat.route.function.PartitionByString">
<property name="partitionLength">512property>
<property name="partitionCount">2property>
<property name="hashSlice">0:2property>
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 |
示例说明:
在rule.xml配置了分片规则后,他会初始化一个数组长度为1024,配置了2个分片所以分为2个部分,0~511
之间的索引存的都是0,512~1023
之间的索引存的都是1。如果插入的数据需要截取的字符串为world
,他会截取(0:2)3个字符为wor,之后对wor进行hash运算,运算出来的结果在和1023进行位于运算,运算的结果为5(结果是二进制,转化为了十进制),之后它就会拿着这个5到数组中找索引为5的元素是多少,对应的是0,这个0代表的就是当前这条记录会路由在第一个分片。(0和1代表的是分片的索引值)
3).测试
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
重新登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_strhash只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
create table tb_strhash(
name varchar(20) primary key,
content varchar(100)
)engine=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO tb_strhash (name,content) VALUES('T1001', UUID());
INSERT INTO tb_strhash (name,content) VALUES('ROSE', UUID());
INSERT INTO tb_strhash (name,content) VALUES('JERRY', UUID());
INSERT INTO tb_strhash (name,content) VALUES('CRISTINA', UUID());
INSERT INTO tb_strhash (name,content) VALUES('TOMCAT', UUID());
效果:因为只配置了2个分片,所以此时只有在第一个分片和第二个分片中有这张表。分片1中有2条数据,分片2中有3条数据。
1).介绍
按照日期及对应的时间周期来分片。
eg:
注意事项:
2).配置
schema.xml中逻辑表配置:
<table name="tb_datepart" dataNode="dn4,dn5,dn6" rule="sharding-by-date" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:没有案例,自己定义
<tableRule name="sharding-by-date">
<rule>
<columns>create_timecolumns>
<algorithm>sharding-by-datealgorithm>
rule>
tableRule>
<function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2022-01-01property>
<property name="sEndDate">2022-01-30property>
<property name="sPartionDay">10property>
function>
分片规则属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
dateFormat | 日期格式 |
sBeginDate | 开始日期 |
sEndDate | 结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入 |
sPartionDay | 分区天数,默认值 10 ,从开始日期算起,每个10天一个分区 |
3).测试
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_datepart只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
create table tb_datepart(
id bigint not null comment 'ID' primary key,
name varchar(100) null comment '姓名',
create_time date null
);
insert into tb_datepart(id,name ,create_time) values(1,'Tom','2022-01-01');
insert into tb_datepart(id,name ,create_time) values(2,'Cat','2022-01-10');
insert into tb_datepart(id,name ,create_time) values(3,'Rose','2022-01-11');
insert into tb_datepart(id,name ,create_time) values(4,'Coco','2022-01-20');
insert into tb_datepart(id,name ,create_time) values(5,'Rose2','2022-01-21');
insert into tb_datepart(id,name ,create_time) values(6,'Coco2','2022-01-30');
insert into tb_datepart(id,name ,create_time) values(7,'Coco3','2022-01-31');
1).介绍
使用场景为按照月份来分片, 每个自然月为一个分片。
2).配置
schema.xml中逻辑表配置:
<table name="tb_monthpart" dataNode="dn4,dn5,dn6" rule="sharding-by-month" />
schema.xml中数据节点配置:在3.5.2水平分表中已经写过了
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />
rule.xml中分片规则配置:
<tableRule name="sharding-by-month">
<rule>
<columns>create_timecolumns>
<algorithm>partbymonthalgorithm>
rule>
tableRule>
<function name="partbymonth" class="io.mycat.route.function.PartitionByMonth">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2022-01-01property>
<property name="sEndDate">2022-03-31property>
function>
分片规则属性含义:
属性 | 描述 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 指定分片函数与function的对应关系 |
class | 指定该分片算法对应的类 |
dateFormat | 日期格式 |
sBeginDate | 开始日期 |
sEndDate | 结束日期,如果配置了结束日期,则代码数据到达了这个日期的分片后,会重复从开始分片插入 |
3).测试
配置完毕后,重新启动MyCat
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
重新登录mycat,并切换到ITCAST数据库
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
#显示的是ITCAST、SHOPPING,他是在schema.xml文件中配置的逻辑库
show databases;
use ITCAST;
#这个逻辑表tb_monthpart只是逻辑上存在的,在真实的数据库中并不存在,所以要进行创建
show tables;
然后在mycat的命令行中
,执行如下SQL创建表、并插入数据,查看数据分布情况。
create table tb_monthpart(
id bigint not null comment 'ID' primary key,
name varchar(100) null comment '姓名',
create_time date null
);
insert into tb_monthpart(id,name ,create_time) values(1,'Tom','2022-01-01');
insert into tb_monthpart(id,name ,create_time) values(2,'Cat','2022-01-10');
insert into tb_monthpart(id,name ,create_time) values(3,'Rose','2022-01-31');
insert into tb_monthpart(id,name ,create_time) values(4,'Coco','2022-02-20');
insert into tb_monthpart(id,name ,create_time) values(5,'Rose2','2022-02-25');
insert into tb_monthpart(id,name ,create_time) values(6,'Coco2','2022-03-10');
insert into tb_monthpart(id,name ,create_time) values(7,'Coco3','2022-03-31');
insert into tb_monthpart(id,name ,create_time) values(8,'Coco4','2022-04-10');
insert into tb_monthpart(id,name ,create_time) values(9,'Coco5','2022-04-30');
在MyCat中,当执行一条SQL语句时,MyCat需要进行SQL解析、分片分析、路由分析、读写分离分析 等操作,最终经过一系列的分析决定将当前的SQL语句到底路由到那几个(或哪一个)节点数据库,数据 库将数据执行完毕后,如果有返回的结果,则将结果返回给MyCat,最终还需要在MyCat中进行结果合 并、聚合处理、排序处理、分页处理等操作,最终再将结果返回给客户端。
而在MyCat的使用过程中,MyCat官方也提供了一个管理监控平台MyCat-Web(MyCat-eye)。Mycat-web 是 Mycat 可视化运维的管理和监控平台,弥补了 Mycat 在监控上的空白。帮 Mycat 分担统计任务和配置管理任务。Mycat-web 引入了 ZooKeeper 作为配置中心,可以管理多个节点
。Mycat-web 主要管理和监控 Mycat 的流量、连接、活动线程和内存等,具备 IP 白名单、邮件告警等模块,还可以统计 SQL 并分析慢 SQL 和高频 SQL 等。为优化 SQL 提供依据。
Mycat默认开通2个端口,可以在server.xml中进行修改。
连接MyCat的管理控制台:
#之前连接的是8066数据访问端口,现在连接的是9066数据库管理端口
#这个密码是连接mycat的密码,之前在server.xml中配置的
mysql -h 192.168.10.210 -P 9066 -uroot -p123456
#注意要加分号
show @@help;
#之前修改配置文件后需要重启mycat才能生效,现在可以使用此命令直接加载配置文件生效。
reload @@config;
命令 | 含义 |
---|---|
show @@help | 查看Mycat管理工具帮助文档 |
show @@version | 查看Mycat的版本 |
reload @@config | 重新加载Mycat的配置文件 |
show @@datasource | 查看Mycat的数据源信息 |
show @@datanode | 查看MyCat现有的分片节点信息 |
show @@threadpool | 查看Mycat的线程池信息 |
show @@sql | 查看执行的SQL |
show @@sql.sum | 查看执行的SQL统计 |
说明:
Mycat-web(Mycat-eye)是对mycat-server提供监控服务,功能不局限于对mycat-server使用,通过它也可以去监控mysql。他通过JDBC连接对Mycat、Mysql监控,监控远程服务器(目前仅限于linux系统)的cpu、内 存、网络、磁盘。
Mycat-eye运行过程中需要依赖zookeeper,因此需要先安装zookeeper。
具体的安装步骤,请参考资料中提供的《MyCat-Web安装文档》
MyCat-web安装文档:安装Zookeeper、安装Mycat-web
http://192.168.10.210:8082/mycat
1).开启MyCat的实时统计功能(server.xml)
#进入到mycat的配置文件目录
cd /usr/local/src/MyCat/mycat/conf/
#直接在左侧打开也可以
vim server.xml
<property name="useSqlStat">1property>
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
2).在Mycat监控界面配置服务地址
Mycat01
192.168.10.210
9066
8066
ITCAST
root
123456
配置好了之后,我们可以通过MyCat执行一系列的增删改查的测试,然后过一段时间之后,打开mycat-eye的管理界面,查看mycat-eye监控到的数据信息。
A.性能监控
B.物理节点
D.SQL表分析
E.SQL监控
F.高频SQL
读写分离的前提是基于主从复制的,简单地说是把对数据库的读和写操作分开,以对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。
方式一:如果应用程序直接连接数据库,那么也就意味着应用程序需要操作两个数据源,执行写入操作连接的是主库,执行读操作连接的是从库。这样的话应用程序在操作时就比较繁琐,那么我们需要根据所执行的业务来决定到底要操作那个数据库,实现起来比较麻烦,不推荐这种方式
。
方式二:通过MyCat即可轻易实现上述功能,不仅可以支持MySQL,也可以支持Oracle和SQL Server关系型数据库。
主要通过mycat给我们提供的2个组件来实现:
之前已经学过了,这里再次复习下。
MySQL的主从复制,是基于二进制日志(binlog)实现的。
不在重新创建2台虚拟机了,直接使用之前学习主从复制的2台虚拟机。
还用到了一个分片1的虚拟机,因为要使用mycat。
主机 | 角色 | 用户名 | 密码 |
---|---|---|---|
192.168.10.200 | master | root | 1234 |
192.168.10.201 | slave | root | 1234 |
备注:主从复制的搭建,可以参考前面课程中 主从复制 章节讲解的步骤操作。
博客地址:日志,主从复制章节
在主库中执行以下sql操作:
mysql -uroot -p1234
create database itcast;
use itcast;
create table tb_user(
id int(11) not null,
name varchar(50) not null,
sex varchar(1),
primary key (id)
)engine=innodb default charset=utf8;
insert into tb_user(id,name,sex) values(1,'Tom','1');
insert into tb_user(id,name,sex) values(2,'Trigger','0');
insert into tb_user(id,name,sex) values(3,'Dawn','1');
查看从库:发现从库也有主库创建的库和表以及表中的数据,说明主从环境搭建成功。
mysql -uroot -p1234
show databases;
use itcast;
show tables;
select * from tb_user;
MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制。
配置的是mycat当中的配置文件
cd /usr/local/src/MyCat/mycat/conf/
<schema name="ITCAST_RW" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn7">
schema>
<dataNode name="dn7" dataHost="dhost7" database="itcast" />
<dataHost name="dhost7" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master" url="jdbc:mysql://192.168.10.200:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
<readHost host="salve" url="jdbc:mysql://192.168.10.201:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
readHost>
writeHost>
dataHost>
上述配置的具体关联对应情况如下:
writeHost代表的是写操作对应的数据库,readHost代表的是读操作对应的数据库。 所以我们要想实现读写分离,就得配置writeHost关联的是主库,readHost关联的是从库。
注意:
而仅仅配置好了writeHost以及readHost还不能完成读写分离,还需要配置一个非常重要的负责均衡的参数 balance(默认是0),取值有4种,具体含义如下:配置1或者3都可以实现读写分离
参数值 | 含义 |
---|---|
0 | 不开启读写分离机制 , 所有读操作都发送到当前可用的writeHost上,也就是说即使你配置了readHost也不生效 |
1 | 全部的readHost 与 备用的writeHost 都参与select 语句的负载均衡(主要针对于双主双从模式) |
2 | 所有的读写操作都随机在writeHost , readHost上分发,并不会读写分离 |
3 | 所有的读请求随机分发到writeHost对应的readHost上执行, writeHost不负担读压力 只负担写压力 |
所以,在一主一从模式的读写分离中,balance配置1或3都是可以完成读写分离的。
配置root用户可以访问SHOPPING、ITCAST 以及 ITCAST_RW逻辑库。
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">SHOPPING,ITCAST,ITCAST_RWproperty>
user>
<user name="user">
<property name="password">123456property>
<property name="schemas">SHOPPINGproperty>
<property name="readOnly">trueproperty>
user>
1)配置完毕MyCat后,重新启动MyCat。
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先停止
bin/mycat stop
#在启动
bin/mycat start
#在mycat目录下查看日志
tail -f logs/wrapper.log
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
show databases;
use ITCAST_RW;
#显示tb_user逻辑表,我们并没有在schema.xml中配置逻辑表,那么这个逻辑表是哪来的呢???
# 它是dn7这个dataNode数据节点对应的itcast数据库,这个数据库下面有那些表
# 这个地方就会展示出来。
show tables;
3)然后观察,在执行增删改操作时,对应的主库及从库的数据变化。 在执行查询操作时,检查主库及从库对应的数据变化。
在mycat中执行
#执行查询操作
select * from tb_user;
#执行插入操作
insert into tb_user(id,name,sex) values (4,"aaaaa",0);
#再次查询:那么这个时候有没有实现读写分离呢?????
# 单纯的通过数据我们是没有办法判断的,因为当我们在进行查询的时候,由于主库和从库当中的数据是
# 一致的,所以查询出来的数据你无法判断查的是主库还是从库当中的数据。
select * from tb_user;
---------------------------------------------------------------------------------
在mysql从库中执行
#解决:因为主库的数据会同步到从库,而从库的数据不会同步到主库,所以我们可以更新从库当中的一个
# 数据比如name值为Tom的字段修改为Tom1,此时从库当中的数据是Tom1,主库当中的数据依然是Tom,
# 这样在查询的时候就知道到底查询的是哪个节点数据库了。
mysql -uroot -p1234
use itcast;
show tables;
select * from tb_user;
update tb_user set name="Tom1" where id=1;
---------------------------------------------------------------------------------
在mycat中接着执行
#这个时候查询的结果为Tom1,说明查询的是从库,实现读写分离了。
select * from tb_user;
#执行插入操作:发现主库和从库都有这条数据,说明走的是主节点
# 原因:因为主节点有数据所以它走的就是主节点,如果他插入的是从节点
# 从节点的数据是不会同步到主数据库的,主数据库也就没有这条数据,
# 所以插入操作走的是主节点,实现了读写分离。
insert into tb_user(id,name,sex) values (5,"bbbb",1);
在mycat中直接查询,无法区分查询的是主库还是从库当中的数据。
在mycat中再次执行查询操作,这个时候查询的结果为Tom1,说明查询的是从库,实现读写分离了。
主库和从库当中都有这条数据,如果他插入的是从节点,从节点的数据是不会同步到主数据库的,主数据库也就没有这条数据,所以插入操作走的是主节点,实现了读写分离。
4)在测试中,我们可以发现当主节点Master宕机之后,业务系统就只能够读,而不能写入数据了。
在主库中停止mysql服务,模拟主节点Master宕机
systemctl stop mysqld
systemctl status mysqld
此时在mycat中只能做查询操作,不能做增删改操作了。
(我自己测试在从库中关闭了mysql服务之后,在mycat中查询和更新操作都报错了
)
那如何解决这个问题呢?这个时候我们就得通过另外一种主从复制结构来解决了,也就是我们接下来讲解的双主双从。
一个主机 Master1 用于处理所有写
请求,它的从机 Slave1 和另一台主机 Master2 还有它的从机 Slave2 负责所有读
请求。当 Master1 主机宕机后,Master2 主机负责写
请求,Master1 、Master2 互为备机。架构图如下:
我们需要准备5台服务器,具体的服务器及软件安装情况如下:
编号 | IP | 预装软件 | 角色 |
---|---|---|---|
1 | 192.168.10.210 | MyCat、MySQL | MyCat中间件服务器 |
2 | 192.168.10.200 | MySQL | M1 |
3 | 192.168.10.201 | MySQL | S1 |
4 | 192.168.10.202 | MySQL | M2 |
5 | 192.168.10.203 | MySQL | S2 |
关闭以上所有服务器的防火墙:
- systemctl stop firewalld
- systemctl disable firewalld
这个地方修改的是mysql中的配置文件
A.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 2^32-1,默认为1
server-id=1
#指定同步的数据库,也就是说指定的这几个数据库会进行主从复制,别的数据库不会进行主从复制的。
binlog-do-db=db01
binlog-do-db=db02
binlog-do-db=db03
# 当前节点在作为从数据库的时候,有写入操作也要更新二进制日志文件
# 因为这个从库它需要从这个主库当中读取二进制日志文件,然后完成主从复制,所以也需要加上这个参数。
log-slave-updates
B.重启MySQL服务器
systemctl restart mysqld
C.创建账户并授权:
mysql -uroot -p1234
#创建itcast用户,并设置密码,该用户可在任意主机连接该MySQL服务
CREATE USER 'itcast'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456';
#为 'itcast'@'%' 用户分配主从复制权限
GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%';
通过指令,查看两台主库的二进制日志坐标
show master status ;
2). Master2(192.168.10.202)
A.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 2^32-1,默认为1
server-id=3
#指定同步的数据库
binlog-do-db=db01
binlog-do-db=db02
binlog-do-db=db03
# 在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates
B.重启MySQL服务器
systemctl restart mysqld
C.创建账户并授权
这个地方因为是直接复制的一主一从的虚拟机,所以账户和授权已经配置过了,创建重复的账号会报错。
mysql -uroot -p1234
#创建itcast用户,并设置密码,该用户可在任意主机连接该MySQL服务
CREATE USER 'itcast'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456';
#为 'itcast'@'%' 用户分配主从复制权限
GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%';
通过指令,查看两台主库的二进制日志坐标
show master status ;
1). Slave1(192.168.10.201)
A.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 232-1,默认为1
server-id=2
B.重新启动MySQL服务器
systemctl restart mysqld
2). Slave2(192.168.10.203)
A.修改配置文件 vim /etc/my.cnf
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 232-1,默认为1
server-id=4
B.重新启动MySQL服务器
systemctl restart mysqld
1).两台从库配置关联的主库
需要注意slave1对应的是master1,slave2对应的是master2。
A. 在 slave1(192.168.10.201)上执行
mysql -uroot -p1234
# 指定主库1的信息
CHANGE MASTER TO MASTER_HOST='192.168.10.200', MASTER_USER='itcast',MASTER_PASSWORD='Root@123456', MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=663;
#开启同步操作
start slave;
#查看从库状态
show slave status \G;
B. 在 slave2(192.168.10.203)上执行
mysql -uroot -p1234
# 指定主库2的信息
CHANGE MASTER TO MASTER_HOST='192.168.10.202', MASTER_USER='itcast',MASTER_PASSWORD='Root@123456', MASTER_LOG_FILE='binlog.000004',MASTER_LOG_POS=663;
#开启同步操作
start slave;
#查看从库状态
show slave status \G;
Master2 复制 Master1,Master1 复制 Master2。
A. 在 Master1(192.168.10.200)上执行
mysql -uroot -p1234
#指定Master2的连接信息
CHANGE MASTER TO MASTER_HOST='192.168.10.202', MASTER_USER='itcast',MASTER_PASSWORD='Root@123456', MASTER_LOG_FILE='binlog.000005',MASTER_LOG_POS=156;
#开启同步操作
start slave;
#查看从库状态
show slave status \G;
B. 在 Master2(192.168.10.202)上执行
mysql -uroot -p1234
#指定Master1的连接信息
CHANGE MASTER TO MASTER_HOST='192.168.10.200', MASTER_USER='itcast',MASTER_PASSWORD='Root@123456', MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=663;
#开启同步操作
start slave;
#查看从库状态
show slave status \G;
经过上述的三步配置之后,双主双从的复制结构就已经搭建完成了。 接下来,我们可以来测试验证一下。
分别在两台主库Master1、Master2上执行DDL、DML语句,查看涉及到的数据库服务器的数据同步情况。
在主库Master1上执行:
create database db01;
use db01;
create table tb_user(
id int(11) not null primary key ,
name varchar(50) not null,
sex varchar(1)
)engine=innodb default charset=utf8mb4;
insert into tb_user(id,name,sex) values(1,'Tom','1');
insert into tb_user(id,name,sex) values(2,'Trigger','0');
insert into tb_user(id,name,sex) values(3,'Dawn','1');
insert into tb_user(id,name,sex) values(4,'Jack Ma','1');
insert into tb_user(id,name,sex) values(5,'Coco','0');
insert into tb_user(id,name,sex) values(6,'Jerry','1');
在主库Master2上执行:
create database db02;
use db02;
create table tb_user(
id int(11) not null primary key ,
name varchar(50) not null,
sex varchar(1)
)engine=innodb default charset=utf8mb4;
insert into tb_user(id,name,sex) values(1,'Tom','1');
insert into tb_user(id,name,sex) values(2,'Trigger','0');
insert into tb_user(id,name,sex) values(3,'Dawn','1');
insert into tb_user(id,name,sex) values(4,'Jack Ma','1');
insert into tb_user(id,name,sex) values(5,'Coco','0');
insert into tb_user(id,name,sex) values(6,'Jerry','1');
效果:此时m1,s1,m2,s2同样都有数据
完成了上述双主双从的结构搭建之后,接下来,我们再来看看如何完成这种双主双从的读写分离。
在mycat的配置文件中进行配置
#配置文件都是存放在mycat解压后的conf目录
cd /usr/local/src/MyCat/mycat/conf/
MyCat控制后台数据库的读写分离和负载均衡由schema.xml文件datahost标签的balance属性控制,通过writeType及switchType来完成失败自动切换的。
1).schema.xml
配置逻辑库:
<schema name="ITCAST_RW2" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn7">
schema>
配置数据节点:
<dataNode name="dn7" dataHost="dhost7" database="db01" />
配置节点主机:
<dataHost name="dhost7" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="master1" url="jdbc:mysql://192.168.10.200:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
<readHost host="salve1" url="jdbc:mysql://192.168.10.201:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
readHost>
writeHost>
<writeHost host="master2" url="jdbc:mysql://192.168.10.202:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
<readHost host="salve2" url="jdbc:mysql://192.168.10.203:3306?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8" user="root" password="1234">
readHost>
writeHost>
dataHost>
属性说明:
写入
操作,这样就完成了读写分离 ;写入
操作,而其它的s1、m2、s2负责读取
操作。写
操作都转发到第1台writeHost,writeHost1挂了, 会切换到writeHost2上;写
操作都随机地发送到配置的writeHost上 ;
2).server.xml
配置root用户也可以访问到逻辑库 ITCAST_RW2。
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">SHOPPING,ITCAST,ITCAST_RW2property>
user>
<user name="user">
<property name="password">123456property>
<property name="schemas">SHOPPINGproperty>
<property name="readOnly">trueproperty>
user>
修改完成配置后重启mycat:
#注意不能到bin目录下
cd /usr/local/src/MyCat/mycat/
#先把之前启动的mycat 关闭掉
bin/mycat stop
#启动
bin/mycat start
#在mycat目录下查看日志,是否启动成功
tail -f logs/wrapper.log
登录MyCat,测试查询及更新操作,判定是否能够进行读写分离,以及读写分离的策略是否正确。
1)登录MyCat
:
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
show databases;
use ITCAST_RW2;
#显示tb_user,同一主一从一样虽然没有配置逻辑表,但是它会把dn7这个数据节点对应的数据库db01
# 下的所有表作为逻辑表显示。db01数据库此时只有tb_user一张表,所以这个地方显示的是tb_user作为逻辑表。
show tables;
#直接查询因为现在4个数据库(m1 s1 m2 s2)中的数据是一样的,所以无无区分查询的是哪一个数据库
#和之前学习一主一从一样,同样可以通过修改从库进行测试
select * from tb_user;
2)修改从库
中的数据
因为m1(主库1)的数据会同步给s1(从库1),m2(主库2)的数据会同步给s2(从库2)。反过来s1(从库1)的数据不会同步给m1(主库1),s2(从库2)的数据不会同步给m2(主库2)。所以此时可以修改s1和s2的字段,这样主库和从库的数据就不相同了,再次执行查询就知道查询的是主库还是从库了。
在从库1上执行
mysql -uroot -p1234
use db01;
show tables;
select * from tb_user;
#此时从库1的字段值为TomS1
update tb_user set name="TomS1" where id=1;
-----------------------------------------------------
在从库2上执行
mysql -uroot -p1234
use db01;
show tables;
select * from tb_user;
#此时从库2的字段值为TomS2
update tb_user set name="TomS2" where id=1;
3)在mycat
中再次进行查询:
select * from tb_user;
4)在mycat
中进行插入操作:
insert into tb_user(id,name,sex) values (7,"AAAAA",0);
5)当主库挂掉一个之后,是否能够自动切换。
在主库1
中停止mysql服务,模拟主节点Master1宕机
#在主库1中执行,不需要登录mysql
#停止mysql服务
systemctl stop mysqld
#查看mysql状态
systemctl status mysqld
在mycat
中执行,查询和插入操作:
#登录mycat后执行
mysql -h 192.168.10.210 -P 8066 -uroot -p123456
use ITCAST_RW2;
show tables;
#可以进行查询
#正常情况:主库1是写入操作,从库2 主库2 从库2负责读取操作
#主库1挂掉之后:主库2负责写入操作,从库2负责读取操作
#当然查询操作走的是从库1 主库2 从库2,主库1挂掉之后影响的是增删改,所以主库1挂不挂掉都不影响查询操作,
# 没办法区分是否实现了高可用。
SELECT * FROM tb_user;
#可以进行插入
#主库1已经挂了,所以此时插入是的主库2,之后同步给从库2。
#这个时候主库2和从库2有这条记录,主库1 和从库1没有这条记录
#主库1挂掉之后影响的是是增删改 也就是说不能在进行增删改操作了,但是现在执行插入操作任然可以成功说明
# 他把主库2变为写入操作了(增删改),从库2进行读取操作,也就实现了高可用。
insert into tb_user(id,name,sex) values (8,"BBBBB",1);
效果: