本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html 今天分析一个诡异问题,一个模拟Slave线程的程序,不断的被Master Ser
本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: http://www.penglixun.com/tech/database/mysql_multi_slave_same_serverid.html
今天分析一个诡异问题,一个模拟Slave线程的程序,不断的被Master Server给kill掉,最终发现是因为有两个Slave使用同样一个server id去连接Master Server,为什么两个Slave用同一个server id会被Master Server给Kill呢?分析了源码,这源于MySQL Replication的重连机制。
我们首先看看一个Slave注册到Master会发生什么,首先Slave需要向Master发送一个COM_REGISTER_SLAVE类型的请求(sql_parse.cc)命令请求,这里Master会使用register_slave函数注册一个Slave到slave_list。
<span style="color: #0000ff;">case</span> COM_REGISTER_SLAVE<span style="color: #008080;">:</span> <span style="color: #008000;">{</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span><span style="color: #000040;">!</span>register_slave<span style="color: #008000;">(</span>thd, <span style="color: #008000;">(</span>uchar<span style="color: #000040;">*</span><span style="color: #008000;">)</span>packet, packet_length<span style="color: #008000;">)</span><span style="color: #008000;">)</span> my_ok<span style="color: #008000;">(</span>thd<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span> <span style="color: #008000;">}</span>
在注册Slave线程的时候会发生什么呢?我们略去无用的代码直接看重点:(repl_failsafe.cc)
<span style="color: #0000ff;">int</span> register_slave<span style="color: #008000;">(</span>THD<span style="color: #000040;">*</span> thd, uchar<span style="color: #000040;">*</span> packet, uint packet_length<span style="color: #008000;">)</span> <span style="color: #008000;">{</span> <span style="color: #0000ff;">int</span> res<span style="color: #008080;">;</span> SLAVE_INFO <span style="color: #000040;">*</span>si<span style="color: #008080;">;</span> uchar <span style="color: #000040;">*</span>p<span style="color: #000080;">=</span> packet, <span style="color: #000040;">*</span>p_end<span style="color: #000080;">=</span> packet <span style="color: #000040;">+</span> packet_length<span style="color: #008080;">;</span> .... <span style="color: #666666;">//省略</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span><span style="color: #000040;">!</span><span style="color: #008000;">(</span>si<span style="color: #000040;">-</span><span style="color: #000080;">></span>master_id<span style="color: #000080;">=</span> uint4korr<span style="color: #008000;">(</span>p<span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span> si<span style="color: #000040;">-</span><span style="color: #000080;">></span>master_id<span style="color: #000080;">=</span> server_id<span style="color: #008080;">;</span> si<span style="color: #000040;">-</span><span style="color: #000080;">></span>thd<span style="color: #000080;">=</span> thd<span style="color: #008080;">;</span> pthread_mutex_lock<span style="color: #008000;">(</span><span style="color: #000040;">&</span>LOCK_slave_list<span style="color: #008000;">)</span><span style="color: #008080;">;</span> unregister_slave<span style="color: #008000;">(</span>thd,<span style="color: #0000dd;">0</span>,<span style="color: #0000dd;">0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #666666;">//关键在这里,先取消注册server_id相同的Slave线程</span> res<span style="color: #000080;">=</span> my_hash_insert<span style="color: #008000;">(</span><span style="color: #000040;">&</span>slave_list, <span style="color: #008000;">(</span>uchar<span style="color: #000040;">*</span><span style="color: #008000;">)</span> si<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #666666;">//把新的Slave线程注册到slave_list</span> pthread_mutex_unlock<span style="color: #008000;">(</span><span style="color: #000040;">&</span>LOCK_slave_list<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #0000ff;">return</span> res<span style="color: #008080;">;</span> ..... <span style="color: #008000;">}</span>
这是什么意思呢?这就是重连机制,slave_list是一个Hash表,server_id是Key,每一个线程注册上来,需要删掉同样server_id的Slave线程,再把新的Slave线程加到slave_list表中。
线程注册上来后,请求Binlog,发送COM_BINLOG_DUMP请求,Master会发送binlog给Slave,代码如下:
<span style="color: #0000ff;">case</span> COM_BINLOG_DUMP<span style="color: #008080;">:</span> <span style="color: #008000;">{</span> ulong pos<span style="color: #008080;">;</span> ushort flags<span style="color: #008080;">;</span> uint32 slave_server_id<span style="color: #008080;">;</span> status_var_increment<span style="color: #008000;">(</span>thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>status_var.<span style="color: #007788;">com_other</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>enable_slow_log<span style="color: #000080;">=</span> opt_log_slow_admin_statements<span style="color: #008080;">;</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span>check_global_access<span style="color: #008000;">(</span>thd, REPL_SLAVE_ACL<span style="color: #008000;">)</span><span style="color: #008000;">)</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span> <span style="color: #ff0000; font-style: italic;">/* TODO: The following has to be changed to an 8 byte integer */</span> pos <span style="color: #000080;">=</span> uint4korr<span style="color: #008000;">(</span>packet<span style="color: #008000;">)</span><span style="color: #008080;">;</span> flags <span style="color: #000080;">=</span> uint2korr<span style="color: #008000;">(</span>packet <span style="color: #000040;">+</span> <span style="color: #0000dd;">4</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>server_id<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span> <span style="color: #ff0000; font-style: italic;">/* avoid suicide */</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span><span style="color: #008000;">(</span>slave_server_id<span style="color: #000080;">=</span> uint4korr<span style="color: #008000;">(</span>packet<span style="color: #000040;">+</span><span style="color: #0000dd;">6</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span> <span style="color: #666666;">// mysqlbinlog.server_id==0</span> kill_zombie_dump_threads<span style="color: #008000;">(</span>slave_server_id<span style="color: #008000;">)</span><span style="color: #008080;">;</span> thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>server_id <span style="color: #000080;">=</span> slave_server_id<span style="color: #008080;">;</span> general_log_print<span style="color: #008000;">(</span>thd, command, <span style="color: #FF0000;">"Log: '%s' Pos: %ld"</span>, packet<span style="color: #000040;">+</span><span style="color: #0000dd;">10</span>, <span style="color: #008000;">(</span><span style="color: #0000ff;">long</span><span style="color: #008000;">)</span> pos<span style="color: #008000;">)</span><span style="color: #008080;">;</span> mysql_binlog_send<span style="color: #008000;">(</span>thd, thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>strdup<span style="color: #008000;">(</span>packet <span style="color: #000040;">+</span> <span style="color: #0000dd;">10</span><span style="color: #008000;">)</span>, <span style="color: #008000;">(</span>my_off_t<span style="color: #008000;">)</span> pos, flags<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #666666;">//不断的发送日志给slave端</span> unregister_slave<span style="color: #008000;">(</span>thd,<span style="color: #0000dd;">1</span>,<span style="color: #0000dd;">1</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #666666;">//发送完成后清理Slave线程,因为执行到这一步肯定是binlog dump线程被kill了</span> <span style="color: #ff0000; font-style: italic;">/* fake COM_QUIT -- if we get here, the thread needs to terminate */</span> error <span style="color: #000080;">=</span> TRUE<span style="color: #008080;">;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span> <span style="color: #008000;">}</span>
mysql_binlog_send函数在sql_repl.cc,里面是轮询Master binlog,发送给Slave。
再来简单看看unregister_slave做了什么(repl_failsafe.cc):
<span style="color: #0000ff;">void</span> unregister_slave<span style="color: #008000;">(</span>THD<span style="color: #000040;">*</span> thd, <span style="color: #0000ff;">bool</span> only_mine, <span style="color: #0000ff;">bool</span> need_mutex<span style="color: #008000;">)</span> <span style="color: #008000;">{</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span>thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>server_id<span style="color: #008000;">)</span> <span style="color: #008000;">{</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span>need_mutex<span style="color: #008000;">)</span> pthread_mutex_lock<span style="color: #008000;">(</span><span style="color: #000040;">&</span>LOCK_slave_list<span style="color: #008000;">)</span><span style="color: #008080;">;</span> SLAVE_INFO<span style="color: #000040;">*</span> old_si<span style="color: #008080;">;</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span><span style="color: #008000;">(</span>old_si <span style="color: #000080;">=</span> <span style="color: #008000;">(</span>SLAVE_INFO<span style="color: #000040;">*</span><span style="color: #008000;">)</span>hash_search<span style="color: #008000;">(</span><span style="color: #000040;">&</span>slave_list, <span style="color: #008000;">(</span>uchar<span style="color: #000040;">*</span><span style="color: #008000;">)</span><span style="color: #000040;">&</span>thd<span style="color: #000040;">-</span><span style="color: #000080;">></span>server_id, <span style="color: #0000dd;">4</span><span style="color: #008000;">)</span><span style="color: #008000;">)</span> <span style="color: #000040;">&&</span> <span style="color: #008000;">(</span><span style="color: #000040;">!</span>only_mine <span style="color: #000040;">||</span> old_si<span style="color: #000040;">-</span><span style="color: #000080;">></span>thd <span style="color: #000080;">==</span> thd<span style="color: #008000;">)</span><span style="color: #008000;">)</span> <span style="color: #666666;">//拿到slave值</span> hash_delete<span style="color: #008000;">(</span><span style="color: #000040;">&</span>slave_list, <span style="color: #008000;">(</span>uchar<span style="color: #000040;">*</span><span style="color: #008000;">)</span>old_si<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #666666;">//从slave_list中拿掉</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">(</span>need_mutex<span style="color: #008000;">)</span> pthread_mutex_unlock<span style="color: #008000;">(</span><span style="color: #000040;">&</span>LOCK_slave_list<span style="color: #008000;">)</span><span style="color: #008080;">;</span> <span style="color: #008000;">}</span> <span style="color: #008000;">}</span>
这就可以解释同样的server_id为什么会被kill,因为一旦注册上去,就会现删除相同server_id的Slave线程,然后把当前的Slave加入,这是因为有时Slave断开了,重新请求上来,当然需要踢掉原来的线程,这就是线程重连机制。
切记,一个MySQL集群中,绝不可以出现相同server_id的实例,否则各种诡异的问题可是接踵而来。

如何有效監控MySQL性能?使用mysqladmin、SHOWGLOBALSTATUS、PerconaMonitoringandManagement(PMM)和MySQLEnterpriseMonitor等工具。 1.使用mysqladmin查看連接數。 2.用SHOWGLOBALSTATUS查看查詢數。 3.PMM提供詳細性能數據和圖形化界面。 4.MySQLEnterpriseMonitor提供豐富的監控功能和報警機制。

MySQL和SQLServer的区别在于:1)MySQL是开源的,适用于Web和嵌入式系统,2)SQLServer是微软的商业产品,适用于企业级应用。两者在存储引擎、性能优化和应用场景上有显著差异,选择时需考虑项目规模和未来扩展性。

在需要高可用性、高級安全性和良好集成性的企業級應用場景下,應選擇SQLServer而不是MySQL。 1)SQLServer提供企業級功能,如高可用性和高級安全性。 2)它與微軟生態系統如VisualStudio和PowerBI緊密集成。 3)SQLServer在性能優化方面表現出色,支持內存優化表和列存儲索引。

mySqlManagesCharacterSetsetSandCollationsyutusututf-8asthEdeFault,允許ConfigurationAtdataBase,table和columnlevels,AndrequiringCarefullageLignmentToavoidMismatches.1)setDefeaultCharactersetTercharactersetEtCollacterSeteTandColletationForAdataBase.2)conformentcollecharactersettersetertersetcollatertersetcollationcollation

MySQL觸發器是與表相關聯的自動執行的存儲過程,用於在特定數據操作時執行一系列操作。 1)觸發器定義與作用:用於數據校驗、日誌記錄等。 2)工作原理:分為BEFORE和AFTER,支持行級觸發。 3)使用示例:可用於記錄薪資變更或更新庫存。 4)調試技巧:使用SHOWTRIGGERS和SHOWCREATETRIGGER命令。 5)性能優化:避免複雜操作,使用索引,管理事務。

在MySQL中創建和管理用戶賬戶的步驟如下:1.創建用戶:使用CREATEUSER'newuser'@'localhost'IDENTIFIEDBY'password';2.分配權限:使用GRANTSELECT,INSERT,UPDATEONmydatabase.TO'newuser'@'localhost';3.修正權限錯誤:使用REVOKEALLPRIVILEGESONmydatabase.FROM'newuser'@'localhost';然後重新分配權限;4.優化權限:使用SHOWGRA

MySQL適合快速開發和中小型應用,Oracle適合大型企業和高可用性需求。 1)MySQL開源、易用,適用於Web應用和中小型企業。 2)Oracle功能強大,適合大型企業和政府機構。 3)MySQL支持多種存儲引擎,Oracle提供豐富的企業級功能。

MySQL相比其他關係型數據庫的劣勢包括:1.性能問題:在處理大規模數據時可能遇到瓶頸,PostgreSQL在復雜查詢和大數據處理上表現更優。 2.擴展性:水平擴展能力不如GoogleSpanner和AmazonAurora。 3.功能限制:在高級功能上不如PostgreSQL和Oracle,某些功能需要更多自定義代碼和維護。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3漢化版
中文版,非常好用

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),