评论:同学会催生“恐聚族” 攀比斗富炫耀成风

2月 6th, 2011 | Posted by | Filed under 随笔日记

http://news.163.com/11/0206/03/6S67VFQM00011229.html

网易这篇文章真是说到了点子上,回家感同身受。

没有人关心生活怎么样,没有人关心工作的意义,只在乎有多少钱,甚至家长也是这样,这是一种多么病态的社会。

一个评论说到了我的感受:
国内已经完全畸形了。德国人该比中国人富得多吧,可是年轻人都买二手车,汽车排量多数都在1.0-1.4升。教授在这里绝对是富人,可是许多教授开着小破车乐颠乐颠的上班。大学的清洁工大妈在教授面前绝对不会低人一等。倒是校长在任何人面前都得客客气气。
因为人们有生活,有追求,有尊严。而国内唯一的最求就是钱和权。而且这钱和权来的越不正当,越说明“有本事”。知识不能改变命运,勤劳不能致富。发达的最有效最便捷途径就是无耻,无耻无底线。
我忍受不了这样的社会,也没有能力改变它,只能通过自己的拼搏冲出国门,换一个环境。新的一年,祝各位善良的人都能通过自己的努力,成功逃离苦海。

标签:

[译]InnoDB官方博客:InnoDB Plugin的性能和可伸缩性

1月 27th, 2011 | Posted by | Filed under 未分类

原文地址:http://blogs.innodb.com/wp/2009/03/plug-in-for-performance-and-scalability/

Why should you care about the latest “early adopter” release of the InnoDB Plugin, version 1.0.3?   One word: performance! The release introduces these features:
为什么你应该关注最近的InnoDB Plugin 1.0.3版?一个词:性能!这个版本包括了这些特性

  • Enhanced concurrency & scalability: the “Google SMP patch” using atomic instructions for mutexing
  • 增强的并发可可伸缩性:”Google 多处理机 补丁” 为Mutext锁操作使用原子操作
  • More efficient memory allocation: ability to use more scalable platform memory allocator
  • 更有效的内存分配:可以使用更多的可扩展内存 分配器(例如tcmalloc)
  • Improved out-of-the-box scalability: unlimited concurrent thread execution by default
  • 改进的即装即用扩展性:默认无限制的线程并发
  • Dynamic tuning: at run-time, enable or disable insert buffering and adaptive hash indexing
  • 动态优化调整:在运行时,打开或者关闭插入缓 存和自适应哈希索引

These new performance features can yield up to twice the throughput or more, depending on your workload, platform and other tuning considerations. In another post, we explore some details about these changes, but first, what do these enhancements mean for performance and scalability?
这些新的性能特新可以提升多大两倍甚至更多的的吞吐量,这依赖于你的负载,平台和其他调整事项。在另一篇文章中,我们会探 讨这些改变的一些细节,但首先,我们现探讨这些性能和可扩展性的增强是什么意思,包括哪些内容

In brief, we’ve tested three different workloads (joins, DBT2 OLTP and a modified sysbench) using a memory-resident database. In all cases, the InnoDB Plugin scales significantly better than the built-in InnoDB in MySQL 5.1. And in some cases, the absolute level of performance is dramatically higher too! The charts below illustrate the kinds of performance gains we’ve measured with release 1.0.3 of the InnoDB Plugin. Your mileage may vary, of course. See the InnoDB website for all the details on these tests.
总之,我们已经使用内存驻留数据库(所有数据都载入在内存中)测试了三种不同的工作负载(关联,DBT2 OLTP和修改过的sysbench)。在所有的情况下,InnoDB Plugin的伸缩性明显优于MySQL 5.1内置的InnoDB。在一些场景中,性能提升的水平高的惊人。下面的图说明了InnoDB Plugin 1.0.3的性能提升。你的测试结果可能不同,当然可以在InnoDB网站看到所有测试的细节。

This release of the InnoDB Plugin incorporates a patch made by Ben Handy and Mark Callaghan at Google to improve multi-core scalability by using more efficient synchronization methods (mutexing and rw-locks) to reduce cpu utilization and contention. We’re grateful for this contribution, and you will be too!
这个InnoDB Plugin版本包含了Google的Ben Handy和Mark Callaghan的补丁来提升多处理机扩展性,包括使用了更有效的同步机制(Mutexing和RW-Locks)来减少CPU利用和竞争。我们非常感 谢这个补丁的贡献,相信你也是。

Now to our test results …
现在来看我们的测试结果

Joins: The following chart shows the performance gains in performing joins, comparing the built-in InnoDB in MySQL (in blue) with the InnoDB Plugin 1.0.3 (in red).
关联:下图展示了执行Join操作时的性能提升,内置InnoDB(蓝)和InnoDB Plugin 1.0.3(红)的比较。

As you can see from the blue bars in the above chart, with MySQL 5.1 using the built-in InnoDB, the total number of joins the system can execute declines as the number of concurrent users increases. In contrast, the InnoDB Plugin slightly improves performance even with one user, and maintains performance as the number of users rises. This performance improvement is due in large part to the use of atomics for mutexing in the InnoDB Plugin.
正如你在上面蓝柱上看到的,MySQL 5.1的内置InnoDB,随着并发数的增加系统的执行速度反而下降了。与此相反,InnoDB Plugin随着并发的提升处理速度甚至略有提高,并且随着用户的增长保持着这种性能。这个性能改善很大程度上是因为对Mutexing使用了原子操作。

Transaction Processing (DBT2): The following chart illustrates a scalability improvement using the OLTP read/write DBT2 benchmark, again comparing the performance of the built-in InnoDB in MySQL with the performance of InnoDB Plugin 1.0.3.
事务处理(DBT2):下入展示了用DBT2测试OLTP读写性能的提升,再次比较了内置InnoDB和InnoDB Plugin 1.0.3的性能。

Here, the InnoDB Plugin scales better than the built-in InnoDB from 16 to 32 users and produces about 12% more throughput with 64 concurrent users, as other bottlenecks are encountered or system capacity is reached. This improvement is likewise due primarily to the changes in mutexing.
这里,InnoDB Plugin伸缩性在16增加到32线程时表现更好,产生比64线程多大约12%的吞吐量。由于其他性能瓶颈或系统容量达到基线。这个提升依然主要依赖于 Mutexing的改变。

Modified Sysbench: This test uses a version of the well-known sysbench workload, modified to include queries based on a secondary index, as suggested by Mark Callaghan of Google.
修改过的sysbench:这个测试使用了著名的sysbench,修改包括基于非主键索引的查询,由Google的 Mark Callaghan建议。

This time, the InnoDB Plugin shows significantly better scalability from 8 to 64 users than the built-in InnoDB in MySQL, yielding as much as 60% more throughput at 64 users. Like the previous examples, this improvement is largely due to the use of atomics for mutexing.
这次,InnoDB Plugin在8~64线程都展示了明显优于内置InnoDB的可伸缩性。在64并发时多大60%的性能提升!像前一个例子,这个提升依然主要靠 Mutexing的原子性。

Modified Sysbench with tcmalloc: This test uses the same modified sysbench workload, but shows the difference between the built-in InnoDB (which uses the internal InnoDB memory allocator) and the InnoDB Plugin when using a more scalable memory allocator, in this case tcmalloc.
使用tcmalloc的修改过的sysbench:这种测试使用相同的sysbench场景,但是不同于内置InnoDB 的是InnoDB Plugin使用了tcmalloc作为内存分配器。

When the new configuration parameter innodb_use_sys_malloc is set to enable use of the memory allocator tcmalloc, the InnoDB Plugin really shines! Transaction throughput continues to scale, and the actual throughput with 64 users has nearly doubled!
当设置innodb_user_sys_malloc变量为tcmalloc作为内存分配器时,InnoDB Plugin依然是亮点!事务吞吐量继续扩展,在64并发时吞吐量提升接近1倍(相对没有tcmalloc的)。

MySQL小技巧问答(一)

1月 19th, 2011 | Posted by | Filed under 未分类

抽空总结一下自己操作MySQL的一些心得体会,做成MySQL小技巧问答系列,给大家作为一些案例参考,也为我自己做一些记录:

1. 在基于ROW的双Master复制下,如何快速大批量订正?
在A<->B的双Master结构下,假设只有一台提供服务,这是我们常用的架构,需要大批量订正数据,如何做最快?用存储过程一批批提交?这有很多的限制,有时候并不可以把一条或多条SQL拆成几段,怎么办呢?binlog不是很好的工具嘛?! ROW格式的binlog,Slave在应用时是直接使用Handler API,并没有走SQL解析,速度非常快,基本上是IO操作了,那么我们可以在备库上直接执行订正SQL,产生的ROW binlog传到主机,就会很快订正完,基本上都比写存储过程快。

2. ROW格式Replication如何实现不带库名的replicate-do-db?
虽然MySQL有replicate-do-db这个参数,但是在ROW格式的binlog下必须使用”db.table”的方式才能生效,USE对ROW格式是无效的。现在我有一个Instance,只需要复制Master的某几个库,但是是ROW格式,SQL都没有使用db前缀,怎么办?可以这么做,把主库需要的库导出来,不需要的库导出结构即可,在Slave导入这些数据及结构,配置skip-slave-errors=all,这样Master复制过来的binlog,只要发现有库有表结构,就不会报找不到表,就不会阻塞复制,但是UPDATE/DELETE过来没有数据也会被跳过错误,间接的实现了replicate-do-db。

3. 大批量乱序数据导入InnoDB很慢如何解决?
InnoDB因为主键聚集索引的关系,如果没有主键或者主键非序列的情况下,导入会越来越慢,如何快速的迁移数据到InnoDB?借助MyISAM的力量是很靠谱的,先关闭InnoDB的Buffer Pool,把内存空出来,建一张没有任何索引的MyISAM表,然后只管插入吧,concurrent_insert=2,在文件末尾并发插入,速度刚刚的,插入完成后,ALTER TABLE把索引加上,记得还有ENGINE=InnoDB,就把MyISAM转到InnoDB了,这样的速度远比直接往InnoDB里插乱序数据来得快。

4. A<–>B–>C–>D结构切换到A<–>B, C<–>D结构出现Slave_lag一直增常如何避免?
这种情况常见与一个双Master集群分离出一套双Master集群,例如从原集群分离一部分库。过快的切换B–>C到C<–>D容易导致主备出现slave_lag,并且一直增长,原因在于A<–>B集群产生的SQL,随同server_id带到了C–>D这个M-S中,当A,B产生的SQL在C,D还没消化完成就CHANGE MASTER为C<–>D时,会导致这写SQL在C,D之间来回传输,因为C,D都认为这个SQL不是自己产生的,因而不销毁,自己执行后写入binlog,于是Slave_Lag就一直增长。
避免的方法很简单,部分写切到C后,先断开B–>C的复制,等一会,看D上已经没有Slave_Lag了,再CHANGE MASTER为C<–>D,这样A,B传过来的SQL都消化完了。

5. 表中存在很多重复数据时,如何删除这些重复数据最快?
在需要给表中某些字段加唯一索引时,而字段中又存在需要重复清理数据的问题,不少DBA都应该遇到过。一般在处理时总是想在数据库中只保留一条,其他的删除,但是这样的SQL写出来总是效率不高,怎么办?其实可以转换思路,把重复的都选出一条出来,存到一张临时表,然后删除原表中所有存在重复的,再把临时表的数据库全部插入原库,这是比较通用并且高效的做法。

MySQL Multi-Master实现方式

1月 14th, 2011 | Posted by | Filed under 程序设计

MySQL Mutil-Master Replication喊了很久了,但是MySQL一直没有去,虽然在MySQL源码中有注释将实现Multi-Master,mi结构体也为Multi-Master做好了准备,但是却一直不见MySQL发布。
但是Multi-Master –> Slave的Repliction确实非常有用,例如一台集中备份机备份所有Master的数据。

实现Multi-Master有几种思路:
1. 修改MySQL源码:修改sql_yacc.yy, sql_lex.cc支持多Master的CHANGE MASTER TO语法,然后修改slave相关的slave.cc,支持开启多个Slave, 将slave io/ slave thread线程扩展为一个slave_list。
2. 利用mysqlbinlog之类的工具,远程注册到Master获取binlog,导入本地Slave服务器。

从效率看,肯定第一种方式效率高,但是风险太大了,并且MySQL版本更新,可能需要变动自己的代码以适应新的MySQL Source, MySQL官方的实现方式肯定是第一种,从源码中的注释可以看出他们的设计思路。但是他们考虑的问题可能是多个Master复制如何处理冲突等异常,因而迟迟不发布。

为了避免过多的入侵MySQL,我采用第二种方式,用一个脚本或者程序等等,去调用mysqlbinlog,用-R远程请求到–to-last-log,然后稍微修改一下啊mysqlbinlog的源码,在日志切换后计数一下,在输出文件末尾打上切换日志的个数,例如:

insert into a values (8)
/*!*/;
# at 1070
#110114 16:16:11 server id 3  end_log_pos 1097 	Xid = 36
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
-- Rorate binlog count: 1

— Rorate binlog count: 1就是日志切换信息,表示切换了一次日志(即传入Master的日志号没有用完)然后tail末尾的end_pos来查看本次同步到哪里了,写到*.info的文件中。

我的脚本需要配置一个multi_master.conf文件,配好每个Master的信息,例如:

#cat multi_master.conf 
[master1]
MASTER_HOST=1.2.3.4
MASTER_USER=plx
MASTER_PASSWORD=plx
MASTER_PORT=3306
MASTER_LOG_NAME=mysql-bin
MASTER_LOG_IDX=000002
MASTER_LOG_POS=521
RELAY_LOG_DIR=/tmp/
RELAY_LOG_NAME=1-relay-bin

[master2]
MASTER_HOST=2.3.4.5
MASTER_USER=plx
MASTER_PASSWORD=plx
MASTER_PORT=3306
MASTER_LOG_NAME=mysql-bin
MASTER_LOG_IDX=000002
MASTER_LOG_POS=581
RELAY_LOG_DIR=/tmp/
RELAY_LOG_NAME=2-relay-bin

[slave]
SLAVE_USER=plx
SLAVE_PASSWORD=plx

SLAVE默认导入本地,所以没有提供主机选项。
配置文件的含义是,定义了master1和master2两个Master,名称其实只要不是slave都行,[slave]中定义了本地导入的用户名和密码。
特有的参数我解释下,没解释的跟MySQL一样,
MASTER_LOG_NAME和MASTER_LOG_IDX组成MySQL中的Master_log_file,RELAY_LOG_DIR表示取回的binlog文件放哪个目录,RELAY_LOG_NAME是Relay文件的文件名,会加上标号,跟MySQL一样,这个脚本会自动处理。
一旦执行过一次,就会生成master1.info之类的文件,来表示当前同步到哪里了,例如下面这个例子:

MASTER_LOG_POS=1482
NAME=master1
MASTER_USER=plx
RELAY_LOG_NAME=1-relay-bin
MASTER_LOG_IDX=2
MASTER_HOST=1.2.3.4
MASTER_LOG_NAME=mysql-bin
MASTER_PORT=3306
RELAY_LOG_DIR=/tmp/
MASTER_PASSWORD=plx
RELAY_LOG_IDX=3

只有找不到*.info的时候,才会使用multi_master.conf。

现在每次调度multi_master_repl.pl都只会运行一次,可以不断的调度multi_master_repl.pl,因为还没有完全搞定KILL信号在Perl脚本的处理,用C重写后会解决,不能暴力kill -9,会导致不知道复制到哪里了。

这是下载地址,切勿用在生产环境,这只是个验证想法的程序。

  MultiMasterRepl.tar.gz (798.9 KiB, 1,226 hits)

下一步我想用C重新实现,在mysqlbinlog源码基础上修改,获取到的日志直接写入到sock或直接导入远程mysql,避免多写一次文件,也欢迎提供新思路。

这是一次执行的日志:

#./multi_master_repl.pl 
(DEBUG) Enter: get_config()
	Info: begin
	(DEBUG) get_config --> master1
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_HOST=1.2.3.4
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_USER=plx
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_PASSWORD=plx
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_PORT=3306
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_LOG_NAME=mysql-bin
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_LOG_IDX=000002
	(DEBUG) get_config --> multi_master.conf --> master1:MASTER_LOG_POS=521
	(DEBUG) get_config --> multi_master.conf --> master1:RELAY_LOG_DIR=/tmp/
	(DEBUG) get_config --> multi_master.conf --> master1:RELAY_LOG_NAME=1-relay-bin
	(DEBUG) get_config --> Found master1.info, Read it
	(DEBUG) get_config --> master1.info --> master1:MASTER_LOG_POS=1097
	(DEBUG) get_config --> master1.info --> master1:NAME=master1
	(DEBUG) get_config --> master1.info --> master1:MASTER_USER=plx
	(DEBUG) get_config --> master1.info --> master1:RELAY_LOG_NAME=1-relay-bin
	(DEBUG) get_config --> master1.info --> master1:MASTER_LOG_IDX=2
	(DEBUG) get_config --> master1.info --> master1:MASTER_HOST=1.2.3.4
	(DEBUG) get_config --> master1.info --> master1:MASTER_LOG_NAME=mysql-bin
	(DEBUG) get_config --> master1.info --> master1:MASTER_PORT=3306
	(DEBUG) get_config --> master1.info --> master1:RELAY_LOG_DIR=/tmp/
	(DEBUG) get_config --> master1.info --> master1:MASTER_PASSWORD=plx
	(DEBUG) get_config --> master1.info --> master1:RELAY_LOG_IDX=2
	(DEBUG) get_config --> Push[master1] to Master_Info_List
	(DEBUG) get_config --> master2
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_HOST=2.3.4.5
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_USER=plx
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_PASSWORD=plx
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_PORT=3306
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_LOG_NAME=mysql-bin
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_LOG_IDX=000002
	(DEBUG) get_config --> multi_master.conf --> master2:MASTER_LOG_POS=581
	(DEBUG) get_config --> multi_master.conf --> master2:RELAY_LOG_DIR=/tmp/
	(DEBUG) get_config --> multi_master.conf --> master2:RELAY_LOG_NAME=2-relay-bin
	(DEBUG) get_config --> Found master2.info, Read it
	(DEBUG) get_config --> master2.info --> master2:MASTER_LOG_POS=1541
	(DEBUG) get_config --> master2.info --> master2:NAME=master2
	(DEBUG) get_config --> master2.info --> master2:MASTER_USER=plx
	(DEBUG) get_config --> master2.info --> master2:RELAY_LOG_NAME=2-relay-bin
	(DEBUG) get_config --> master2.info --> master2:MASTER_LOG_IDX=2
	(DEBUG) get_config --> master2.info --> master2:MASTER_HOST=2.3.4.5
	(DEBUG) get_config --> master2.info --> master2:MASTER_LOG_NAME=mysql-bin
	(DEBUG) get_config --> master2.info --> master2:MASTER_PORT=3306
	(DEBUG) get_config --> master2.info --> master2:RELAY_LOG_DIR=/tmp/
	(DEBUG) get_config --> master2.info --> master2:MASTER_PASSWORD=plx
	(DEBUG) get_config --> master2.info --> master2:RELAY_LOG_IDX=2
	(DEBUG) get_config --> Push[master2] to Master_Info_List
	(DEBUG) get_config --> multi_master.conf --> slave:SLAVE_USER=plx
	(DEBUG) get_config --> multi_master.conf --> slave:SLAVE_PASSWORD=plx
(DEBUG) Enter: get_config()
	Info: exit
(DEBUG) Enter: create_slave_threads()
	Info: begin
	(DEBUG) create_slave_threads --> Creating run_slave Threads...
(DEBUG) Enter: run_slave()
	Info: begin [tid: 1]
	(DEBUG) run_slave(0) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) run_slave --> mysqlbinlog: ./mysqlbinlog -h1.2.3.4 -uplx -pplx -R -t --start-position=1097 mysql-bin.000002 > /tmp/1-relay-bin.000002
Warning: ./mysqlbinlog: unknown variable 'loose_default-character-set=utf8'
	(DEBUG) run_slave(0) --> NO KILL SIGNAL --> g_is_killed =>0
(DEBUG) Enter: import_to_slave()
	Info: begin [Param: p_master_idx=>0]
	(DEBUG) import_to_slave(0) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) import_to_slave(0) --> Importing Relay Log /tmp/1-relay-bin.000002 To Slave...
	(DEBUG) create_slave_threads --> Created 2 run_slave Threads
(DEBUG) Enter: run_slave()
	Info: begin [tid: 2]
	(DEBUG) run_slave(1) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) run_slave --> mysqlbinlog: ./mysqlbinlog -h2.3.4.5 -uplx -pplx -R -t --start-position=1541 mysql-bin.000002 > /tmp/2-relay-bin.000002
Warning: ./mysqlbinlog: unknown variable 'loose_default-character-set=utf8'
	(DEBUG) run_slave(1) --> NO KILL SIGNAL --> g_is_killed =>0
(DEBUG) Enter: import_to_slave()
	Info: begin [Param: p_master_idx=>1]
	(DEBUG) import_to_slave(1) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) import_to_slave(1) --> Importing Relay Log /tmp/2-relay-bin.000002 To Slave...
(DEBUG) Enter: update_master_info()
	Info: begin [Param: p_master_idx=>0]
(DEBUG) Enter: update_master_info()
	Info: begin [Param: p_master_idx=>1]
	(DEBUG) update_master_info(0) --> Now Master-Log is mysql-bin.000002 Pos is 1482
(DEBUG) Enter: update_master_info_file()
	Info: begin [Param: p_master_idx=>0]
	(DEBUG) update_master_info_file(0) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_LOG_POS=1482
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> NAME=master1
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_USER=plx
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> RELAY_LOG_NAME=1-relay-bin
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_LOG_IDX=2
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_HOST=1.2.3.4
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_LOG_NAME=mysql-bin
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_PORT=3306
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> RELAY_LOG_DIR=/tmp/
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> MASTER_PASSWORD=plx
	(DEBUG) update_master_info_file(0) --> Writing master1.info --> RELAY_LOG_IDX=3
	(DEBUG) update_master_info_file(0) --> Created master1.info
(DEBUG) Enter: update_master_info_file(0)
	Info: exit
(DEBUG) Enter: update_master_info(0)
	Info: exit
(DEBUG) Enter: import_to_slave(0)
	Info: exit
(DEBUG) Enter: run_slave(0)
	Info: exit
	(DEBUG) update_master_info(1) --> Now Master-Log is mysql-bin.000002 Pos is 2120
(DEBUG) Enter: update_master_info_file()
	Info: begin [Param: p_master_idx=>1]
	(DEBUG) update_master_info_file(1) --> NO KILL SIGNAL --> g_is_killed =>0
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_LOG_POS=2120
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> NAME=master2
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_USER=plx
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> RELAY_LOG_NAME=2-relay-bin
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_LOG_IDX=2
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_HOST=2.3.4.5
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_LOG_NAME=mysql-bin
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_PORT=3306
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> RELAY_LOG_DIR=/tmp/
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> MASTER_PASSWORD=plx
	(DEBUG) update_master_info_file(1) --> Writing master2.info --> RELAY_LOG_IDX=3
	(DEBUG) update_master_info_file(1) --> Created master2.info
(DEBUG) Enter: update_master_info_file(1)
	Info: exit
(DEBUG) Enter: update_master_info(1)
	Info: exit
(DEBUG) Enter: import_to_slave(1)
	Info: exit
(DEBUG) Enter: run_slave(1)
	Info: exit
(DEBUG) Enter: create_slave_threads()
	Info: exit

MySQL多个Slave同一server_id的冲突原因分析

1月 7th, 2011 | Posted by | Filed under 未分类

今天分析一个诡异问题,一个模拟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。

  case COM_REGISTER_SLAVE:
  {
    if (!register_slave(thd, (uchar*)packet, packet_length))
      my_ok(thd);
    break;
  }

在注册Slave线程的时候会发生什么呢?我们略去无用的代码直接看重点:(repl_failsafe.cc)

int register_slave(THD* thd, uchar* packet, uint packet_length)
{
  int res;
  SLAVE_INFO *si;
  uchar *p= packet, *p_end= packet + packet_length;
.... //省略
  if (!(si->master_id= uint4korr(p)))
    si->master_id= server_id;
  si->thd= thd;
  pthread_mutex_lock(&LOCK_slave_list);
  unregister_slave(thd,0,0); //关键在这里,先取消注册server_id相同的Slave线程
  res= my_hash_insert(&slave_list, (uchar*) si); //把新的Slave线程注册到slave_list
  pthread_mutex_unlock(&LOCK_slave_list);
  return res;
.....
}

这是什么意思呢?这就是重连机制,slave_list是一个Hash表,server_id是Key,每一个线程注册上来,需要删掉同样server_id的Slave线程,再把新的Slave线程加到slave_list表中。

线程注册上来后,请求Binlog,发送COM_BINLOG_DUMP请求,Master会发送binlog给Slave,代码如下:

  case COM_BINLOG_DUMP:
    {
      ulong pos;
      ushort flags;
      uint32 slave_server_id;

      status_var_increment(thd->status_var.com_other);
      thd->enable_slow_log= opt_log_slow_admin_statements;
      if (check_global_access(thd, REPL_SLAVE_ACL))
        break;

      /* TODO: The following has to be changed to an 8 byte integer */
      pos = uint4korr(packet);
      flags = uint2korr(packet + 4);
      thd->server_id=0; /* avoid suicide */
      if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
        kill_zombie_dump_threads(slave_server_id);
      thd->server_id = slave_server_id;

      general_log_print(thd, command, "Log: '%s'  Pos: %ld", packet+10,
                      (long) pos);
      mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); //不断的发送日志给slave端
      unregister_slave(thd,1,1); //发送完成后清理Slave线程,因为执行到这一步肯定是binlog dump线程被kill了
      /*  fake COM_QUIT -- if we get here, the thread needs to terminate */
      error = TRUE;
      break;
    }

mysql_binlog_send函数在sql_repl.cc,里面是轮询Master binlog,发送给Slave。

再来简单看看unregister_slave做了什么(repl_failsafe.cc):

void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
{
  if (thd->server_id)
  {
    if (need_mutex)
      pthread_mutex_lock(&LOCK_slave_list);

    SLAVE_INFO* old_si;
    if ((old_si = (SLAVE_INFO*)hash_search(&slave_list,
                                           (uchar*)&thd->server_id, 4)) &&
        (!only_mine || old_si->thd == thd)) //拿到slave值
    hash_delete(&slave_list, (uchar*)old_si); //从slave_list中拿掉

    if (need_mutex)
      pthread_mutex_unlock(&LOCK_slave_list);
  }
}

这就可以解释同样的server_id为什么会被kill,因为一旦注册上去,就会现删除相同server_id的Slave线程,然后把当前的Slave加入,这是因为有时Slave断开了,重新请求上来,当然需要踢掉原来的线程,这就是线程重连机制。

切记,一个MySQL集群中,绝不可以出现相同server_id的实例,否则各种诡异的问题可是接踵而来。

标签: ,