共计 3242 个字符,预计需要花费 9 分钟才能阅读完成。
自动写代码机器人,免费开通
这篇文章给大家分享的是有关 MySQL 怎么生成唯一的 server-id 的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。
前言
我们都知道 MySQL 用 server-id 来一的标识某个数据库实例,并在链式或双主复制结构中用它来避免 sql 语句的无限循环。这篇文章分享下我对 server-id 的理解,然后比较和权衡生成唯一 server-id 的几种方式。
server_id 的用途
简单说来,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 语句的无限循环。
注意:相同 server-id 的 event 在 io_thread 这一层就过滤了;而对于 replicate-(do|ignore)- 等规则,则是在 sql_thread 这一层过滤的。io_thread 和 sql_thread 都有过滤的功能。
server_id 为何不能重复
在同一个集群中,server-id 一旦重复,可能引发一些诡异问题。
看看下面两种情况:
图 1:主库与从库的 server-id 不同,但是两个或多个从库的 server-id 相同
这种情况下复制会左右摇摆。当两个从库的 server-id 相同时,如果从库 1 已经连接上主库,此时从库 2 也需要连接到主库,发现之前有 server-id 相同的连接,就会先注销该连接,然后重新注册。
参考下面的代码片段:
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 一会是正在连接中。
图 2:链式或双主结构中,主库与从库的 server-id 相同
从库 1 同时又是 relay 数据库,它能正确同步,然后把 relay-log 内容重写到自己的 binlog 中。当 server-id 为 100 的从库 2 io 线程获取 binlog 时,发现所有内容都是源自于自己,就会丢弃这些 event。因此从库 2 无法正确同步主库的数据。只有直接写 relay server 的 event 能正确同步到从库 2。
上面两种情况可以看到,在同一个 replication set 中,保持 server-id 的唯一性非常重要。
server_id 的动态修改
无意中发现 server-id 竟然是可以动态修改的,可别高兴的太早。好处是,上面图 1 的情况下,直接修改其中一个从库的 server-id 就可以解决 server-id 冲突的问题。坏处很隐蔽,如下图的结构:
现在假设 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 问题:http://hatemysql.com/2010/10/15/ 主备备的两个备机转为双 master 时出现的诡异 slave-lag 问题 /。
举这两个例子只是想说明修改 server-id 有点危险,最好不要去修改,那么能一步到位生成它吗?
生成唯一的 server_id
常用的方法有如下几种:
1. 采用随机数
mysql 的 server-id 是 4 字节整数,范围从 0 -4294967295,因此采用该范围内的随机数来作为 server-id 产生冲突的可能性是非常小的。
2. 采用时间戳
直接用 date +%s 来生成 server-id。一天 86400 秒来计算,往后计算 50 年,最大的 server-id 也才使用到 86400*365*50,完全在 server-id 范围内。
3. 采用 ip 地址 + 端口
这是我们经常采用的方法。例如 ip 为 192.168.122.23,端口为 3309,那么 server-id 可以写为 122233309。产生冲突的可能性比较小:遇到 *.*.122.23 或者 *.*.12.223,而且搭建了同一个 replication set 的 3309 才会出现。
4. 采用集中的发号器
在管理服务器上采用自增的 id 来统一分配 server-id。这可以保证不冲突,但是需要维护中心节点。
5. 分开管理每个 replication set
在每个 replication set 中为 mysql 库增加一个管理表,保证每个从库的 server-id 不冲突。
上面的几种方法都不赖,但是:
方法 4 加了维护负担,而且开发环境、测试环境、线上环境都维护一套发号器的话,有点麻烦,混在一起又可能遇到网段隔离的风险,还有发号器数据库权限的问题难于控制。所以不推荐。
方法 5 实现了自治,但是管理成本有点高。从库要能够写主库的 server-id 表,复杂。
5 种方法都存在的问题是,使用冷备的数据来扩容,server-id 需要手动去修改,否则就与冷备源的 server-id 冲突。而且,当 mysql 启动的时候,你无法判断该 mysql 是刚通过备份扩容的,还是之前一直正常运行的。所以你不知道这个 server-id 到底要不要改。而我希望 server-id 对 dba 完全透明,又绝不产生冲突,即可彻底屏蔽这个讨厌的东西。
建议的方法
其实很简单。ipv4 是 4 字节的整数,与 server-id 的范围完全一样。我们认为只有 ip 地址 + 端口才能唯一的确定一个 mysql 实例,所以总是希望把 ip 信息和端口信息都集成到 server-id 中。但是别忘了,一个 ip 上不能同时启动两个一样的端口。所以,server-id 只需采用 ip 地址的整数形式:select INET_ATON(192.168.12.45),3232238637!所有新上线的实例,mysql 启动脚本强制对 server-id 进行检查,发现 server-id 不对就进行纠正,然后启动。这种方法有个前提条件:同一机器上的多个 instance 不要有主从关系,否则 server-id 一样就会导致问题。这种情况一般只会在测试环境出现,在线上基本是没有的。满足了这个前提,所有问题迎刃而解。
感谢各位的阅读!关于“MySQL 怎么生成唯一的 server-id”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
向 AI 问一下细节