Mycat实现单库水平分表、按月分表

简介: Mycat实现单库分表,实现按月分表,实现时间戳按月分表

Mycat实现单库水平分表、按月分表


前期准备


下载地址:http://dl.mycat.org.cn/。选择版本进行下载即可。


本文使用的是1.6.7.6Linux版本。



解压即可使用。


注意事项


Mycat单库分表需要准备空白表,提前制定好分表规则。


分表的时候使用到了该字段作为分表字段,数据会按照制定好的分表规则存入不同的数据库或表里面。Mycat中是不允许修改作为分表依据的列的,所以更新是需要去掉此列。


取模分表


对分片字段求摸运算。也是水平分表最常用规则。


配置步骤:


  • 修改配置文件schema.xml
<tablename="test_table"primaryKey="id"subTables="test_table_$1-10"dataNode="dn1"rule="fk-pro-file"/>


  • 修改配置文件rule.xml
<tableRulename="mod_rule"><rule><columns>customer_id</columns><algorithm>mod-long</algorithm></rule></tableRule>


  • columns:分片字段
  • algorithm:分片函数

mod-long规则为配置文件自带的,修改count属性。代表意思为,根据count数取模

<functionname="mod-long"class="io.mycat.route.function.PartitionByMod"><!-- how many data nodes --><propertyname="count">10</property></function>
  • 需单独在数据节点dn1创建test_table_1test_table_10
    建表语句:
CREATETABLE `test_table_1`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_2`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_3`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_4`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_5`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_6`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_7`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_8`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_9`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_table_10`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


  • 重启Mycat
  • 连接Mycat
  • 插入数据
INSERTINTO test_table(id, NAME)VALUES(1,'张三1');INSERTINTO test_table(id, NAME)VALUES(2,'张三2');INSERTINTO test_table(id, NAME)VALUES(3,'张三3');INSERTINTO test_table(id, NAME)VALUES(4,'张三4');INSERTINTO test_table(id, NAME)VALUES(5,'张三5');INSERTINTO test_table(id, NAME)VALUES(6,'张三6');INSERTINTO test_table(id, NAME)VALUES(7,'张三7');INSERTINTO test_table(id, NAME)VALUES(8,'张三8');INSERTINTO test_table(id, NAME)VALUES(9,'张三9');INSERTINTO test_table(id, NAME)VALUES(10,'张三10');


  • 登录数据库dn1查看数据分布情况


范围约定分表


此分片适用于提前规划好分片字段某个范围属于哪个分片。


配置步骤:


  • 修改配置文件schema.xml
<tablename="test_range"primaryKey="id"subTables="test_range_$1-3"dataNode="dn1"rule="auto_sharding_long"/>


  • 修改配置文件rule.xml
<tableRulename="auto_sharding_long"><rule><columns>id</columns><algorithm>rang-long</algorithm></rule></tableRule>


  • columns:分片字段
  • algorithm:分片函数
<functionname="rang-long"class="io.mycat.route.function.AutoPartitionByLong"><propertyname="mapFile">autopartition-long.txt</property><propertyname="defaultNode">0</property></function>
  • mapFile:标识配置文件名称
  • defaultNode:默认节点。小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点,设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错。
  • 修改配置文件autopartition-long.txt
0-99=0
100-199=1
200-300=2


  • 需单独在数据节点dn1创建test_range_1test_range_3
    建表语句:
CREATETABLE `test_range_1`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_range_2`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_range_3`(  `id` BIGINT,  `name` VARCHAR(255)) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


  • 重启Mycat
  • 连接Mycat
  • 插入数据
INSERTINTO test_range(id, NAME)VALUES(1,'1');INSERTINTO test_range(id, NAME)VALUES(100,'100');INSERTINTO test_range(id, NAME)VALUES(200,'200');


  • 登录数据库dn1查看数据分布情况


按月分表


此规则为按天分片。设定时间格式、范围。


配置步骤:


  • 修改配置文件schema.xml
<tablename="test_month"primaryKey="id"subTables="test_month_2022$1-12"dataNode="dn1"rule="sharding-by-month"/>


  • 修改配置文件rule.xml
<tableRulename="sharding-by-month"><rule><columns>create_date</columns><algorithm>partbymonth</algorithm></rule></tableRule>


  • columns:分片字段
  • algorithm:分片函数
<functionname="partbymonth"class="io.mycat.route.function.PartitionByMonth"><propertyname="dateFormat">yyyy-MM-dd</property><propertyname="sBeginDate">2022-01-01</property><!-- <property name="sEndDate">2022-12-31</property> --></function>
  • dateFormat :日期格式。
  • 字段类型为date使用yyyy-MM-dd
  • 字段类型为datetime使用yyyy-MM-dd HH:mm:ss
  • sBeginDate :开始日期
  • sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
  • 需单独在数据节点dn1创建test_month_20221test_month_202212
    建表语句:
CREATETABLE `test_month_20221`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20222`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20223`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20224`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20225`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20226`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20227`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20228`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_20229`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_202210`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_202211`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_202212`(  `id` BIGINT,  `name` VARCHAR(255),  `create_date` DATE) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


  • 重启Mycat
  • 连接Mycat
  • 插入数据
INSERTINTO test_month(id,NAME,create_date)VALUES(1,'2022-01-01','2022-01-01');INSERTINTO test_month(id,NAME,create_date)VALUES(2,'2022-02-01','2022-02-01');INSERTINTO test_month(id,NAME,create_date)VALUES(3,'2022-03-01','2022-03-01');INSERTINTO test_month(id,NAME,create_date)VALUES(4,'2022-04-01','2022-04-01');INSERTINTO test_month(id,NAME,create_date)VALUES(5,'2022-05-01','2022-05-01');INSERTINTO test_month(id,NAME,create_date)VALUES(6,'2022-06-01','2022-06-01');INSERTINTO test_month(id,NAME,create_date)VALUES(7,'2022-07-01','2022-07-01');INSERTINTO test_month(id,NAME,create_date)VALUES(8,'2022-08-01','2022-08-01');INSERTINTO test_month(id,NAME,create_date)VALUES(9,'2022-09-01','2022-09-01');INSERTINTO test_month(id,NAME,create_date)VALUES(10,'2022-10-01','2022-10-01');INSERTINTO test_month(id,NAME,create_date)VALUES(11,'2022-11-01','2022-11-01');INSERTINTO test_month(id,NAME,create_date)VALUES(12,'2022-12-01','2022-12-01');


  • 登录数据库dn1查看数据分布情况


按月分表-适配时间戳


该方式针对于表设计时,将日期字段类型设置为bigint,存储时间戳方式。Mycat按月分表仅支持datedatetime方式,如果需要按照时间戳来进行分表,则需要更改源码,增加分表规则。


下载Mycat源码


GitHub源码地址(目前最新版本为1.6.7.6-release):


https://github.com/MyCATApache/Mycat-Server.git


可使用git clone 下载源码


切换分支、tag命令:


git checkout 分支/tag名称


增加适配时间戳方式


  1. src/main/java源码目录下的io.mycat.route.function包下创建类PartitionByMonthTime。代码如下:
packageio.mycat.route.function;
importio.mycat.config.model.rule.RuleAlgorithm;
importorg.apache.log4j.Logger;
importjava.text.ParseException;
importjava.text.SimpleDateFormat;
importjava.util.*;
/*** 例子 通过时间戳按月份列分区 ,每个自然月一个分片,格式 between操作解析的范例** @author Micromaple*/publicclassPartitionByMonthTimeextendsAbstractPartitionAlgorithmimplementsRuleAlgorithm {
privatestaticfinalLoggerLOGGER=Logger.getLogger(PartitionByMonthTime.class);
/** 开始时间戳 */privateStringlBeginDate;
/** 默认格式 */privateStringdateFormat="yyyy-MM-dd";
/** 场景 */privateintscene=-1;
/** 结束时间戳 */privateStringlEndDate;
privateCalendarbeginDate;
privateCalendarendDate;
privateintnPartition;
privateThreadLocal<SimpleDateFormat>formatter;
@Overridepublicvoidinit() {
if (lBeginDate==null&&lEndDate==null) {
nPartition=12;
scene=1;
initFormatter();
beginDate=Calendar.getInstance();
beginDate.set(Calendar.MONTH, 0);
endDate=Calendar.getInstance();
endDate.set(Calendar.MONTH, 11);
return;
        }
beginDate=Calendar.getInstance();
beginDate.setTime(newDate(Long.parseLong(lBeginDate)));
initFormatter();
if (lEndDate!=null) {
endDate=Calendar.getInstance();
endDate.setTime(newDate(Long.parseLong(lEndDate)));
nPartition= ((endDate.get(Calendar.YEAR) -beginDate.get(Calendar.YEAR)) *12+endDate.get(Calendar.MONTH) -beginDate.get(Calendar.MONTH)) +1;
if (nPartition<=0) {
thrownewIllegalArgumentException("Incorrect time range for month partitioning!");
            }
        } else {
nPartition=-1;
        }
    }
privatevoidinitFormatter() {
formatter=newThreadLocal<SimpleDateFormat>() {
@OverrideprotectedSimpleDateFormatinitialValue() {
returnnewSimpleDateFormat(dateFormat);
            }
        };
    }
/*** For circulatory partition, calculated value of target partition needs to be* rotated to fit the partition range*/privateintreCalculatePartition(inttargetPartition) {
// 没有指定end_date,不是循环使用的情况,直接返回对应的targetPartitionif (nPartition==-1) {
returntargetPartition;
        }
/*** If target date is previous of start time of partition setting, shift* the delta range between target and start date to be positive value*/if (targetPartition<0) {
targetPartition=nPartition- (-targetPartition) %nPartition;
        }
if (targetPartition>=nPartition) {
targetPartition=targetPartition%nPartition;
        }
returntargetPartition;
    }
@OverridepublicIntegercalculate(StringcolumnValue) {
try {
if (scene==1) {
CalendarcurTime=Calendar.getInstance();
curTime.setTime(newDate(Long.parseLong(columnValue)));
returncurTime.get(Calendar.MONTH);
            }
inttargetPartition;
CalendarcurTime=Calendar.getInstance();
curTime.setTime(newDate(Long.parseLong(columnValue)));
targetPartition= ((curTime.get(Calendar.YEAR) -beginDate.get(Calendar.YEAR))
*12+curTime.get(Calendar.MONTH)
-beginDate.get(Calendar.MONTH));
/*** For circulatory partition, calculated value of target partition needs to be* rotated to fit the partition range*/if (nPartition>0) {
targetPartition=reCalculatePartition(targetPartition);
            }
// 防止越界的情况if (targetPartition<0) {
targetPartition=0;
            }
returntargetPartition;
        } catch (Exceptione) {
thrownewIllegalArgumentException(newStringBuilder().append("columnValue:").append(columnValue)
                .append(" Please check if the format satisfied.").toString(), e);
        }
    }
@OverridepublicInteger[] calculateRange(StringbeginValue, StringendValue) {
try {
returndoCalculateRange(beginValue, endValue, beginDate);
        } catch (ParseExceptione) {
LOGGER.error("error", e);
returnnewInteger[0];
        }
    }
privateInteger[] doCalculateRange(StringbeginValue, StringendValue, CalendarbeginDate) throwsParseException {
intstartPartition, endPartition;
CalendarpartitionTime=Calendar.getInstance();
SimpleDateFormatformat=newSimpleDateFormat(dateFormat);
partitionTime.setTime(newDate(Long.parseLong(beginValue)));
startPartition= ((partitionTime.get(Calendar.YEAR) -beginDate.get(Calendar.YEAR))
*12+partitionTime.get(Calendar.MONTH)
-beginDate.get(Calendar.MONTH));
partitionTime.setTime(newDate(Long.parseLong(endValue)));
endPartition= ((partitionTime.get(Calendar.YEAR) -beginDate.get(Calendar.YEAR))
*12+partitionTime.get(Calendar.MONTH)
-beginDate.get(Calendar.MONTH));
List<Integer>list=newArrayList<>();
while (startPartition<=endPartition) {
IntegernodeValue=reCalculatePartition(startPartition);
if (nodeValue<0) {
nodeValue=0;
            }
if (Collections.frequency(list, nodeValue) <1) {
list.add(nodeValue);
            }
startPartition++;
        }
intsize=list.size();
// 当在场景1: "2015-01-01", "2014-04-03" 范围出现的时候// 是应该返回null 还是返回 [] ?return (list.toArray(newInteger[size]));
    }
@OverridepublicintgetPartitionNum() {
intnPartition=this.nPartition;
returnnPartition;
    }
publicvoidsetlBeginDate(StringlBeginDate) {
this.lBeginDate=lBeginDate;
    }
publicvoidsetDateFormat(StringdateFormat) {
this.dateFormat=dateFormat;
    }
publicvoidsetlEndDate(StringlEndDate) {
this.lEndDate=lEndDate;
    }
}


  1. 执行打包命令mvn clean install -Dmaven.test.skip=true
  2. 找到target/classes/io/mycat/route/function目录下PartitionByMonthTime类编译后的class
  3. Mycat中间件服务的根目录找到lib文件夹进入,找到Mycat-server-1.6.7.6-release.jar
  4. 使用解压软件打开Mycat-server-1.6.7.6-release.jar
  5. 进入io/mycat/route/function目录,将PartitionByMonthTime类编译后的class放至该目录下
  6. 修改配置文件schema.xml
<tablename="test_month_time"primaryKey="id"subTables="test_month_time_2022$1-12"dataNode="dn1"rule="sharding-by-month-time"/>


  1. 修改配置文件rule.xml
<tableRulename="sharding-by-month-time"><rule><columns>create_time</columns><algorithm>partbymonthtime</algorithm></rule></tableRule>


  • dateFormat :日期格式
  • lBeginDate :开始日期
  • lEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入
  1. 需单独在数据节点dn1创建test_month_time_20221test_month_time_202212
    建表语句:
CREATETABLE `test_month_time_20221`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20222`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20223`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20224`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20225`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20226`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20227`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20228`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_20229`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_202210`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_202211`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;CREATETABLE `test_month_time_202212`(  `id` BIGINT,  `name` VARCHAR(255),  `create_time` bigint) CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


  1. 重启Mycat
  • 连接Mycat
  • 插入数据
INSERTINTO test_month_time(id,NAME,create_time)VALUES(1,'2022-01-01','1640966400000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(2,'2022-02-01','1643644800000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(3,'2022-03-01','1646064000000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(4,'2022-04-01','1648742400000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(5,'2022-05-01','1651334400000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(6,'2022-06-01','1654012800000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(7,'2022-07-01','1656604800000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(8,'2022-08-01','1659283200000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(9,'2022-09-01','1661961600000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(10,'2022-10-01','1664553600000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(11,'2022-11-01','1667232000000');INSERTINTO test_month_time(id,NAME,create_time)VALUES(12,'2022-12-01','1669824000000');


  • 登录数据库dn1查看数据分布情况
相关文章
|
6月前
|
缓存 关系型数据库 MySQL
分库分表知识总结(四)
分库分表知识总结(四)
76 1
|
6月前
|
存储 大数据 数据库
分库分表知识总结(三)之水平分表
分库分表知识总结(三)之水平分表
100 0
|
6月前
|
存储 监控 数据库
分库分表知识总结(二)之垂直分表
分库分表知识总结(二)之垂直分表
87 1
|
6月前
|
SQL 存储 数据库连接
什么是分库分表,为什么要分库分表?
笔者经常将缓存、分库分表、消息队列定义为高并发三剑客。开发互联网应用系统时,分库分表是一个绕不开的技术点。 这篇文章,我们会探讨如下问题:
|
2月前
|
SQL Oracle 关系型数据库
分库分表
分库分表
|
1月前
|
存储 Java 关系型数据库
分库分表专题
分库分表专题
|
4月前
|
中间件 数据库
分库分表全局查询
【7月更文挑战第12天】
139 12
|
6月前
|
存储 算法 数据库连接
为什么要分库分表
为什么要分库分表
为什么要分库分表
|
6月前
|
缓存 监控 Java
分库分表带来的问题
分库分表带来的问题
|
存储 负载均衡 Oracle
分库分表介绍
分库分表是一种用于解决大规模数据存储和查询性能问题的数据库架构设计技术。它将一个数据库拆分成多个独立的数据库实例(分库),并将每个数据库实例的表进一步拆分成多个子表(分表)。这样可以提高数据库的并发处理能力和查询性能。
249 0