社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

用Python开发MySQL增强半同步BinlogServer(T3实战篇)

老叶茶馆 • 4 年前 • 338 次点击  
导读
作者:曾永伟,知数堂10期学员,多年JAVA物流行业开发管理经验和PHP/Python跨境电商开发管理经验,对数据库系统情有独钟,善于运用SQL编程简化业务逻辑,去年开始正式从业MySQL DBA, 专注于DB系统自动化运维、MySQL云上实践。
本文为python-mysql-binlogserver系列的第三篇(T3实战篇),之后会陆续发布此系列的其他文章,请大家点击在看或者收藏,自行练习。

一想到你在关注我就忍不住有点紧张


一、概述

通过前两篇的基础,我们已经能够深入理解二进制与MySQL的协议关系了,并且能够结合自己的需求,从官方文档找出相应的文档来实现自己的功能,希望大家鱼渔双收。

需要特别强调的是,通过文中的例子和思路,要学会把一个复杂的问题进行以拆分,细化成一个一个的小任务后再逐个去实现,特别是对接触编码不久的同学特别重要,从Hello World模式开始编码在任何阶段都适用(它是一个最小无依赖的可执行单元),因为它能排除众多干扰项,让你可以专注自己的小目标进行编码和测试。

目前我们已经能够用Socket和MySQL进行基本通信了,也能够处理Binlog文件的Event了,离我们的BinlogServer仅有一步之遥了,这一步就是通过Socket读取Binlog Event并保存到本地。

这一篇之所以叫实战篇,就是希望你不要停在只看不练的阶段,这里再重提一下基础篇的要点:

只有会认真看文档的DBA才是好DBA,只会认真看代码的Engineer,一定不是好Engineer。代码一定要运行起来,On Runtime才会有价值,才会让你变成好Engineer. ^_^


二、Python学习方法论

在这面在分享一个快速提高Python Coding的方法:

  1. Reinventing the wheel with github

从产品设计和生产的层面来讲,我们要避免重复造轮子,作为一个Engineer,在众多的学习方法中,重复造轮子是最好的方式,没有之一。

Github有太多的优秀项目值得我们学习,我们都可以把它们当成"轮子"重复制造一遍,深入学习它们的制造"工艺",然后用到自己的项目中。如果你还没有找到一个合适的"轮子",那就从py-mysql-binlogserver开始吧,它配备了完整入门教程,没有第三方依赖,非常适合学习(项目的初衷也为了准备一个公司内部分享的Python实战项目)。

"轮子"找到了,剩下就事情就是需要你去"搬砖"了,一行一行的搬到自己的环境中,不一定要照抄,最好是在理解的基础上,能用变通的方式来实现,举一反三,达到相同的效果,相信用这种方式你很快就能学到很多东西,并能够在此项目的基础上做出更多的功能,如MySQL实时同步或迁移工具,到时候你会发现要将MySQL的热点数据真正实时地同步到NoSQL中是多么容易。


三、学习使用tcpdump和Wireshark

在通信篇,我们完成了和MySQL Server的基本通信:认证和执行SQL查询,接下来我们就需要分析出MySQL半同步复制的流程和具体网包内容,然后使用Python的Socket来模拟网络收发包,最终从Master上把Binlog Event获取回来。

抓包工具组合tcpdump和Wireshark对特别重要工具对我们非常有帮助,它对程序的分析和排错起到最重要的作用。在实现BinlogServer之前,必须要先用一个正常的复制环境中dump出一个正常无误的数据包进行参考,当自己的程序出现问题,则可以一个包一个包的进行对比,有时甚至要一个字节一个字节对行对比,方能快速地找出程序的问题。

1、使用tcpdump抓包

首先我们需要建立一个MySQL的复制环境,一主一从即可,在从库Change Master之后,start slave之前,使用tcpdump记录下所发生的一切:

  1. # Master

  2. $ tcpdump -i enp0s8 port 3306 -w /tmp/3306-repl.cap


  3. # Slave

  4. mysql> start slave;


  5. # Master

  6. mysql> do some trx;


  7. # Slave

  8. mysql> stop slave;


  9. # Master, 中断tcpdump,得到 3306-repl.cap 文件。

2、使用Wireshark看包

直接用Wireshark打开/tmp/3306-repl.cap, 我们先分析出start slave后,从库都发送了哪些包:

  1. # 第一步部分,先认证

  2. Server Greeting

  3. Login Request


  4. # 第二部分, 从库发起各种查询和指令, 这里可先不用关心每一个是做什么用的

  5. Statement: SELECT UNIX_TIMESTAMP()

  6. Statement: SELECT @@GLOBAL .SERVER_ID

  7. Statement: SET @master_heartbeat_period= 30000001024

  8. Statement: SET @master_binlog_checksum= @@global.binlog_checksum

  9. Statement: SELECT @@GLOBAL.GTID_MODE

  10. Statement: SELECT @@GLOBAL.SERVER_UUID

  11. Statement: SET @slave_uuid= 'ba66414c-d10d-11e9-b4b0-0800275ae9e7'

  12. Command : Register Slave (21)

  13. Statement: SELECT @@global.rpl_semi_sync_master_enabled

  14. Statement: SET @rpl_semi_sync_slave= 1

  15. Command: Send Binlog GTID (30)

可以发现,除了Command: Register Slave 和 Command: Register Slave 外,其它Statement都是普通query,在上一节中我们都实现了。其中发送Register Slave后,就可以在主库用show slave hosts进行查看了,发送Send Binlog GTID之后,Master就源源不断的把Binlog Event发送过来了。如果执行了SET @rplsemisync_slave= 1后,Master将会启用半同步协议进行传输并等待从库发送ACK直到超时。


四、异步Binlog dump协议

上面的抓包是一个标准的启动半同步复制的流程,有一些query不是必须的(如:验证Master状态,注册Slave,设置心跳值等),接下来我们先模拟一个最简单的Binlog dump流程:

  1. # learn_packet4_dump.py


  2. # 封装binlog dump包

  3. def get_dump_pos(log_file, log_pos, server_id):

  4. """

  5. https://dev.mysql.com/doc/internals/en/com-binlog-dump.html

  6. 1 [12] COM_BINLOG_DUMP

  7. 4 binlog-pos

  8. 2 flags

  9. 4 server-id

  10. string[EOF] binlog-filename

  11. """

  12. COM_BINLOG_DUMP = 0x12

  13. buffer = struct.pack(', len(log_file) + 11) \

  14. + struct.pack(', COM_BINLOG_DUMP)


  15. buffer += struct.pack(', log_pos)


  16. flags = 0

  17. buffer += struct.pack(', flags)

  18. buffer += struct.pack(', server_id)

  19. buffer += log_file.encode()


  20. return buffer



  21. if __name__ == "__main__":


  22. conn = get_conn("192.168.1.100", 3306, "repl", "repl1234")


  23. # 请跟据自己的环境进行修改

  24. log_file = "mysql-bin.000012"

  25. log_pos = 4

  26. dump = get_dump_pos(log_file, log_pos, 3306100)

  27. conn.send(dump)


  28. print("=== Dump Binlog Event ===")

  29. while True:

  30. _header = conn.recv(5)

  31. _length = struct.unpack(", (_header[0:3] + b"\x00"))[0]

  32. _sequenceId = struct.unpack(", _header[3:4 ])[0]

  33. _packetType = struct.unpack(", _header[4:])[0]


  34. if _packetType == 0xfe: # EOF

  35. break


  36. _payload = conn.recv(_length - 1)

  37. dump_packet(_header + _payload, f"read packet [{_sequenceId}]")

一个简单Binlog dump进程就写好了,为了简单化,它省略了所有的非必要动作,通过MySQL Server的验证并得到连接后,就直接向服务器发送Binlob dump指令,看一下输出:

  1. === Dump Binlog Event ===

  2. read packet [1]

  3. 00000000 2C 00 00 01 00 00 00 00 00 04 74 72 32 00 2B 00 ,....... ..tr2.+.

  4. 00000010 00 00 00 00 00 00 20 00 04 00 00 00 00 00 00 00 ...... . ........

  5. 00000020 6D 79 73 71 6C 2D 62 69 6E 2E 30 30 30 30 31 35 mysql-bi n.000015


  6. read packet [2]

  7. 00000000 78 00 00 02 00 76 4C C9 5D 0F 74 72 32 00 77 00 x....vLÉ ].tr2.w.

  8. 00000010 00 00 7B 00 00 00 00 00 04 00 35 2E 37 2E 32 30 ..{..... ..5.7 .20

  9. 00000020 2D 6C 6F 67 00 00 00 00 00 00 00 00 00 00 00 00 -log.... ........

  10. 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........

  11. 00000040 00 00 00 00 00 00 00 00 00 00 00 00 76 4C C9 5D ........ ....vL É]

  12. 00000050 13 38 0D 00 08 00 12 00 04 04 04 04 12 00 00 5F .8...... ......._

  13. 00000060 00 04 1A 08 00 00 00 08 08 08 02 00 00 00 0A 0A ........ ........

  14. 00000070 0A 2A 2A 00 12 34 00 00 3B CE F1 09 .**..4.. ;Îñ.


  15. read packet [3]

  16. 00000000 6C 00 00 03 00 77 4C C9 5D 23 74 72 32 00 6B 00 l....wLÉ ]#tr2.k.

  17. 00000010 00 00 E6 00 00 00 80 00 02 00 00 00 00 00 00 00 ..æ...�. ........

  18. 00000020 F0 EA 18 E0 3C FF 11 E9 94 88 08 00 27 5A E9 E7 ðê.à<..> ��..'Zéç

  19. 00000030 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ........ ........

  20. 00000040 1B 00 00 00 00 00 00 00 F6 70 08 50 F3 FD 11 E9 ........ öp.Póý.é

  21. 00000050 B2 C1 08 00 27 5A E9 E7 01 00 00 00 00 00 00 00 ²Á..' Zéç ........

  22. 00000060 01 00 00 00 00 00 00 00 0F 00 00 00 00 00 00 00 ........ ........


  23. read packet [4]

  24. 00000000 3E 00 00 04 00 FF 4C C9 5D 21 74 72 32 00 3D 00 >.....LÉ ]!tr2.=.

  25. 00000010 00 00 23 01 00 00 00 00 00 F0 EA 18 E0 3C FF 11 ..#..... .ðê.à<..>

  26. 00000020 E9 94 88 08 00 27 5A E9 E7 1B 00 00 00 00 00 00 é��..'Zé ç.......

  27. 00000030 00 02 00 00 00 00 00 00 00 00 01 00 00 00 00 00 ........ ........

  28. 00000040 00 00 ..


  29. read packet [5]

  30. 00000000 44 00 00 05 00 FF 4C C9 5D 02 74 72 32 00 43 00 D.....LÉ ].tr2.C.

  31. 00000010 00 00 66 01 00 00 08 00 05 00 00 00 00 00 00 00 ..f..... ........

  32. 00000020 03 00 00 1A 00 00 00 00 00 00 01 00 00 20 40 00 ........ ..... @.

  33. 00000030 00 00 00 06 03 73 74 64 04 E0 00 E0 00 E0 00 64 .....std .à.à.à.d

  34. 00000040 62 33 00 42 45 47 49 4E b3.BEGIN

接下来我们再"优雅"一下我们的码,并把Package中header去掉,只要真正的Binlog Event内容(把它存到文件中,就是我们Binlog File了):

  1. # learn_packet4_dump2.py


  2. def fetch_events(conn):

  3. while True:

  4. _header = conn.recv(5)

  5. _length = struct.unpack(", (_header [0:3] + b"\x00"))[0]

  6. _sequenceId = struct.unpack(", _header[3:4])[0]

  7. _packetType = struct.unpack(", _header[4:])[0]


  8. if _packetType == 0xfe: # EOF

  9. break

  10. _payload = conn.recv(_length - 1)

  11. yield _payload



  12. if __name__ == "__main__":


  13. conn = get_conn("192.168.1.100", 3306, "repl", "repl1234")


  14. log_file = "mysql-bin.000015"

  15. log_pos = 4

  16. dump = get_dump_pos(log_file, log_pos, 3306100)

  17. conn.send(dump)


  18. print("=== Dump Binlog Event ===")

  19. for event in fetch_events(conn):

  20. timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack(', event[:19])

  21. print("Binlog Event[%s]: [%s] %s %s" % (timestamp,

  22. event_type,

  23. event_map.get(event_type), log_pos))

  24. dump_packet(event, f "Read event packet:")

使用Python yield生成器来处理event的获取动作,就可以使用很优雅的方式来迭代event。来看一下输出:

  1. === Dump Binlog Event ===

  2. Binlog Event[0]: [4] ROTATE_EVENT 0

  3. Read event packet:

  4. 00000000 00 00 00 00 04 74 72 32 00 2B 00 00 00 00 00 00 .....tr2 .+......

  5. 00000010 00 20 00 04 00 00 00 00 00 00 00 6D 79 73 71 6C . ...... ...mysql

  6. 00000020 2D 62 69 6E 2E 30 30 30 30 31 35 -bin.000 015


  7. Binlog Event[1573473398]: [15] FORMAT_DESCRIPTION_EVENT 123

  8. Read event packet:

  9. 00000000 76 4C C9 5D 0F 74 72 32 00 77 00 00 00 7B 00 00 vLÉ].tr2 .w...{..

  10. 00000010 00 00 00 04 00 35 2E 37 2E 32 30 2D 6C 6F 67 00 .....5.7 .20-log.

  11. 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........

  12. 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........

  13. 00000040 00 00 00 00 00 00 00 76 4C C9 5D 13 38 0D 00 08 .......v LÉ].8...

  14. 00000050 00 12 00 04 04 04 04 12 00 00 5F 00 04 1A 08 00 ........ .._.....

  15. 00000060 00 00 08 08 08 02 00 00 00 0A 0A 0A 2A 2A 00 12 ........ ....**..

  16. 00000070 34 00 00 3B CE F1 09 4..;Îñ.


  17. Binlog Event[1573473399]: [35] PREVIOUS_GTIDS_LOG_EVENT 230

  18. Read event packet:

  19. 00000000 77 4C C9 5D 23 74 72 32 00 6B 00 00 00 E6 00 00 wLÉ]#tr2 .k...æ..

  20. 00000010 00 80 00 02 00 00 00 00 00 00 00 F0 EA 18 E0 3C .�...... ...ðê.à<

  21. 00000020 FF 11 E9 94 88 08 00 27 5A E9 E7 01 00 00 00 00 ..é��..' Zéç.....

  22. 00000030 00 00 00 01 00 00 00 00 00 00 00 1B 00 00 00 00 ........ ........

  23. 00000040 00 00 00 F6 70 08 50 F3 FD 11 E9 B2 C1 08 00 27 ...öp.Pó ý.é²Á..'

  24. 00000050 5A E9 E7 01 00 00 00 00 00 00 00 01 00 00 00 00 Zéç..... ........

  25. 00000060 00 00 00 0F 00 00 00 00 00 00 00 ........ ...


  26. Binlog Event[1573473535]: [33] GTID_LOG_EVENT 291

  27. Read event packet:

  28. 00000000 FF 4C C9 5D 21 74 72 32 00 3D 00 00 00 23 01 00 .LÉ]!tr2 .=...#..

  29. 00000010 00 00 00 00 F0 EA 18 E0 3C FF 11 E9 94 88 08 00 ....ðê.à <..>

  30. 00000020 27 5A E9 E7 1B 00 00 00 00 00 00 00 02 00 00 00 'Zéç.... ........

  31. 00000030 00 00 00 00 00 01 00 00 00 00 00 00 00 ........ .....


  32. Binlog Event[1573473535]: [2] QUERY_EVENT 358

  33. Read event packet:

  34. 00000000 FF 4C C9 5D 02 74 72 32 00 43 00 00 00 66 01 00 .LÉ].tr2 .C...f..

  35. 00000010 00 08 00 05 00 00 00 00 00 00 00 03 00 00 1A 00 ........ ........

  36. 00000020 00 00 00 00 00 01 00 00 20 40 00 00 00 00 06 03 ........ @......

  37. 00000030 73 74 64 04 E0 00 E0 00 E0 00 64 62 33 00 42 45 std.à.à. à.db3.BE

  38. 00000040 47 49 4E GIN


  39. Binlog Event[1573473535]: [19] TABLE_MAP_EVENT 401

  40. Read event packet:

  41. 00000000 FF 4C C9 5D 13 74 72 32 00 2B 00 00 00 91 01 00 .LÉ].tr2 .+...�..

  42. 00000010 00 00 00 70 00 00 00 00 00 01 00 03 64 62 33 00 ...p.... ....db3.

  43. 00000020 02 74 33 00 02 03 0F 02 28 00 02 .t3..... (..


  44. Binlog Event[1573473535]: [30] WRITE_ROWS_EVENT 441

  45. Read event packet:

  46. 00000000 FF 4C C9 5D 1E 74 72 32 00 28 00 00 00 B9 01 00 .LÉ].tr2 .(...¹..

  47. 00000010 00 00 00 70 00 00 00 00 00 01 00 02 00 02 FF FC ...p.... .......ü

  48. 00000020 06 00 00 00 03 32 32 32 .....222


  49. Binlog Event[1573473535]: [16] XID_EVENT 468

  50. Read event packet:

  51. 00000000 FF 4C C9 5D 10 74 72 32 00 1B 00 00 00 D4 01 00 .LÉ].tr2 .....Ô..

  52. 00000010 00 00 00 12 00 00 00 00 00 00 00 ........ ...


五、半同步Binlog dump协议

在普通的Dump基础上,我们只需要在会话级别设置SET @rplsemisync_slave=1就可以让Master使用半同步复制协议进行传送Binlog Event,同时Master开始半同步协议后,Packet中会多两个字节, 需要额外处理。

另外,需要注意的是发送ACK的时机,format=ROW时,在每个事务的最后一个XID——EVENT发送Semi ack给Master, 当事务中DDL或format=Statement时,在QUERY_EVENT之后发送Semi ack给Master:

  1. def get_semi_ack(log_file, log_pos):

  2. # 1 0xef kPacketMagicNum

  3. # 8 log_pos

  4. # n binlog_filename


  5. buff = b'\xef'

  6. buff += struct.pack(", log_pos)

  7. buff += log_file. encode("utf8")


  8. return struct.pack(", len(buff)) + buff



  9. if __name__ == "__main__":

  10. conn = get_conn("192.168.1.100", 3306, "repl", "repl1234")

  11. query(conn, "select @@version_comment" )

  12. # 启用增强半同步

  13. query(conn, "SET @rpl_semi_sync_slave=1")


  14. log_file = "mysql-bin.000016"

  15. log_pos = 4


  16. dump = get_dump_pos(log_file, log_pos, 3306100)

  17. dump_packet(dump, "Dump bin log:")

  18. conn.send(dump)


  19. for event in fetch_events(conn):


  20. timestamp, event_type, server_id, event_size, log_pos, flags = struct.unpack(', event[2:21])

  21. print("Binlog Event[%s]: [%s] %s %s" % (timestamp ,

  22. event_type,

  23. event_map.get(event_type), log_pos))

  24. dump_packet(event, f"Read event packet:")


  25. if event_type in (XID_EVENT, QUERY_EVENT):

  26. # TODO 从ROTATE_EVENT中解析当前的binlog文件名

  27. semi_ack = get_semi_ack(log_file, log_pos)

  28. dump_packet(semi_ack, "Send semi ack:")

  29. conn.sendall(semi_ack)

输出结果:

  1. Dump Binlog Event:

  2. 00000000 1B 00 00 00 12 04 00 00 00 00 00 74 72 32 00 6D ........ ...tr2.m

  3. 00000010 79 73 71 6C 2D 62 69 6E 2E 30 30 30 30 31 36 ysql-bin .000016


  4. Binlog Event[0]: [4] ROTATE_EVENT 0

  5. Read event packet:

  6. 00000000 00 00 00 00 04 74 72 32 00 2B 00 00 00 00 00 00 .....tr2 .+......

  7. 00000010 00 20 00 04 00 00 00 00 00 00 00 6D 79 73 71 6C . ...... ...mysql

  8. 00000020 2D 62 69 6E 2E 30 30 30 30 31 36 -bin.000 016


  9. Binlog Event[1573474715]: [15] FORMAT_DESCRIPTION_EVENT 123

  10. Read event packet:

  11. 00000000 9B 51 C9 5D 0F 74 72 32 00 77 00 00 00 7B 00 00 QÉ].tr2 .w...{..

  12. 00000010 00 00 00 04 00 35 2E 37 2E 32 30 2D 6C 6F 67 00 .....5.7 .20-log.

  13. 00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........

  14. 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........

  15. 00000040 00 00 00 00 00 00 00 00 00 00 00 13 38 0D 00 08 ........ ....8...

  16. 00000050 00 12 00 04 04 04 04 12 00 00 5F 00 04 1A 08 00 ........ .._.....

  17. 00000060 00 00 08 08 08 02 00 00 00 0A 0A 0A 2A 2A 00 12 ........ ....**..

  18. 00000070 34 00 00 B0 58 2A BE 4.X


  19. Binlog Event[1573474715]: [35] PREVIOUS_GTIDS_LOG_EVENT 230

  20. Read event packet:

  21. 00000000 9B 51 C9 5D 23 74 72 32 00 6B 00 00 00 E6 00 00 QÉ]#tr2 .k...æ..

  22. 00000010 00 80 00 02 00 00 00 00 00 00 00 F0 EA 18 E0 3C .�...... ...ðê.à<

  23. 00000020 FF 11 E9 94 88 08 00 27 5A E9 E7 01 00 00 00 00 ..é��..' Zéç.....

  24. 00000030 00 00 00 01 00 00 00 00 00 00 00 1F 00 00 00 00 ........ ........

  25. 00000040 00 00 00 F6 70 08 50 F3 FD 11 E9 B2 C1 08 00 27 ...öp.Pó ý.é²Á..'

  26. 00000050 5A E9 E7 01 00 00 00 00 00 00 00 01 00 00 00 00 Zéç..... ........

  27. 00000060 00 00 00 0F 00 00 00 00 00 00 00 ........ ...


  28. Binlog Event[ 1573474750]: [33] GTID_LOG_EVENT 291

  29. Read event packet:

  30. 00000000 BE 51 C9 5D 21 74 72 32 00 3D 00 00 00 23 01 00 ¾QÉ]!tr2 .=...#..

  31. 00000010 00 00 00 00 F0 EA 18 E0 3C FF 11 E9 94 88 08 00 ....ðê.à <..>

  32. 00000020 27 5A E9 E7 1F 00 00 00 00 00 00 00 02 00 00 00 'Zéç.... ........

  33. 00000030 00 00 00 00 00 01 00 00 00 00 00 00 00 ........ .....


  34. Binlog Event[1573474750]: [2] QUERY_EVENT 358

  35. Read event packet:

  36. 00000000 BE 51 C9 5D 02 74 72 32 00 43 00 00 00 66 01 00 ¾QÉ].tr2 .C...f..

  37. 00000010 00 08 00 05 00 00 00 00 00 00 00 03 00 00 1A 00 ........ ........

  38. 00000020 00 00 00 00 00 01 00 00 20 40 00 00 00 00 06 03 ........ @......

  39. 00000030 73 74 64 04 E0 00 E0 00 E0 00 64 62 33 00 42 45 std.à.à. à.db3.BE

  40. 00000040 47 49 4E GIN


  41. Send semi ack:

  42. 00000000 19 00 00 00 EF 66 01 00 00 00 00 00 00 6D 79 73 ....ïf.. .....mys

  43. 00000010 71 6C 2D 62 69 6E 2E 30 30 30 30 31 36 ql-bin.0 00016

我们的程序是从第1个Event开始Dump, 包含了Master上已经提交的事务(只要Binlog File没有被Purge掉之前,都可以Dump下来),当Semi Ack的位置追上Master后,Mater的每个事务提交就会等待Slave进程发送ACK包,再开启下一下事务的提交流程。我们可以通过在 Master上查询status确认半同步是否生效:

  1. mysql> show global status like '%semi%' ;

  2. +--------------------------------------------+-------+

  3. | Variable_name | Value |

  4. +--------------------------------------------+-------+

  5. | Rpl_semi_sync_master_clients | 1 |

  6. | Rpl_semi_sync_master_net_avg_wait_time | 0 |

  7. | Rpl_semi_sync_master_net_wait_time | 0 |

  8. | Rpl_semi_sync_master_net_waits | 29 |

  9. | Rpl_semi_sync_master_no_times | 1 |

  10. | Rpl_semi_sync_master_no_tx | 8 |

  11. | Rpl_semi_sync_master_status | ON | <== 重点

  12. | Rpl_semi_sync_master_timefunc_failures | 0 |

  13. | Rpl_semi_sync_master_tx_avg_wait_time | 1654 |

  14. | Rpl_semi_sync_master_tx_wait_time | 8271 |

  15. | Rpl_semi_sync_master_tx_waits | 5 |

  16. | Rpl_semi_sync_master_wait_pos_backtraverse | 0 |

  17. | Rpl_semi_sync_master_wait_sessions | 0 |

  18. | Rpl_semi_sync_master_yes_tx | 5 | <== 重点

  19. | Rpl_semi_sync_slave_status | OFF |

  20. +--------------------------------------------+-------+

  21. 15 rows in set (0.00 sec)


  22. mysql> insert into t3 select null,222;

  23. Query OK, 1 row affected (0.00 sec)

  24. Records: 1 Duplicates: 0 Warnings: 0


  25. mysql> show global status like '%semi%';

  26. +--------------------------------------------+-------+

  27. | Variable_name | Value |

  28. +--------------------------------------------+-------+

  29. | Rpl_semi_sync_master_clients | 1 |

  30. | Rpl_semi_sync_master_net_avg_wait_time | 0 |

  31. | Rpl_semi_sync_master_net_wait_time | 0 |

  32. | Rpl_semi_sync_master_net_waits | 30 |

  33. | Rpl_semi_sync_master_no_times | 1 |

  34. | Rpl_semi_sync_master_no_tx | 8 |

  35. | Rpl_semi_sync_master_status | ON |

  36. | Rpl_semi_sync_master_timefunc_failures | 0 |

  37. | Rpl_semi_sync_master_tx_avg_wait_time | 1721 |

  38. | Rpl_semi_sync_master_tx_wait_time | 10328 |

  39. | Rpl_semi_sync_master_tx_waits | 6 |

  40. | Rpl_semi_sync_master_wait_pos_backtraverse | 0 |

  41. | Rpl_semi_sync_master_wait_sessions | 0 |

  42. | Rpl_semi_sync_master_yes_tx | 6 | <== 重点

  43. | Rpl_semi_sync_slave_status | OFF |

  44. +--------------------------------------------+-------+

  45. 15 rows in set (0.00 sec)

可以看出通semi_sync提交的事务在增加了,说明我们的半同步Binlog Dump已经大功告成。


六、如何实现Master协议

server.py是在socketserver的基础上实现了一些必要的MySQL Server协议(简单例子:learnsocket4servermulitthread.py),如认证,执行查询,发送Binlog Event等等,为了简化代简,一些查询被用"缓存包"的形式来实际的,这些缓存包可以通过proxy.py来获取,同时使用proxy.py也可以非常方便的拿来观察MySQL的通信包, 也为开发MySQL中间件提供了基础, 更多的功能等待大家来实现。


小结

到目前为此,我们通过循序渐进的方式,从MySQL的第一个Greeting包,到最后的半同步下Binlog Event包全部搞定了,接下来我们只需要再增加Binlog文件的存储功能、Dump断点续传功能等,就可以实现一个完整的Binlog Sever功能了, 当然这些功能都已经含在项目,通过这一系列的教程, 相信你已经有能力去读懂py-mysql-binlogserver的大部分代码了,更多的"制造工艺"等你去发现。

 


推荐阅读:
用Python开发MySQL增强半同步BinlogServer(T1基础篇)
用Python开发MySQL增强半同步BinlogServer(T2通信篇)




扫码加入MySQL技术Q群
(群号:650149401)
   
点“在看”给我一朵小黄花



Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/50755
 
338 次点击