分表分库学习(二)之“Mycat基本概念“

1.Mycat 介绍与核心概念

1.1基本介绍

**历史:**从阿里 cobar 升级而来,由开源组织维护,2.0 正在开发中。
**定位:**运行在应用和数据库之间,可以当做一个 MySQL 服务器使用,实现对 MySQL 数据库的分库分表,也可以通过 JDBC 支持其他的数据库。
在这里插入图片描述
Mycat 的关键特性

  1. 可以当做一个 MySQL 数据库来使用
  2. 支持 MySQL 之外的数据库,通过 JDBC 实现
  3. 解决了分表分库带来的所有问题,多表 join、分布式事务、全局序列号、翻页排序
  4. 支持 ZK 配置,带监控 mycat-web

1.2核心概念

在这里插入图片描述

概念 含义
主机 物理主机,一台服务器,一个数据库服务,一个 3306 端口
物理数据库 真实的数据库
物理表 真实的表,例如数据库的 order_info 表
分片 将原来单个数据库的数据切分后分散存储在不同的数据库节点
分片节点 分片以后数据存储的节点
分片键 分片依据的字段,例如 order_info 表以 id 为依据分片,id 就是分片键,通常是主键
分片算法 分片的规则,例如随机、取模、范围、哈希、枚举以及各种组合算法
逻辑表 相对于物理表,是分片表聚合后的结果,对于客户端来说跟真实的表没有区别
逻辑数据库 相对于物理数据库,是数据节点聚合后的结果,例如 catmall

下载、解压 Mycat(有 Windows 版本,可以在本地数据库测试)
http://dl.mycat.io/

linux安装命令

wget http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz 
tar -xzvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz 

Mycat 解压以后有 5 个目录:

目录 作用
bin 启动目录
catlet 空目录
conf 配置目录
lib jar 包依赖
logs 日志目录

2.Mycat配置详解

主要的配置文件 server.xml、schema.xml、rule.xml 和具体的分片配置文件。 坑非常多,配置错误会导致无法启动,这个时候要看日志! 注意备份,不知道什么时候就跑不起来了……

2.1 server.xml

包含系统配置信息。
system 标签:例如字符集、线程数、心跳、分布式事务开关等等。
user 标签:配置登录用户和权限。

<user name="root" defaultAccount="true"> 
	<property name="password">123456</property> 
	<property name="schemas">catmall</property> 
</user>

mycat对密码加密

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

2.2 schema.xml

schema 在 MySQL 里面跟数据库是等价的。
schema.xml 包括逻辑库、表、分片规则、分片节点和数据源,可以定义多个 schema。 这里面有三个主要的标签(table、dataNode、dataHost):

  • table
    表名和库名最好都用小写
    table标签定义了逻辑表,以及逻辑表分布的节点和分片规则:
<schema name="catmall" checkSQLschema="false" sqlMaxLimit="100">
	<!-- 范围分片 --> 
	<table name="customer" primaryKey="id" dataNode="dn1,dn2,dn3" rule="rang-long-cust" />
	<!-- 取模分片 --> 
	<table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order" >
		<!-- ER 表 --> 
		<childTable name="order_detail" primaryKey="id" joinKey="order_id" parentKey="order_id"/> 
	</table> 
	<!-- 全局表 --> 
	<table name="student" primaryKey="sid" type="global" dataNode="dn1,dn2,dn3" /> 
</schema>
配置 作用
primaryKey 指定该逻辑表对应真实表的主键。MyCat 会缓存主键(通过 primaryKey 属性配置)与具体 dataNode 的信息。当分片规则(rule)使用非主键进行分片时,那么在使用主键进行查询时,MyCat 就会通过缓存先确定记录在哪个 dataNode 上,然后再在该 dataNode 上执行查询。 如果没有缓存/缓存并没有命中的话,还是会发送语句给所有的dataNode。
dataNode 数据分片的节点
autoIncrement 自增长(全局序列),true 代表主键使用自增长策略
type 全局表:global。其他:不配置
  • dataNode
    数据节点与物理数据库的对应关系。
<dataNode name="dn1" dataHost="host1" database="xhccat" />
  • dataHost
    配置物理主机的信息,readhost 是从属于 writehost 的。
<dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
	<heartbeat>select user()</heartbeat>
 	<!-- can have multi write hosts --> 
 	<writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> 
	 	<!-- can have multi read hosts --> 
	 	<readHost host="hostS2" url="192.168.8.146:3306" user="root" password="xxx"/> 		
 	</writeHost> 
 	<writeHost host="hostS1" url="localhost:3316" user="root" password="123456"/>
  	<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --> 
</dataHost>

参数详解:
balance: 负载的配置,决定 select 语句的负载

作用
0 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。
1 所有读操作都随机发送到当前的 writeHost 对应的 readHost 和备用的 writeHost
2 所有的读操作都随机发送到所有的 writeHost,readHost 上
3 所有的读操作都只发送到 writeHost 的 readHost 上

writeType: 读写分离的配置,决定 update、delete、insert 语句的负载

作用
0 所有写操作都发送到可用的 writeHost 上(默认第一个,第一个挂了以后发到第二个)
1 所有写操作都随机的发送到 writeHost

switchType: 主从切换配置

作用
-1 表示不自动切换
1 默认值,表示自动切换
2 基于 MySQL 主从同步的状态决定是否切换,心跳语句为 show slave status
3 基于 MySQL galary cluster 的切换机制(适合集群)(1.4.1),心跳语句为 show status like ‘wsrep%’。

2.3 rule.xml

定义了分片规则和算法
分片规则:

<tableRule name="rang-long-cust"> 
	<rule> 
		<columns>id</columns> 
		<algorithm>func-rang-long-cust</algorithm> 
	</rule> 
</tableRule>

分片算法

<function name="func-rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong"> 
	<property name="mapFile">rang-long-cust.txt</property> 
</function>

分片配置:rang-long-cust.txt

10001-20000=1 
0-10000=0 
20001-100000=2

2.4 使用zookeeper管理远程Mycat配置文件

https://www.cnblogs.com/leeSmall/p/9551038.html

2.5 启动停止

进入 mycat/bin 目录(注意要先启动物理数据库):

操作 命令
启动 ./mycat start
停止 ./mycat stop
重启 ./mycat restart
查看状态 ./mycat status
前台运行 ./mycat console

连接

mysql -uroot -p123456 -h 192.168.8.151 -P8066 catmall

3.Mycat 分片验证示例

explain 可以用来看路由结果

在三个数据库中建表

CREATE TABLE `customer`
(
    `id`   int(11)      DEFAULT NULL,
    `name` varchar(255) DEFAULT NULL
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

CREATE TABLE `order_info`
(
    `order_id`    int(11) NOT NULL COMMENT '订单 ID',
    `uid`         int(11)  DEFAULT NULL COMMENT '用户 ID',
    `nums`        int(11)  DEFAULT NULL COMMENT '商品数量',
    `state`       int(2)   DEFAULT NULL COMMENT '订单状态',
    `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`order_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

CREATE TABLE `order_detail`
(
    `order_id` int(11) NOT NULL COMMENT '订单号',
    `id`       int(11) NOT NULL COMMENT '订单详情',
    `goods_id` int(11)        DEFAULT NULL COMMENT '货品 ID',
    `price`    decimal(10, 2) DEFAULT NULL COMMENT '价格',
    `is_pay`   int(2)         DEFAULT NULL COMMENT '支付状态',
    `is_ship`  int(2)         DEFAULT NULL COMMENT '是否发货',
    `status`   int(2)         DEFAULT NULL COMMENT '订单详情状态',
    PRIMARY KEY (`order_id`, `id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

CREATE TABLE `student`
(
    `sid`  int(8) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) DEFAULT NULL,
    `qq`   varchar(255) DEFAULT NULL,
    PRIMARY KEY (`sid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

schema.xml

<table name="customer" dataNode="dn1,dn2,dn3" rule="rang-long-cust" primaryKey="id"/>
<table name="order_info" dataNode="dn1,dn2,dn3" rule="mod-long-order">
       <childTable name="order_detail" joinKey="order_id" parentKey="order_id" primaryKey="id"/>
</table>
<table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid" type="global"/>

<!-- 数据节点配置-->
<dataNode name="dn1" dataHost="host1" database="xhcdatabase"/> 
<dataNode name="dn2" dataHost="host2" database="xhcdatabase"/>
<dataNode name="dn3" dataHost="host3" database="xhcdatabase"/>

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

<dataHost balance="0" maxCon="1000" minCon="10" name="host2" writeType="0" switchType="1"
          slaveThreshold="100" dbType="mysql" dbDriver="native">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM1" url="192.168.8.150:3306" password="123456" user="root"/>
</dataHost>

<dataHost balance="0" maxCon="1000" minCon="10" name="host3" writeType="0" switchType="1"
          slaveThreshold="100" dbType="mysql" dbDriver="native">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM1" url="192.168.8.151:3306" password="123456" user="root"/>
</dataHost>

3.1范围分片配置—rule.xml

<tableRule name="rang-long-cust">
    <rule>
        <columns>id</columns>
        <algorithm>rang-long-cust</algorithm>
    </rule>
</tableRule>

<function name="rang-long-cust" class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">rang-long-cust.txt</property>
</function>

插入customer表验证

INSERT INTO `customer` (`id`, `name`) VALUES (6666, '赵先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (7777, '钱先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (16666, '孙先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (17777, '李先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (26666, '周先生'); 
INSERT INTO `customer` (`id`, `name`) VALUES (27777, '吴先生');

3.2 取模分片(ER 表)配置—rule.xml

<tableRule name="mod-long-order">
    <rule>
        <columns>order_id</columns>
        <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    <property name="count">3</property>
</function>

sql

-- order_info
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (1, 1000001, 1, 2, '2019-9-23 14:35:37', '2019-9-23 14:35:37'); 
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (2, 1000002, 1, 2, '2019-9-24 14:35:37', '2019-9-24 14:35:37'); 
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (3, 1000003, 3, 1, '2019-9-25 11:35:49', '2019-9-25 11:35:49');

-- order_detail
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (3, 20180001, 85114752, 19.99, 1, 1, 1); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (1, 20180002, 25411251, 1280.00, 1, 1, 0); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (1, 20180003, 62145412, 288.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180004, 21456985, 399.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180005, 21457452, 1680.00, 1, 1, 2); 
INSERT INTO `order_detail` (`order_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180006, 65214789, 9999.00, 1, 1, 3);

3.3 全局表配置—rule.xml

<table name="student" dataNode="dn1,dn2,dn3" primaryKey="sid" type="global"/>
-- student
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (1, '张三', '166669999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (2, '李四', '466669999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (3, '王五', '368828888');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (4, '马六', '655556666'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (5, '赵七', '265286999'); 
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (6, '小明', '516895555');

4.Mycat全局id

Mycat 全局序列实现方式主要有 4 种:本地文件方式、数据库方式、本地时间戳算法、ZK。也可以自定义业务序列。 注意获取全局 ID 的前缀都是:MYCATSEQ_

4.1 文件方式

配置文件 server.xml sequnceHandlerType 值:
0 文件 1 数据库 2 本地时间戳 3 ZK

<property name="sequnceHandlerType">0</property>

文件方式,配置 conf/sequence_conf.properties

CUSTOMER.HISIDS= 
CUSTOMER.MINID=10000001 
CUSTOMER.MAXID=20000000 
CUSTOMER.CURID=10000001

语法:select next value for MYCATSEQ_CUSTOMER

INSERT INTO `customer` (`id`, `name`) VALUES (next value for MYCATSEQ_CUSTOMER, 'xhc');

优点:本地加载,读取速度较快。
缺点:当 Mycat 重新发布后,配置文件中的 sequence 需要替换。Mycat 不能做集群部署。

4.2 数据库方式

<property name="sequnceHandlerType">1</property>

配置: sequence_db_conf.properties
把这张表创建在 146 上,所以是 dn1

#sequence stored in datanode 
GLOBAL=dn1 
CUSTOMER=dn1

在第一个数据库节点上创建 MYCAT_SEQUENCE 表:

DROP TABLE IF EXISTS MYCAT_SEQUENCE;
CREATE TABLE MYCAT_SEQUENCE
(
    name          VARCHAR(50) NOT NULL,
    current_value INT         NOT NULL,
    increment     INT         NOT NULL DEFAULT 1,
    remark        varchar(100),
    PRIMARY KEY (name)
) ENGINE = InnoDB;

注:可以在 schema.xml 配置文件中配置这张表,供外部访问。

<table name="mycat_sequence" dataNode="dn1" autoIncrement="true" primaryKey="id"></table>

创建存储过程——获取当前 sequence 的值

DROP FUNCTION IF EXISTS `mycat_seq_currval`; DELIMITER ;;
CREATE
    DEFINER = `root`@`%` FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET latin1 DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64); SET retval = "-999999999,null";
    SELECT concat(CAST(current_value AS CHAR), ",", CAST(increment AS CHAR))
    INTO retval
    FROM MYCAT_SEQUENCE
    WHERE name = seq_name;
    RETURN retval;
END ;;DELIMITER ;

创建存储过程,获取下一个 sequence

DROP FUNCTION IF EXISTS `mycat_seq_nextval`; DELIMITER ;;
CREATE
    DEFINER = `root`@`%` FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS varchar(64) CHARSET latin1 DETERMINISTIC
BEGIN
    UPDATE MYCAT_SEQUENCE SET current_value = current_value + increment WHERE name = seq_name;
    RETURN mycat_seq_currval(seq_name);
END ;;DELIMITER ;

创建存储过程,设置 sequence

DROP FUNCTION IF EXISTS `mycat_seq_setval`; DELIMITER ;;
CREATE
    DEFINER = `root`@`%` FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50), value INTEGER) RETURNS varchar(64) CHARSET latin1 DETERMINISTIC
BEGIN
    UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name; RETURN mycat_seq_currval(seq_name);
END ;;DELIMITER ;

插入记录

INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('GLOBAL', 1, 100,''); 
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment,remark) VALUES ('ORDERS', 1, 100,'订单表使 用');

测试

select next value for MYCATSEQ_ORDERS

4.3 本地时间戳方式

ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) ,长度为 18 位

<property name="sequnceHandlerType">2</property>

配置文件 sequence_time_conf.properties

#sequence depend on TIME 
WORKID=01 
DATAACENTERID=01

验证:select next value for MYCATSEQ_GLOBAL

4.4 ZK方式

修改 conf/myid.properties
设置 loadZk=true(启动时会从 ZK 加载配置,一定要注意备份配置文件,并且先用 bin/init_zk_data.sh,把配置文件写入到 ZK)

<property name="sequnceHandlerType">3</property>

配置文件:sequence_distributed_conf.properties

# 代表使用 zk 
INSTANCEID=ZK
# 与 myid.properties 中的 CLUSTERID 设置的值相同 
CLUSTERID=010

复制配置文件

cd /usr/local/soft/mycat/conf 
cp *.txt *.xml *.properties zkconf/ 
chown -R zkconf/
cd /usr/local/soft/mycat/bin 
./init_zk_data.sh

验证:select next value for MYCATSEQ_GLOBAL

4.5 使用

在 schema.xml 的 table 标签上配置 autoIncrement=“true”,不需要获取和指定序列的情况下,就可以使用全局 ID 了。

5 Mycat 监控与日志查看

5.1 监控

  • 命令行监控
    连接到管理端口 9066,注意必须要带 IP
mysql -uroot -h127.0.0.1 -p123456 -P9066

全部命令:

mysql>show @@help;
命令 作用
show @@server 查看服务器状态,包括占用内存等
show @@database 查看数据库
show @@datanode 查看数据节点
show @@datasource 查看数据源
show @@connection 该命令用于获取 Mycat 的前端连接状态,即 应用与 mycat 的连接
show @@backend 查看后端连接状态
show @@cache 查看缓存使用情况 SQLRouteCache:sql 路由缓存。 TableID2DataNodeCache : 缓存表主键与分片对应关系。 ER_SQL2PARENTID :缓存 ER 分片中子表与父表关系
reload @@config 重新加载基本配置,使用这个命令时 mycat 服务不可用
show @@sysparam 参看参数
show @@sql.high 执行频率高的 SQL
show @@sql.slow 慢 SQL 设置慢 SQL 的命令:reload @@sqlslow=5 ;
  • mycatweb监控
    https://github.com/MyCATApache/Mycat-download/tree/master/mycat-web-1.0
    Mycat-eye 是 mycat 提供的一个监控工具,它依赖于 ZK。 本地必须要运行一个 ZK,必须先启动 ZK。
    参考:https://gper.club/articles/7e7e7f7ff7g59gc3g64
    下载 mycat-web
cd /usr/local/soft 
wget http://dl.mycat.io/mycat-web-1.0/Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz 
tar -xzvf Mycat-web-1.0-SNAPSHOT-20170102153329-linux.tar.gz

启动 mycat-web

cd mycat-web nohup 
./start.sh &

停止:kill start.jar 相关的进程
访问端口 8082
http://192.168.8.151:8082/mycat/
mycat server.xml 配置

<!-- 1 为开启实时统计、0 为关闭 --> 
<property name="useSqlStat">1</property>

重启 mycat 服务生效

5.2 日志

log4j 的 level 配置要改成 debug

  • wrapper.log 日志
    wrapper 日志:mycat 启动,停止,添加为服务等都会记录到此日志文件,如果系统环境配置错误或缺少配置时,导致 Mycat 无法启动,可以通过查看 wrapper.log 定位具体错误原因。
  • mycat.log 日志
    mycat.log 为 mycat 主要日志文件,记录了启动时分配的相关 buffer 信息,数据源连接信息,连接池,动态类加载信息等等。
    在 conf/log4j2.xml 文件中进行相关配置,如保留个数,大小,字符集,日志文件大 小等。

猜你喜欢

转载自blog.csdn.net/nonage_bread/article/details/111772698