{"id":2847,"date":"2018-04-08T10:12:01","date_gmt":"2018-04-08T09:12:01","guid":{"rendered":"https:\/\/kudzia.eu\/b\/?p=2847"},"modified":"2018-04-08T11:24:10","modified_gmt":"2018-04-08T10:24:10","slug":"multi-master-mysql-replication-with-servers-on-different-continents","status":"publish","type":"post","link":"https:\/\/kudzia.eu\/b\/2018\/04\/multi-master-mysql-replication-with-servers-on-different-continents\/","title":{"rendered":"multi-master mysql replication with servers on 3 different continents"},"content":{"rendered":"<p>at work i&#8217;m using mysql replication quite extensively. first it was a straightforward one-way replication that has been rock-solid for us since 2009. in 2012, for another type of data, we&#8217;ve started using master-master setup. initially the servers were in different European countries, eventually the secondary site was moved to North America while primary one remained in Europe. i&#8217;ve described the setup in <a href=\"https:\/\/kudzia.eu\/b\/2011\/06\/mysql-curcular-master-master-replication\/\">this post<\/a>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication0.png\" alt=\"\" width=\"491\" height=\"268\" class=\"alignnone size-full wp-image-2849\" srcset=\"https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication0.png 491w, https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication0-300x164.png 300w\" sizes=\"auto, (max-width: 491px) 100vw, 491px\" \/><\/p>\n<p>clients periodically select preferred site based on the lowest response time. their request generate simple read\/write queries; updates get asynchronously replicated between both sites. if you google about similar setup you&#8217;ll find a lot of warnings &#8211; this architecture might lead to inconsistency when contradicting updates arrive nearly at the same time to both sites. in our case it&#8217;s highly unlikely. also, we have extensive monitoring and alerting based on pt-table-checksum and a simple automated conflict resolution script. it has worked very well for us and gave multi-year 100% service availability also in a few events of network outages affecting either of the datacenters. <\/p>\n<p>in 2017 we&#8217;ve expanded this setup and added a third site &#8211; in South East Asia. it was done to bring the client-facing application servers closer to our new users in that region and decrease the UI latency. users like responsive interfaces. as with the original setup from 2012 i was reluctant at first &#8211; i expected to see plenty of inconsistencies. it did not happen &#8211; so far it worked flawlessly. here&#8217;s the new architecture:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication1.png\" alt=\"\" width=\"718\" height=\"268\" class=\"alignnone size-full wp-image-2851\" srcset=\"https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication1.png 718w, https:\/\/kudzia.eu\/b\/wp-content\/uploads\/2018\/04\/blog-mysql-replication1-300x112.png 300w\" sizes=\"auto, (max-width: 718px) 100vw, 718px\" \/><\/p>\n<p>in this setup asql0 is using <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/replication-multi-source.html\">multi-source replication<\/a> &#8211; a mechanism that is available since mysql 5.7. it fetches and replies binlogs from both bsql0 and csql0; thanks to the <i>log-slave-updates<\/i> directive it also acts as replication master for asql0b that collects all updates done to asql0, bsql0, csql0.<\/p>\n<p>configuration needed for this setup:<\/p>\n<p>asql0 &#8211; the &#8216;central&#8217; node &#8211; my.cnf:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nserver-id               = 10 # must be unique across all servers connected in the replication graph\r\nlog_bin                 = \/var\/log\/mysql\/mysql-bin.log\r\nauto_increment_increment=3 # thanks to this and one below we avoid conflicting auto-increment ids when inserts occur around the same time on different replication masters\r\nauto_increment_offset=3    # this one must be different for each master, in my case it's 3 for asql0, 4 for bsql0, 5 for csql0\r\nlog-slave-updates          # allows updates done on bsql0 to reach csql0 and asql0b\r\nbind-address=*             \r\nmaster-info-repository=TABLE # more durable way of storing the replication position\r\nrelay-log-info-repository=TABLE\r\nbinlog_format=STATEMENT   # ROW would be nice but i don't want to give up on the pt-table-checksum which requires statement based replication\r\n<\/pre>\n<p>bsql0 &#8211; my.cnf:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nserver-id               = 11\r\nlog_bin                 = \/var\/log\/mysql\/mysql-bin.log\r\nauto_increment_increment=3\r\nauto_increment_offset=4\r\nbind-address=*\r\nbinlog_format=STATEMENT\r\n<\/pre>\n<p>csql0 &#8211; my.cnf:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nserver-id               = 12\r\nlog_bin                 = \/var\/log\/mysql\/mysql-bin.log\r\nauto_increment_increment=3\r\nauto_increment_offset=5\r\nbind-address=*\r\nbinlog_format=STATEMENT\r\n<\/pre>\n<p>to connect bsql0, csql0, asql0b to the master i had to restore on them master&#8217;s backup that included binlog position and issue on then:<\/p>\n<p>find bsql0 master position &#8211; on bsql0:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nshow master status;\r\n<\/pre>\n<p>let asql0 connect to bsql0:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nchange master to master_host='bsql0',master_user='repl',master_password='repl',master_log_file='mysql-bin.000003',master_log_pos=154 for channel 'btoa';\r\n<\/pre>\n<p>similarly for csql0 &#8211; find out its current master position and let asql0 connect o it:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nchange master to master_host='csql0',master_user='repl',master_password='repl',master_log_file='mysql-bin.000003',master_log_pos=154 for channel 'ctoa';\r\n<\/pre>\n<p>then let bsql0 to replicate from asql0; bing log position taken from asql0 backup that was earlier restored on bsql0 &#8211; on bsql0:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nchange master to master_host='asql0',master_user='repl',master_password='repl',master_log_file='mysql-bin.000003',master_log_pos=154 ;\r\n<\/pre>\n<p>and on csql0 &#8211; similarly:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nchange master to master_host='asql0',master_user='repl',master_password='repl',master_log_file='mysql-bin.000003',master_log_pos=154 ;\r\n<\/pre>\n<p>if you&#8217;ve reached that far you&#8217;ve probably already realized that asql0 is a single point of failure. during the outage of asql0 transactions from bsql0 will not reach csql0 and vice-versa. we can tolerate it for a few minutes but not hours. our solution:<\/p>\n<ul>\n<li>sql table &#8216;repl_test&#8217; with two columns &#8211; hostname and timestamp. three rows in it &#8211; one for asql0, another bsql0, last &#8211; csql0<\/li>\n<li>each of the master servers updates every 3 seconds entry for its hostname with the current timestamp; each of the masters keeps local clock in sync using NTP<\/li>\n<li>a periodically run consistency script calculates difference between max and min timestamps; if it&#8217;s below a tolerance threshold &#8211; it sends &#8216;all ok&#8217; message to the site c<\/li>\n<li>if site c does not receive &#8216;all ok&#8217; message for more than 2 minutes &#8211; it shuts down the client access to it; thanks to in-built failover clients switch to site a or site b<\/li>\n<\/ul>\n<p>this simple mechanism allows us to withstand failure of site a &#8211; in the worst case all of the users will switch to the site b.<\/p>\n<p>the real setup is more complex. in addition to above there are:<\/p>\n<ul>\n<li>multiple database shards, each replicated in a similar fashion<\/li>\n<li>more read-only slaves in different locations, used for backups, <a href=\"https:\/\/kudzia.eu\/b\/2017\/12\/mysql-on-btrfs\/\">snapshots<\/a><\/li>\n<li>cron jobs frequently fetching binlogs from all master servers to central backup server<\/li>\n<li>vpns \/ ssl tunnels between the sites protecting the replication data flowing between the sites<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>at work i&#8217;m using mysql replication quite extensively. first it was a straightforward one-way replication that has been rock-solid for us since 2009. in 2012, for another type of data, we&#8217;ve started using master-master setup. initially the servers were in different European countries, eventually the secondary site was moved to North America while primary one [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,1],"tags":[12],"class_list":["post-2847","post","type-post","status-publish","format-standard","hentry","category-tech","category-uncategorized","tag-mysql"],"_links":{"self":[{"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/posts\/2847","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/comments?post=2847"}],"version-history":[{"count":12,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/posts\/2847\/revisions"}],"predecessor-version":[{"id":2861,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/posts\/2847\/revisions\/2861"}],"wp:attachment":[{"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/media?parent=2847"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/categories?post=2847"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kudzia.eu\/b\/wp-json\/wp\/v2\/tags?post=2847"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}