我们都知道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相同的连接,就会先注销该连接,然后重新注册。 
参考下面的代码片段:

repl_failsafe (register_slave) download

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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问题 。 
举这两个例子只是想说明修改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不冲突。

哪一种更好

上面的几种方法都不赖,但是:

1. 方法4加了维护负担,而且开发环境、测试环境、线上环境都维护一套发号器的话,有点麻烦,混在一起又可能遇到网段隔离的风险,还有发号器数据库权限的问题难于控制。所以不推荐。

2. 方法5实现了自治,但是管理成本有点高。从库要能够写主库的server-id表,复杂。

3. 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地址的整数形式!自定义的mysql启动脚本强制对server-id进行检查,发现server-id不对就进行纠正,然后启动。上面所有的问题,都会迎刃而解。

欢迎拍砖。

如何生成唯一的server Id,server_id为何不能重复?的更多相关文章

  1. 生成唯一32位ID编码代码Java(GUID)

    源码下载链接:http://pan.baidu.com/s/1jGCEWlC 扫扫关注"茶爸爸"微信公众号 坚持最初的执着,从不曾有半点懈怠,为优秀而努力,为证明自己而活. /* ...

  2. php 生成唯一id的几种解决方法

    php 生成唯一id的几种解决方法   网上查了下,有很多的方法 1.md5(time() . mt_rand(1,1000000)); 这种方法有一定的概率会出现重复 2.php内置函数uniqid ...

  3. PHP使用SnowFlake算法生成唯一ID

    前言:最近需要做一套CMS系统,由于功能比较单一,而且要求灵活,所以放弃了WP这样的成熟系统,自己做一套相对简单一点的.文章的详情页URL想要做成url伪静态的格式即xxx.html 其中xxx考虑过 ...

  4. js 如何生成唯一且不可预测的 ID

    通常数据库可以生成唯一的 ID,最多的就是数字序列,也有像 MongoDB 这样产生组合序列的,不过这种形式的 ID 由于是序列,是可以预测的.如果想得到不可预测且唯一的 ID,方法还是有的. 下面主 ...

  5. C#生成唯一的ID保存到数据库

    直接用.NET Framework 提供的 Guid() 函数: Guid.NewGuid()是指生成唯一码的规则 System.Guid.NewGuid().ToString()全球唯一标识符 (G ...

  6. 生成唯一的id(转)

    很多朋友都利用md5()来生成唯一的编号,但是md5()有几个缺点:1.无序,导致数据库中排序性能下降.2.太长,需要更多的存储空间.其实PHP中自带一个函数来生成唯一的id,这个函数就是uniqid ...

  7. php生成唯一id/唯一标识符/唯一订单号

    /** * php 生成唯一id * https://blog.csdn.net/hzqghost/article/details/18914681 */ function guid($factor= ...

  8. 如何使用php生成唯一ID的4种方法

    php生成唯一ID的应用场景非常普遍,如临时缓存文件名称,临时变量,临时安全码等,uniqid()函数基于以微秒计的当前时间,生成一个唯一的 ID.由于生成唯一ID与微秒时间关联,因此ID的唯一性非常 ...

  9. PHP获取时间戳和微秒数以及生成唯一ID

    microtime函数 描述:返回当前Unix时间戳和微秒数 语法:mixed microtime( [ bool $get_as_float ] ) //直接输出 echo microtime(); ...

随机推荐

  1. 快速定位 Android APP 当前页面的三种方法(Activity / Fragment)

    方法一.通过adb命令打印当前页面: Android 如何快速定位当前页面是哪个Activity or Fragment (1)查看当前Activity :adb shell "dumpsy ...

  2. 使用numpy产生随机数

    numpy中的random模块包含了很多方法可以用来产生随机数,这篇文章将对random中的一些常用方法做一个总结. 1.numpy.random.rand(d0, d1, ..., dn) 作用:产 ...

  3. (转)一位资深程序员大牛给予Java初学者的学习路线建议

    原文:http://geek.csdn.net/news/detail/238256 Java学习这一部分其实也算是今天的重点,这一部分用来回答很多群里的朋友所问过的问题,那就是你是如何学习Java的 ...

  4. 使用windows脚本移动文件

    1. 移动脚本 在部署web项目时,一般需要将打包的war包发布到Tomcat目录下,所以自己就在网上查找资料写了一个简略的移动文件的脚本,如下: @echo off echo "使用bat ...

  5. 各种梯度下降 bgd sgd mbgd adam

    转载  https://blog.csdn.net/itchosen/article/details/77200322 各种神经网络优化算法:从梯度下降到Adam方法     在调整模型更新权重和偏差 ...

  6. BZOJ.4241.历史研究(回滚莫队 分块)

    题目链接 \(Description\) 长度为n的数列,m次询问,每次询问一段区间最大的 \(A_i*tm_i\) (重要度*出现次数) \(Solution\) 好像可以用莫队做,但是取max的操 ...

  7. 洛谷.T22136.最长不下降子序列(01归并排序 分治)

    题目链接 \(Description\) 给定一个长为n的序列,每次可以反转 \([l,r]\) 区间,代价为 \(r-l+1\).要求在\(4*10^6\)代价内使其LIS长度最长,并输出需要操作的 ...

  8. php date_default_timezone_set()设置时区

    <?php echo function_exists(date_default_timezone_set)."<br>";//在这他总是返回1,这函数是判断这里面 ...

  9. git小笔记

    git账号:eggsy.cao@pictureworks.biz eggsycao 1.新建文件夹,并进入,命令窗口:git init 2.提交文件 git add fileName(git add ...

  10. React系列文章:Webpack模块组织关系

    现代前端开发离不开打包工具,以Webpack为代表的打包工具已经成为日常开发必备之利器,拿React技术栈为例,我们ES6形式的源代码,需要经过Webpack和Babel处理,才能生成发布版文件,在浏 ...