MySQL分片高可用集羣之Fabric部署使用

Fabric是Oracle公司開發的既有分片又有讀寫分離的MySQL集羣管理工具,雖然個人覺得目前版本還有很多缺陷,但應該會逐步完善,將來會是一個不錯的工具。


第一步:下載並安裝 Fabric


Fabric可以從MySQL官網下載,她屬於MySQL Utilites裏,官方下載地址是http://dev.mysql.com/downloads/utilities/

我下載的是源碼版的,mysql-utilities-1.5.6.zip,因爲是Python所寫,所以安裝跟C語言的不同:

$ unzip mysql-utilities-1.5.6.zip
$ cd mysql-utilities-1.5.6/
$ python setup.py build
$ sudo python setup.py install

安裝好後,原先的python腳本去掉了py後綴,默認放在/usr/local/bin/目錄下,可直接執行。


第一步:部署 MySQL5.6 多實例


運行Fabric前,我們要先準備若干各數據庫,根據我們的需要配置不同數量的數據庫,我們先部署基本的讀寫分離的主從方式,Fabric的需要的利用MySQL的GTID特性進行主從複製,因此需要MySQL5.6以上版本,另外由於MariaDB的GTID特性跟MySQL不同,Fabric不支持MariaDB,使用MariaDB的話有報錯。所以只能用MySQL5.6以上版本。

我們至少需要部署3個MySQL實例才能看到Fabric的功效,

作用
地址
端口
數據文件路徑配置文件路徑

Fabric元數據庫
localhost
10000/dev/shm/data/fa00fabric/fa00.cnf

業務數據庫 1localhost10011/dev/shm/data/fa11fabric/fa11.cnf
localhost10012/dev/shm/data/fa12fabric/fa12.cnf

TIP:由於我在一臺電腦上啓動了很多數據庫,因此將數據文件都放在了內存硬盤中,我的機器默認有的是/dev/shm,其他的機器根據會有所不同,如/run/shm等,這個路徑選擇無關緊要,只要事先創建父目錄。因爲使用內存硬盤,MySQL自身的內存需求降低,我降低了MySQL需要的內存使用量,這個也根據自己的情況選擇。注:生產環境不可這樣配置。

主從數據庫部署可以參考文章:http://bangbangba.blog.51cto.com/3180873/1701857

http://bangbangba.blog.51cto.com/3180873/1702294 

下面我們還是先修改配置文件,需要修改的內容如下:(以fa00.cnf舉例)

# 請自行調整前面6行的數字,每個數據庫都不能相同,這裏是改了最後兩個00對應的數字。
[client]
port            = 10000
socket          = /tmp/fa00.sock

[mysqld]
port            = 10000
socket          = /tmp/fa00.sock
datadir         = /dev/shm/data/fa00
server-id       = 10000
user            = lyw

# 主從複製相關
log-bin=mysql-bin
gtid-mode               = on
log-slave-updates       = true
enforce-gtid-consistency= true

# 文件、內存大小,節約內存。
innodb_buffer_pool_size = 32M
innodb_log_file_size    = 5M

修改好3個文件配置文件 fa00.cnf,fa11.cnf,fa12.cnf 後,我們初始化數據,並且啓動,這次我們採用批量操作的方式,減少工作量。在mysql目錄下創建如下腳本init_start.sh和初始化數據庫文件fabric.sql,並執行init_start.sh,即可創建所有數據庫,並啓動。

fabric.sql 內容爲:

use mysql;
delete from user where user='';
flush privileges;
grant all on *.* to 'fabric'@'%' identified by '123456';
create database lyw;
reset master;


init_start.sh 內容爲:

#! /bin/bash
mkdir -p /dev/shm/data
for cnf in `ls fabric/*.cnf`
do
        scripts/mysql_install_db --defaults-file=$cnf
        bin/mysqld --defaults-file=$cnf &
done

# 等待一下,讓mysqld啓動完成,
sleep 3

for cnf in `ls fabric/*.cnf`
do
        bin/mysql --defaults-file=$cnf -uroot < fabric.sql
done

準備好腳本後,執行init_start.sh即完成所有數據庫的初始化和啓動工作,可以啓動mysql客戶端檢查下數據庫是否都初始化好。Fabric不需要主動執行change master to這行sql語句來開啓主從,而是交由Fabric自身去執行。到此數據庫以準備好,接下來開始真正的Fabric配置。


第三步:Fabric 讀寫分離主從配置


fabric的默認配置文件路徑爲/usr/local/etc/mysql/fabric.cfg,其他安裝方法會是/etc/mysql/fabric.cfg(其他系統根據自己情況配置)因此爲了方便後面的操作,我們還是使用這個配置文件,當然也可以用--config 參數指定配置文件。

fabric.cfg內容如下:

[DEFAULT]
prefix = /usr/local
sysconfdir = /usr/local/etc
logdir = /var/log

# storage 配置的是fabric元數據存儲的數據庫
[storage]
address = localhost:10000
user = fabric
password = 123456
database = fabric
auth_plugin = mysql_native_password
connection_timeout = 6
connection_attempts = 6
connection_delay = 1

[servers]
user = fabric
password = 123456
backup_user = fabric_backup
backup_password = secret
restore_user = fabric_restore
restore_password = secret
unreachable_timeout = 5

# fabric對外的協議,這裏是xmlrpc協議
[protocol.xmlrpc]
address = localhost:32274
threads = 5
user = admin
password = 123456
disable_authentication = yes
realm = MySQL Fabric
ssl_ca = 
ssl_cert = 
ssl_key = 

# fabric對外的協議,這裏是mysql協議,能用mysql連接,但是不能跟普通數據庫一樣操作
[protocol.mysql]
address = localhost:32275
user = admin
password = 123456
disable_authentication = yes
ssl_ca = 
ssl_cert = 
ssl_key = 

[executor]
executors = 5

[logging]
level = INFO
url = file:///var/log/fabric.log

[sharding]
mysqldump_program = /usr/bin/mysqldump
mysqlclient_program = /usr/bin/mysql

[statistics]
prune_time = 3600

[failure_tracking]
notifications = 300
notification_clients = 50
notification_interval = 60
failover_interval = 0
detections = 3
detection_interval = 6
detection_timeout = 1
prune_time = 3600

[connector]
ttl = 1

配置好後,需要初始化fabric的元數據

$ mysqlfabric manage setup
。。。。。。
Finishing initial setup
=======================
Password for admin user is not yet set.
Password for admin/xmlrpc:                 這裏需要設置admin的密碼
Repeat Password:                           重複輸入密碼
Password set.
Password set.

這時fa00這個數據庫已經有了fabric的元數據,我們可以用mysql客戶端查看。

然後啓動

$ mysqlfabric manage start

自此雖然啓動了fabric,但是還沒有和前面配置的後兩個業務數據庫產生聯繫,接下來我們需要用命令行建立聯繫。我先看一下mysqlfabric的分組相關幫助,

$ mysqlfabric help group

Commands available in group 'group' are:
    group activate group_id  [--synchronous] 
    group description group_id  [--description=NONE] [--synchronous] 
    group deactivate group_id  [--synchronous] 
    group create group_id  [--description=NONE] [--synchronous] 
    group remove group_id server_id  [--synchronous] 
    group add group_id address  [--timeout=NONE] [--update_only] [--synchronous] 
    group health group_id  
    group lookup_servers group_id  [--server_id=NONE] [--status=NONE] [--mode=NONE] 
    group destroy group_id  [--synchronous] 
    group demote group_id  [--update_only] [--synchronous] 
    group promote group_id  [--slave_id=NONE] [--update_only] [--synchronous] 
    group lookup_groups  [--group_id=NONE]

我們首先需要創建一個組group-1:

$ mysqlfabric group create group-1

然後將兩個業務數據庫10011和10012放入這個組中:

$ mysqlfabric group add group-1 127.0.0.1:10011
$ mysqlfabric group add group-1 127.0.0.1:10012

然後可以查看這時group-1這個組的狀態

$ mysqlfabric group lookup_servers group-1
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status      mode weight
------------------------------------ --------------- --------- --------- ------
d4919ca2-754a-11e5-8a5e-34238703623c 127.0.0.1:10011 SECONDARY READ_ONLY    1.0
d6597f06-754a-11e5-8a5e-34238703623c 127.0.0.1:10012 SECONDARY READ_ONLY    1.0

從status 和 mode 這兩個字段可以看出,這個時候剛加入的兩個服務器還沒有正式生效,都是作爲從庫只讀的身份,兩個庫還沒有建立實質的聯繫,我們需要將其中一個數據庫提升爲可寫的主:

$ mysqlfabric group promote group-1

$ mysqlfabric group lookup_servers group-1
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
d4919ca2-754a-11e5-8a5e-34238703623c 127.0.0.1:10011 SECONDARY  READ_ONLY    1.0
d6597f06-754a-11e5-8a5e-34238703623c 127.0.0.1:10012   PRIMARY READ_WRITE    1.0

用promote命令提升後,其中一臺會被提升爲主數據庫,其他的都爲從。主從也可以通過mysql客戶端查看,在從庫執行下面的命令顯示如下,主庫執行則沒有信息

mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 127.0.0.1
                  Master_User: fabric
                  Master_Port: 10012
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 151
               Relay_Log_File: lyw-hp-relay-bin.000002
                Relay_Log_Pos: 361
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
                          。。。。。。

至此主從方式以基本可用,我們迫不及待地想要嘗試下客戶端如何使用了。官方提供python和java兩種客戶的,因此如果用Fabric,業務語言最好是這兩種。

Fabric是python所寫,我們這裏就用python客戶端做例子,直接打開python交互界面

$ python
Python 2.7.9 (default, Apr  2 2015, 15:33:21) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mysql.connector
>>> from mysql.connector import fabric
>>> conn = mysql.connector.connect(fabric={"host":"localhost", "port":32274, "username":"lyw", "password":"123456"}, user="fabric", password="123456", autocommit=True)
>>> conn.set_property(group='group-1', scope=fabric.SCOPE_GLOBAL, mode=fabric.MODE_READWRITE)
>>> cur = conn.cursor()
>>> cur.execute('create database lyw')
>>> cur.execute('use lyw;')
>>> cur.execute('create table t1 (id int, v varchar(32))')
>>> cur.execute('insert into t1 values(1, "aaa"), (2,"bbb")')
>>> cur.execute('select * from t1')
>>> cur.fetchall()
[(1, u'aaa'), (2, u'bbb')]

#下面切換到從庫下只讀數據,這時下面的sql語句是切換到了從庫執行,需要重新'use lyw'
>>> conn.set_property(group='group-1', scope=fabric.SCOPE_GLOBAL, mode=fabric.MODE_READONLY)
>>> cur.execute('use lyw')
>>> cur.execute('select * from t1')
>>> cur.fetchall()
[(1, u'aaa'), (2, u'bbb')]

這個python例子可以很好的看出主從方式下fabric的基本使用,

注意:請勿在從庫執行寫操作,否則數據會不符合預期,不會同步到主。


此時如果主數據庫掛了,那麼寫操作將不可用,fabric還不會自動切換,要想進行自動切換,需要執行以下命令:

$ mysqlfabric group activate group-1
主庫掛掉幾秒鐘後
$ mysqlfabric group lookup_servers group-1
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address  status       mode weight
------------------------------------ --------------- ------- ---------- ------
a30d844d-7550-11e5-8a84-34238703623c 127.0.0.1:10011 PRIMARY READ_WRITE    1.0
a4d66a55-7550-11e5-8a84-34238703623c 127.0.0.1:10012  FAULTY READ_WRITE    1.0

然後我們幹掉主庫,過幾秒種查看狀態,可以發現從庫以切換爲主庫。原先的主庫變爲FAULTY

但是當掛掉的源主庫重新啓動時,不會自動加入到集羣裏面,需要先remove,再add回來才行。

$ mysqlfabric group remove group-1 127.0.0.1:10012
$ mysqlfabric group add group-1 127.0.0.1:10012
$ mysqlfabric group lookup_servers group-1
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
a30d844d-7550-11e5-8a84-34238703623c 127.0.0.1:10011   PRIMARY READ_WRITE    1.0
a4d66a55-7550-11e5-8a84-34238703623c 127.0.0.1:10012 SECONDARY  READ_ONLY    1.0

狀態變爲只讀從庫,再去查數據,可以發現宕機期間的數據被恢復(需要一定時間)



第四步:分片+主從 部署


我們前面準備了很多腳本,部署已很方便,這裏我先拆除前面的數據,重新部署,讀者可以根據自己喜好操作。

我們這次要部署如下列表的數據庫

作用
地址
端口
文件路徑
配置路徑
Fabric元數據
localhost
10000
/dev/shm/data/fa00fabric/fa00.cnf
業務數據庫 全局
localhost10091/dev/shm/data/fa91fabric/fa91.cnf
localhost10092/dev/shm/data/fa92fabric/fa92.cnf
業務數據庫 1localhost10011/dev/shm/data/fa11fabric/fa11.cnf
localhost10012/dev/shm/data/fa12fabric/fa12.cnf
業務數據庫 2localhost10021/dev/shm/data/fa21fabric/fa21.cnf
localhost10022/dev/shm/data/fa22fabric/fa22.cnf
業務數據庫 3localhost10031/dev/shm/data/fa31fabric/fa31.cnf
localhost10032/dev/shm/data/fa32fabric/fa32.cnf

我們複製之前的配置文件,修改其中的主要數據,準備好這9個配置文件fa00.cnf ~ fa32.cnf

然後我們我們初始化環境

$ sh init_start.sh
$ mysqlfabric manage setup
$ mysqlfabric manage start

只要3行就初始化完,輕鬆吧。

然後我們要創建4個fabric組,group-g,group-1,group-2,group-3

$ mysqlfabric group create group-g
$ mysqlfabric group create group-1
$ mysqlfabric group create group-2
$ mysqlfabric group create group-3

將數據庫放入這4個組中,

$ mysqlfabric group add group-g 127.0.0.1:10091
$ mysqlfabric group add group-g 127.0.0.1:10092

$ mysqlfabric group add group-1 127.0.0.1:10011
$ mysqlfabric group add group-1 127.0.0.1:10012

$ mysqlfabric group add group-2 127.0.0.1:10021
$ mysqlfabric group add group-2 127.0.0.1:10022

$ mysqlfabric group add group-3 127.0.0.1:10031
$ mysqlfabric group add group-3 127.0.0.1:10032

並且都選出主來

$ mysqlfabric group promote group-g
$ mysqlfabric group promote group-1
$ mysqlfabric group promote group-2
$ mysqlfabric group promote group-3

現在爲止只是純粹的創建了4個獨立的組,我們用的命令都是group相關的,接下來就是分片的重頭戲了,需要用sharding相關命令,我們還是先來看下sharding的簡單幫助

$ mysqlfabric help sharding

Commands available in group 'sharding' are:
    sharding list_definitions  
    sharding remove_definition shard_mapping_id  [--synchronous] 
    sharding move_shard shard_id group_id  [--update_only] [--synchronous] 
    sharding disable_shard shard_id  [--synchronous] 
    sharding remove_table table_name  [--synchronous] 
    sharding split_shard shard_id group_id  [--split_value=NONE] [--update_only] [--synchronous] 
    sharding create_definition type_name group_id  [--synchronous] 
    sharding add_shard shard_mapping_id groupid_lb_list  [--state=DISABLED] [--synchronous] 
    sharding add_table shard_mapping_id table_name column_name  [--synchronous] 
    sharding lookup_table table_name  
    sharding enable_shard shard_id  [--synchronous] 
    sharding remove_shard shard_id  [--synchronous] 
    sharding list_tables sharding_type  
    sharding prune_shard table_name  [--synchronous] 
    sharding lookup_servers table_name key  [--hint=LOCAL]

好,首先我們需要創建一個definition:

$ mysqlfabric sharding create_definition RANGE group-g
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
c106bd7a-e8f8-405e-97ec-6886cec87346        1       1      1

記下result字段的數字1,這個就是shard_mapping_id,後續操作都需要他。

然後增加一個表定義,後面3各參數分別是shard_mapping_id, 表名,分片的字段名

$ mysqlfabric sharding add_table  1  lyw.table1  id

接着增加分片的區間定義,這時纔將那3個組用上

$ mysqlfabric sharding add_shard 1 "group-1/0, group-2/10000, group-3/20000" --state=ENABLED

因爲我們這裏用的是range方式,所以每個組的後面都要加一個分組的起始數字。

後面的--state=ENABLED 表示馬上生效。

基於range的分片就創建好了,我們還是迫不及待的去測試一下。我們還是用python交互界面執行

$ python
Python 2.7.9 (default, Apr  2 2015, 15:33:21) 
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mysql.connector
>>> from mysql.connector import fabric
>>> conn = mysql.connector.connect(fabric={"host":"localhost", "port":32274, "username":"lyw", "password":"123456"}, user="fabric", password="123456", autocommit=True)
>>> conn.set_property(group='group-g', scope=fabric.SCOPE_GLOBAL, mode=fabric.MODE_READWRITE)
>>> cur = conn.cursor()
>>> cur.execute('create database lyw')
>>> cur.execute('use lyw;')
>>> cur.execute('create table table1 (id int, v varchar(32))')

這時,我們去8個數據庫直接查看,會發現所有數據庫都創建了數據庫lyw和表table1;因爲我們使用了scope=fabric.SCOPE_GLOBAL 參數,所以這個操作在10091這個庫執行後,會同步到其他所有數據庫中去,記得其他數據庫的數據是有延遲的。

然後我們進行分片插入數據,fabric的分片操作有點麻煩,用兩個函數輔助一下比較方便,下面代碼可以粘貼到交互界面中去。

import random

def ins(conn, table, key):
     conn.set_property(tables=[table], key=key, scope=fabric.SCOPE_LOCAL, mode=fabric.MODE_READWRITE)
     cur=conn.cursor()
     #cur.execute('use lyw;')
     cur.execute("insert into %s values('%s', 'aaa')" % (table, key) )

def rand_ins(conn, table, count):
    for i in range(count):
        key = random.randint(0, 30000)
        ins(conn, table, key)

然後我們在交互界面執行rand_ins函數隨機插入若干條數據

>>> connl = mysql.connector.connect(fabric={"host":"localhost", "port":32274, "username":"lyw", "password":"123456"}, user="fabric", password="123456", autocommit=True)
>>> rand_ins(connl, 'lyw.table1', 100)

這樣就向數據庫隨機插入了100條數據,我們可以用mysql客戶端直接鏈接後面的6個庫查看,一般情況每個庫都有數據庫,第一組數據都是小於10000的數據,第二組都是10000-20000的數據,第三組都是20000以上的數據,並且行數總和爲100,就是我們插入的總條數。

注意:使用分片時,如果key換了,就需要重新獲得遊標和數據庫,即ins函數的前3行,使用數據庫也可以直接將表名寫在sql語句中。否則插入數據會不符合預期。


除了RANGE分片外,fabric還提供了其他的方式,有HASH,RANGE_DATETIME, RANGE_STRING

我們下面講一下HASH分片。


HASH 分片

$ mysqlfabric sharding create_definition HASH group-g
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
9ba5c378-9f99-43bc-8f54-7580cff565f6        1       1      2
$ mysqlfabric sharding add_table 2 lyw.table2 id
$ mysqlfabric sharding add_shard 2 "group-1, group-2, group-3" --state=ENABLED

配置已完成,只要3行命令,hash方式的分組後面不用加數字,分組由系統自行計算獲得。

我們還是測試一下

>>> conn = mysql.connector.connect(fabric={"host":"localhost", "port":32274, "username":"lyw", "password":"123456"}, user="fabric", password="123456", autocommit=True)
>>> conn.set_property(group='group-g', scope=fabric.SCOPE_GLOBAL, mode=fabric.MODE_READWRITE)
>>> cur = conn.cursor()
>>> cur.execute('use lyw;')
>>> cur.execute('create table table2 (id int, v varchar(32))')
>>> connl = mysql.connector.connect(fabric={"host":"localhost", "port":32274, "username":"lyw", "password":"123456"}, user="fabric", password="123456", autocommit=True)
>>> rand_ins(connl, 'lyw.table2', 100)

同樣可以發現每個庫中都有了數據,只是沒有太好的規律,並且可能還會發現每個數據庫的數據行數相差比較遠,這個看運氣,可能相差10倍之大,因此我覺得目前fabric的Hash方法不夠好,分佈太不均勻,HASH方式不建議使用。


RANGE_DATETIME 分片

$ mysqlfabric sharding create_definition RANGE_DATETIME group-g
$ mysqlfabric sharding add_table 3 lyw.table3 dt
$ mysqlfabric sharding add_shard 3 'group-1/2015-1-1, group-2/2015-2-1,group-3/2015-3-1' --state=ENABLED

這樣3行就可以了,記得寫數據的時候key用datetime.date類型,不能用datetime.datetime。

當需要按照時間分區的時候,就可以這樣做,然而我個人認爲,最近的時間是熱數據的情況下,用fabric分片其實並不是否妥當,用mysql自帶的時間分片會更好。

注意:目前版本fabric在datetime上有bug,如果加上幾分幾秒,需要修改3處代碼,方可運行,由於我只是作了簡短測試,可能還有其他bug,所以不在此發佈diff。


RANGE_STRING 分片

$ mysqlfabric sharding create_definition RANGE_STRING group-g
$ mysqlfabric sharding add_table 4 lyw.table4 name
$ mysqlfabric sharding add_shard 4 'group-1/a, group-2/c,group-3/e' --state=ENABLED

這樣name爲a和b開頭的行都會放在group-1,c和d開頭的都會放在group-2,大於等於e的都會放在group-3裏面了。這種分片方式還是有挺大用處的,十分推薦。可以用前面的ins函數測試。


以上是Fabric的基本功能使用,一點點去深究後會發現Fabric也存在這樣那樣的缺陷,可能不適合您的業務場景,另外我寫了篇Cobar集羣的部署使用,和MySQL Cluster的部署使用,請大家參考。

在多種集羣之間選擇猶豫時,可以參考下《MySQL Cluster, Fabric, Cobar 三大集羣特性對比》這篇文章,一定會對你有所幫助的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章