MySQL Group Replication(簡稱MGR)是MySQL官方於2016年12月推出的一個全新的高可用與高擴展的解決方案。MGR是MySQL官方在5.7.17版本引進的一個數據庫高可用與高擴展的解決方案,以插件形式提供,實現了分布式下數據的最終一致性, 它是MySQL5.7版本出現的新特性,它提供了高可用、高擴展、高可靠的MySQL集群服務。MySQL組復制分單主模式和多主模式,mysql 的復制技術僅解決了數據同步的問題,如果 master 宕機,意味着數據庫管理員需要介入,應用系統可能需要修改數據庫連接地址或者重啟才能實現。(這里也可以使用數據庫中間件產品來避免應用系統數據庫連接的問題,例如 mycat 和 atlas 等產品)。組復制在數據庫層面上做到了,只要集群中大多數主機可用,則服務可用,也就是說3台服務器的集群,允許其中1台宕機。
1. MGR組復制的特點
- 高一致性:基於分布式paxos協議實現組復制,保證數據一致性;
- 高容錯性:自動檢測機制,只要不是大多數節點都宕機就可以繼續工作,內置防腦裂保護機制;
- 高擴展性:節點的增加與移除會自動更新組成員信息,新節點加入后,自動從其他節點同步增量數據,直到與其他節點數據一致;
- 高靈活性:提供單主模式和多主模式,單主模式在主庫宕機后能夠自動選主,所有寫入都在主節點進行,多主模式支持多節點寫入。
2. MGR與傳統復制的區別和大幅改進
2.1 傳統復制 (即異步復制)
主-從復制:有一個主和不等數量的從。主節點執行的事務會異步發送給從節點,在從節點重新執行。即是在主節點執行和提交事務,然后把他們異步的發送到從節點,行復制的重新執行主節點的SQL語句,這是一個 shared-nothing 的系統,默認情況下所有 server 成員都有一個完整的數據副本。
2.2 半同步復制
它在協議中添加了一個同步步驟。 這意味着主節點在提交時需要等待從節點確認它已經接收到事務。只有這樣,主節點才能繼續提交操作。 半同步相對異步來說, Master會確認Slave是否接到數據,更加安全。
2.3 並行復制
並行復制:復制->廣播->正式復制.
一般Mysql主從復制有三個線程參與,都是單線程:Binlog Dump(主) -> IO Thread (從) -> SQL Thread(從)。復制出現延遲一般出在兩個地方: - SQL線程忙不過來 (可能需要應用數據量較大,可能和從庫本身的一些操作有鎖和資源的沖突;主庫可以並發寫,SQL線程不可以;主要原因) - 網絡抖動導致IO線程復制延遲(次要原因)。 MySQL主從復制延遲的解決辦法:MySQL從5.6開始有了SQL Thread多個的概念,可以並發還原數據,即並行復制技術。並行復制的機制,是MySQL的一個非常重要的特性,可以很好的解決MySQL主從延遲問題! 在MySQL 5.6中,設置參數slave_parallel_workers = 4(>1),即可有4個SQL Thread(coordinator線程)來進行並行復制,其狀態為:Waiting for an evant from Coordinator。但是其並行只是基於Schema的,也就是基於庫的。如果數據庫實例中存在多個Schema,這樣設置對於Slave復制的速度可以有比較大的提升。通常情況下單庫多表是更常見的一種情形,那基於庫的並發就沒有卵用。其核心思想是:不同schema下的表並發提交時的數據不會相互影響,即slave節點可以用對relay log中不同的schema各分配一個類似SQL功能的線程,來重放relay log中主庫已經提交的事務,保持數據與主庫一致。 MySQL 5.6版本支持所謂的並行復制,但是其並行只是基於schema的,也就是基於庫的。如果用戶的MySQL數據庫實例中存在多個schema,對於從機復制的速度的確可以有比較大的幫助。但是基於schema的並行復制存在兩個問題: 1) crash safe功能不好做,因為可能之后執行的事務由於並行復制的關系先完成執行,那么當發生crash的時候,這部分的處理邏輯是比較復雜的。 2) 最為關鍵的問題是這樣設計的並行復制效果並不高,如果用戶實例僅有一個庫,那么就無法實現並行回放,甚至性能會比原來的單線程更差。而 單庫多表是比多庫多表更為常見的一種情形 。 注意:mysql 5.6的MTS是基於庫級別的並行,當有多個數據庫時,可以將slave_parallel_workers設置為數據庫的數量,為了避免新建庫后來回修改,也可以將該參數設置的大一些。設置為庫級別的事務時,不允許這樣做,會報錯。 在MySQL 5.7中,引入了基於組提交的並行復制(官方稱為Enhanced Multi-threaded Slaves,即MTS),設置參數slave_parallel_workers>0並且global.slave_parallel_type=‘LOGICAL_CLOCK’,即可支持一個schema下,slave_parallel_workers個的worker線程並發執行relay log中主庫提交的事務。其核心思想:一個組提交的事務都是可以並行回放(配合binary log group commit);slave機器的relay log中 last_committed相同的事務(sequence_num不同)可以並發執行。其中,變量slave-parallel-type可以有兩個值:1)DATABASE 默認值,基於庫的並行復制方式;2)LOGICAL_CLOCK,基於組提交的並行復制方式; MySQL 5.7開啟Enhanced Multi-Threaded Slave很簡單,只需要在Slave從數據庫的my.cnf文件中如下配置即可: # slave slave-parallel-type=LOGICAL_CLOCK slave-parallel-workers=8 #一般建議設置4-8,太多的線程會增加線程之間的同步開銷 master_info_repository=TABLE relay_log_info_repository=TABLE relay_log_recovery=ON MySQL 5.7是基於組提交的並行復制,並且是支持"真正"的並行復制功能,這其中最為主要的原因:就是slave服務器的回放與主機是一致的, 即master服務器上是怎么並行執行的slave上就怎樣進行並行回放。不再有庫的並行復制限制,對於二進制日志格式也無特殊的要求(基於庫的並行復制也沒有要求)。 MySQL5.7的並行復制,期望最大化還原主庫的並行度,實現方式是在binlog event中增加必要的信息,以便slave節點根據這些信息實現並行復制。MySQL5.7的並行復制建立在group commit的基礎上,所有在主庫上能夠完成prepared的語句表示沒有數據沖突,就可以在slave節點並行復制。 所以在並行復制環境中,除了在Slace從數據庫中配置之外,還需要在Master主數據庫上的my.cnf文件中添加binlog_group_commit配置,否則從庫無法做到基於事物的並行復制: binlog_group_commit_sync_delay = 100 binlog_group_commit_sync_no_delay_count = 10 binlog_group_commit_sync_delay,這個參數控制着日志在刷盤前日志提交要等待的時間,默認是0也就是說提交后立即刷盤,但是並不代表是關閉了組提交,當設置為0以上的時候,就允許多個事物的日志同時間一起提交刷盤,也就是我們說的組提交。組提交是並行復制的基礎,我們設置這個值的大於0就代表打開了組提交的延遲功能,而組提交是默認開啟的。最大值只能設置為1000000微妙。 binlog_group_commit_sync_no_delay_count ,這個參數表示我們在binlog_group_commit_sync_delay等待時間內,如果事物數達到這個參數的設定值,就會觸動一次組提交,如果這個值設為0的話就不會有任何的影響。如果到達時間但是事物數並沒有達到的話,也是會進行一次組提交操作的。 MySQL 5.7並行復制的思想簡單易懂,一言以蔽之: 一個組提交的事務都是可以並行回放 ,因為這些事務都已進入到事務的prepare階段,則說明事務之間沒有任何沖突(否則就不可能提交)。為了兼容MySQL 5.6基於庫的並行復制,5.7引入了新的變量slave-parallel-type,其可以配置的值有: - DATABASE:默認值,基於庫的並行復制方式 - LOGICAL_CLOCK:基於組提交的並行復制方式 支持並行復制的GTID 如何知道事務是否在一組中,又是一個問題,因為原版的MySQL並沒有提供這樣的信息。在MySQL 5.7版本中,其設計方式是將組提交的信息存放在GTID中。那么如果用戶沒有開啟GTID功能,即將參數gtid_mode設置為OFF呢?故MySQL 5.7又引入了稱之為Anonymous_Gtid的二進制日志event類型,如: mysql> SHOW BINLOG EVENTS in 'mysql-bin.000003';
2.4 組復制 (MGR)
MGR組復制原理
組復制是一種可用於實現容錯系統的技術。 復制組是一個通過消息傳遞相互交互的 server 集群。通信層提供了原子消息(atomic message)和完全有序信息交互等保障機制實現了基於復制協議的多主更新 復制組由多個 server成員構成,並且組中的每個 server 成員可以獨立地執行事務。但所有讀寫(RW)事務只有在沖突檢測成功后才會提交。只讀(RO)事務不需要在沖突檢測,可以立即提交。句話說,對於任何 RW 事務,提交操作並不是由始發 server 單向決定的,而是由組來決定是否提交。准確地說,在始發 server 上,當事務准備好提交時,該 server 會廣播寫入值(已改變的行)和對應的寫入集(已更新的行的唯一標識符)。然后會為該事務建立一個全局的順序。最終,這意味着所有 server 成員以相同的順序接收同一組事務。因此,所有 server 成員以相同的順序應用相同的更改,以確保組內一致。
MySQL組復制協議工作流程:
需要注意:MGR組復制是一種 share-nothing 復制方案,其中每個 server 成員都有自己的完整數據副本。
MGR實現了基於復制協議的多主更新
-> 復制組由多個 server成員構成,並且組中的每個 server 成員可以獨立地執行事務。但所有讀寫(RW)事務只有在沖突檢測成功后才會提交。只讀(RO)事務不需要在沖突檢測,可以立即提交。
-> 換句話說,對於任何 RW 事務,提交操作並不是由始發 server 單向決定的,而是由組來決定是否提交。准確地說,在始發 server 上,當事務准備好提交時,該 server 會廣播寫入值(已改變的行)和對應的寫入集(已更新的行的唯一標識符)。然后會為該事務建立一個全局的順序。最終,這意味着所有 server 成員以相同的順序接收同一組事務。因此,所有 server 成員以相同的順序應用相同的更改,以確保組內一致。
-> 組復制使您能夠根據在一組 server 中復制系統的狀態來創建具有冗余的容錯系統。因此,只要它不是全部或多數 server 發生故障,即使有一些 server 故障,系統仍然可用,最多只是性能和可伸縮性降低,但它仍然可用。server 故障是孤立並且獨立的。它們由組成員服務來監控,組成員服務依賴於分布式故障檢測系統,其能夠在任何 server 自願地或由於意外停止而離開組時發出信號。
-> 他們是由一個分布式恢復程序來確保當有 server 加入組時,它們會自動更新組信息到最新。並且多主更新確保了即使在單個服務器故障的情況下也不會阻止更新,不必進行 server故障轉移。因此,MySQL 組復制保證數據庫服務持續可用。
-> 值得注意的一點是,盡管數據庫服務可用,但當有一個 server 崩潰時,連接到它的客戶端必須定向或故障轉移到不同的 server。這不是組復制要解決的問題。連接器,負載均衡器,路由器或其他形式的中間件更適合處理這個問題。
總之,MGR組復制提供了高可用性,高彈性,可靠的 MySQL 服務。
MGR故障檢測
故障檢測是提供關於哪些 server 可能已死的信息(猜測)的分布式服務。 某個 server 無響應時觸發猜測,組中其余成員進行協調決定以排除給定成員。如果某個 server 與組的其余成員隔離,則它會懷疑所有其他 server 都失敗了。由於無法與組達成協議(因為它無法確保仲裁成員數),其懷疑不會產生后果。當服務器以此方式與組隔離時,它無法執行任何本地事務。 在線 server 列表通常稱為視圖,新成員server的加入離開,無論是自願還是被迫的離開,該組都會動態地重新規划其配置,並觸發視圖更新
MGR的限制
- 存儲引擎必須為Innodb,即僅支持InnoDB表,並且每張表一定要有一個主鍵,用於做write set的沖突檢測;
- 每個表必須提供主鍵;
- 只支持ipv4,網絡需求較高;
- 必須打開GTID特性,二進制日志格式必須設置為ROW,用於選主與write set;
- COMMIT可能會導致失敗,類似於快照事務隔離級別的失敗場景;
- 目前一個MGR集群組最多支持9個節點;
- 不支持外鍵於save point特性,無法做全局間的約束檢測與部分部分回滾;
- 二進制日志binlog不支持Replication event checksums;
- 多主模式(也就是多寫模式) 不支持SERIALIZABLE事務隔離級別;
- 多主模式不能完全支持級聯外鍵約束;
- 多主模式不支持在不同節點上對同一個數據庫對象並發執行DDL(在不同節點上對同一行並發進行RW事務,后發起的事務會失敗);
MGR組復制優勢
- 彈性復制(高擴展性):server動態添加移除
- 高可用分片(高擴展性):分片實現寫擴展,每個分片是一個復制組。
- 替代主從復制(高擴展性):整組寫入,避免單點爭用。
- 自動化系統:自動化部署Mysql復制到已有復制協議的自動化系統。
- 故障檢測與容錯:自動檢測,若服務faild,組內成員大多數達成認為該服務已不正常,則自動隔離。
- 組內成員會構成一個視圖,組內成員主動加入或離開(主動或被動),都會更新組配置,更新視圖。成員自願離開,先更新組配置,然后采用大多數成員(不包含主動脫離的成員)意見是否確認該成員離開更新視圖。如果是故障要排除,則需大多數服務確認(包括故障成員意見),然后才會更新組配置和視圖。
- 最大允許即時故障數:f=(n-1)/2,多數正常則正常
3. 組復制兩種運行模式
-> 在單主模式下, 組復制具有自動選主功能,每次只有一個 server成員接受更新。單寫模式group內只有一台節點可寫可讀,其他節點只可以讀。對於group的部署,需要先跑起primary節點(即那個可寫可讀的節點,read_only = 0)然后再跑起其他的節點,並把這些節點一一加進group。其他的節點就會自動同步primary節點上面的變化,然后將自己設置為只讀模式(read_only = 1)。當primary節點意外宕機或者下線,在滿足大多數節點存活的情況下,group內部發起選舉,選出下一個可用的讀節點,提升為primary節點。primary選舉根據group內剩下存活節點的UUID按字典序升序來選擇,即剩余存活的節點按UUID字典序排列,然后選擇排在最前的節點作為新的primary節點。
-> 在多主模式下, 所有的 server 成員都可以同時接受更新。group內的所有機器都是primary節點,同時可以進行讀寫操作,並且數據是最終一致的。
按照我的理解來說:
單主模式:比多主模式多一個選舉程序,第一次引導開啟集群的為主,后加入的為追隨者(也可以叫從機Slave),只有住的有讀寫權限,別的追隨者在加入組的時候自動把權限禁了。如果主的掛了,其他服務器會根據UUID和一個值(類似權重)進行重新選主。每次選主都會重新把權限禁一遍。
多主模式:所有服務器加入組時,讀寫權限全部放開,大家都可以讀寫,但是只能更改不同行的數據,如果后加入集群的服務器改了一行數據,那前面的服務器就不能再對這行數據進行改動了,如果改動則報事務回滾取消改動,而后加入的可以改前面加入集群改過的數據。
環境: centos 7.6.1810 三台ip分別: 10.10.100.186 10.10.100.194 10.10.100.195
一、mysql 8.0.18 MGR搭建
1、下載mysql免安裝包, 准備好系統環境
mysql-8.0.18-linux-glibc2.12-x86_64.tar #不選擇安裝系統平台下載即可,此版本解壓即可使用
systemctl stop firewalld.service #停止防火牆
systemctl disable firewalld.service #禁止開機啟動
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux
cat /etc/sysconfig/selinux
vim /etc/hosts #增加三台機器的ip域名映射
dmesg | grep -i scheduler #確定centos7的io調度算法 deadline
echo "vm.swappiness = 0" >>/etc/sysctl.conf #0表示盡力使用物理內存,100盡力使用swap分區
sysctl -p
#通過sysctl -a 查看
cat /etc/security/limits.conf #更改系統限制
...
* soft nproc 65536
* hard nproc 65536
* soft nofile 65536
* hard nofile 65536
2、安裝mysql,初始化配置,開啟group_replication
-----創建mysql用戶和組
# useradd -u 1001 -d /home/mysql -m mysql
# id mysql
# rpm -e `rpm -qa|grep mysql` --nodeps
# yum install -y autoconf automake imake libxml2-devel expat-devel cmake gcc gcc-c++ libaio libaio-devel bzr bison libtool ncurses5-devel ncurses-devle perl-DBD-mysql
-----解壓安裝包並設置權限,並創建相關目錄
tar xf mysql-8.0.18-linux-glibc2.12-x86_64.tar -C /usr/local/
cd /usr/local
mv mysql-8.0.18-linux-glibc2.12-x86_64 mysql
chown mysql:mysql /usr/local/mysql -R
cp -f mysql/support-files/mysql.server /etc/init.d/mysqld
vim /etc/my.cnf #建立配置文件
#根據實際情況配置 [client] socket=/service/data/sock/mysql.sock #sock文件所在路徑 [mysqld] user=mysql server-id=186 basedir=/usr/local/mysql/ socket=/service/data/sock/mysql.sock pid-file=/service/data/sock/mysql.pid datadir=/service/data/data tmpdir=/service/data/tmpdir log-error=/service/data/log/error.log log_timestamps = SYSTEM slow_query_log=1 log_queries_not_using_indexes=1 slow_query_log_file=/service/data/slowlog/slow-query.log log-bin=/service/data/binlog/mysql-bin relay-log=/service/data/relaylog/mysql-relay-bin innodb_data_home_dir=/service/data/ib innodb_log_group_home_dir=/service/data/redo innodb_undo_directory=/service/data/undo/ innodb_undo_tablespaces=3 character_set_server=utf8mb4 #MGR使用樂觀鎖,所以官網建議隔離級別是RC,減少鎖粒度 transaction_isolation=READ-COMMITTED gtid_mode=on enforce_gtid_consistency=1 binlog_format=row secure_file_priv=/tmp performance_schema=ON innodb_redo_log_archive_dirs=/service/data/redo_archive #因為集群會在故障恢復時互相檢查binlog的數據,所以需要記錄下集群內其他服務器發過來已經執行過的binlog,按GTID來區分是否執行過. log-slave-updates=1 #binlog校驗規則,5.6之后的高版本是CRC32,低版本都是NONE,但是MGR要求使用NONE binlog_checksum=NONE #基於安全的考慮,MGR集群要求復制模式要改成slave記錄記錄到表中,不然就報錯 master_info_repository=TABLE relay_log_info_repository=TABLE #記錄事務的算法,官網建議設置該參數使用 XXHASH64 算法 transaction_write_set_extraction = XXHASH64 #相當於此GROUP的名字,是UUID值,不能和集群內其他GTID值的UUID混用,可用uuidgen來生成 loose-group_replication_group_name = 'de720075-8b42-4833-a4d9-a60e8efb8f5a' #是否隨服務器啟動而自動啟動組復制,不建議直接啟動,怕故障恢復時有擾亂數據准確性的特殊情況 loose-group_replication_start_on_boot = OFF #本地MGR的IP地址和端口,host:port,是MGR的端口,不是數據庫的端口 loose-group_replication_local_address = '10.10.100.186:33061' #需要接受本MGR實例控制的服務器IP地址和端口,是MGR的端口,不是數據庫的端口 loose-group_replication_group_seeds = '10.10.100.186:33061,10.10.100.194:33061,10.10.100.195:33061' #開啟引導模式,添加組成員,用於第一次搭建MGR或重建MGR的時候使用,只需要在集群內的其中一台開啟 loose-group_replication_bootstrap_group = OFF #IP地址白名單,不會允許來自外部主機的連接,按需安全設置 #loose-group_replication_ip_whitelist = report_host=10.10.100.186 report_port=3306
[mysqld] server_id = 19 user = mysql expire_logs_days = 10 log_bin_trust_function_creators = ON explicit_defaults_for_timestamp = ON symbolic_links = 0 show_compatibility_56 = ON lower_case_table_names = 1 plugin_load = validate_password.so plugin_load = group_replication.so secure_file_priv=/tmp tmp_table_size = 48M max_heap_table_size = 48M max_connections = 2000 max_connect_errors = 20000 port = 3306 datadir = /service/data/ socket = /service/data/mysqld.sock log_bin = /service/data/mysql-bin skip_name_resolve log_error = /service/data/mysqld.log pid-file = /service/data/mysqld.pid innodb_flush_method = O_DIRECT innodb_flush_log_at_trx_commit=2 gtid_mode = ON enforce-gtid-consistency = true binlog_format = ROW master_info_repository = TABLE relay_log_info_repository = TABLE binlog_checksum = NONE log_slave_updates = ON transaction_write_set_extraction = XXHASH64 loose_group_replication_start_on_boot = OFF loose_group_replication_bootstrap_group = OFF loose_group_replication_group_name = aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaddddd loose_group_replication_local_address = '10.10.100.19:6606' loose_group_replication_group_seeds = '10.10.100.19:6606,10.10.100.28:6606,10.10.100.39:6606' loose_group_replication_member_weight = 80 group_replication_single_primary_mode = TRUE auto_increment_increment = 1 loose_group_replication_auto_increment_increment = 1 sql_mode = NO_ENGINE_SUBSTITUTION query_cache_type = 0 event_scheduler = ON innodb_log_group_home_dir = /service/data/ innodb_buffer_pool_size = 12G innodb_log_file_size = 512M innodb_file_per_table = ON innodb_log_files_in_group = 2 innodb_buffer_pool_instances = 8 innodb_page_cleaners = 8 innodb_read_io_threads = 8 character_set_server = utf8mb4 collation_server = utf8mb4_unicode_ci innodb_undo_directory = /service/data/ log_timestamps = SYSTEM innodb_page_cleaners = 32 innodb_doublewrite=0 innodb_flush_log_at_trx_commit=2 innodb_flush_method=O_DIRECT_NO_FSYNC innodb_thread_concurrency=64 sync_binlog=0 [mysql] default_character_set = utf8mb4 socket = /service/data/mysqld.sock [client] default_character_set = utf8mb4 socket = /service/data/mysqld.sock
mkdir -p /service/data/{data,sock,tmpdir,log,ib,redo,undo,slowlog,binlog,relaylog}
chown -R mysql.mysql /service/
-----更改/home/mysql/.bash_profie和/etc/profile,增加以下內容
PATH=$PATH:$HOME/.local/bin:$HOME/bin:/usr/local/mysql/bin
LD_LIBRARY_PATH=/usr/local/mysql/lib/
export PATH
export LD_LIBRARY_PATH
-------------------------------------------
./usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --initialize-insecure &
./bin/mysqld_safe --defaults-file=/etc/my.cnf & /etc/my.cnf>/etc/mysql/my.cnf>$mysql_home/my.cnf>~/.my.cnf
ps -ef |grep mysql
netstat -anultp |grep 3306
yum -y install net-tools
mysql -S /service/data/sock/mysql.sock #登錄數據庫
mysql>install plugin group_replication soname 'group_replication.so'; #安裝插件
mysql>set sql_log_bin=0; #關閉bin log
mysql>alter user root@'%' identified with mysql_native_password by 'password';
mysql>grant all on *.* to root@'%' with grant option;
mysql>create user repl@'%';
mysql>alter user 'repl'@'%' identified with mysql_native_password by 'repl';
mysql>grant replication slave on *.* to repl@'%'with mysql_native identified by 'repl';
mysql>select user,host,plugin from mysql.user; #查看用戶 登錄主機 使用加密模式
mysql>flush privileges;
mysql>set sql_log_bin=1;
mysql>change master to master_user='repl',master_password='repl' for channel 'group_replication_recovery'; #創建復制
mysql>SET GLOBAL group_replication_bootstrap_group=ON;
mysql>START GROUP_REPLICATION;
mysql>SET GLOBAL group_replication_bootstrap_group=OFF;
mysql>SELECT * FROM performance_schema.replication_group_members; #查看MGR組信息
---------------報錯無法加入或者組成員是recovering狀態--------
mysql>set global group_replication_allow_local_disjoint_gtids_join=ON; #開啟前關閉
-----recovering狀態
mysql>show global variables like 'gtid_purged'; #查看主庫的gtid
mysql>stop group_replication; #從庫操作
mysql>reset master; #從庫操作
mysql>set @@global.gtid_purged='主庫查到的gtid';
mysql>start group_replication;
mysql>select * from performance_schema.replication_group_members; #再次查看mgr組信息
二、proxysql 搭建
2.1、下載proxysql並安裝在 194 195兩台機器上
rpm -ivh proxysql-2.0.8-1-centos7.x86_64.rpm
service proxysql start
2.2、在任意一台mysql上查看當前的 primary master
SELECT member_host as "primary master" FROM performance_schema.global_status JOIN performance_schema.replication_group_members WHERE variable_name = 'group_replication_primary_member' AND member_id=variable_value;
+----------------+
| primary master |
+----------------+
| 10.10.100.186 |
+----------------+
1 row in set (0.00 sec)
2.3、在primary master 10.10.100.186止執行以下:
mysql> create USER monitor@'%';
mysql> GRANT SELECT on sys.* to 'monitor'@'%' identified by 'monitor';
mysql> create USER proxysql@'%';
mysql> grant all privileges on *.* to 'proxysql'@'%' identified by 'proxysql';
mysql> flush privileges;
2.4、在primary master的mysql上創建proxysql所需的表和函數
mysql > USE sys;
DELIMITER $$
CREATE FUNCTION IFZERO(a INT, b INT)
RETURNS INT
DETERMINISTIC
RETURN IF(a = 0, b, a)$$
CREATE FUNCTION LOCATE2(needle TEXT(10000), haystack TEXT(10000), offset INT)
RETURNS INT
DETERMINISTIC
RETURN IFZERO(LOCATE(needle, haystack, offset), LENGTH(haystack) + 1)$$
CREATE FUNCTION GTID_NORMALIZE(g TEXT(10000))
RETURNS TEXT(10000)
DETERMINISTIC
RETURN GTID_SUBTRACT(g, '')$$
CREATE FUNCTION GTID_COUNT(gtid_set TEXT(10000))
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE result BIGINT DEFAULT 0;
DECLARE colon_pos INT;
DECLARE next_dash_pos INT;
DECLARE next_colon_pos INT;
DECLARE next_comma_pos INT;
SET gtid_set = GTID_NORMALIZE(gtid_set);
SET colon_pos = LOCATE2(':', gtid_set, 1);
WHILE colon_pos != LENGTH(gtid_set) + 1 DO
SET next_dash_pos = LOCATE2('-', gtid_set, colon_pos + 1);
SET next_colon_pos = LOCATE2(':', gtid_set, colon_pos + 1);
SET next_comma_pos = LOCATE2(',', gtid_set, colon_pos + 1);
IF next_dash_pos < next_colon_pos AND next_dash_pos < next_comma_pos THEN
SET result = result +
SUBSTR(gtid_set, next_dash_pos + 1,
LEAST(next_colon_pos, next_comma_pos) - (next_dash_pos + 1)) -
SUBSTR(gtid_set, colon_pos + 1, next_dash_pos - (colon_pos + 1)) + 1;
ELSE
SET result = result + 1;
END IF;
SET colon_pos = next_colon_pos;
END WHILE;
RETURN result;
END$$
CREATE FUNCTION gr_applier_queue_length()
RETURNS INT
DETERMINISTIC
BEGIN
RETURN (SELECT sys.gtid_count( GTID_SUBTRACT( (SELECT
Received_transaction_set FROM performance_schema.replication_connection_status
WHERE Channel_name = 'group_replication_applier' ), (SELECT
@@global.GTID_EXECUTED) )));
END$$
CREATE FUNCTION gr_member_in_primary_partition()
RETURNS VARCHAR(3)
DETERMINISTIC
BEGIN
RETURN (SELECT IF( MEMBER_STATE='ONLINE' AND ((SELECT COUNT(*) FROM
performance_schema.replication_group_members WHERE MEMBER_STATE != 'ONLINE') >=
((SELECT COUNT(*) FROM performance_schema.replication_group_members)/2) = 0),
'YES', 'NO' ) FROM performance_schema.replication_group_members JOIN
performance_schema.replication_group_member_stats USING(member_id));
END$$
CREATE VIEW gr_member_routing_candidate_status AS SELECT
sys.gr_member_in_primary_partition() as viable_candidate,
IF( (SELECT (SELECT GROUP_CONCAT(variable_value) FROM
performance_schema.global_variables WHERE variable_name IN ('read_only',
'super_read_only')) != 'OFF,OFF'), 'YES', 'NO') as read_only,
sys.gr_applier_queue_length() as transactions_behind, Count_Transactions_in_queue as 'transactions_to_cert' from performance_schema.replication_group_member_stats;$$
DELIMITER ;
2.5、在 194和195上各執行一遍以下命令:
# su - mysql
$mysql -u admin -padmin -h 127.0.0.1 -P6032 --prompt='Admin>' --default-auth=mysql_native_password
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.186',3306);
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.194',3306);
Admin> INSERT INTO mysql_servers(hostgroup_id,hostname,port) VALUES (0,'10.10.100.195',3306);
Admin>insert into mysql_group_replication_hostgroups (writer_hostgroup,backup_writer_hostgroup,
reader_hostgroup, offline_hostgroup,active,max_writers,writer_is_also_reader,max_transactions_behind)
values (0,4,3,1,1,1,0,100);
Admin> save mysql servers to disk;
Query OK, 0 rows affected (0.01 sec)
Admin> load mysql servers to runtime;
Admin> insert into mysql_users(username, password) values('proxysql', 'BGT%6yhn');
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(1,1,'^SELECT.*FOR UPDATE$',0,1);
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(2,1,'^SELECT',0,1);
Admin> INSERT INTO mysql_query_rules(rule_id,active,match_pattern,destination_hostgroup,apply) VALUES(3,1,'^^[^SEL]',0,1);
Admin> LOAD MYSQL VARIABLES TO RUNTIME;
Admin> SAVE MYSQL VARIABLES TO DISK;
Admin> LOAD MYSQL SERVERS TO RUNTIME;
Admin> SAVE MYSQL SERVERS TO DISK;
Admin> LOAD MYSQL USERS TO RUNTIME;
Admin> SAVE MYSQL USERS TO DISK;
Admin>exit
2.6、在194和195上分別重啟proxysql
service proxysql restart
2.7、此時實現了在 194和195上可以通過6033端口訪問mysql:
mysql -u root -p -P 6033
三、在 194和195上安裝keepalived:
3.1、分別執行安裝
yum -y install openssl-devel
tar -xf keepalived-2.0.19.tar.gz
cd keepalived-2.0.19
./configure --prefix=/usr/local/keepalived
make && make install
cp /usr/local/keepalived/sbin/keepalived /etc/rc.d/init.d/keepalived
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/keepalived
mkdir /etc/keepalived
3.2、在mysql 194上創建arp.sh chk_proxy.sh keepalived.conf 文件
cat arp.sh
#!/bin/bash
VIP=10.10.100.111
GATEWAY=10.10.100.1
/sbin/arping -I eth0 -c 5 -s $VIP $GATEWAY > /dev/null 2>&1
cat chk_proxy.sh
#!/bin/bash
killall -0 proxysql
cat keepalived.conf
global_defs {
# Keepalived process identifier
router_id proxy_HA
# lvs_id proxy_HA
}
# Script used to check if Proxy is running
vrrp_script check_proxy {
script "/etc/keepalived/chk_proxy.sh"
interval 2
weight 10
user root
}
vrrp_sync_group VG001 {
group {
VI_001
}
notify_master "/etc/keepalived/arp.sh"
}
# Virtual interface
# The priority specifies the order in which the assigned interface to take over in a failover
vrrp_instance VI_001 {
# state MASTER
state BACKUP
interface eth0
virtual_router_id 75
priority 125
# nopreempt
# The virtual ip address shared between the two loadbalancers
virtual_ipaddress {
10.10.100.111
}
track_script {
check_proxy
}
}
3.3、在mysql 195上創建 arp.sh chk_proxy.sh keepalived.conf 文件:
cat arp.sh
#!/bin/bash
VIP=10.10.100.111
GATEWAY=10.10.100.1
/sbin/arping -I eth0 -c 5 -s $VIP $GATEWAY > /dev/null 2>&1
cat chk_proxy.sh
#!/bin/bash
killall -0 proxysql
cat keepalived.conf
global_defs {
# Keepalived process identifier
router_id proxy_HA
# lvs_id proxy_HA
}
# Script used to check if Proxy is running
vrrp_script check_proxy {
script "/etc/keepalived/chk_proxy.sh"
interval 2
weight 10
user root
}
vrrp_sync_group VG001 {
group {
VI_001
}
notify_master "/etc/keepalived/arp.sh"
}
# Virtual interface
# The priority specifies the order in which the assigned interface to take over in a failover
vrrp_instance VI_001 {
# state MASTER
state BACKUP
interface eth0
virtual_router_id 75
priority 120
# nopreempt
# The virtual ip address shared between the two loadbalancers
virtual_ipaddress {
10.10.100.111
}
track_script {
check_proxy
}
}
3.4、分別重啟mysql194 mysql195上的keepalived:
/etc/init.d/keepalived
3.5、在mysql194 MYSQL195 查看vip及keepalived狀態:
ip addr list |grep 10.10.100.111
3.6、可通過vip訪問proxysql和mysql:
mysql -h 10.10.100.111 -u proxysql -p -P 6033

