专栏首页生活不止眼前的代码基于MyCat1.6.5的同库分表 主从分离 自定义分片规则

基于MyCat1.6.5的同库分表 主从分离 自定义分片规则

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/yingziisme/article/details/81836871

本文基于MyCat最新的源码 https://github.com/MyCATApache/Mycat-Server MyCat作为目前比较主流的数据库中间件,开源也便于个人作自定义的开发

MyCat 官网: http://www.mycat.io/ 开发指南: http://www.mycat.io/document/mycat-definitive-guide.pdf

之前本来基于1.6使用,但是实际测试过程中发现不支持同库分表,也是就是subTables这个属性,于是下载了最新的源码使用

同库分表配置

schema.xml Schema.xml 作为 MyCat 中重要的配置文件之一,管理着 MyCat 的逻辑库、表、分片规则、DataNode 以及 DataSource。

<!-- name:显示的表名 sqlMaxLimit:最多查询的数据条数 -->
<schema name="aaadb" checkSQLschema="false" sqlMaxLimit="100">
    <!-- name:逻辑表表名 primaryKey:逻辑表对应真实表的主键 autoIncrement:主键自增长 dataNode:逻辑表所属的dataNode rule:逻辑表使用的规则名字 -->
    <table name="testtb" primaryKey="id" autoIncrement="true" subTables="testtb$1-12" dataNode="dn11" rule="part-by-hour"/>
</schema>

<dataNode name="dn11" dataHost="localhost1" database="aaadb1" />

<!-- balance="3",所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力 -->
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
    <!-- 用于和后端数据库进行心跳检查的语句 -->
    <heartbeat>select user()</heartbeat>
    <!-- 写实例 -->
    <writeHost host="hostM1" url="localhost:3306" user="root" password="123456">
        <!-- 读实例 -->
        <readHost host="hostS1" url="localhost:4406" user="root" password="123456" />
    </writeHost>
</dataHost>

这里有一个前提是配置好3306端口和4406端口两个数据库,将3306设置为写数据库,4406设置为读数据库,实际应用中应该把两个数据库配置好主从同步,以便数据一致性,这里为了看出读写分离,并未做主从同步。

rule.xml 定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。

<!-- columns: 对应数据库中的字段 algorithm: 下面定义的function的名字 -->
<tableRule name="part-by-hour">
    <rule>
        <columns>create_date</columns>
        <algorithm>part-by-hour</algorithm>
    </rule>
</tableRule>

<!-- class: 分片规则的类 -->
<function name="part-by-hour"
          class="io.mycat.route.function.PartitionByHour">
    <!-- name: 分片规则的类中需要传入的初始化值 -->
    <property name="timeInerval">8</property>
    <property name="partitionNum">12</property>
    <property name="beginDate">2018-08-16 00:00:00</property>
</function>

这里的io.mycat.route.function.PartitionByHour分片规则是自定义的规则,根据时间间隔来区别

server.xml 几乎保存了所有 mycat 需要的系统配置信息。其在代码内直接的映射类为 SystemConfig 类。

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

通过以上的配置我们就实现了对aaadb数据库的testtb进行同库分表,实际数据将写在后端mysql数据库中的testtb1~12表中,同时配置了读写分离,写数据的时候写入3306端口的数据库,读数据从4406端口的数据库读,通过写入一条数据,可以发现在3306端口的数据中可以查到该数据,而直接查询却查不到该数据(从4406中查询,4406端口和3306端口没有配置同步数据)。

io.mycat.route.function.PartitionByHour分片规则是自定义的规则,在源码中所有分片都存放于io.mycat.route.function包中,照着其他分片规则定义的模式自定义了一个分片规则

/**
 * PartitionByHour
 *
 * @author MT.LUO
 * 2018/8/18 15:10
 * @Description:  根据初始时间beginDate,每隔timeInerval小时的时间,存储在不同分片,
 *                  超过partitionNum又从0节点开始存储
 */
public class PartitionByHour extends AbstractPartitionAlgorithm implements RuleAlgorithm {
    /**
     * 时间间隔的单位是小时
     */
    private int timeInerval = 1;

    /**
     * 一共有多少个分片
     */
    private int partitionNum = 1;

    /**
     * 开始存储的日期  yyyy-MM-dd HH:mm:ss
     */
    private String beginDate;
    private String dateFormat = "yyyy-MM-dd HH:mm:ss";

    private long tBeginDate;

    private static final long oneHour = 3600000;
    private ThreadLocal<SimpleDateFormat> formatter;

    public void setTimeInerval(int timeInerval) {
        this.timeInerval = timeInerval;
    }

    public void setPartitionNum(int partitionNum) {
        this.partitionNum = partitionNum;
    }

    public void setBeginDate(String beginDate) {
        this.beginDate = beginDate;
    }

    /**
     * 初始化数据,主要是设置起始时间
     */
    @Override
    public void init() {
        try {
            tBeginDate = new SimpleDateFormat(dateFormat).parse(beginDate).getTime();

            formatter = new ThreadLocal<SimpleDateFormat>() {
                @Override
                protected SimpleDateFormat initialValue() {
                    return new SimpleDateFormat(dateFormat);
                }
            };

        } catch (ParseException e) {
            throw new java.lang.IllegalArgumentException(e);
        }
    }

    /**
     * 实际分片计算规则的函数
     * @param columnValue 传入的数据字段值
     * @return 返回dataNode的标号
     */
    @Override
    public Integer calculate(String columnValue) {
        try {
            String tmp = columnValue.substring(0, columnValue.length() - 5) + "00:00";
            System.out.println(".................columnValue: " + columnValue + ", " + tmp);
            long currentTime = new SimpleDateFormat(dateFormat).parse(tmp).getTime();
            System.out.println(".................PartitionByHour: " + tmp + ", " + currentTime);
            if ((currentTime - tBeginDate) >= 0) {
                int count = (int) ((currentTime - tBeginDate) / oneHour);
                System.out.println(".................count: " + count);
                return (count / timeInerval) % partitionNum;

            } else {
                throw new java.lang.IllegalArgumentException("invalid columnValue param: that value should be bigger " +
                        "" + "than " + beginDate);
            }
        } catch (ParseException e) {
            throw new java.lang.IllegalArgumentException("invalid columnValue param: that format should be " +
                    "yyyy-MM-dd" + " HH:mm:ss");
        }
    }

}

使用的时候就如上面rule.xml写的那样 function的class属性写分片规则的类 在property 的name属性中写规则需要初始化的数值

<function name="part-by-hour"
          class="io.mycat.route.function.PartitionByHour">
    <property name="timeInerval">8</property>
    <property name="partitionNum">12</property>
    <property name="beginDate">2018-08-16 00:00:00</property>
</function>

在实际使用中,因为应用项目使用了spring data jpa,在连接数据库的时候遇到了报错(springboot2.0.3)

Caused by: java.sql.SQLException: No dataNode found ,please check tables defined in schema:aaadb

由于使用同库分表的时候,mycat层显示出来的数据库是aaadb,表为testtb。 后端对应的是aaadb,表为testtb1~12。 因为jpa会发送show full tables like ‘%’; mycat在解析这条语句的时候会把这条语句发送到后端数据库,从而使应用程序拿到的表为testtb1~12。jpa 会进一步发送show full columns from testtb1。这是mycat就会报错。

目前的处理方法是修改io.mycat.server.parser. ServerParseShow里面的fullpattern令其匹配这句话。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringDataJPA笔记(4)-命名查询与基础规则

    在相应的Repository接口里面定义一个同名的方法,Spring会先找是否有同名的NamedQuery,如果有,那么就不会按照接口定义的方法来解析

    yingzi_code
  • SpringDataJPA笔记(13)-Union查询

    在JPA中,对Union查询可以通过两种方式,一种是通过Inheritance的注解来实现,一种是通过子查询来实现,个人觉得子查询的方式更加灵活一点

    yingzi_code
  • 分布式缓存--一致性hash原理和hash槽,以及算法实现

    我们在使用n台存储设备存储数据的时候,常规做法有将数据根据key%n这样计算放在哪台服务器,但是在扩容的时候就会遇到数据迁移的问题,比如扩容m台服务器,以前是k...

    yingzi_code
  • Mycat 核心配置详解

    由上图可以看到 Mycat 的核心配置文件均采用xml格式,这几个配置文件的用途如下:

    端碗吹水
  • 鼠标点击层以外的地方层隐藏

    根据去哪儿(http://www.qunar.com/)输入框点击按钮而写 实现功能效果: 1、点击按钮,相应层显示,点击层以外的部分层隐藏; 2、重复点击按钮...

    Porschev
  • Zabbix批量导入主机

    在实际工作环境中我们一个集群里面可能有十几上百台一摸一样的主机,需要监控的内容也是一摸一样的,这个时候我们就可以使用下面的方式批量导入主机了

    张琳兮
  • python实现斗地主分牌洗牌

    本文实例为大家分享了python实现斗地主分牌洗牌的具体代码,供大家参考,具体内容如下

    砸漏
  • 搭建知识库xwiki

    之前自己使用Django admin和suit完成了初步的个人知识库管理,基本完成了我自己的浏览器标签库和微信公众号的统一管理,但是有一个问题,那就是目前我只能...

    jeanron100
  • 前端成神之路-vue02

    海仔
  • Python3快速入门(九)——Pyth

    一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,与进程内...

    py3study

扫码关注云+社区

领取腾讯云代金券