wsrep_args.data_dir表示的是當前數據庫的目錄,對應變量wsrep_data_home_dir
wsrep_args.node_name表示的是當前實例的名字,對應變量wsrep_node_name,
通常默認會被設置爲機器名
wsrep_args.node_address表示當前節點的地址,對應變量wsrep_node_address,能夠不設置,若是不設置的話,系統會本身找到ip。
wsrep_args.node_incoming表示能夠接收鏈接請求的節點,對應狀態變量wsrep_incoming_addresses
wsrep_args.state_id表示一個節點的UUID,對應狀態變量wsrep_cluster_state_uuid
再下面的是幾個回調函數的初始化,須要回調函數的緣由有:
- mysql與galera是分層的,上層能夠調用galera的接口來複制數據庫的,可是對於galera,它是不知道如何提交一個事務,如何判斷寫入結果集是否是衝突,也不知道如何作sst,更不知道如何解析binlog來作複製。因此上層必需要向galera下層說明如何去解析,如何去執行等。
- 框架的通常實現方式,這屬於廢話。
下面分別介紹每一個回調函數的功能:
- wsrep_log_cb:用來作日誌的,由於galera產生一些日誌後,須要告訴mysql,轉換爲mysql的日誌,因此它須要這個接口告訴mysql,讓mysql記錄底層產生的日誌。
- wsrep_view_handler_cb:這是Galera啓動以後,第一個須要執行的回調函數,在新節點的Galera加入到而且已經鏈接到集羣中時,新node會拿本身的local_state_uuid和集羣的cluster_state_uuid對比,若是這二者有區別時,就須要作sst或者ist,此時這個回調函數所作的工做是,根據不一樣的同步方式,準備不一樣的同步命令:
- 對於mysqldump,這裏只須要將數據接收地址及執行方式告訴集羣便可。由於對於這種的實現方式是,doner直接在遠程鏈接新加入的mysql實例,將它本身的全部數據dump出來直接用mysql執行了,因此新加入的節點不須要作任何操做,由於doner在導入的時候,若是新節點已經啓動完成,也是能夠作操做的。
- 對於rsync,這裏須要作的是告訴集羣同步方式及執行命令,由於這與mysqldump不一樣,對於rsync與xtrabackup,它們用到的同步方式分別是腳本wsrep_sst_rsync,wsrep_sst_xtrabackup,這2個腳本都放在安裝目錄的bin下面,在這裏會把相應的命令生成出來,對於rsync,相似是這樣的命令:
wsrep_sst_rsync --role 'joiner' --address '192.168.236.231' --auth 'pxc_sst:txsimIj3eSb8fttz' --datadir '/home/q/zhufeng.wang/pxcdata2/' --defaults-file '/home/q/zhufeng.wang/pxcdata2/my.cnf' --parent '11048',
在這個命令中,--role指明當前節點是joiner仍是doner,由於在腳本中會作不一樣處理,--address表示向什麼地址同步,--auth是在配置文件中指定的權限信息,--datadir表示當前新加入的庫的數據目錄,--defaults-file表示新加入節點的配置文件,--parent表示加入者的進程id號。
這裏生成的命令的做用是,以joiner的角色執行腳本wsrep_sst_rsync,從腳本中能夠看到,它會給mysql返回一個「ready 192.168.236.231:4444/rsync_sst」的字符串,地址和ip表示的是加入節點上面用來傳數據的RSYNC(具體說應該是NC)的地址和端口,4444是腳本中已經寫好的值,若是這個端口已經在用了,則會失敗。然後面的rsync_sst是用來檢查是否是已經有人在作了,若是文件rsync_sst.pid已經存在,則說明正在執行,此時執行會失敗。若是正常的話則會建立另外一個文件rsync_sst.conf,這是一個配置文件,用來讓加入節點作rsync操做的,裏面指定了path,read only,timeout等信息。
上面的問題解釋清楚以後,腳本須要執行的就是等待donor給它傳數據了,它會被阻塞,直到數據成功傳送完成。
此時加入節點已經將信息「執行方式+192.168.236.231:4444/rsync_sst「傳給了Galera,Galera在獲得這個信息,而且等到選擇一個合適的donor出來以後,這個donor會給端口4444傳數據。腳本等待而且接收數據完成以後,就說明恢復完成了,由於rsync是直接複製文件的。那麼此時腳本會返回一個UUID:seqno信息給加入節點,加入節點在這以前一直處於等待狀態,若是收到這些信息以後說明恢復完成,則繼續啓動數據庫。
- 對於xtrabackup,前面信息都是同樣的,只是用到的腳本不一樣,腳本是wsrep_sst_xtrabackup,也一樣會先執行一個參數角色爲joiner的腳本wsrep_sst_xtrabackup,這個部分也會給加入節點返回一個字符串信息:ready 192.168.236.231:4444/xtrabackup_sst,返回以後,腳本就在等待NC將全部donor備份的數據傳過來直到完成。仍是一樣地,Galera獲得192.168.236.231:4444/xtrabackup_sst信息以後,集羣會選擇一個donor出來給這個地址發送備份數據。等待傳送完成以後,腳本會繼續執行,此時執行的就是xtrabackup的數據恢復操做,恢復完成以後,腳本會給加入節點輸出UUID:seqno信息,而此時加入節點一直在等待的,若是發現腳本給它這些信息以後,說明已經作完sst,那麼加入完成,數據庫正常繼續啓動。
- wsrep_apply_cb:這個函數其實很容易明白,從名字便可看出是apply,由於在Galera層,上面已經講過了,它是不知道binlog是什麼東西的,它只知道key是什麼東西,同時它判斷是否是衝突也就是根據key來實現的,根本不會用到binlog,那麼在Galera中,若是已經判斷到一個節點上面的一個事務操做沒有出現衝突,那Galera怎麼知道如何去作複製呢?讓其它節點也產生一樣的修改呢?那麼這個函數就是告訴Galera怎麼去作複製,在下層,若是判斷出不衝突,則Galera直接執行這個回調函數便可,由於這個函數處理的直接就是binlog的恢復操做。它拿到的數據就是一個完成的binlog數據。
- wsrep_commit_cb:一樣的道理,對於Galera,也是有事務的,它在完成一個操做以後,若是本地執行成功,則須要執行提交,若是失敗了,則要回滾,那麼Galera也仍是不知道如何去提交,或者回滾,或者回滾提交須要作什麼事情,那麼這裏也是要告訴Galera這些東西。
- wsrep_unordered_cb:什麼都不作
- wsrep_sst_donate_cb:這個回調函數很重要,從名字看出,它是用來提供donate的,確實是的,在上面wsrep_view_handler_cb中介紹的作SST的時候已經介紹了一點這方面的信息,那這個函數是何時用呢?
它是在wsrep_view_handler_cb函數告訴加入節點的地址端口及執行方式以後,這個是告訴pxc集羣了,那麼集羣會選擇一個合適的節點去作donor,那麼選擇出來以後,它怎麼知道如何去作?那麼這裏就是要告訴它如何去作,把數據發送到哪裏等等,由於wsrep_view_handler_cb告訴集羣的信息只是一個地址、端口及執行方式,是一個字符串而已,那麼wsrep_sst_donate_cb拿到的東西就是這個信息,而此時加入節點正處於等待donor傳數據給它的狀態,應該是阻塞的。
那麼這個回調函數作的是什麼呢?首先它會根據傳給它的執行方式判斷是如何去作,如今有幾種狀況:
- 對於mysqldump方式,這裏作的事情是執行下面的命令: "wsrep_sst_mysqldump "
WSREP_SST_OPT_USER " '%s' "
WSREP_SST_OPT_PSWD " '%s' "
WSREP_SST_OPT_HOST " '%s' "
WSREP_SST_OPT_PORT " '%s' "
WSREP_SST_OPT_LPORT " '%u' "
WSREP_SST_OPT_SOCKET " '%s' "
WSREP_SST_OPT_DATA " '%s' "
WSREP_SST_OPT_GTID " '%s:%lld'"
很明顯,它如今執行的是腳本 wsrep_sst_mysqldump ,後面是它的參數,其實裏面用到的就是mysqldump命令,腳本中主要的操做是mysqldump $AUTH -S$WSREP_SST_OPT_SOCKET --add-drop-database --add-drop-table --skip-add-locks --create-options --disable-keys --extended-insert --skip-lock-tables --quick --set-charset --skip-comments --flush-privileges --all-databases
能夠看出它是將全庫導出的,導出以後直接在遠程將導出的sql文件經過mysql命令直接執行了,簡單說就是遠程執行了一堆的sql語句,將全部的信息複製到(遠程執行sql)新加入節點中。而這也正是mysqldump不像其它2種執行方式同樣在新加入節點還須要作些操做,包括恢復,等待等操做,mysqldump方式在新加入節點也是不須要等待
- 對於rsync方式,執行的命令是:wsrep_sst_rsync "
WSREP_SST_OPT_ROLE
" 'donor' "
WSREP_SST_OPT_ADDR
" '%s' "
WSREP_SST_OPT_AUTH
" '%s' "
WSREP_SST_OPT_SOCKET
" '%s' "
WSREP_SST_OPT_DATA
" '%s' "
WSREP_SST_OPT_CONF
" '%s' "
WSREP_SST_OPT_GTID
" '%s:%lld'"
能夠看出,它執行的仍是腳本
wsrep_sst_rsync
,不過如今的角色是
donor,在腳本中,它就是把庫下面全部的有用的文件都一塊兒用rsync傳給加入節點的NC,這又與上面說的接起來了,這邊傳數據,那麼收數據,這邊完成以後,那麼收數據完成了,則SST也就作完了。
- 對於Xtrabackup方式,執行的命令是同樣的,只是用到的腳本是wsrep_sst_xtrabackup,道理與上面徹底相同,這邊作備份,那麼接收數據,等各自完成以後,也就完成了SST。
- wsrep_synced_cb:這個回調函數的做用是,在mysql啓動的時候,會有一些狀態不一致的狀況,那麼當狀態不一致的時候,系統會將當前的狀態設置爲不可用,也就是wsrep_ready這個狀態變量爲OFF,那麼爲了保證數據的一致性,或者因爲複製啓動的時候會影響到數據的一致性,那麼在這個狀態的時候,從庫的複製線程會等待這個狀態變爲ON的時候纔會繼續執行,不然一直等待,那麼這個回調函數作的事情就是告訴新加入節點或者donor節點,如今的同步已經完成了,狀態已是一致的了,能夠繼續作下面的操做了。因此此時複製會繼續開始。
自從上次說了
wsrep_init_startup以後,感受已通過了好久了,中間大概都有5000個字了。。。
那麼如今還接着
wsrep_init_startup繼續說:
在執行了上面所說的wsrep_init以後,接下來要作的事情是:
執行函數
wsrep_start_replication,這個所作的事情是調用第一個接口
connect,這個函數的功能已經說過了,就是加入集羣,算是報個到吧。
接着再作建立一個線程
wsrep_rollback_process,專門作回滾操做。
接着再作
wsrep_create_appliers,專門作APPLY操做,這裏建立的線程是
wsrep_replication_process,能夠有多個,這個會根據變量wsrep_slave_threads(其實只建立wsrep_slave_threads-1個線程,後面會說到緣由)來建立的,這個就是真正的所謂「多線程複製」。
在這個線程中,用到了接口
wsrep->recv(wsrep, (
void
*)thd);
對於函數recv,歷來不返回,除非是出錯了,這裏面的邏輯下面簡單說一下:
仍是從上面作SST開始吧:
在作SST前,其實只是建立一個線程,若是設置的線程數大於1,則作上面已經說了,會建立wsrep_slave_threads-1個線程,這緣由是:
SST這前,只建立一個,是爲了讓單獨一個線程去處理SST的工做。
上面已經說過了,在新加入節點將地址信息發送給集羣以後,集羣會選擇一個donor出來
而建立的那一個單獨的線程
wsrep_replication_process會阻塞在
wsrep->recv裏面,由於選擇出來的donor發現它是新加入的節點,因此它會直接在裏面調用回調函數
wsrep_sst_donate_cb,它的參數就是集羣給它的「方式+192.168.236.231:4444/rsync_sst」相似的東西,因此這就回到了上面對回調函數
wsrep_sst_donate_cb的說明了,它先作備份,而後再將數據複製到新加入節點中。
在作完SST以後,而後把剩下的線程建立起來(若是配置的wsrep_slave_threads大於1的話
)。
再接着上面的
wsrep_init_startup函數說:
此時調用最後一個
wsrep_sst_wait,很明顯,是等待SST完成,前面已經說過了,新節點一直在等,直到已經作完SST,而後再繼續啓動數據庫。
到此爲止,談到的都是Galera的啓動過程及SST的東西,下面就正常執行過程講述一下,能夠大概的瞭解它的原理。
首先客戶端選擇一個集羣中的服務器鏈接上去以後,此時其實與鏈接一個單點是徹底相同的。
在執行一個sql的時候,在非pxc的狀況下,執行的最終調用的函數是
mysql_parse,而對於pxc來講,它調用的是
wsrep_mysql_parse,對於它們的區別,後面再講。
首先,將一個操做能夠分爲幾個階段:
- 語句分析:對於這個階段,執行的都是本地操做,不會涉及到集羣的操做,因此和普通的mysql執行分析操做沒有區別。
- 本地執行:對於第2階段,這裏會作一個很重要的操做,那就是在插入、更新、刪除記錄時會調用一個wsrep_append_keys函數,它的做用就是將當前被修改的記錄的關鍵字(其實就是當前表的關鍵字在這行記錄中對應的列的值,若是沒有關鍵字,則就是rowid,由於pxc只支持innodb)提取出來,再按照Galera接口須要要的格式組裝起來,而後再經過Galera的接口append_key(上面已經詳細介紹過了)傳給集羣。
對於不一樣操做,其實KEY的最終數據是不一樣的,若是是插入,KEY固然只有新記錄的鍵值,對於更新,包括了舊數據及新數據的鍵值,對於刪除,則只有老數據的鍵值。同時若是鍵值具備多個列,則它們的組合方式是以列優先,其次纔是新舊值,也就是說,對於更新記錄,它的順序應該是下面這樣的:
舊鍵第一個值 |
新鍵第一個值 |
舊鍵第二個值 |
新鍵第二個值 |
...... |
...... |
舊鍵第n個值 |
新鍵第n個值 |
固然對於插入行,只是簡單的下面的格式:
新鍵第一個值 |
新鍵第二個值 |
...... |
新鍵第n個值 |
仍是上面說的,galera徹底是靠這些KEY來判斷是否是已經和其它客戶端的操做形成了衝突的。
- 事務提交:在這個階段,首先會作一個兩階段提交的wsrep_prepare,這裏面會執行一個核心操做,對應的函數是wsrep_run_wsrep_commit,在這個函數中作了2個重要的操做,首先將這個事務產生的binlog經過接口append_data傳給集羣,這部分data是與上面的key對應的,組成了key-value結構,key是用來判斷是否是衝突的,value是用來複制的,它是完完整整的binlog,在作複製的時候其它節點直接拿來作apply。另外一件事就是調用接口pre_commit,這在上面已經說過了,其實就裏請求集羣對當前key-value進行驗證,若是驗證經過,則地本繼續執行提交,將數據固化,而若是驗證失敗,則回滾便可。
那麼
如今鏈接服務器的工做流程已經介紹完,可是對於其它的節點而言,由於集羣已經驗證經過,本地服務器已經提交完成,那麼其它節點在驗證經過以後,會在recv接口中繼續執行,調用前面說到的
wsrep_apply_cb
回調函數,那麼此
時它拿到的就是上面事務提交時的binlog數據,直接作apply就行了。
上面提到,在pxc中用到的執行函數爲wsrep_mysql_parse,而在普通的mysql中用到的是mysql_parse,這2個的區別是什麼呢?首先
wsrep_mysql_parse調用了mysql_parse,只是在它以後,還作了另一些操做,下面就這個操做作一個描述:
這裏存在一種狀況,在當前執行節點的某一個事務在執行pre_commit操做並等待的過程當中,此時有另外一個事務,這個事務有多是來自其它節點的複製事務(在本地表現爲複製線程),它的優先級比當前事務高,而且正好這2個事務在某一個數據行上面是衝突的,那麼此時就會將當前事務殺死(
其實是調用了一次abort_pre_commit函數,這個函數也是Galera的一個接口,上面沒有作過介紹
),而正在此時,pre_commit已經在其它節點上面完成驗證,而且其它節點都已經複製完成,在這種狀況下,
pre_commit函數會返回一個錯誤碼
WSREP_BF_ABORT,表示其它節點複製完成,當前事務被殺死,那麼此時當前鏈接會一直返回到
wsrep_mysql_parse中,也就是跳出
mysql_parse(由於上面的操做其實都在這個函數中,只是
wsrep_mysql_parse將mysql_parse包了一層
),執行後
面的操做,這裏的操做是在將剛纔被殺死的事務從新作一遍,以slave模式作一遍,像複製同樣,具體對應的函數爲
wsrep_replay_transaction。
關於區別,僅此而已。
PXC的DDL實現說明
下面再詳細說一下關於在PXC中的DDL實現方式,這裏面可能存在一些問題:
pxc對於DDL,就不那麼嚴謹了,全部的DDL都是經過簡單的對象封鎖實現的,這裏又用到了Galera的另外2個接口,以前沒有介紹到的,分別是:
wsrep_status_t (*to_execute_start)(wsrep_t* wsrep,
wsrep_conn_id_t conn_id,
const wsrep_key_t* keys,
size_t keys_num,
const struct wsrep_buf* action,
size_t count,
wsrep_trx_meta_t* meta);
及
wsrep_status_t (*to_execute_end)(wsrep_t* wsrep, wsrep_conn_id_t conn_id);
to_execute_start是用來上鎖的,主要的參數就是keys,裏面的信息就是數據庫名及表名等對象名,若是是對庫操做,則只有庫名,參數
action表示的是當前執行的DDL的語句級的binlog,那麼這個是用來複制的,在其它節點直接執行這個binlog就行了。
to_execute_end是解鎖的,等中間的操做完成以後,就會調用這個接口。
在鎖定區間內,由於這是Galera的接口,因此至關於在整個集羣中對操做對象作了鎖定。
在鎖定以後,執行的DDL操做只是本地的,與集羣的是沒有任何關係的,由於此時其實已經經過一開始的
to_execute_start函數已經在其它節點上面將這個操做已經完成了。
關於這一點,很容易測試出來
構造一個2個節點的集羣,第一個節點先設置wsrep_on爲0,使得它的任何操做不作同步;
如今建立一個表,這個表是本地的,不會複製到另外一個節點上面去。
此時將wsrep_on設置爲1
而後再執行一次這個建立表的語句
此時發現,本地的執行報錯了,而後再觀察一下另外一個節點,居然神奇的出現了?
其實這是一種不一致的狀態。
上面的測試,若是第二次建立的表名是相同的,可是結構不一樣,這就致使2個節點的結構不一樣,而建立成功了。
上面的測試是說到了另外一個節點某一個表不存在的狀況,本地是存在的。
如今若是反過來講,本地是不存在的,而另外一個節點已經存在,那麼按照上面執行的邏輯能夠知道,經過
to_execute_start,
另外一個節點的複製確定是不能成功的,由於已經存在了,這點只會在log文件中寫一個錯誤日誌表現出來,而沒有采起任何其它措施,那個節點仍是正常的。而本地節點則正常的建立這個表,問題仍是同樣的。
其實上面關於ddl的問題官方的wiki上面已經說清楚了,請參照:
具體這個問題爲:
Q: How DDLs are handled by Galera?
Q&A
- 若是出現不一致,節點如何被集羣踢出
構造下面一種場景:
正常運行的2個節點的集羣,分別命名爲A和B,如今先在A上面建立一個表
A:create table t (sno int primary key);
B:而後發現B已經同步了這個表
A:設置A節點,讓它的修改再也不同步到其它節點,這是一種手動構造不一致的方式
set wsrep_on=0;
而後在A上面插入一條記錄,那麼這個記錄就不會被複制到B上面
A:insert into t values(1);
此時查看A已經有一條1的記錄,而B上面仍是空表,說明是正常的。
如今在B的節點上面插入一條一樣的記錄,由於上面的wsrep_on只是設置了不從A同步數據到B,而此時B仍是能夠同步數據到A的,那麼在B上執行一樣的插入語句:
B:insert into t values(1);
此時A掛了……
分析:這個掛是在recv接口中發生的,由於複製是在這個裏面發生的,經過調用回調函數
wsrep_apply_cb實現,而此時A節點的t表已經有一條記錄了,B節點再作的時候,這裏複製會執行失敗,致使
wsrep_apply_cb接口執行時會報錯,而後recv知道報錯以後,就直接將本身這個進程退出了,這樣就形成了節點被中踢出的現象(實際上是本身不想幹了)。不過這種表現上是優雅的,由於本身在離開前已經辦好了一切離開的手續了,屬於正常退出。
到如今爲止,全部的關於pxc實現方式已經基本搞清楚,其它問題均是mysql自己的問題node
總結:pxc架構上面可謂是實現了真正的集羣,最主要的特色是多主的,而且基本沒有延時,這樣就解決了mysql主從複製的不少問題,但其中仍是存在一些問題的,好比2個節點容易出現腦裂現象,這必需要經過另外一個工具去作一個監控及評判,而官方推薦最好是搞三個節點,但可能會形成必定程度的空間及機器的浪費,但在實際使用時能夠適當的在一個實例上面多放置幾個庫,來提升使用效率。mysql