MySQL怎么生成唯一的server

47次阅读
没有评论

共计 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 问一下细节

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-12-04发表,共计3242字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)