1. 程式人生 > >Mycat分庫分表的簡單實踐 / 用Mycat,學會資料庫讀寫分離、分表分庫

Mycat分庫分表的簡單實踐 / 用Mycat,學會資料庫讀寫分離、分表分庫

MySQL的使用場景中,讀寫分離只是方案中的一部分,想要擴充套件,勢必會用到分庫分表,可喜的是Mycat裡已經做到了,今天花時間測試了一下,感覺還不錯。

關於分庫分表

當然自己也理了一下,分庫分表的這些內容,如果分成幾個策略或者階段,大概有下面的幾種。

Mycat分庫分表的簡單實踐

最上面的第一種是直接拆表,比如資料庫db1下面有test1,test2,test3三個表,通過中介軟體看到的還是表test,裡面的資料做了這樣的拆分,能夠在一定程度上分解壓力,如果細細品來,和分割槽表的套路有些像。

接下來的幾類也是不斷完善,把表test拆解到多個庫中,多個伺服器中,如果做了讀寫分離,全套的方案這樣的拆解改進還是很大的。如此來看,資料庫中介軟體做了很多應用和資料庫之間的很多事情,能夠流行起來除了技術原因還是有很多其他的因素。

分庫分表的測試環境模擬

如果要在一臺伺服器上測試分庫分表,而且要求架構方案要全面,作為技術可行性的一個判定參考,是否可以實現呢。

如果模擬一主兩從的架構,模擬服務分佈在3臺伺服器上,這樣的方案需要建立9個例項,每個例項上有3個db需要分別拆分。

大體的配置如下:

master1: 埠33091

(m1)slave1: 埠33092

(m1)slave2: 埠33093

master2: 埠33071

(m2)slave1: 埠33072

(m2)slave2: 埠33073

master3: 埠33061

(m3)slave1: 埠33062

(m3)slave2: 埠33063

畫個圖來說明一下,其中db1,db2,db3下面有若干表,需要做sharding

Mycat分庫分表的簡單實踐

Mycat分庫分表的簡單實踐

所以我們需要模擬的就是這個事情。

使用Mycat碰到的幾個小問題解惑

使用Mycat的時候碰到了幾個小問題,感覺比較有代表性,記錄了一下。

問題1:

首先是使用Mycat連線到資料庫之後,如果不切換到具體的資料庫下,使用[資料庫名].[表名]的方式會丟擲下面的錯誤,可見整個過程中,Mycat攔截了SQL資訊做了過濾,在轉換的時候找不到目標路由。當然實際使用中,規範使用肯定不會有這個問題。

mysql> select * from db1.shard_auto;

ERROR 1064 (HY000): find no Route:select * from db1.shard_auto

問題2:

在配置了sharding策略之後,insert語句丟擲了下面的錯誤,這個是對語法的一個基本的要求。

mysql> insert into shard_mod_long values(1,'aa',date);

ERROR 1064 (HY000): partition table, insert must provide ColumnList

問題3:

如果sharding策略配置有誤,很可能出現表訪問正常,但是DML會有問題,提示資料衝突了。至於如何配置sharding,下面會講。

mysql> select * from shard_mod_long;

Empty set (0.00 sec)

mysql> insert into shard_mod_long(ID,name,shard_date) values(1,'aa',current_date);

ERROR 1105 (HY000): Duplicate entry '1' for key 'PRIMARY'

問題4:

如果sharding的配置有誤,很可能出現多份冗餘資料。

檢視執行計劃就一目瞭然,通過data_node可以看到資料指向了多個目標庫。

mysql> explain insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date);

+-----------+------------------------------------------------+

| DATA_NODE | SQL |

+-----------+------------------------------------------------+

| pxcNode11 | insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date) |

| pxcNode21 | insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date) |

| pxcNode31 | insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date) |

+-----------+------------------------------------------------+

這種情況如果有一定的需求還是蠻不錯的,做sharding可惜了。問題就在於下面的這個table配置。

<table name="shard_auto" primaryKey="ID" type="global" dataNode="pxcNode11,pxcNode21,pxcNode31" rule="auto-sharding-long" />

需要去掉 type="global"的屬性,讓它sharding。

Mycat裡面的sharding策略

Mycat的分片策略很豐富,這個是超出自己的預期的,也是Mycat的一大亮點。

大體分片規則如下,另外還有一些其他分片方式這裡不全部列舉:

(1)分片列舉:sharding-by-intfile

(2)主鍵範圍:auto-sharding-long

(3)一致性hash:sharding-by-murmur

(4)字串hash解析:sharding-by-stringhash

(5)按日期(天)分片:sharding-by-date

(6)按單月小時拆分:sharding-by-hour

(7)自然月分片:sharding-by-month

在開始之前,我們要建立下面的表來模擬幾個sharding的場景,表名根據需求可以改變。

create table shard_test(ID int primary key, name varchar(20),shard_date date);

主鍵範圍分片

主鍵範圍分片是參考了主鍵值,按照主鍵值的分佈來分佈資料庫在不同的庫中,我們先在對應的sharding節點上建立同樣的表結構。

關於sharding的策略,需要修改rule.xml檔案。

常用的sharding策略已經在Mycat裡面實現了,如果要自行實現也可以定製。比如下面的規則,是基於主鍵欄位ID來做sharding,分佈的演算法是rang-long,引用了function rang-long,這個function是在對應的一個Java類中實現的。

<tableRule name="auto-sharding-long">

<rule>

<columns>ID</columns>

<algorithm>rang-long</algorithm>

</rule>

<function name="rang-long"

class="io.mycat.route.function.AutoPartitionByLong">

<property name="mapFile">autopartition-long.txt</property>

當然主鍵的範圍是不固定的,可以根據需求來定製,比如按照一百萬為單位,或者1000位單位,檔案是 autopartition-long.txt 檔案的內容預設如下,模板裡是分為了3個分片,如果要定製更多的就需要繼續配置了,目前來看這個配置只能夠承載15億的資料量,可以根據需求繼續擴充套件定製。

# range start-end ,data node index

# K=1000,M=10000.

0-500M=0

500M-1000M=1

1000M-1500M=2

插入一些資料來驗證一下,我們可以檢視執行計劃來做基本的驗證,配置無誤,資料就根據規則流向了指定的資料庫下的表裡。

mysql> explain insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date);

+-----------+------------------------------------------------+

| DATA_NODE | SQL |

+-----------+------------------------------------------------+

| pxcNode11 | insert into shard_auto(ID,name,shard_date) values(1,'aa',current_date) |

+-----------+------------------------------------------------+

還有一個檢視sharding效果的小方法,比如我插入一個極大的值,保證和其他資料不在一個分片上,我們執行查詢語句兩次,結果會有點變化。

sharing的效果

mysql> select * from shard_auto;

+---------+------+------------+

| ID | name | shard_date |

+---------+------+------------+

| 1 | aa | 2017-09-06 |

| 2 | bb | 2017-09-06 |

| 5000001 | aa | 2017-09-06 |

+---------+------+------------+

3 rows in set (0.00 sec)

稍作停頓,繼續執行。

mysql> select * from shard_auto;

+---------+------+------------+

| ID | name | shard_date |

+---------+------+------------+

| 5000001 | aa | 2017-09-06 |

| 1 | aa | 2017-09-06 |

| 2 | bb | 2017-09-06 |

+---------+------+------------+

3 rows in set (0.01 sec)

Hash分片

Hash分片其實企業級應用尤其廣泛,我覺得一個原因是通過這種資料路由的方式,得到的資料情況是基本可控的,和業務的關聯起來比較直接。很多拆分方法都是根據mod方法來平均分佈資料。

sharding的策略在rule.xml裡面配置,還是預設的mod-long規則,引用了演算法mod-long,這裡是根據sharding的節點數來做的,預設是3個。

<tableRule name="mod-long">

<rule>

<columns>id</columns>

<algorithm>mod-long</algorithm>

</rule>

</tableRule>

<function name="mod-long" class="io.mycat.route.function.PartitionByMod">

<!-- how many data nodes -->

<property name="count">3</property>

</function>

比如檢視兩次insert的結果情況。

mysql> explain insert into shard_mod_long(ID,name,shard_date) values(4,'dd',current_date);

+-----------+------------------------------------------------+

| DATA_NODE | SQL |

+-----------+------------------------------------------------+

| pxcNode22 | insert into shard_mod_long(ID,name,shard_date) values(4,'dd',current_date) |

+-----------+------------------------------------------------+

mysql> explain insert into shard_mod_long(ID,name,shard_date) values(5,'ee',current_date);

+-----------+------------------------------------------------+

| DATA_NODE | SQL |

+-----------+------------------------------------------------+

| pxcNode23 | insert into shard_mod_long(ID,name,shard_date) values(5,'ee',current_date) |

+-----------+------------------------------------------------+

可以看到資料還是遵循了節點的規律,平均分佈。

至於schema.xml的配置,是整個分庫的核心,我索性也給出一個配置來,供參考。

<?xml version="1.0"?>

<!DOCTYPE mycat:schema SYSTEM "schema.dtd">

<mycat:schema xmlns:mycat="http://io.mycat/">

<!-- 定義MyCat的邏輯庫 -->

<schema name="db1" checkSQLschema="false" sqlMaxLimit="100" >

     <table name="shard_mod_long" primaryKey="ID" type="global" dataNode="pxcNode11,pxcNode21,pxcNode31" rule="mod-long" />

     <table name="shard_auto" primaryKey="ID" type="global" dataNode="pxcNode11,pxcNode21,pxcNode31" rule="auto-sharding-long" />

</schema>

<!-- 定義MyCat的資料節點 -->

<dataNode name="pxcNode11" dataHost="dtHost" database="db1" />

<dataNode name="pxcNode21" dataHost="dtHost2" database="db1" />

<dataNode name="pxcNode31" dataHost="dtHost3" database="db1" />

<!-- 定義資料主機dtHost,連線到MySQL讀寫分離叢集 ,schema中的每一個dataHost中的host屬性值必須唯一-->

<!-- dataHost實際上配置就是後臺的資料庫叢集,一個datahost代表一個數據庫叢集 -->

<!-- balance="1",全部的readHost與stand by writeHost參與select語句的負載均衡-->

<!-- writeType="0",所有寫操作傳送到配置的第一個writeHost,這裡就是我們的hostmaster,第一個掛了切到還生存的第二個writeHost-->

<dataHost name="dtHost" maxCon="500" minCon="20" balance="1"

     writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">

     <!--心跳檢測 -->

     <heartbeat>show slave status</heartbeat>

     <!--配置後臺資料庫的IP地址和埠號,還有賬號密碼 -->

      <writeHost host="hostMaster" url="192.168.163.128:33091" user="mycat_user" password="mycat" />

</dataHost>

<dataHost name="dtHost2" maxCon="500" minCon="20" balance="1"

     writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">

     <!--心跳檢測 -->

     <heartbeat>show slave status</heartbeat>

     <!--配置後臺資料庫的IP地址和埠號,還有賬號密碼 -->

     <writeHost host="hostMaster" url="192.168.163.128:33071" user="mycat_user" password="mycat" />

</dataHost>

<dataHost name="dtHost3" maxCon="500" minCon="20" balance="1"

     writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">

     <!--心跳檢測 -->

     <heartbeat>show slave status</heartbeat>

     <!--配置後臺資料庫的IP地址和埠號,還有賬號密碼 -->

     <writeHost host="hostMaster" url="192.168.163.128:33061" user="mycat_user" password="mycat" />

</dataHost>

</mycat:schema>

=================================================================================================

用Mycat,學會資料庫讀寫分離、分表分庫

php疑難雜症鋪 2017-09-13 14:31

用Mycat,學會資料庫讀寫分離、分表分庫

系統開發中,資料庫是非常重要的一個點。除了程式的本身的優化,如:SQL語句優化、程式碼優化,資料庫的處理本身優化也是非常重要的。主從、熱備、分表分庫等都是系統發展遲早會遇到的技術問題問題。Mycat是一個廣受好評的資料庫中介軟體,已經在很多產品上進行使用了。希望通過這篇文章的介紹,能學會Mycat的使用。

安裝

Mycat官網:http://www.mycat.io/

可以瞭解下Mycat的背景和應用情況,這樣使用起來比較有信心。

Mycat下載地址:http://dl.mycat.io/

官網有個文件,屬於詳細的介紹,初次入門,看起來比較花時間。

下載:

建議大家選擇 1.6-RELEASE 版本,畢竟是比較穩定的版本。

安裝:

根據不同的系統選擇不同的版本。包括linux、windows、mac,作者考慮還是非常周全的,當然,也有原始碼版的。(ps:原始碼版的下載後,只要配置正確,就可以正常執行除錯,這個贊一下。)

Mycat的安裝其實只要解壓下載的目錄就可以了,非常簡單。

安裝完成後,目錄如下:

目錄 說明
bin mycat命令,啟動、重啟、停止等
catlet catlet為Mycat的一個擴充套件功能
conf Mycat 配置資訊,重點關注
lib Mycat引用的jar包,Mycat是java開發的
logs 日誌檔案,包括Mycat啟動的日誌和執行的日誌。

配置

Mycat的配置檔案都在conf目錄裡面,這裡介紹幾個常用的檔案:

檔案 說明
server.xml Mycat的配置檔案,設定賬號、引數等
schema.xml Mycat對應的物理資料庫和資料庫表的配置
rule.xml Mycat分片(分庫分表)規則

Mycat的架構其實很好理解,Mycat是代理,Mycat後面就是物理資料庫。和Web伺服器的Nginx類似。對於使用者來說,訪問的都是Mycat,不會接觸到後端的資料庫。

我們現在做一個主從、讀寫分離,簡單分表的示例。結構如下圖:

伺服器 IP 說明
Mycat 192.168.0.2 mycat伺服器,連線資料庫時,連線此伺服器
database1 192.168.0.3 物理資料庫1,真正儲存資料的資料庫
database2 192.168.0.4 物理資料庫2,真正儲存資料的資料庫

Mycat作為主資料庫中介軟體,肯定是與程式碼弱關聯的,所以程式碼是不用修改的,使用Mycat後,連線資料庫是不變的,預設埠是8066。連線方式和普通資料庫一樣,如:jdbc:mysql://192.168.0.2:8066/

server.xml

示例

用Mycat,學會資料庫讀寫分離、分表分庫

重點關注下面這段,其他預設即可。

引數 說明
user 使用者配置節點
--name 登入的使用者名稱,也就是連線Mycat的使用者名稱
--password 登入的密碼,也就是連線Mycat的密碼
--schemas 資料庫名,這裡會和schema.xml中的配置關聯,多個用逗號分開,例如需要這個使用者需要管理兩個資料庫db1,db2,則配置db1,dbs
--privileges 配置使用者針對表的增刪改查的許可權,具體見文件吧

我這裡配置了一個賬號test 密碼也是test,針對資料庫lunch,讀寫許可權都有,沒有針對表做任何特殊的許可權。

schema.xml

schema.xml是最主要的配置項,首先看我的配置檔案。

用Mycat,學會資料庫讀寫分離、分表分庫

用Mycat,學會資料庫讀寫分離、分表分庫

引數 說明
schema 資料庫設定,此資料庫為邏輯資料庫,name與server.xml中schema對應
dataNode 分片資訊,也就是分庫相關配置
dataHost 物理資料庫,真正儲存資料的資料庫

每個節點的屬性逐一說明:

schema:

屬性 說明
name 邏輯資料庫名,與server.xml中的schema對應
checkSQLschema 資料庫字首相關設定,建議看文件,這裡暫時設為folse
sqlMaxLimit select 時預設的limit,避免查詢全表

table:

屬性 說明
name 表名,物理資料庫中表名
dataNode 表儲存到哪些節點,多個節點用逗號分隔。節點為下文dataNode設定的name
primaryKey 主鍵欄位名,自動生成主鍵時需要設定
autoIncrement 是否自增
rule 分片規則名,具體規則下文rule詳細介紹

dataNode

屬性 說明
name 節點名,與table中dataNode對應
datahost 物理資料庫名,與datahost中name對應
database 物理資料庫中資料庫名

dataHost

屬性 說明
name 物理資料庫名,與dataNode中dataHost對應
balance 均衡負載的方式
writeType 寫入方式
dbType 資料庫型別
heartbeat 心跳檢測語句,注意語句結尾的分號要加。

應用場景

資料庫分表分庫

配置如下:

用Mycat,學會資料庫讀寫分離、分表分庫

用Mycat,學會資料庫讀寫分離、分表分庫

我在192.168.0.2、192.168.0.3均有資料庫lunch。

lunchmenu、restaurant、userlunch、users這些表都只寫入節點dn1,也就是192.168.0.2這個服務,而dictionary寫入了dn1、dn2兩個節點,也就是192.168.0.2、192.168.0.3這兩臺伺服器。分片的規則為:mod-long。

主要關注rule屬性,rule屬性的內容來源於rule.xml這個檔案,Mycat支援10種分表分庫的規則,基本能滿足你所需要的要求,這個必須贊一個,其他資料庫中介軟體好像都沒有這麼多。

table中的rule屬性對應的就是rule.xml檔案中tableRule的name,具體有哪些分表和分庫的實現,建議還是看下文件。我這裡選擇的mod-long就是將資料平均拆分。因為我後端是兩臺物理庫,所以rule.xml中mod-long對應的function count為2,見下面部分程式碼:

用Mycat,學會資料庫讀寫分離、分表分庫

資料庫讀寫分離

配置如下:

用Mycat,學會資料庫讀寫分離、分表分庫

這樣的配置與前一個示例配置改動如下:

刪除了table分配的規則,以及datanode只有一個

datahost也只有一臺,但是writehost總添加了readhost,balance改為1,表示讀寫分離。

以上配置達到的效果就是102.168.0.2為主庫,192.168.0.3為從庫。

注意:Mycat主從分離只是在讀的時候做了處理,寫入資料的時候,只會寫入到writehost,需要通過mycat的主從複製將資料複製到readhost,這個問題當時候我糾結了好久,資料寫入writehost後,readhost一直沒有資料,以為是自己配置的問題,後面才發現Mycat就沒有實現主從複製的功能,畢竟資料庫本身自帶的這個功能才是最高效穩定的。

至於其他的場景,如同時主從和分表分庫也是支援的了,只要瞭解這個實現以後再去修改配置,都是可以實現的。而熱備及故障專業官方推薦使用haproxy配合一起使用,大家可以試試。

使用

Mycat的啟動也很簡單,啟動命令在Bin目錄:

用Mycat,學會資料庫讀寫分離、分表分庫

如果在啟動時發現異常,在logs目錄中檢視日誌。

  • wrapper.log 為程式啟動的日誌,啟動時的問題看這個

  • mycat.log 為指令碼執行時的日誌,SQL指令碼執行報錯後的具體錯誤內容,檢視這個檔案。mycat.log是最新的錯誤日誌,歷史日誌會根據時間生成目錄儲存。

mycat啟動後,執行命令不成功,可能實際上配置有錯誤,導致後面的命令沒有很好的執行。

Mycat帶來的最大好處就是使用是完全不用修改原有程式碼的,在mycat通過命令啟動後,你只需要將資料庫連線切換到Mycat的地址就可以了。如下面就可以進行連線了:

用Mycat,學會資料庫讀寫分離、分表分庫

連線成功後可以執行sql指令碼了。

所以,可以直接通過sql管理工具(如:navicat、datagrip)連線,執行指令碼。我一直用datagrip來進行日常簡單的管理,這個很方便。

Mycat還有一個管理的連線,埠號是9906.

用Mycat,學會資料庫讀寫分離、分表分庫

連線後可以根據管理命令檢視Mycat的執行情況,當然,喜歡UI管理方式的人,可以安裝一個Mycat-Web來進行管理,有興趣自行搜尋。

簡而言之,開發中使用Mycat和直接使用Mysql機會沒有差別。

常見問題

使用Mycat後總會遇到一些坑,我將自己遇到的一些問題在這裡列一下,希望能與大家有共鳴:

  • Mycat是不是配置以後,就能完全解決分表分庫和讀寫分離問題?

    Mycat配合資料庫本身的複製功能,可以解決讀寫分離的問題,但是針對分表分庫的問題,不是完美的解決。或者說,至今為止,業界沒有完美的解決方案。

    分表分庫寫入能完美解決,但是,不能完美解決主要是聯表查詢的問題,Mycat支援兩個表聯表的查詢,多餘兩個表的查詢不支援。 其實,很多資料庫中介軟體關於分表分庫後查詢的問題,都是需要自己實現的,而且節本都不支援聯表查詢,Mycat已經算做地非常先進了。

    分表分庫的後聯表查詢問題,大家通過合理資料庫設計來避免。

  • Mycat支援哪些資料庫,其他平臺如 .net、PHP能用嗎?

    官方說了,支援的資料庫包括MySQL、SQL Server、Oracle、DB2、PostgreSQL 等主流資料庫,很贊。

    儘量用Mysql,我試過SQL Server,會有些小問題,因為部分語法有點差異。

  • Mycat 非JAVA平臺如 .net、PHP能用嗎?

    可以用。這一點MyCat做的也很棒。

參考

《Mycat權威指南》: http://www.mycat.io/document/Mycat_V1.6.0.pdf

官網 :http://www.mycat.io/

如果想熟練使用Mycat,建議要仔細看看官方推薦的文件,可能需要花點時間。本文只是簡單的介紹下Mycat的配置,希望能快速讓大家對Mycat有個認識,官方的文件理解起來也很容易,只是需要的時間更多,本文為說明的引數,請參考官方文件。