MyCat基本使用

587 阅读10分钟

先发制人:分库分表参考: 分库分表面试参考


1,分片概述

连续
 范围分片
 日期/时间分片
优点:
    1)范围条件查询消耗资源少(不需要汇总数据) 
    2)扩容无需迁移数据(分片固定)
缺点:
    1)存在数据热点的可能性 
    2)并发访问能力受限于单一或少量DataNode(访问集中)
 
离散
 取模
 枚举
 一致性哈希 (比如把分片键的值进行哈希,然后根据哈希值进行算法计算)
 固定分片哈希
 取模范围
 范围取模
优点:
    1)并发访问能力增强(负载到不同的节点)
    2)范围条件查询性能提升(并行计算) [性能提升仅仅是多个节点都查询了一部分数据]
缺点:
    1)数据扩容比较困难,涉及到数据迁移问题 
    2)数据库连接消耗比较多

2,分片测试

2.1 测试 全局表

在Mycat连接中gupao数据库中执行
--测试全局表
INSERT INTO `dict` (`id`, `param_code`, `param_name`) VALUES (1, '0731', '长沙市');

然后三张表都有了数据

image.png image.png image.png 查询呢?原来会随机路由到三个数据节点中某一个 EXPLAIN SELECT * from dict;

image.png image.png

2.2 枚举分片

122123124数据节点中创建表:
CREATE TABLE `sharding_by_intfile` (
    `age` int(11) NOT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在schema.xml中配置:
<table name="sharding_by_intfile" primaryKey="age"  dataNode="122-imall,123-imall,124-imall" rule="qs-sharding-by-intfile" />

在rule.xml中配置:
<tableRule name="qs-sharding-by-intfile">
        <rule>
                <columns>age</columns>
                <algorithm>hash-int</algorithm>
        </rule>
</tableRule>

更改partition-hash-int.txt文件内容如下:
16=0
17=1
18=2

然后在Mycat服务节点上执行下面sql:
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_intfile` (age,db_nm) VALUES (18, database());

结果:

image.png image.png image.png

2.3 范围分片

之前schema.xml中配置:
<table name="customer" primaryKey="id" dataNode="122-imall,123-imall,124-imall" rule="auto-sharding-long" />

修改算法中的文件autopartition-long.txt
原来:
0-500M=0
500M-1000M=1
1000M-1500M=2
改成:
0-10000=0
10001-20000=1
20001-30000=2

原理:在Mycat上执行的时候,执行的sql语句会根据schema.xml配置的数据节点以及rule.xml中配置的算法找到真正要落地的数据节点,
     然后把相应的sql路由到相应的数据节点上进行执行。

在Mycat服务中执行下面sql:

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, '吴先生');

image.png

image.png

image.png

image.png 在Mycat服务上查询

image.png

2.4 取模分片

-- rule.xml中
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
        <!-- 有几个节点,这里count就要配置几 -->
        <property name="count">3</property>
</function>
--测试取模分片(在mycat连接中gupao数据库中执行)
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (1, '李大彪', '166669999');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (4, '菜狗子', '655556666');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (2, '等候那場雪', '466669999');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (5, '猫老公', '265286999');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (3, 'tj-大白', '368828888');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (6, '大郎', '516895555');

但是报错:

Error : bad insert sql (sharding column:ID not provided,INSERT INTO `student` (`sid`, `name`, `qq`)

原来是rule.xml中 mod-long算法没改

-- 原来的
<tableRule name="mod-long">
    <rule>
            <columns>id</columns>
            <algorithm>mod-long</algorithm>
    </rule>
</tableRule>
-- 改成
<tableRule name="mod-long">
    <rule>
            <columns>sid</columns>
            <algorithm>mod-long</algorithm>
    </rule>
</tableRule>

然后重启mMycat服务,执行sql文件成功!

image.png

image.png

image.png

image.png

2.5 一致性哈希分片

参考:一致性哈希算法是什么?怎么判定哈希算法的好坏

122123124数据节点中创建表:
CREATE TABLE `sharding_by_intfile` (
    `age` int(11) NOT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在schema.xml中配置:
<table name="sharding_by_murmur" primaryKey="id"  dataNode="122-imall,123-imall,124-imall" rule="qs-sharding-by-murmur" />

在rule.xml中新增:
<function name="qs-murmur" class="io.mycat.route.function.PartitionByMurmurHash">
        <property name="seed">0</property><!-- 默认是0 -->
        <property name="count">3</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
        <property name="virtualBucketTimes">160</property>
</function>

然后在Mycat服务节点上执行下面sql:
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (1, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (2, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (3, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (4, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (5, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (6, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (7, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (8, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (9, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (10, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (11, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (12, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (13, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (14, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (15, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (16, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (17, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (18, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (19, database());
INSERT INTO `sharding_by_murmur` (id,db_nm) VALUES (20, database());

查询:

image.png image.png image.png

2.6 固定分片哈希

122123124数据节点中创建表:
CREATE TABLE `sharding_by_long` (
  `id` int(10) DEFAULT NULL,
  `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在schema.xml中配置:
<table name="sharding_by_long"  dataNode="122-imall,123-imall,124-imall" rule="qs-sharding-by-long" />

在rule.xml中配置:
<tableRule name="qs-sharding-by-long">
        <rule>
                <columns>id</columns>
                <algorithm>qs-sharding-by-long</algorithm>
        </rule>
</tableRule>
<function name="qs-sharding-by-long" class="io.mycat.route.function.PartitionByLong">
        <!-- partitionCount的数量必须要和partitionLength的数量一样,比如都是两个 -->
        <property name="partitionCount">2,1</property>
        <property name="partitionLength">256,512</property>
</function>


然后在Mycat服务节点上执行下面sql:
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (222, database());  
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (333, database());  
INSERT INTO `sharding_by_long` (id,db_nm) VALUES (666, database());  
<property name="partitionCount">2,1</property>
<property name="partitionLength">256,512</property>
详解:partitionCount的数字加一起等于节点数,2+1=3; partitionCount和partitionLength的个数必须一致。如下图,partitionCount表示分成了两组,第一组是2,表示256有2个{[0-254],[255-511]},第二组是1,表示512有1个{[512-1023]}

image.png 查询:

image.png image.png image.png

2.7 取模范围分片 (注:连续分片和离散分片的组合)

取模范围:先取模再范围,比如先模100后再范围

在122,123,124数据节点中创建表:
CREATE TABLE `sharding_by_pattern` (
    `id` varchar(20) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在schema.xml中新增配置:
<table name="sharding_by_pattern" primaryKey="id"  dataNode="122-imall,123-imall,124-imall" rule="sharding-by-pattern" />

在rule.xml中新增配置:
<tableRule name="sharding-by-pattern">
        <rule>
                <columns>id</columns>
                <algorithm>sharding-by-pattern</algorithm>
        </rule>
</tableRule>
<function name="sharding-by-pattern"  class="io.mycat.route.function.PartitionByPattern">
        //先取模再范围,比如先模100后再范围(1-20=0,21-70=1,71-100=2)
        <property name="patternValue">100</property>
        <property name="defaultNode">0</property>
        <property name="mapFile">partition-pattern.txt</property>
</function>

//新增partition-pattern.txt文件
1-20=0
21-70=1
71-100=2

然后在Mycat服务节点上执行下面sql:
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (19, database()); 
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (222, database());  
INSERT INTO `sharding_by_pattern` (id,db_nm) VALUES (371, database()); 

查询:

image.png image.png image.png

2.8 范围取模 (先范围后取模)

取模范围:先取模再范围,比如先模100后再范围

在122123124数据节点中创建表:
CREATE TABLE `sharding_by_rang_mod` (
    `id` bigint(20) DEFAULT NULL,
    `db_nm` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在schema.xml中新增配置:
<table name="sharding_by_range_mod"  dataNode="122-imall,123-imall,124-imall" rule="qs-sharding-by-range-mod"/>

在rule.xml中新增配置:
<tableRule name="qs-sharding-by-range-mod">
                <rule>
                        <columns>id</columns>
                        <algorithm>qs-range-mod</algorithm>
                </rule>
        </tableRule>
<function name="qs-range-mod" class="io.mycat.route.function.PartitionByRangeMod">
        <property name="mapFile">partition-range-mod.txt</property>
</function>

//修改partition-range-mod.txt文件
0-20000=1     //id在0-20000只能路由在122节点上
20001-40000=2  //id在20001-40000之间的模20落在123节点,模21落在124节点

在Mycat服务上新增数据:
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (666, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (6666, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (16666, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (21111, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (22222, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (23333, database());  
INSERT INTO `sharding_by_rang_mod` (id,db_nm) VALUES (24444, database());  

查询: image.png image.png image.png

2.9 单库分表

1),之前schema.xml中的的配置:
<!-- 单库分表,只在一个节点上根据月份分成三个表  -->
<table name="fee" primaryKey="id" subTables="fee2025$1-3" dataNode="122-gupao" rule="sharding-by-month" />

2),修改rule.xml文件
<function name="partbymonth" class="io.mycat.route.function.PartitionByMonth">
        <property name="dateFormat">yyyy-MM-dd</property>
        <property name="sBeginDate">2015-01-01</property>
</function>
改为:
<function name="partbymonth" class="io.mycat.route.function.PartitionByMonth">
        <property name="dateFormat">yyyy-MM-dd</property>
        <property name="sBeginDate">2025-01-01</property>
</function>
-- 在第一个数据库节点(122)gupao数据库创建单库分片表,创建好之后在Mycat服务中都有这些表了
CREATE TABLE `fee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

CREATE TABLE `fee20251`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) 
);
CREATE TABLE `fee20252`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) 
);
CREATE TABLE `fee20253`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) 
);

然后在Mycat服务连接中gupao数据库中执行

INSERT INTO `fee` (`id`, `create_time`) VALUES (1, '2025-1-1 14:46:19');
INSERT INTO `fee` (`id`, `create_time`) VALUES (2, '2025-2-1 14:46:19');
INSERT INTO `fee` (`id`, `create_time`) VALUES (3, '2025-3-1 14:46:19');

image.png 然后可以看到数据节点122上面

image.png

image.png

image.png

2.10 ER分片

image.png

<table name="order_info" dataNode="122-imall,123-imall,124-imall" rule="mod-long-order">
        <childTable name="order_detail" primaryKey="id" joinKey="order_id" parentKey="order_id"> </childTable>
</table>
-- 测试ER分片(在Mycat连接中imall数据库中执行)
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (1, 1000001, 1, 2, '2025-9-23 14:35:37', '2025-9-23 14:35:37');
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (2, 1000002, 1, 2, '2025-9-24 14:35:37', '2025-9-24 14:35:37');
INSERT INTO `order_info` (`order_id`, `uid`, `nums`, `state`, `create_time`, `update_time`) VALUES (3, 1000003, 3, 1, '2025-9-25 11:35:49', '2025-9-25 11:35:49');

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);

-- 122 image.png image.png -- 123 image.png image.png -- 124 image.png image.png

2.11 非分片表

之前schema.xml中配置:
<table name="noshard" primaryKey="id" autoIncrement="true" dataNode="122-gupao" />


-- 在第一个数据库节点(122)gupao数据库创建非分片表
CREATE TABLE `noshard` (
  `id` bigint(30) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

truncate table noshard;

-- 测试非分片表(在Mycat连接中gupao数据库中执行)
INSERT INTO `noshard` (`id`, `name`) VALUES (1, '这是一条没有分片的数据');

查询的时候就只会把sql路由到固定的数据节点上进行查询:

image.png

3,全局ID

1,文件方式(0)
2,数据库方式(1)
3,时间戳方式(2)
4,ZK方式(3)

<!-- 0文件方式,1数据库方式,3时间戳方式,4ZK方式 -->
<property name="sequenceHandlerType">2</property>

3.1 文件方式

<property name="sequenceHandlerType">0</property>
select 'next value for MYCATSEQ_CUSTOMER';

image.png

image.png

3.2 数据库方式

server.xml中配置:
<property name="sequenceHandlerType">1</property>
schema.xml中配置:
<table name="mycat_sequence" dataNode="122-imall" autoIncrement="true" primaryKey="id"></table>
//新建一张表和三个函数
-- 在第一个数据库节点上(122)imall数据库创建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;

-- 122-imall创建函数——获取当前sequence的值
DROP FUNCTION IF EXISTS `mycat_seq_currval`;
DELIMITER ;;
CREATE DEFINER=`heymen_127`@`%` 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 ;
-- 122-imall创建函数,获取下一个sequence
DROP FUNCTION IF EXISTS `mycat_seq_nextval`;
DELIMITER ;;
CREATE DEFINER=`heymen_127`@`%` 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 ;
-- 122-imall创建函数,设置sequence
DROP FUNCTION IF EXISTS `mycat_seq_setval`;
DELIMITER ;;
CREATE DEFINER=`heymen_127`@`%` 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 ; 

--122-imall数据节点插入记录
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,'订单表使用');


修改sequence_db_conf.properties文件

GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1
改为:
GLOBAL=122-imall
COMPANY=122-imall
CUSTOMER=122-imal
ORDERS=122-imall

然后Mycat服务节点上执行

select 'next value for MYCATSEQ_ORDERS';

image.png

3.3 时间戳方式

在server.xml中配置:

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

其实是使用了sequence_time_conf.properties文件,此文件不需修改

//其实就是雪花算法,【雪花算法就是基于时间戳的】
#sequence depend on TIME
WORKID=01
DATAACENTERID=01

在Mycat服务节点运行:

select 'next value for MYCATSEQ_GLOBAL';

每次执行生成的都不一样:

image.png

image.png 那么每次insert的时候必须要把 select 'next value for MYCATSEQ_GLOBAL' 写在指定的ID的位置上呢?没有必要,可以在schema.xml中配置一个字段:拿不分库的noshard来来说,配置一下:

<!-- 配置autoIncrement="true" 然后在新增记录的时候不设置id字段,则会用mycat的自增ID -->
<table name="noshard" primaryKey="id" autoIncrement="true" dataNode="122-gupao" />

测试

insert into `noshard` (`name`) VALUES ('这里是测试mycat全局自增ID的');

image.png

image.png

4,使用问题

4.1 Mycat连接MySQL8.0遇到的问题

把Mycat的lib目录下的MySQL驱动包升级到8.0 比如:mysql-connector-java-8.0.16.jar