{"id":7237,"date":"2015-08-04T11:38:13","date_gmt":"2015-08-04T09:38:13","guid":{"rendered":"http:\/\/code.openark.org\/blog\/?p=7237"},"modified":"2015-08-04T11:38:13","modified_gmt":"2015-08-04T09:38:13","slug":"on-show-binlogrelaylog-events","status":"publish","type":"post","link":"https:\/\/code.openark.org\/blog\/mysql\/on-show-binlogrelaylog-events","title":{"rendered":"On SHOW BINLOG\/RELAYLOG EVENTS"},"content":{"rendered":"<p>Some notes after working with <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/show-binlog-events.html\">SHOW BINLOG EVENTS<\/a> and <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/show-relaylog-events.html\">SHOW RELAYLOG EVENTS<\/a> statements; there are a few gotchas and some interesting facts. My reflections also follow.<\/p>\n<p>I&#8217;m calling these commands from <a href=\"https:\/\/github.com\/outbrain\/orchestrator\">orchestrator<\/a> when working with <a href=\"https:\/\/speakerdeck.com\/shlominoach\/pseudo-gtid-and-easy-mysql-replication-topology-management\">Pseudo-GTID<\/a> (which I do <em>alot<\/em>). I prefer to work with agent-free design, where a single, remote service can do\u00a0everything: examine replication status, scan binary logs for information, and recover broken topologies via gluing together servers that were not previously directly associated.<\/p>\n<p>Alas, documentation is short on these commands, and some stuff I learned the hard way.<\/p>\n<p>Basically, <strong>SHOW BINLOG\/RELAYLOG EVENTS<\/strong> commands are a\u00a0poor man&#8217;s replacement to <strong>mysqlbinlog<\/strong>, only you can issue them on MySQL protocol, and you do not have to have the binary\/relay log files locally on your host.<\/p>\n<h3>Fun\u00a0fact<\/h3>\n<p>The binary logs are called so because they are compressed. You are familiar with the binlog position you see on <strong>SHOW MASTER STATUS<\/strong> or <strong>SHOW SLAVE STATUS<\/strong>. You are familiar with the binlog position as you see it when you execute <strong>&#8220;mysqlbinlog mybinlog.001234&#8221;<\/strong>. The position of a new entry equals to the file size of the binary log at that time. If:<\/p>\n<blockquote>\n<pre>$ ls -l master\/data\/mysql-bin.015901\r\n-rw-rw---- 1 user user 401408 Jul 18 02:44 master\/data\/mysql-bin.015901<\/pre>\n<\/blockquote>\n<p>Then the next entry will be at position <strong>401408<\/strong>, as this is the file size in bytes.<\/p>\n<p>And so when MySQL writes an entry to the binary log, it (of course) knows the entry&#8217;s position in the binary log, but then also immediately knows the position of the next entry.<\/p>\n<p>We&#8217;ll revisit this fact later.<\/p>\n<h3>Output of SHOW BINLOG\/RELAYLOG EVENTS<\/h3>\n<p>The output of both statement depends on the <strong>binlog_format<\/strong>. In Statement Based Replication it may look like:<!--more--><\/p>\n<blockquote>\n<pre>$gt; SHOW BINLOG EVENTS IN 'mysql-bin.015903' LIMIT 40,20;\r\n+------------------+------+------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------+\r\n| Log_name         | Pos  | Event_type | Server_id | End_log_pos | Info                                                                                                                |\r\n+------------------+------+------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------+\r\n| mysql-bin.015903 | 3650 | Gtid       |         1 |        3698 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:790000'                                              |\r\n| mysql-bin.015903 | 3698 | Query      |         1 |        3785 | BEGIN                                                                                                               |\r\n| mysql-bin.015903 | 3785 | User var   |         1 |        3832 | @`id`=7                                                                                                             |\r\n| mysql-bin.015903 | 3832 | Query      |         1 |        3969 | use `test`; replace into test.t (id, i, t) values (@id, @id, now())                                                 |\r\n| mysql-bin.015903 | 3969 | Xid        |         1 |        4000 | COMMIT \/* xid=967 *\/                                                                                                |\r\n| mysql-bin.015903 | 4000 | Gtid       |         1 |        4048 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:790001'                                              |\r\n| mysql-bin.015903 | 4048 | Query      |         1 |        4135 | BEGIN                                                                                                               |\r\n| mysql-bin.015903 | 4135 | User var   |         1 |        4182 | @`id`=9                                                                                                             |\r\n| mysql-bin.015903 | 4182 | Query      |         1 |        4319 | use `test`; replace into test.t (id, i, t) values (@id, @id, now())                                                 |\r\n| mysql-bin.015903 | 4319 | Xid        |         1 |        4350 | COMMIT \/* xid=969 *\/                                                                                                |\r\n| mysql-bin.015903 | 4350 | Gtid       |         1 |        4398 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:790002'                                              |\r\n| mysql-bin.015903 | 4398 | Query      |         1 |        4576 | use `meta`; drop view if exists `meta`.`_pseudo_gtid_hint__asc(55B50F9C:00000000000000D4:C96BF9F7)` |\r\n| mysql-bin.015903 | 4576 | Gtid       |         1 |        4624 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:790003'                                              |\r\n| mysql-bin.015903 | 4624 | Query      |         1 |        4711 | BEGIN                                                                                                               |\r\n| mysql-bin.015903 | 4711 | User var   |         1 |        4758 | @`id`=6                                                                                                             |\r\n| mysql-bin.015903 | 4758 | Query      |         1 |        4895 | use `test`; replace into test.t (id, i, t) values (@id, @id, now())                                                 |\r\n| mysql-bin.015903 | 4895 | Xid        |         1 |        4926 | COMMIT \/* xid=971 *\/                                                                                                |\r\n| mysql-bin.015903 | 4926 | Gtid       |         1 |        4974 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:790004'                                              |\r\n| mysql-bin.015903 | 4974 | Query      |         1 |        5077 | BEGIN                                                                                                               |\r\n| mysql-bin.015903 | 5077 | User var   |         1 |        5133 | @`hostname`=_utf8 0x736E6F6163682D616D7339 COLLATE utf8_general_ci                                                  |\r\n+------------------+------+------------+-----------+-------------+---------------------------------------------------------------------------------------------------------------------+\r\n<\/pre>\n<\/blockquote>\n<p>The first thing to note is that there&#8217;s\u00a0far less data in this output as compared to\u00a0<strong>mysqlbinlog<\/strong>&#8216;s. For example, you can&#8217;t see the TIMESTAMP at which the queries were issued. You can&#8217;t see additional metadata like RAND() seed. Thus far it is documented.<\/p>\n<p>You do know the binlog file name, position, end-position (which is the position of the next entry), originating <strong>server_id<\/strong>.<\/p>\n<p>However with\u00a0Row Based Replication things are even less informative:<\/p>\n<blockquote>\n<pre>$gt; SHOW BINLOG EVENTS IN 'mysql-bin.015902' LIMIT 20,20;\r\n+------------------+------+-------------+-----------+-------------+------------------------------------------------------------------------+\r\n| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                                                   |\r\n+------------------+------+-------------+-----------+-------------+------------------------------------------------------------------------+\r\n| mysql-bin.015902 | 1365 | Query       |         1 |        1453 | BEGIN                                                                  |\r\n| mysql-bin.015902 | 1453 | Table_map   |         1 |        1535 | table_id: 71 (meta.pseudo_gtid_status)                                 |\r\n| mysql-bin.015902 | 1535 | Update_rows |         1 |        1829 | table_id: 71 flags: STMT_END_F                                         |\r\n| mysql-bin.015902 | 1829 | Xid         |         1 |        1860 | COMMIT \/* xid=26 *\/                                                    |\r\n| mysql-bin.015902 | 1860 | Gtid        |         1 |        1908 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:789767' |\r\n| mysql-bin.015902 | 1908 | Query       |         1 |        1988 | BEGIN                                                                  |\r\n| mysql-bin.015902 | 1988 | Table_map   |         1 |        2035 | table_id: 70 (test.t)                                                  |\r\n| mysql-bin.015902 | 2035 | Update_rows |         1 |        2129 | table_id: 70 flags: STMT_END_F                                         |\r\n| mysql-bin.015902 | 2129 | Xid         |         1 |        2160 | COMMIT \/* xid=29 *\/                                                    |\r\n| mysql-bin.015902 | 2160 | Gtid        |         1 |        2208 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:789768' |\r\n| mysql-bin.015902 | 2208 | Query       |         1 |        2288 | BEGIN                                                                  |\r\n| mysql-bin.015902 | 2288 | Table_map   |         1 |        2335 | table_id: 70 (test.t)                                                  |\r\n| mysql-bin.015902 | 2335 | Update_rows |         1 |        2429 | table_id: 70 flags: STMT_END_F                                         |\r\n| mysql-bin.015902 | 2429 | Xid         |         1 |        2460 | COMMIT \/* xid=32 *\/                                                    |\r\n| mysql-bin.015902 | 2460 | Gtid        |         1 |        2508 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:789769' |\r\n| mysql-bin.015902 | 2508 | Query       |         1 |        2588 | BEGIN                                                                  |\r\n| mysql-bin.015902 | 2588 | Table_map   |         1 |        2635 | table_id: 70 (test.t)                                                  |\r\n| mysql-bin.015902 | 2635 | Update_rows |         1 |        2729 | table_id: 70 flags: STMT_END_F                                         |\r\n| mysql-bin.015902 | 2729 | Xid         |         1 |        2760 | COMMIT \/* xid=34 *\/                                                    |\r\n| mysql-bin.015902 | 2760 | Gtid        |         1 |        2808 | SET @@SESSION.GTID_NEXT= '230ea8ea-81e3-11e4-972a-e25ec4bd140a:789770' |\r\n+------------------+------+-------------+-----------+-------------+------------------------------------------------------------------------+\r\n<\/pre>\n<\/blockquote>\n<p>What&#8217;s this? We don&#8217;t get the statement anymore. I mean, sure, this is RBR, but there&#8217;s no more info! All we knows is there was an <strong>Update_rows<\/strong> on test.t; that also only after we&#8217;ve crossed two different entries, one mapping a test.t to <strong>70<\/strong>, the other telling us an <strong>UPDATE<\/strong> operation was made on <strong>70<\/strong>.<\/p>\n<p>This is the reason <a href=\"http:\/\/code.openark.org\/blog\/mysql\/pseudo-gtid-row-based-replication\">why Pseudo GTID injection is made via DDL<\/a>.<\/p>\n<p>With RBR we also get some excessive information. The <strong>table_id<\/strong> mentioned above is true for this server; but on a slave the table would have a different id. The SHOW output would look different. Likewise, the commit number as in <strong>&#8220;COMMIT \/* xid=32 *\/&#8221;<\/strong> changes from server to server. There are also other entries which may look different; character encoding changes shape. Really weird stuff.<\/p>\n<p>The output of <strong>SHOW RELAYLOG EVENTS<\/strong> is very similar, except for the following:<\/p>\n<ol>\n<li>Obviously we&#8217;re parsing and outputting relay logs<\/li>\n<li><strong>Pos<\/strong> column relates to the position in the relay log where the entry starts (makes sense)<\/li>\n<li><strong>End_log_pos<\/strong> column relates to the next binlog position <em>of the master&#8217;s binary log<\/em> that correlates to this query. Read that again. There is no telling what the end-log-pos is for the relay log entry. We&#8217;ll revisit that shortly.<\/li>\n<\/ol>\n<h3>Locking issues<\/h3>\n<p>Unfortunately, issuing a <strong>SHOW BINLOG EVENTS<\/strong>\u00a0<a href=\"https:\/\/bugs.mysql.com\/bug.php?id=76618\">completely locks down the binary logs<\/a> on the server.<\/p>\n<p>Yes. This means you cannot write to the binary logs while issuing the <strong>SHOW<\/strong> statement. And that means you cannot write, everything piles up and your server is under complete lock down.<\/p>\n<p>Before you throw your hands up in the air and move on to read something that can cheer you up, note that things\u00a0are\u00a0not completely bad. We are able at <strong>Booking.com<\/strong> to scan <strong>24<\/strong> hours worth of binary logs (i.e. dozens of hundreds of binary logs) on business-critical\u00a0MySQL masters without\u00a0making an impact. How?<\/p>\n<h3>SHOW&#8230;LIMIT offset, row_count vs SHOW&#8230;FROM pos LIMIT row_count<\/h3>\n<p>Of course the\u00a0solution is to do many small steps instead of few giant steps. Reading all of a binary log&#8217;s entries is suicidal to your application &amp; database. The <strong>SHOW BINLOG\/RELAYLOG EVENTS<\/strong> statements support a <strong>LIMIT<\/strong> clause.<\/p>\n<p>As per previous example, you may:<\/p>\n<blockquote>\n<pre>SHOW BINLOG EVENTS IN 'mysql-bin.015903' LIMIT 40,20;<\/pre>\n<\/blockquote>\n<p>This skips <strong>40<\/strong> entries (<strong>offset<\/strong>), then reads <strong>20<\/strong> <strong>(row_count<\/strong>).<\/p>\n<p>However much like non-indexed table scan, to skip the first <strong>40<\/strong> entries you must nonetheless iterate them. Since we don&#8217;t know exactly where in the binlog the <strong>41st<\/strong> entry begins, we need to unpack the binary log from start and find it out the full-scan way.<\/p>\n<p>Don&#8217;t iterate a binary log&#8217;s entries via <strong>LIMIT 0,1000<\/strong>; <strong>LIMIT 1000,1000<\/strong>; <strong>LIMIT 2000,1000<\/strong>; &#8230; This is O(n\u00b2)<\/p>\n<p>In contrast, you may:<\/p>\n<blockquote>\n<pre>SHOW BINLOG EVENTS IN 'mysql-bin.015903' <strong>FROM<\/strong> 3650 LIMIT 20;<\/pre>\n<\/blockquote>\n<p>Remember that an entry&#8217;s position is the binlog file size at the time before the entry was written? To execute the above MySQL <em>merely <strong>seeks<\/strong><\/em> the binlog file 3650 bytes ahead. No scan required.<\/p>\n<p>You must provide with a valid position value. If I just guess my way:<\/p>\n<blockquote>\n<pre>&gt; SHOW BINLOG EVENTS IN 'mysql-bin.015903' FROM 3651 LIMIT 20;\r\nERROR 1220 (HY000): Error when executing command SHOW BINLOG EVENTS: Wrong offset or I\/O error<\/pre>\n<\/blockquote>\n<p>And so you must have prior knowledge about positions. This is fine if you&#8217;re scanning a binary log: you simply read the <strong>End_log_pos<\/strong> of the last entry you&#8217;ve just read and memorize it. Thus, to scan a binary log, you can:<\/p>\n<blockquote>\n<pre>&gt;\u00a0SHOW BINLOG EVENTS IN 'mysql-bin.015903' LIMIT 20;\r\n\r\n&gt;\u00a0SHOW BINLOG EVENTS IN 'mysql-bin.015903' FROM 1499 LIMIT 20;\r\n\r\n&gt;\u00a0SHOW BINLOG EVENTS IN 'mysql-bin.015903' FROM\u00a03651 LIMIT 20;<\/pre>\n<\/blockquote>\n<p>But it&#8217;s more difficult to do this for relay logs: remember that the <strong>End_log_pos<\/strong>\u00a0in <strong>SHOW RELAYLOG EVENTS<\/strong> relates to the next position in the <em>master&#8217;s binary log<\/em>. So you need to resort to an off-by-one or something. However the good news is that reading relay logs <em>does not<\/em> block writes on your server.<\/p>\n<h3>Locking thoughts<\/h3>\n<p>As per my comments in the bug report, I don&#8217;t see why <strong>SHOW BINLOG EVENTS<\/strong> should always lock writes:<\/p>\n<ul>\n<li>If we&#8217;re scanning an old binary log (not the one being written to) then obviously we should not be blocking\u00a0writes.<\/li>\n<li>If we&#8217;re scanning the current binlog, but are known to end with a statement that is already written (i.e. not trying to scan past the current writing position) then we should not be blocking writes.<\/li>\n<li>OK to block writes when trying to read past current position.<\/li>\n<li>OK to block on a <strong>FLUSH MASTER LOGS<\/strong>, <strong>PURGE MASTER LOGS<\/strong><\/li>\n<\/ul>\n<h3>Why is the output so limited?<\/h3>\n<p>I&#8217;m actually unsure why the output of these commands is so limited as compared to the <strong>mysqlbinlog<\/strong> output. A replicating slave talks to the master over MySQL protocol; the slave <em>does get<\/em> all the data right. It <em>does get<\/em> event metadata (timestamp, random seed, session variable). It <em>does get<\/em> complete statement\/row data. What I&#8217;m saying is that this functionality is already built in; it could be very easily added to the <strong>SHOW<\/strong> commands.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some notes after working with SHOW BINLOG EVENTS and SHOW RELAYLOG EVENTS statements; there are a few gotchas and some interesting facts. My reflections also follow. I&#8217;m calling these commands from orchestrator when working with Pseudo-GTID (which I do alot). I prefer to work with agent-free design, where a single, remote service can do\u00a0everything: examine [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"enabled":false},"version":2}},"categories":[5],"tags":[8],"class_list":["post-7237","post","type-post","status-publish","format-standard","hentry","category-mysql","tag-replication"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p2bZZp-1SJ","_links":{"self":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/7237","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/comments?post=7237"}],"version-history":[{"count":18,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/7237\/revisions"}],"predecessor-version":[{"id":7326,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/posts\/7237\/revisions\/7326"}],"wp:attachment":[{"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/media?parent=7237"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/categories?post=7237"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/code.openark.org\/blog\/wp-json\/wp\/v2\/tags?post=7237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}