標籤:驅動 acl -o 字元 協議 關於 rgb sts span
最近出現個同步問題,我們資料的入庫順序為mysql->redis,業務層只對mysql做操作,而同步到redis的操作是採用開源的驅動包自己寫的同步工具,最近程式更新重啟,時有時無的報同樣的錯:
error ERROR 1105 (HY000): Failed to register slave: too long 'report-host'"
出現報錯重啟一兩次又正常,起初懷疑是程式裡面傳的什麼有問題,因為自己寫的小同步程式沒出現過這個報錯,也就讓開發人員在檢測,經過檢查的確沒有傳什麼關於host的參數,於是去官網搜尋了下report_host碰碰運氣看是否有這個參數,還真有........,官網介紹如下:
The host name or IP address of the slave to be reported to the master during slave registration. This value appears in the output of SHOW SLAVE HOSTS on the master server. Leave the value unset if you do not want the slave to register itself with the master.
大概意思就是這個參數是主從建立時由slave傳遞hostname用的,用show slave hosts命令可以看到,如果沒有加這個參數是看不到的,只能在processlist查看,於是開發的童鞋再到驅動包裡排查發現有去擷取了本機hostname傳遞,到這基本確定是由於傳遞的hostname超過mysql限制的長度造成的,那為什麼更新啟動時報錯,重啟之後又正常了,繼續.......
我們應用是部署在docker叢集中,docker在利用鏡像啟動時是利用應用程式名稱加隨機字串產生一個隨機的hostname,再重啟之後產生的會短一點,這就造成了第一次啟動會報錯,而重啟之後就正常,找到原因再來看mysql哪裡可以修改下限制,經過尋找沒有關於這個的參數.............於是對源碼進行搜尋,發現report_host的長度是寫死60:
int register_slave_on_master(MYSQL* mysql, Master_info *mi, bool *suppress_warnings){ uchar buf[1024], *pos= buf; size_t report_host_len=0, report_user_len=0, report_password_len=0; DBUG_ENTER("register_slave_on_master"); *suppress_warnings= FALSE; if (report_host) report_host_len= strlen(report_host); //擷取report_host的長度 if (report_host_len > HOSTNAME_LENGTH) //判斷report_host長度是否超過HOSTNAME_LENGTH=60的限制 { sql_print_warning("The length of report_host is %zu. " "It is larger than the max length(%d), so this " "slave cannot be registered to the master%s.", report_host_len, HOSTNAME_LENGTH, mi->get_for_channel_str()); DBUG_RETURN(0); } ........................... int4store(pos, server_id); pos+= 4; //前4bytes寫入server_id pos= net_store_data(pos, (uchar*) report_host, report_host_len);//這裡則是寫入report_host及length,規則為length佔1bytes,後面緊跟report_host pos= net_store_data(pos, (uchar*) report_user, report_user_len); pos= net_store_data(pos, (uchar*) report_password, report_password_len); ......................................}
這是源碼中slave端在協議建立時組協議包的縮減內容,其實在這裡對report_host的長度進行了檢查,如果超過60的長度就會報錯,就無法建立主從,但是我們是自己寫的工具而且驅動包裡只是擷取hostname並未對它的長度進行判斷
int register_slave(THD* thd, uchar* packet, size_t packet_length){ int res; SLAVE_INFO *si; uchar *p= packet, *p_end= packet + packet_length; const char *errmsg= "Wrong parameters to function register_slave"; if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0)) return 1; if (!(si = (SLAVE_INFO*)my_malloc(key_memory_SLAVE_INFO, sizeof(SLAVE_INFO), MYF(MY_WME)))) goto err2; /* 4 bytes for the server id */ if (p + 4 > p_end) { my_error(ER_MALFORMED_PACKET, MYF(0)); my_free(si); return 1; } thd->server_id= si->server_id= uint4korr(p); //首先前面4bytes擷取server_id p+= 4; get_object(p,si->host, "Failed to register slave: too long 'report-host'"); //這裡就是擷取report_host,以及做判斷,報錯內容和我們應用報錯內容一致,si->host是char host[HOSTNAME_LENGTH+1]既61的長度 get_object(p,si->user, "Failed to register slave: too long 'report-user'"); get_object(p,si->password, "Failed to register slave; too long 'report-password'"); ........................}
上面這段縮減內容即是master接收到slave發起的協議包之後的操作,在其中出現了我們的報錯內容,而get_object中是這麼進行判斷的:
#define get_object(p, obj, msg) { uint len; ...................... len= (uint)*p++; if (p + len > p_end || len >= sizeof(obj)) { errmsg= msg; goto err; } .........................}
根據對源碼的尋找分析,mysql對report_host限制為最長60個位元組長度,也就是非中文的60個字元,這是個比較冷門的參數,而在源碼中又寫死了長度,我們只有對服務名及docker的hostname做控制解決這個問題
該問題在我們平時使用中其實很少遇到,而官網也沒有介紹這個長度限制,剛開始遇到的時候有點摸不到頭緒,如果有童鞋自己做同步可以做參考,我們使用的go-mysql這個開源包,如果有使用的可以注意下
ps:mysql技術交流qq群479472450
mysql一個冷門參數引起的同步故障