咱們都知道MySQL用server-id來惟一的標識某個數據庫實例,並在鏈式或雙主複製結構中用它來避免sql語句的無限循環。這篇文章分享下我對server-id的理解,而後比較和權衡生成惟一server-id的幾種方式。html
簡單說來,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一旦重複,可能引起一些詭異問題。
看看下面兩種狀況:sql
這種狀況下複製會左右搖擺。當兩個從庫的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
從庫1同時又是relay數據庫,它能正確同步,而後把relay-log內容重寫到本身的binlog中。當server-id爲100的從庫2 io線程獲取binlog時,發現全部內容都是源自於本身,就會丟棄這些event。所以從庫2沒法正確同步主庫的數據。只有直接寫relay server的event能正確同步到從庫2。
上面兩種狀況能夠看到,在同一個replication set中,保持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
經常使用的方法有以下幾種:線程
mysql的server-id是4字節整數,範圍從0-4294967295,所以採用該範圍內的隨機數來做爲server-id產生衝突的可能性是很是小的。
直接用date +%s來生成server-id。一天86400秒來計算,日後計算50年,最大的server-id也才使用到86400*365*50,徹底在server-id範圍內。
這是咱們常常採用的方法。例如ip爲192.168.122.23,端口爲3309,那麼server-id能夠寫爲122233309。產生衝突的可能性比較小:遇到*.*.122.23 或者*.*.12.223,並且搭建了同一個replication set的3309纔會出現。
在管理服務器上採用自增的id來統一分配server-id。這能夠保證不衝突,可是須要維護中心節點。
在每一個replication set中爲mysql庫增長一個管理表,保證每一個從庫的server-id不衝突。
上面的幾種方法都不賴,可是:
其實很簡單。ipv4是4字節的整數,與server-id的範圍徹底同樣。咱們認爲只有ip地址+端口才能惟一的肯定一個mysql實例,因此老是但願把ip信息和端口信息都集成到server-id中。可是別忘了,一個ip上不能同時啓動兩個同樣的端口。因此,server-id只需採用ip地址的整數形式!自定義的mysql啓動腳本強制對server-id進行檢查,發現server-id不對就進行糾正,而後啓動。上面全部的問題,都會迎刃而解。