雙主+haproxy手工切換的一個注意點

之前設計的切換邏輯

1 查詢slve的延遲情況,超過N秒延遲則等待或者返回失敗,確保業務影響時間最短

2 登陸proxy節點,disable當前hproxy,使得後續通過proxy的業務連接失敗

3 登陸proxy節點,shutdown當前通過proxy連接的會話(如果sql能快速完成,這步其實可用不做,可以做個時間閾值檢測,當N秒以後還有業務層連接則kill)

4 記錄當前主庫的binlog和pos點

5 等待雙主間的數據完全一致,即從庫執行到步驟4記錄的pos點後,可以利用MASTER_POS_WAIT(log_name,log_pos[,timeout])內置函數實現

6 執行切換,即enable M-M中的備用節點

7 返回切換成功


故障現象

切換以後,show slave status出現大量的主鍵衝突報錯


原因分析

通過分析,發現通過haproxy來殺死會話實際上並不靠譜,當連接建立以後,即使通過haproxy殺死會話或者殺死mysql client所在的客戶端進程,實際上沒有效果,依舊會往下繼續執行成功,如果此時主庫insert了記錄爲10的自增id沒有完成

當新的主庫連接進來後,當時的慢的sql還沒跑完,即自增id爲10的記錄還沒同步過來

這時新主庫再次insert一條記錄,產生的自增id依舊是10 

慢SQL這時在主庫執行完成,傳到新主庫後主鍵衝突,報錯

場景模擬

1 通過proxy登陸mysql,執行以下命令

mysql> select * from test_ha_switch;
Empty set (0.00 sec)


mysql> insert into test_ha_switch(dd) values (sleep(100));

此時SQL處於運行中

2 登陸proxy,通過socat殺死會話

echo "shutdown sessions server ha-proxy/10.9.188.208" | socat stdio /opt/udb/instance/haproxy/7378229c-7ada-4f90-b1ec-96d704979426/stats

3 此時再回去看步驟1中的會話,發現已經是處於ERROR 2013 (HY000): Lost connection to MySQL server during query

mysql> insert into test_ha_switch(dd) values (sleep(100));
ERROR 2013 (HY000): Lost connection to MySQL server during query

4 再次查詢該表,發現記錄依舊寫進去了

mysql> select * from test_ha_switch;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    1536352
Current database: jiang
+----+------+
| id | dd   |
+----+------+
|  1 |    0 |
+----+------+
1 row in set (0.01 sec)


另外如果沒有proxy,通過殺死客戶端所在的進程的方式也一樣很好模擬

# mysql -S /opt/udb/instance/mysql-5.6/5770e236-fef6-4d61-bc61-beb6d69f7dbe/mysqld.sock -uucloudbackup  jiang -e "show processlist;"
+---------+--------------+-------------------+-------+---------+------+-------+------------------+
| Id      | User         | Host              | db    | Command | Time | State | Info             |
+---------+--------------+-------------------+-------+---------+------+-------+------------------+
| 1536517 | ucloudbackup | 10.9.125.20:62374 | jiang | Sleep   |    7 |       | NULL             |
| 1536566 | ucloudbackup | localhost         | jiang | Query   |    0 | init  | show processlist |
+---------+--------------+-------------------+-------+---------+------+-------+------------------+
# nohup mysql -S /opt/udb/instance/mysql-5.6/5770e236-fef6-4d61-bc61-beb6d69f7dbe/mysqld.sock -uucloudbackup  jiang -e "insert into test_ha_switch(dd) select dd from test_ha_switch;" &
[1] 28752
# nohup: ignoring input and appending output to `nohup.out'

# ps -ef|grep test_ha_switch                                                                                                                                                                        
root     28752 17668  0 18:10 pts/0    00:00:00 mysql -S /opt/udb/instance/mysql-5.6/5770e236-fef6-4d61-bc61-beb6d69f7dbe/mysqld.sock -uucloudbackup -px xxxxxxxx jiang -e insert into test_ha_switch(dd) select dd from test_ha_switch;
root     29117 17668  0 18:10 pts/0    00:00:00 grep test_ha_switch
# kill -9 28752
[1]+  Killed                  nohup mysql -S /opt/udb/instance/mysql-5.6/5770e236-fef6-4d61-bc61-beb6d69f7dbe/mysqld.sock -uucloudbackup jiang -e "insert into test_ha_switch(dd) select dd from test_ha_switch;"
# mysql -S /opt/udb/instance/mysql-5.6/5770e236-fef6-4d61-bc61-beb6d69f7dbe/mysqld.sock -uucloudbackup -pJfKS9FFXvk jiang -e "show processlist;"                                                    
+---------+--------------+-------------------+-------+---------+------+--------------+--------------------------------------------------------------+
| Id      | User         | Host              | db    | Command | Time | State        | Info                                                         |
+---------+--------------+-------------------+-------+---------+------+--------------+--------------------------------------------------------------+
| 1536517 | ucloudbackup | 10.9.125.20:62374 | jiang | Sleep   |   29 |              | NULL                                                         |
| 1536570 | ucloudbackup | localhost         | jiang | Query   |   16 | Sending data | insert into test_ha_switch(dd) select dd from test_ha_switch |
| 1536575 | ucloudbackup | localhost         | jiang | Query   |    0 | init         | show processlist                                             |
+---------+--------------+-------------------+-------+---------+------+--------------+--------------------------------------------------------------+


優化方法

優化方法其實也比較簡單,就是在切換邏輯的3和4之間,通過直接登陸主節點的方式(這時已經無法通過proxy登陸db了),如果當前還有業務連接,則通過mysql kill語法kill掉當前的業務連接即可;

值得一提的是,kill線程時要確保正常退出,可能會存在這種情況,查看的時候還有兩個連接,你kill之前第一個連接已經斷開了,那麼你再去kill這個線程程序可能就會報錯退出,從而第二個線程實際上都沒有kill掉

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