目录
一、什么是Mycat
二、为什么使用数据库中间件?
三、中间件的应用场景
3.1 读写分离
3.2:数据分片
四、Mycat的原理
五、Mycat完成读写分离
5.1 搭建mysql数据库的主从模式
5.1.1:mysql主从模式的原理
5.1.2:搭建主节点--master
5.1.3:配置从节点
六、MyCat完成读写分离
6.1 验证MyCat完成读写分离
七、Mycat---垂直分库
7.1 修改mycat配置文件--schema.xml文件
7.2 分别再DN1节点和DN2节点创建不同的库
7.3 通过mycat完成表的创建
八、Mycat---水平分表
8.3 修改schema.xml文件
8.4 修改rule.xml文件
8.5 我们orders表只在mydb01库中存在,不在mydb02库中,所以我们需要再mydb02也创建orders表
九、水平拆分后join
十、全局表
十一、常用分表的规则
11.1 取模
11.2 分片枚举
11.3、范围约定
11.4 按日期(天)分片
十二、Mycat的高可用
Mycat是数据库中间件,所谓中间件,是一类连接软件组件和应用的计算机软件,以便软件各部件之间的通信。
例如 tomcat,web的中间件。而数据库中间件是连接Java应用程序和数据库中间的软件。
Java与数据库的紧密耦合
高访问量高并发对数据库的压力
读写请求数据不一致
我们现在普遍的Java应用程序都是直接连接了MySQL软件进行读写操作,也就是我们在Java中的配置文件等定义了mysql的数据源,直接连接到了我们的mysql软件,但是当某些情况下我们可能需要用到了多个数据库,这个时候我们可能就需要配多个数据源去连接我们的多个数据库,这个时候我们进行sql操作的时候就会很麻烦,因为Java与数据库有了一个紧密的耦合度,但是如果我们在Java应用程序与mysql中间使用了mycat,我们只需要访问mycat就可以了,至于数据源等问题,mycat会直接帮我们搞定。
再来说一下高访问量高并发,我们都知道mysql数据库实际上在数据查询上是有一个瓶颈的,当我们的数据太多的时候,对于互联网上有高并发的请求的时候,这个时候对我们mysql的压力是非常大的,当访问量一大,就可能会出现查不出数据,响应的时间太长等,这个时候我们可能需要有多个服务器对数据库进行读写分离,以及对数据库进行集群,这个时候我们的sql语句要进行分类,哪个sql语句要访问哪个数据库,这个时候只要交给中间件就可以了。
垂直拆分(分库)、水平拆分(分表)
首先我们的数据库有多个表
当我们的表足够多的时候,也会造成整个数据库的瓶颈,这个时候查询是非常慢的,这个时候我们可能要对这个数据库进行垂直拆分,也就是分库
我们需要垂直拆分了表4 5 6 放到另外一个库中。
当我们垂直拆分了之后,可能又会出现单个表中的数据达到千万以上,这个时候对表造成了一个瓶颈,这个时候我们对表进行拆分。
我们可以把表的一部分数据拆分到另外的一个数据库。
中间件---完成: (1)读写分离 (2)垂直分库 (3)水平分表
Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发 往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用 Mycat 还是 MySQL
从上面的从我们可以看出,mycat可以对sql进行读写分离的操作。 当执行的sql语句为查询则访问从节点MySQL,如果执行的sql语句为写操作则访问主节点。
从上层来看,复制分成三步:
Master 主库在事务提交时,会把数据变更作为事件Events 记录在二进制日志文件 Binlog 中。
主库推送二进制日志文件 Binlog 中的日志事件到从库的中继日志 Relay Log 。
slave重做中继日志中的事件,将改变反映它自己的数据。
准备的条件
1. 安装数据库----如果没安装就查看这个网址---https://www.cnblogs.com/tony-hyn/p/15777762.html
2. 启动mysql -----systemctl start mysqld
3. 查看mysql是否设置了远程连接. window中的navicat连接mysql.
克隆一份虚拟机
修改mysql的UUID保证不一样
找到 find -name auto.cnf 并删除该问题
重启mysql服务器
158作为主节点
在master 的配置文件(/etc/my.cnf)中,配置如下内容:
#mysql 服务ID,保证整个集群环境中唯一 server-id=1 #mysql binlog 日志的存储路径和文件名 log-bin=/var/lib/mysql/mysqlbin #错误日志,默认已经开启 #log-err #mysql的安装目录 #basedir #mysql的临时目录 #tmpdir #mysql的数据存放目录 #datadir #是否只读,1 代表只读, 0 代表读写 read-only=0 #忽略的数据, 指不需要同步的数据库 binlog-ignore-db=mysql #指定同步的数据库 #binlog-do-db=db01
执行完毕之后,需要重启Mysql: systemctl restart mysqld
查看master状态 show master status;
字段含义:
File : 从哪个日志文件开始推送日志文件
Position : 从哪个位置开始推送日志
Binlog_Ignore_DB : 指定不需要同步的数据库
在 slave 端/etc/my.cnf配置文件中,配置如下内容:
#mysql服务端ID,唯一 server-id=2 #指定binlog日志 log-bin=/var/lib/mysql/mysqlbin
执行完毕之后,需要重启Mysql: systemctl restart mysqld
执行如下指令 :---指定主从关系
change master to master_host= '192.168.223.158', master_user='root', master_password='123456',master_log_file='mysqlbin.000001',master_log_pos=154;
指定当前从库对应的主库的IP地址,用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志。
开启同步操作 :
start slave;
show slave status\G;
上面截图的必须是yes才可以。
停止同步操作:
stop slave;
reset master;
准备一台mycat服务器。192.168.223.160.
修改配置文件
schema.xml: 定义我们的逻辑库和节点信息
rule.xml: 定义分表的规则
server.xml 定义mycat账户和密码
1.修改schema.xml
select user()
2.修改server.xml
定义逻辑账户和密码
******
TESTDB
3. 启动MyCat
springboot+整合mybatis-plus
原因是mycat使用的mysql驱动版本为5.1的 而springboot的mysql驱动8.0的。 必须保证他们的版本号一致
从上面的结果无法看出读取的是主节点还是从节点的数据。为了让你能看出从slave节点读取的数据,我们故意让从节点的数据与主节点不一致。
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类, 分布到不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面
分库的原则:
一个问题:在两台主机上的两个数据库中的表,能否关联查询? 不能
分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里。
#客户表 rows:20万
CREATE TABLE customer(
id INT AUTO_INCREMENT,
NAME VARCHAR(200),
PRIMARY KEY(id)
);
#订单表 rows:600万
CREATE TABLE orders(
id INT AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
#订单详细表 rows:600万
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
#订单状态字典表 rows:20
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
客户表----独立放入一个库中.
订单表 订单详情表 订单状态表----独立放入一个库中
为了减少服务器的启动数量.---我们就把原来的主从关系先取消
stop slave; 关闭主从关系
select user()
select user()
dn1=====>mydb01
dn2=====>mydb02
mysql -uaaa -P 8066 -h 192.168.223.160 -p123456
-u: mycat逻辑账户
-P: mycat的端口号
-h: mycat服务端的ip
-p: mycat的逻辑密码
#客户表 rows:20万 CREATE TABLE customer( id INT AUTO_INCREMENT, NAME VARCHAR(200), PRIMARY KEY(id) ); #订单表 rows:600万 CREATE TABLE orders( id INT AUTO_INCREMENT, order_type INT, customer_id INT, amount DECIMAL(10,2), PRIMARY KEY(id) ); #订单详细表 rows:600万 CREATE TABLE orders_detail( id INT AUTO_INCREMENT, detail VARCHAR(2000), order_id INT, PRIMARY KEY(id) ); #订单状态字典表 rows:20 CREATE TABLE dict_order_type( id INT AUTO_INCREMENT, order_type VARCHAR(200), PRIMARY KEY(id) );
1、简介
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中。
2、水平分表按照什么列来分? 例如: 订单表
订单表 rows:1000万
CREATE TABLE orders( id INT AUTO_INCREMENT, order_type INT, customer_id INT, amount DECIMAL(10,2), PRIMARY KEY(id) );
按照一列进行拆分。
按照id---- 查询订单时---查询最近的订单数据。---之前的订单很少有人访问
按照订单的日期---双11 双12 ---这种日期的数据存入一张表---该表的记录会非常多。
客户id---->比较均匀的分到相应的表中,而且访问也比较均匀。
select user()
select user()
该文件是用来定义分表的规则
#订单表 rows:600万
CREATE TABLE orders(
id INT AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);通过mycat往orders表添加数据。
分表插入数据时,必须提高列的列表。因为你如果不提供人家无法知道对应那一列的值
INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);
join:联表查询。
思考: 如果订单详情表和订单表进行联表查询,由于订单表中的记录被拆分到两个库中了,而我们的订单详情表在一个库中存在,那么如果真的要联表查询的化,订单详情只会关联当前库中的订单表。
原则:
使用ER表解决上面的字表关联查询的问题,其将子表的存储位置依赖于主表,并且物理上紧邻存放,因此彻底解决了JION 的效率和性能问 题,根据这一思路,提出了基于E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上。
修改schema.xml
select user()
select user()
在159服务器创建订单详情表
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
通过mycat添加订单详情表的记录
INSERT INTO orders_detail(id,detail,order_id) values(7,'detail1',1);
INSERT INTO orders_detail(id,detail,order_id) VALUES(8,'detail1',2);
INSERT INTO orders_detail(id,detail,order_id) VALUES(9,'detail1',3);
INSERT INTO orders_detail(id,detail,order_id) VALUES(10,'detail1',4);
INSERT INTO orders_detail(id,detail,order_id) VALUES(11,'detail1',5);
INSERT INTO orders_detail(id,detail,order_id) VALUES(12,'detail1',6);
java代码测试 ------联表查询
@Autowired
private OrderDetailMapper orderDetailMapper;
@Test
public void test08(){
List allWithOrders = orderDetailMapper.findAllWithOrders();
allWithOrders.stream().forEach(System.out::println);
}
dao层
od.id odid,detail,order_id,o.id oid,order_type,customer_id,amount
dict---->状态 比如支付状态 性别 0 1
订单数据字典表.---存放订单得状态---->支付 未支付 取消 待发货 已发货 已确认。。。。
由于订单数据字典表 再每个节点上都需要。所以我们把数据字典表定义为全局表。
什么样得表适合做全局表.
变动不频繁
数据量总体变化不大
数据规模不大,很少有超过数十万条记录.
鉴于此,Mycat 定义了一种特殊的表,称之为“全局表”,全局表具有以下特性:
全局表的插入、更新操作会实时在==所有节点上执行==,保持各个分片的数据一致性
全局表的查询操作,只从一个节点获取
全局表可以跟任何一个表进行 JOIN 操作将字典表或者符合字典表特性的一些表定义为全局表,则从另外一个方面,很好的解决了数据JOIN 的难题。通过全局表+基于 E-R 关系的分片策略,Mycat 可以满足 80%以上的企业应用开发
修改schema.xml
select user()
select user()
通过mycat创建全局表
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
添加数据
INSERT INTO DICT_ORDER_TYPE(id,order_type) VALUES(101,'Pay');
INSERT INTO DICT_ORDER_TYPE(id,order_type) VALUES(102,'NotPay');
观察到每个节点都存在该表,而且该表的数据内容是一样
此规则为对分片字段求摸运算。也是水平分表最常用规则
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则。
实现方式如下:1.修改schema.xml配置文件
测试表为orders_ware_info,配置在dn1和dn2节点,规则是新增一个sharding_by_intfile
2.修改rule.xml配置文件
3.修改partition-hash-int.txt配置文件
表示areacode为110就存到第一个数据节点,为120就存到第二个数据节点
4.重启Mycat使配置生效
使用mycat创建订单归属区域信息表:
CREATE TABLE orders_ware_info ( `id` INT AUTO_INCREMENT comment '编号', `order_id` INT comment '订单编号', `address` VARCHAR(200) comment '地址', `areacode` VARCHAR(20) comment '区域编号', PRIMARY KEY(id) );
插入数据:
INSERT INTO ORDERS_WARE_INFO(id, order_id,address,areacode) VALUES (1,1,'北京','110'); INSERT INTO ORDERS_WARE_INFO(id, order_id,address,areacode) VALUES (2,2,'天津','120');
通过数据库直接查询:
spdb_pzex:
spdb_portal_wechat:
此分片适用于,提前规划好分片字段某个范围属于哪个分片。
1.修改schema.xml配置文件
上面定义表以及所存储的数据节点和分片规则等
2.修改rule.xml配置文件
3. 修改autopartition-long.txt配置文件
4.重启Mycat使配置生效
在mycat中创建支付信息表payment_info:
CREATE TABLE payment_info( `id` INT AUTO_INCREMENT comment '编号', `order_id` INT comment '订单编号', `payment_status` INT comment '支付状态', PRIMARY KEY(id));
插入数据:
INSERT INTO PAYMENT_INFO (id,order_id,payment_status) VALUES (1,101,0);INSERT INTO PAYMENT_INFO (id,order_id,payment_status) VALUES (2,102,1);INSERT INTO PAYMENT_INFO (id,order_id ,payment_status) VALUES (3,103,0);INSERT INTO PAYMENT_INFO (id,order_id,payment_status) VALUES (4,104,1);
分别查看两个host数据节点的数据:
spdb_pzex:
spdb_portal_wechat:
此规则为按天分片。设定时间格式、范围 ---日志
1.修改schema.xml配置文件
2.修改rule.xml配置文件
需要自定义一个分片函数shardingByDate:
3.重启Mycat使配置生效
在mycat中创建用户信息表login_info:
CREATE TABLE login_info( `id` INT AUTO_INCREMENT comment '编号', `user_id` INT comment '用户编号', `login_date` date comment '登录日期', PRIMARY KEY(id));
插入数据:
INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (1,101,'2019-01-01'); INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (2,102,'2019-01-02'); INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (3,103,'2019-01-03'); INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (4,104,'2019-01-04'); INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (5,103,'2019-01-05'); INSERT INTO LOGIN_INFO(id,user_id,login_date) VALUES (6,104,'2019-01-06');
分别查看两个host数据节点的数据:
spdb_pzex:
spdb_portal_wechat:
详情可查看:基于 HA 机制的 Mycat 高可用_爱上口袋的天空的博客-CSDN博客
总结: mycat -- 读写分离 垂直分库 水平分表[ 联表操作【ER原理】 水平拆分的规则]。