共計 3242 個字符,預計需要花費 9 分鐘才能閱讀完成。
自動寫代碼機器人,免費開通
這篇文章給大家分享的是有關 MySQL 怎么生成唯一的 server-id 的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。
前言
我們都知道 MySQL 用 server-id 來一的標識某個數據庫實例,并在鏈式或雙主復制結構中用它來避免 sql 語句的無限循環。這篇文章分享下我對 server-id 的理解,然后比較和權衡生成唯一 server-id 的幾種方式。
server_id 的用途
簡單說來,server_id 有兩個用途:
1. 用來標記 binlog event 的源產地,就是 SQL 語句最開始源自于哪里。
2. 用于 IO_thread 對主庫 binlog 的過濾。如果沒有設置 replicate-same-server-id=1,那么當從庫的 io_thread 發現 event 的源與自己的 server-id 相同時,就會跳過該 event,不把該 event 寫入到 relay log 中。從庫的 sql_thread 自然就不會執行該 event。這在鏈式或雙主結構中可以避免 sql 語句的無限循環。
注意:相同 server-id 的 event 在 io_thread 這一層就過濾了;而對于 replicate-(do|ignore)- 等規則,則是在 sql_thread 這一層過濾的。io_thread 和 sql_thread 都有過濾的功能。
server_id 為何不能重復
在同一個集群中,server-id 一旦重復,可能引發一些詭異問題。
看看下面兩種情況:
圖 1:主庫與從庫的 server-id 不同,但是兩個或多個從庫的 server-id 相同
這種情況下復制會左右搖擺。當兩個從庫的 server-id 相同時,如果從庫 1 已經連接上主庫,此時從庫 2 也需要連接到主庫,發現之前有 server-id 相同的連接,就會先注銷該連接,然后重新注冊。
參考下面的代碼片段:
int register_slave(THD* thd, uchar* packet, uint packet_length)
int res;
SLAVE_INFO *si;
if (!(si- master_id= uint4korr(p)))
si- master_id= server_id;
si- thd= thd;
pthread_mutex_lock(LOCK_slave_list);
/* 先注銷相同 server-id 的連接 */
unregister_slave(thd,0,0);
/* 重新注冊 */
res= my_hash_insert(slave_list, (uchar*) si);
pthread_mutex_unlock(LOCK_slave_list);
return res;
}
兩臺從庫不停的注冊,不停的注銷,會產生很多 relay log 文件,查看從庫狀態會看到 relay log 文件名不停改變,從庫的復制狀態一會是 yes 一會是正在連接中。
圖 2:鏈式或雙主結構中,主庫與從庫的 server-id 相同
從庫 1 同時又是 relay 數據庫,它能正確同步,然后把 relay-log 內容重寫到自己的 binlog 中。當 server-id 為 100 的從庫 2 io 線程獲取 binlog 時,發現所有內容都是源自于自己,就會丟棄這些 event。因此從庫 2 無法正確同步主庫的數據。只有直接寫 relay server 的 event 能正確同步到從庫 2。
上面兩種情況可以看到,在同一個 replication set 中,保持 server-id 的唯一性非常重要。
server_id 的動態修改
無意中發現 server-id 竟然是可以動態修改的,可別高興的太早。好處是,上面圖 1 的情況下,直接修改其中一個從庫的 server-id 就可以解決 server-id 沖突的問題。壞處很隱蔽,如下圖的結構:
現在假設 active-master 因為某種原因與 passive-master 的同步斷開后,passive-master 上進行了一些 ddl 變更。然后某 dba 突發奇想把 passive-master 的 server-id 修改為 400。當雙 master 的復制啟動后,那些之前在 passive-master 上執行的 server-id 為 200 的 ddl 變更,會從此陷入死循環。如果是 alter table t engine=innodb,它會一直不停,可能你會發現。但是像 update a=a+1;這樣的 sql,你很難發現。當然這種場景只是我的杜撰,這兒有個更真實的例子主備備的兩個備機轉為雙 master 時出現的詭異 slave lag 問題:http://hatemysql.com/2010/10/15/ 主備備的兩個備機轉為雙 master 時出現的詭異 slave-lag 問題 /。
舉這兩個例子只是想說明修改 server-id 有點危險,最好不要去修改,那么能一步到位生成它嗎?
生成唯一的 server_id
常用的方法有如下幾種:
1. 采用隨機數
mysql 的 server-id 是 4 字節整數,范圍從 0 -4294967295,因此采用該范圍內的隨機數來作為 server-id 產生沖突的可能性是非常小的。
2. 采用時間戳
直接用 date +%s 來生成 server-id。一天 86400 秒來計算,往后計算 50 年,最大的 server-id 也才使用到 86400*365*50,完全在 server-id 范圍內。
3. 采用 ip 地址 + 端口
這是我們經常采用的方法。例如 ip 為 192.168.122.23,端口為 3309,那么 server-id 可以寫為 122233309。產生沖突的可能性比較小:遇到 *.*.122.23 或者 *.*.12.223,而且搭建了同一個 replication set 的 3309 才會出現。
4. 采用集中的發號器
在管理服務器上采用自增的 id 來統一分配 server-id。這可以保證不沖突,但是需要維護中心節點。
5. 分開管理每個 replication set
在每個 replication set 中為 mysql 庫增加一個管理表,保證每個從庫的 server-id 不沖突。
上面的幾種方法都不賴,但是:
方法 4 加了維護負擔,而且開發環境、測試環境、線上環境都維護一套發號器的話,有點麻煩,混在一起又可能遇到網段隔離的風險,還有發號器數據庫權限的問題難于控制。所以不推薦。
方法 5 實現了自治,但是管理成本有點高。從庫要能夠寫主庫的 server-id 表,復雜。
5 種方法都存在的問題是,使用冷備的數據來擴容,server-id 需要手動去修改,否則就與冷備源的 server-id 沖突。而且,當 mysql 啟動的時候,你無法判斷該 mysql 是剛通過備份擴容的,還是之前一直正常運行的。所以你不知道這個 server-id 到底要不要改。而我希望 server-id 對 dba 完全透明,又絕不產生沖突,即可徹底屏蔽這個討厭的東西。
建議的方法
其實很簡單。ipv4 是 4 字節的整數,與 server-id 的范圍完全一樣。我們認為只有 ip 地址 + 端口才能唯一的確定一個 mysql 實例,所以總是希望把 ip 信息和端口信息都集成到 server-id 中。但是別忘了,一個 ip 上不能同時啟動兩個一樣的端口。所以,server-id 只需采用 ip 地址的整數形式:select INET_ATON(192.168.12.45),3232238637!所有新上線的實例,mysql 啟動腳本強制對 server-id 進行檢查,發現 server-id 不對就進行糾正,然后啟動。這種方法有個前提條件:同一機器上的多個 instance 不要有主從關系,否則 server-id 一樣就會導致問題。這種情況一般只會在測試環境出現,在線上基本是沒有的。滿足了這個前提,所有問題迎刃而解。
感謝各位的閱讀!關于“MySQL 怎么生成唯一的 server-id”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
向 AI 問一下細節