本文内容遵从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
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

Atom编辑器mac版下载
最流行的的开源编辑器

SecLists
SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

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

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)