天天看點

基于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令其比對這句話。