Mycat分庫分表(一)

       隨着業務變得愈來愈複雜,用戶愈來愈多,集中式的架構性能會出現巨大的問題,好比系統會愈來愈慢,並且時不時會宕機,因此必需要解決高性能和可用性的問題。這個時候數據庫的優化就顯得尤其重要,在說優化方案前,先分析下數據庫性能瓶頸的緣由有哪些;node

     1.1數據庫性能瓶頸的分析

           好比說在高併發的狀況下鏈接數不夠了。或者數據量太大,查詢效率變得愈來愈低。或者是由於存儲的問題,數據庫所在的機器性能降低了。這些問題,歸根結底都是受到了硬件的限制,好比 CPU,內存,磁盤,網絡等等。在集中式的架構裏面,咱們通常是增長硬件設施來解決這些問題的,比喻換CPU,升級內存,擴展磁盤,升級帶寬等等。但咱們本次要說的不是硬件的優化,做爲一個程序員,若是隻會增長硬件那也沒有啥優越感;言歸正傳,下面來講下數據庫的軟優化方案;mysql

     1.2數據庫優化方案對比

         1.2.一、重啓

               可能有不少朋友以爲這有點搞笑,但重啓真是釋放資源的最好方法;對於好久都沒關閉的數據庫服務器,重啓會使其釋放資源,致使反應             速度會不少;因此對於夜間服務器空隙時間長的公司,能夠寫一個腳本,讓數據庫在夜間空隙時進行自動重啓;程序員

          1.2.1 SQL 與索引web

              當 SQL 語句寫得很是複雜,好比關聯的表很是多,條件很是多,查詢所消耗的時間很是長,這樣的一個 SQL 就叫慢 SQL,關於慢SQL我           在去年的文章中有講解,有興趣能夠本身去看下。由於 SQL 語句是咱們本身編寫的,可控性是最高的,因此第一步就是檢查 SQL。在不少情           況下咱們優化的目標是爲了用到索引。算法

         1.2.2 表與存儲引擎sql

              若是 SQL 自己沒有什麼大問題,咱們接着就要檢查咱們查詢的目標,也就是表結構的設計有沒有問題。好比你對於字段類型和長度的選              擇,或者表結構是否是須要拆分或者合併,不一樣的表應該選擇什麼存儲引擎,是否是要分區等等。數據庫

         1.2.3 架構

        表結構若是也沒有問題,那就要上升到數據庫服務的層面,從架構層面進行優化。由於數據都是在磁盤上存儲,若是加了索引仍是很慢,乾脆能夠把數據在內存裏面緩存起來,這個時候能夠部署緩存服務器。查詢數據先查緩存,沒有再查數據庫,例如(布隆過濾器)。這樣既能夠減小數據庫的壓力,又能夠提高查詢速度。若是一臺數據庫服務器承受不了訪問壓力,能夠部署集羣作負載均衡。固然這些數據庫節點應該有自動同步的機制。有了主從同步以後,就能夠主從複製實現讀寫分離,讓寫的服務都訪問 master 服務器,讀的請求都訪問從服務器。有了讀寫分離以後,問題並無徹底解決:一、只有一個 master,寫的壓力沒有獲得分攤;二、全部的節點都存儲相同的數據,在一個節點出現存儲瓶頸的時候,磁盤不夠用了其餘的節點也同樣會遇到這個問題。因此這個時候咱們要用到分佈式環境中一個很是重要的手段:分片,每一個節點都只存儲整體數據的一部分,那這個就是咱們今天要說分庫分表。分片之後,爲了提高可用性,能夠再對分片作冗餘。緩存

       1.2.4 數據庫配置

       若是經過架構層面沒有解決問題,或者機器雖然配置很高可是性能沒有發揮到極致,還能夠優化數據庫的配置,好比鏈接數,緩衝區大小等等。 服務器

 

2、 分庫分表的類型和特色

      拆分一共就兩種,一種叫垂直拆分,一種叫水平拆分。網絡

      垂直切分:基於表或字段劃分,表結構不一樣。咱們有單庫的分表,也有多庫的分庫。

      水平切分:基於數據劃分,表結構相同,數據不一樣,也有同庫的水平切分和多庫的切分。

2.1垂直切分

垂直分表有兩種,一種是單庫的,一種是多庫的。字段太多了,就要拆表,表太多了,就要拆庫。

 2.1.1 單庫垂直分表

單庫分表,好比:用戶信息表,拆分紅基本信息表,聯繫方式表等等。

2.1.2 多庫垂直分表

      多庫垂直分表就是把原來存儲在一個庫的不一樣的表,拆分到不一樣的數據庫。比喻說當若是數據庫中有一個表的增加速度很是快,當垂直切 分並無從根本上解決單庫單表數據量過大的問題。在這個時候,咱們還須要對咱們的數據作一個水平的切分。這個時候,一個應用須要多個數據庫。

2.2水平切分

水平切分就是按照數據的維度分佈不一樣的表中,能夠是單庫的,也能夠是多庫的。

2.2.1 單庫水平分表

       這個拿銀行的交易系統講解最容易,銀行的天天交易流水很是大,可是大部分客戶只會查近一年或近一個月的流水單,對於歷史很是長的流水訪問量會少不少,這時就能夠對流水錶進行水平拆分了。

2.2.2 多庫水平分表

另外一種是多庫的水平分表。好比客戶表,咱們拆分到多個庫存儲,表結構是徹底同樣的。

2.3分庫分錶帶來的問題

前面說了不少分庫分表的場景及好處,但世間萬物都是有利就有弊下面就來講下他的弊端

2.3.1 跨庫關聯查詢

      好比在跨庫關聯時,因爲要關聯的表是在不一樣的數據庫,那麼咱們確定不能直接使用 join 的這種方式去作關聯查詢。但咱們有幾個解決方案,例如字段冗餘、mycat等。

2.3.2 分佈式事務

      若是是在一個數據庫裏面,咱們能夠用本地事務來控制,可是在不一樣的數據庫裏面就不行了。這裏必需要出現一個協調者的角色,讓你們統一行動,並且要分紅多個階段,通常是先肯定都能成功才成功,只要有一我的不能成功,就要所有失敗。

 3、 Mycat 概念與配置

3.1 Mycat 介紹與核心概念

Mycat的官網網址:http://www.mycat.org.cn/mycat運行在應用和數據庫之間,能夠當作一個 MySQL 服務器使用(不管是在工具仍是在代碼或者命令行中均可以直接鏈接)。實現對 MySQL 數據庫的分庫分表,也能夠經過 JDBC 支持其餘的數據庫。

Mycat 的關鍵特性:

    • 能夠當作一個 MySQL 數據庫來使用
    • 支持 MySQL 以外的數據庫,經過 JDBC 實現
    • 解決了咱們提到的全部問題,多表 join、分佈式事務、全局序列號、翻頁排序
    • 支持 ZK 配置,帶監控 mycat-web(已經中止維護)
    • 2.0 已經發布;文檔許久沒有更新

3.2 Mycat 配置詳解

咱們先從官網下載Mycat包,有各類版本,我爲了方便下了win

 

 

 

Mycat 解壓之後有 5 個目錄:

 

 

 主要的配置文件 server.xml、schema.xml、rule.xml 和具體的分片配置文件。

3.2.1 server.xml

包含系統配置信息。

system 標籤:例如字符集、線程數、心跳、分佈式事務開關等等。

user 標籤:配置登陸用戶和權限。

<user name="root" defaultAccount="true">
<property name="password">root</property>
<property name="schemas">ghymycat,ljxmycat</property>

<!-- 表級 DML 權限設置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>

3.2.2 schema.xml

schema 在 MySQL 裏面跟數據庫是等價的。schema.xml 包括邏輯庫、表、分片規則、分片節點和數據源,能夠定義多個 schema。

這裏面有三個主要的標籤(table、dataNode、dataHost):

<table/>

表名和庫名最好都用小寫

定義了邏輯表,以及邏輯表分佈的節點和分片規則:

<schema name="ghymycat" checkSQLschema="false" sqlMaxLimit="100">
        <!--範圍分片表-->
        <table name="customer" primaryKey="id" dataNode="103-ghymycat,104-ghymycat,105-ghymycat" rule="auto-sharding-long" />
        <!--ER分片表-->
        <table name="er_scope" dataNode="103-ghymycat,104-ghymycat,105-ghymycat" rule="mod-long-order" >
            <childTable name="er_detail" primaryKey="id" joinKey="er_id" parentKey="er_id"/>
        </table>
        <table name="mycat_sequence" dataNode="103-ghymycat" autoIncrement="true" primaryKey="id"></table>
    </schema>

    <schema name="ljxmycat" checkSQLschema="false" sqlMaxLimit="100">
        <!--取模分片表-->
        <table name="student" primaryKey="sid" dataNode="103-ljxmycat,104-ljxmycat,105-ljxmycat" rule="mod-long" />
        <!--非分片表-->
        <table name="noshard" primaryKey="id" autoIncrement="true" dataNode="103-ljxmycat" />
        <!--全局表-->
        <table name="dict" primaryKey="id" type="global" dataNode="103-ljxmycat,104-ljxmycat,105-ljxmycat" />
        <!--單庫分片表-->
        <table name="fee" primaryKey="id" subTables="fee2025$1-3" dataNode="103-ljxmycat" rule="sharding-by-month" />
    </schema>

配置

做用

checkSQLschema

在查詢 SQL 中去掉邏輯庫名

sqlMaxLimit

 自動加上 limit 控制數據的返回

 

primaryKey

指定該邏輯表對應真實表的主鍵。MyCat 會緩存主鍵(經過 primaryKey 屬性配置)與具體 dataNode 的信息。

 

primaryKey 當分片規則(rule)使用非主鍵進行分片時,那麼在使用主鍵進行查詢時,MyCat 就會經過緩存先肯定記錄在哪一個 dataNode 上,而後再在該 dataNode 上執行查詢。

 

若是沒有緩存/緩存並無命中的話,仍是會發送語句給全部的 dataNode。

dataNode

 

數據分片的節點

autoIncrement

 

自增加(全局序列),true 表明主鍵使用自增加策略

type

 

全局表:global。其餘:不配置

<!--數據節點與物理數據庫的對應關係-->
<dataNode name="103-ghymycat" dataHost="host103" database="ghymycat" />
    <dataNode name="104-ghymycat" dataHost="host104" database="ghymycat" />
    <dataNode name="105-ghymycat" dataHost="host105" database="ghymycat" />

    <dataNode name="103-ljxmycat" dataHost="host103" database="ljxmycat" />
    <dataNode name="104-ljxmycat" dataHost="host104" database="ljxmycat" />
    <dataNode name="105-ljxmycat" dataHost="host105" database="ljxmycat" />

配置物理主機的信息,readhost 是從屬於 writehost 的。

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

    <dataHost name="host104" maxCon="1000" minCon="10" balance="0"
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <heartbeat>select user()</heartbeat>
        <writeHost host="hostM1" url="192.168.2.104:3306" user="root"
                   password="root">
            <!-- <readHost host="hostS1"></readHost>  -->
        </writeHost>
    </dataHost>

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

balance:負載的配置,決定 select 語句的負載

 

 

 writeType:讀寫分離的配置,決定 update、delete、insert 語句的負載

 

switchType:主從切換配置

 

 

 

3.2.3 rule.xml

 定義了分片規則和算法分片規則:

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

分片算法:

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

分片配置:

autopartition-long.txt
10001-20000=1

0-10000=0

20001-100000=2

3.3 Mycat 分片驗證

先準備三個數據庫,我是在建了三個虛擬機,分別在每臺上裝了mysql

       

 

 

 

--在全部數據庫節點上建立數據庫ghymycat,建立3張表
-- 範圍分片表
CREATE TABLE `scope` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ER分片表
CREATE TABLE `er_scope` (
  `er_id` int(11) NOT NULL ,
  `uid` int(11) DEFAULT NULL ,
  `nums` int(11) DEFAULT NULL,
  `state` int(2) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '建立時間',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`er_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ER分片表
CREATE TABLE `er_detail` (
  `er_id` int(11) NOT NULL,
  `id` int(11) NOT NULL,
  `goods_id` int(11) DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `is_pay` int(2) DEFAULT NULL,
  `is_ship` int(2) DEFAULT NULL,
  `status` int(2) DEFAULT NULL,
  PRIMARY KEY (`er_id`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 建立表,在三個ljxmycat庫中建立dict及student表
CREATE TABLE `dict` (
  `id` int(11) DEFAULT NULL,
  `param_code` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `param_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

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=utf8mb4;


-- 在第一個數據庫ljxmycat節點(103)數據庫建立非分片表
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;


-- 庫內分表
-- 在第一個數據庫ljxmycat節點(103)數據庫建立單庫分片表
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

 

 

 如上圖紅框所示就表明啓動成功,mycat的默認端口是8066,咱們用Navicat premium鏈接成功所會以下圖所示,他會把全部表內容進行一個聚合;

 

 

 

 

3.3.1 範圍分片

               在mycat的ghymycat中執行分片測試數據,scpoe是按範圍進行分配的,分配的規則前面也有配置

-- 範圍分片scope表
INSERT INTO `scope` (`id`, `name`) VALUES (6666, '張三');
INSERT INTO `scope` (`id`, `name`) VALUES (7777, '李四');
INSERT INTO `scope` (`id`, `name`) VALUES (16666, '王五');
INSERT INTO `scope` (`id`, `name`) VALUES (17777, '孫六');
INSERT INTO `scope` (`id`, `name`) VALUES (26666, '王二麻子');
INSERT INTO `scope` (`id`, `name`) VALUES (27777, '趙七')

插入數據完成後咱們查看 ,會發現是分散在三個數據庫上的

 

 

 

 

 

 

 

 

 

 

 

 

3.3.2 取模分片表

 ljxmycat庫中的student表,咱們從下圖可知,咱們配置的模數是3

 

 

 

-- 取模分片表(ljxmycat庫中的student表)
-- 測試取模分片(在mycat鏈接中ljxmycat數據庫中執行)
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, '李大郎', '368828888');
INSERT INTO `student` (`sid`, `name`, `qq`) VALUES (6, '孫子', '516895555');

插入完成後本身能夠查下,會發現模餘數0的在第一個接點,餘數1的在第二個節點上,餘數2的在第三個節點上

 

 

 

3.3.3 取模分片(ER 表)

 在實際生產環境中咱們有些表的數據是存在邏輯的主外鍵關係的,好比訂單表 er_scope和er_detail,有主外鍵的配置以下圖

 

 

 執行插入數據

INSERT INTO `er_scope` (`er_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 `er_scope` (`er_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 `er_scope` (`er_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 `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (3, 20180001, 85114752, 19.99, 1, 1, 1);

INSERT INTO `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (1, 20180002, 25411251, 1280.00, 1, 1, 0);

INSERT INTO `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (1, 20180003, 62145412, 288.00, 1, 1, 2);

INSERT INTO `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180004, 21456985, 399.00, 1, 1, 2);

INSERT INTO `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180005, 21457452, 1680.00, 1, 1, 2);

INSERT INTO `er_detail` (`er_id`, `id`, `goods_id`, `price`, `is_pay`, `is_ship`, `status`) VALUES (2, 20180006, 65214789, 9999.00, 1, 1, 3);

插入完成後咱們會發現模數分佈規則和上一個例子同樣,惟一區別的是,關聯的外鍵表數據存放會和主表放在同一個庫裏面

 

 

 

 

 

 

3.3.4 全局表

               ljxmycat數據庫,dict 表:全局表

執行下面語句插入

INSERT INTO `dict` (`id`, `param_code`, `param_name`) VALUES (1, '111', '全局就是全部庫都有同樣的');

3.3.5 非分片表

 ljxmycat數據庫,noshard 表

INSERT INTO `noshard` (`id`, `name`) VALUES (1, '分片的數據');

3.3.6 庫內分表

 

 插入數據

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

而後本身在對應的物理庫查看能夠看到在一個庫裏分表了

3.4 Mycat 全局 ID

關於全局ID這塊網上有篇文章寫的挺全的,若是要配置能夠參照文章中配置,網址:https://blog.51cto.com/mynode/1910570

相關文章
相關標籤/搜索