如何生成惟一的server Id,server_id爲什麼不能重複?

咱們都知道MySQL用server-id來惟一的標識某個數據庫實例,並在鏈式或雙主複製結構中用它來避免sql語句的無限循環。這篇文章分享下我對server-id的理解,而後比較和權衡生成惟一server-id的幾種方式。html

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語句的無限循環。mysql

注意:相同server-id的event在io_thread這一層就過濾了;而對於replicate-(do|ignore)-等規則,則是在sql_thread這一層過濾的。io_thread和sql_thread都有過濾的功能。

server_id爲什麼不能重複

在同一個集羣中,server-id一旦重複,可能引起一些詭異問題。 
看看下面兩種狀況:sql

圖1:主庫與從庫的server-id不一樣,可是兩個或多個從庫的server-id相同

這種狀況下複製會左右搖擺。當兩個從庫的server-id相同時,若是從庫1已經鏈接上主庫,此時從庫2也須要鏈接到主庫,發現以前有server-id相同的鏈接,就會先註銷該鏈接,而後從新註冊。 
參考下面的代碼片斷:數據庫

repl_failsafe (register_slave) download服務器

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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一會是正在鏈接中。app

圖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衝突的問題。壞處很隱蔽,以下圖的結構: ui



如今假設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問題 。 
舉這兩個例子只是想說明修改server-id有點危險,最好不要去修改,那麼能一步到位生成它嗎?spa

如何生成惟一的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不衝突。

哪種更好

上面的幾種方法都不賴,可是:

1. 方法4加了維護負擔,並且開發環境、測試環境、線上環境都維護一套發號器的話,有點麻煩,混在一塊兒又可能遇到網段隔離的風險,還有發號器數據庫權限的問題難於控制。因此不推薦。

2. 方法5實現了自治,可是管理成本有點高。從庫要可以寫主庫的server-id表,複雜。

3. 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地址的整數形式!自定義的mysql啓動腳本強制對server-id進行檢查,發現server-id不對就進行糾正,而後啓動。上面全部的問題,都會迎刃而解。

歡迎拍磚。

相關文章
相關標籤/搜索