MHA 切換的一個(gè)“坑”

本文目錄:背景測(cè)試場(chǎng)景問題分析小結(jié)

背景

在一次變更中使用 MHA 進(jìn)行主從切換蛔翅,命令如下:


```shell

masterha_master_switch--master_state=alive--conf=/etc/mha/mha3306.cnf--new_master_host=xx.xx.xx.xx--new_master_port=3306--interactive=0--orig_master_is_new_slave

```

然而卻遇到了報(bào)錯(cuò),如下:

[error][/usr/share/perl5/vendor_perl/MHA/ServerManager.pm,ln1213]XX.XX.XX.XXis badasanewmaster![error][/usr/share/perl5/vendor_perl/MHA/MasterRotate.pm,ln232]Failed togetnewmaster![error][/usr/share/perl5/vendor_perl/MHA/ManagerUtil.pm,ln177]GotERROR:at/bin/masterha_master_switch line53.

看報(bào)錯(cuò)是認(rèn)為指定的新主是一個(gè)bad new master虏劲。

遇到這個(gè)報(bào)錯(cuò)內(nèi)心是懵的郑口,明明切換前檢查集群狀態(tài)有梆、masterha_check_repl都是正常的骆撇。嗯……還是對(duì) MHA 的原理了解不夠深入。

當(dāng)時(shí)也沒時(shí)間去研究為什么報(bào)錯(cuò)了羽德,于是就手工切換了几莽,接下來就讓我們一起去探索為什么會(huì)出現(xiàn)這個(gè)報(bào)錯(cuò)吧!

說明一下宅静,線上主從集群的環(huán)境是這樣的:

角色MySQL版本

MMySQL 5.6.40

S1MySQL 5.6.40

S2MySQL 5.7.29

S3MySQL 5.7.29

PS:為什么主從版本會(huì)不一致呢章蚣?是因?yàn)檎谧錾?jí),本次切換就是為了將 S2 切換為主姨夹,然后將低版本的兩個(gè)實(shí)例升級(jí)上去纤垂。

測(cè)試場(chǎng)景

線上通過手工切換繞過了 MHA 的報(bào)錯(cuò),后面要進(jìn)行分析具體原因磷账。因?yàn)楝F(xiàn)場(chǎng)環(huán)境新主的版本和老主庫版本是不一樣的峭沦,猜想是否 MHA 不支持跨版本切換,之前也沒有留意這個(gè)問題逃糟。于是在測(cè)試環(huán)境中進(jìn)行了一波測(cè)試吼鱼,下面列出測(cè)試場(chǎng)景和測(cè)試結(jié)論,有興趣的可以自己測(cè)試一下:

測(cè)試場(chǎng)景原master版本新master版本其他slaves 版本切換結(jié)果

場(chǎng)景15.6.405.7.29無切換成功

場(chǎng)景25.7.295.6.40無切換成功

場(chǎng)景35.6.405.7.295.6.38切換失敗

場(chǎng)景45.6.385.7.295.6.40切換失敗

場(chǎng)景55.6.385.7.295.7.29切換成功

現(xiàn)象是這么個(gè)現(xiàn)象履磨,是不是很好奇蛉抓,為什么只有一個(gè)從庫的時(shí)候,跨版本可以切換成功剃诅,當(dāng)還有其他從庫的時(shí)候某些情況可以切換成功巷送,某些情況又切換失敗,往下看吧矛辕!

問題分析

先去google一下笑跛,搜索關(guān)鍵詞:mha .. is bad as a new master,

然后搜出來的并沒有我想要的結(jié)果聊品,有些參考價(jià)值的文章如下:

https://blog.51cto.com/u_860143/2431044 【和我的場(chǎng)景相似飞蹂,但什么解釋也沒說】

https://www.modb.pro/db/50655【不太明確,當(dāng)時(shí)沒理解】

窮途末路翻屈,只能去源碼中翻翻了陈哑,畢竟 MHA 一款開源的工具【不逼自己一把就不知道自己英文還是不錯(cuò)的】 找到 MHA 選主的相關(guān)代碼,首先定義了幾個(gè)數(shù)組:

slaves 數(shù)組:選取 alive 的 slaves

latest 數(shù)組:從 alive slave 中選取復(fù)制位點(diǎn)最新的 slaves

pref 數(shù)組:配置文件中配置了 candidate_master 的 slaves

bad 數(shù)組:后面解釋

接著在進(jìn)行選主的時(shí)候按照以下的順序進(jìn)行選舉:

選舉優(yōu)先級(jí)最高的 slave 作為新主(通常是手工切換指定的 new master)伸眶,如果該 slave 不能作為新主惊窖,則報(bào)錯(cuò)退出,否則如果是故障切換厘贼,則進(jìn)行下面的步驟

選擇復(fù)制位點(diǎn)最新并且在 pref 數(shù)組里的 slave 作為新主界酒,如果復(fù)制位點(diǎn)最新的 slave 不在 pref 數(shù)組中,則繼續(xù)下面步驟

從 pref 中選擇一個(gè) slave 作為新主嘴秸,如果沒有選出則繼續(xù)

選擇復(fù)制位點(diǎn)最新的 slave 作為新主毁欣,如果沒有選出則繼續(xù)

從所有的 slave 中進(jìn)行選擇

經(jīng)過以上步驟仍然選擇不出主則選舉失敗

注意:前面的6個(gè)選舉步驟庇谆,都需要保證新主不在 bad 數(shù)組中


# Picking upnewmaster# If preferred node is specified,oneofactive preferred nodes will benewmaster.# If the latest server behinds toomuch(i.e.stopping sql threadforonline backups),we should not use itasanewmaster,but we should fetch relay log there.Even though preferred master is configured,it does not become a masterifit's far behind.sub select_new_master{my $self=shift;my $prio_new_master_host=shift;my $prio_new_master_port=shift;my $check_replication_delay=shift;$check_replication_delay=1if(!defined($check_replication_delay));my $log=$self->{logger};my @latest=$self->get_latest_slaves();my @slaves=$self->get_alive_slaves();my @pref=$self->get_candidate_masters();my @bad=$self->get_bad_candidate_masters($latest[0],$check_replication_delay);if($prio_new_master_host&&$prio_new_master_port){my $new_master=$self->get_alive_server_by_hostport($prio_new_master_host,$prio_new_master_port);if($new_master){my $a=$self->get_server_from_by_id(\@bad,$new_master->{id});unless($a){$log->info("$prio_new_master_host can be new master.");return$new_master;}else{$log->error("$prio_new_master_host is bad as a new master!");return;}}else{$log->error("$prio_new_master_host is not alive!");return;}}$log->info("Searching new master from slaves..");$log->info(" Candidate masters from the configuration file:");$self->print_servers(\@pref);$log->info(" Non-candidate masters:");$self->print_servers(\@bad);return$latest[0]if($#pref<0&&$#bad<0&&$latest[0]->{latest_priority});if($latest[0]->{latest_priority}){$log->info(" Searching from candidate_master slaves which have received the latest relay log events..")if($#pref>=0);foreach my$h(@latest){foreach my$p(@pref){if($h->{id}eq $p->{id}){return$hif(!$self->get_server_from_by_id(\@bad,$p->{id}));}}}$log->info("? Not found.")if($#pref>=0);}#newmasteris not latest? $log->info(" Searching from all candidate_master slaves..")if($#pref>=0);foreach my$s(@slaves){foreach my$p(@pref){if($s->{id}eq $p->{id}){my $a=$self->get_server_from_by_id(\@bad,$p->{id});return$sunless($a);}}}$log->info("? Not found.")if($#pref>=0);if($latest[0]->{latest_priority}){$log->info(" Searching from all slaves which have received the latest relay log events..");foreach my$h(@latest){my $a=$self->get_server_from_by_id(\@bad,$h->{id});return$hunless($a);}$log->info("? Not found.");}# noneoflatest servers can not be a master? $log->info(" Searching from all slaves..");foreach my$s(@slaves){my $a=$self->get_server_from_by_id(\@bad,$s->{id});return$sunless($a);}$log->info("? Not found.");return;}

因?yàn)閳?bào)錯(cuò)是說新主是 bad ,那我們重點(diǎn)看下新主為什么會(huì)被判定為 bad 凭疮,如何判定的饭耳。獲取 bad 列表的函數(shù)是get_bad_candidate_masters,如下哭尝,可以看出具有以下五種情況的 slave 會(huì)被判定為 bad :

dead servers

{no_master} >= 1【在配置文件中設(shè)置了no_master】

log_bin is disabled【未開啟binlog】

{oldest_major_version} eq '0'【MySQL major 版本不是最舊的】

too much replication delay【延遲大哥攘,與 master 的 binlog position 差距大于 100000000】

# The following servers can not be master:#-dead servers#-Set no_masterinconffiles(i.e.DRservers)#-log_bin is disabled#-Major version is not the oldest#-too much replication delaysubget_bad_candidate_masters($$$){my $self=shift;my $latest_slave=shift;my $check_replication_delay=shift;my $log=$self->{logger};my @servers=$self->get_alive_slaves();my @ret_servers=();foreach(@servers){if($_->{no_master}>=1||$_->{log_bin}eq'0'||$_->{oldest_major_version}eq'0'||($latest_slave&&($check_replication_delay&&$self->check_slave_delay($_,$latest_slave)>=1))){push(@ret_servers,$_);}}return@ret_servers;}

對(duì)于1-3,5很好理解材鹦,而且線上后來通過監(jiān)控進(jìn)行了排查逝淹,并不存在這些問題,于是重點(diǎn)看下4是如何來進(jìn)行定義的桶唐。

找到相關(guān)的函數(shù):

subcompare_slave_version($){my $self=shift;my @servers=$self->get_alive_servers();my $log=$self->{logger};$log->debug(" Comparing MySQL versions..");my $min_major_version;foreach(@servers){my $dbhelper=$_->{dbhelper};--如果dead或不為從庫栅葡,則跳過判斷? ? nextif($_->{dead}||$_->{not_slave});my $parsed_major_version=MHA::NodeUtil::parse_mysql_major_version($_->{mysql_version});if(!$min_major_version||$parsed_major_version<$min_major_version){$min_major_version=$parsed_major_version;}}foreach(@servers){my $dbhelper=$_->{dbhelper};nextif($_->{dead}||$_->{not_slave});my $parsed_major_version=MHA::NodeUtil::parse_mysql_major_version($_->{mysql_version});if($min_major_version==$parsed_major_version){$_->{oldest_major_version}=1;}else{$_->{oldest_major_version}=0;}}$log->debug("? Comparing MySQL versions done.");}

可以看到,這里首先會(huì)從 alive_servers 中獲取最小的版本尤泽,也就是min_major_version:

如果實(shí)例是 dead 或非從庫欣簇,則不比較該實(shí)例,否則進(jìn)行比較坯约,關(guān)鍵代碼next if (?>dead||

接下來熊咽,根據(jù)傳入的 server 的parsed_major_version【MySQL 的主版本,例如闹丐,5.6横殴,5.7】和min_major_version進(jìn)行對(duì)比:

如果parsed_major_version==min_major_version,則oldest_major_version=1卿拴;否則oldest_major_version=0

綜上可以看出衫仑,新主的版本號(hào),需要是所有從庫中版本最低的才能作為新的主庫堕花,否則將不能作為新的主庫文狱。

到這里,問題就水落石出了缘挽,回到我們前面測(cè)試的場(chǎng)景中瞄崇,就弄明白了:

場(chǎng)景1和場(chǎng)景2只有一個(gè)從庫的時(shí)候,跨版本切換可以切換成功壕曼,是因?yàn)檫@個(gè)從庫的主版本就是 min_major_version

場(chǎng)景3和場(chǎng)景4中切換失敗的原因是杠袱,新主的主版本為5.7,而所有從庫中最小的主版本號(hào)為5.6窝稿,因此不能切換

但是,MHA 為什么會(huì)這樣設(shè)計(jì)呢凿掂?

MySQL 源端(master)低版本到目標(biāo)端(slave)高版本數(shù)據(jù)復(fù)制是沒有問題伴榔,源端(master)高版本到目標(biāo)端(slave)數(shù)據(jù)復(fù)制可能會(huì)出現(xiàn)問題纹蝴。即:5.7可以作為8.0版本的從庫,5.6可以作為5.7的從庫踪少;但是8.0作為5.7或者5.7作為5.6的從庫就會(huì)有問題塘安。這個(gè)在官方有介紹:https://dev.mysql.com/doc/refman/5.7/en/replication-compatibility.html

不過 MHA 在比較最小版本的時(shí)候沒有比較原主庫的版本,這在切換的時(shí)候還是可能會(huì)出現(xiàn)低版本向高版本復(fù)制的情況援奢,比如測(cè)試場(chǎng)景1兼犯,不知道是基于什么考慮,歡迎大家留言討論集漾。

小結(jié)

MHA 選主邏輯:

選舉優(yōu)先級(jí)最高的 slave 作為新主(通常是手工切換指定的 new master)切黔,如果該 slave 不能作為新主,則報(bào)錯(cuò)退出具篇,否則如果是故障切換纬霞,則進(jìn)行下面的步驟

選擇復(fù)制位點(diǎn)最新并且在設(shè)置了 candidate_master 的 slave 作為新主,如果復(fù)制位點(diǎn)最新的 slave 沒有設(shè)置 candidate_master 驱显,則繼續(xù)下面步驟

從設(shè)置了 candidate_master 中選擇一個(gè) slave 作為新主诗芜,如果沒有選出則繼續(xù)

選擇復(fù)制位點(diǎn)最新的 slave 作為新主,如果沒有選出則繼續(xù)

從所有的 slave 中進(jìn)行選擇

經(jīng)過以上步驟仍然選擇不出主則選舉失敗

注意:前面的6個(gè)選舉步驟埃疫,都需要保證新主不在bad數(shù)組中

bad 數(shù)組定義如下:

dead servers

{no_master} >= 1【在配置文件中設(shè)置了no_master】

log_bin is disabled【未開啟binlog】

{oldest_major_version} eq '0'【MySQL major 版本不是最舊的】

too much replication delay【延遲大伏恐,與 master 的 binlog position 差距大于 100000000】

其中4這個(gè)是比較容易忽視的一點(diǎn),需要注意栓霜!

轉(zhuǎn)載于https://cloud.tencent.com/developer/article/1875033

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末翠桦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子叙淌,更是在濱河造成了極大的恐慌秤掌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹰霍,死亡現(xiàn)場(chǎng)離奇詭異闻鉴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茂洒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門孟岛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人督勺,你說我怎么就攤上這事渠羞。” “怎么了智哀?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵次询,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瓷叫,道長(zhǎng)屯吊,這世上最難降的妖魔是什么送巡? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮盒卸,結(jié)果婚禮上骗爆,老公的妹妹穿的比我還像新娘。我一直安慰自己蔽介,他們只是感情好摘投,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著虹蓄,像睡著了一般犀呼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上武花,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天圆凰,我揣著相機(jī)與錄音,去河邊找鬼体箕。 笑死专钉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的累铅。 我是一名探鬼主播跃须,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼娃兽!你這毒婦竟也來了菇民?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤投储,失蹤者是張志新(化名)和其女友劉穎第练,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玛荞,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡娇掏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勋眯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婴梧。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖客蹋,靈堂內(nèi)的尸體忽然破棺而出塞蹭,到底是詐尸還是另有隱情,我是刑警寧澤讶坯,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布番电,位于F島的核電站,受9級(jí)特大地震影響辆琅,放射性物質(zhì)發(fā)生泄漏漱办。R本人自食惡果不足惜担汤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洼冻。 院中可真熱鬧,春花似錦隅很、人聲如沸撞牢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屋彪。三九已至,卻和暖如春绒尊,著一層夾襖步出監(jiān)牢的瞬間畜挥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工婴谱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟹但,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓谭羔,卻偏偏與公主長(zhǎng)得像华糖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瘟裸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容