3. Semisynchronous Replication
Google has an extensive set of patches for MySQL and InnoDB to tailor the server and the storage engine. One
of the patches that is available for MySQL version 5.0 is the
semisynchronous replication patch. MySQL has since reworked the patch
and released it with MySQL 5.5.
The idea behind semisynchronous replication is to ensure the
changes are written to disk on at least one slave before allowing
execution to continue. This means that for each connection, at most one
transaction can be lost due to a master crash.
It is important to understand that the semisynchronous replication
patch does not hold off commits of the transaction; it just avoids sending a reply back to the
client until the transaction has been written to the relay log of at
least one slave. Figure 7 shows the order of
the calls when committing a transaction. As you can see, the transaction
is committed to the storage engine before the transaction is sent to the
slave, but the return from the client’s commit call occurs after the
slave has acknowledged that the transaction is in durable
storage.
This means that for each connection, it is possible to lose a
transaction if a crash occurs after the transaction has been committed
to the storage engine but before the transaction has been sent to the
slave. However, since the acknowledgment of the transaction goes to the
client after the slave has acknowledged that it has the transaction, at
most one transaction can be lost.
This usually means that, at most, one transaction can be lost per
client, but if the client has multiple connections active with the
master at the same time, it can lose one transaction for each connection
if the client is committing multiple transactions at the same time and
the server crashes.
3.1. Configuring semisynchronous replication
To use semisynchronous replication, both the master and the slave need to
support it, so both the master and the slave have to be running MySQL
version 5.5 or later and have semisynchronous replication enabled. If
either the master or the slave does not support semisynchronous
replication, it will not be used, but replication works as usual,
meaning that more than one transaction can be lost unless special
precautions are taken to ensure each transaction reaches the slave
before a new transaction is started.
Use the following steps to enable semisynchronous
replication:
Install the master plug-in on the master:
master> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Install the slave plug-in on each slave:
slave> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Once you have installed the plug-ins, enable them on the
master and the slave. This is controlled through two server
variables that are also available as options, so to ensure that
the settings take effect even after restart, it is best to bring
down the server and add the options to the my.cnf file of the master:
[mysqld]
rpl-semi-sync-master-enabled = 1
and to the slave:
[mysqld]
rpl-semi-sync-slave-enabled = 1
If you followed the instructions just given, you now have a
semisynchronous replication setup and can test it, but consider these
cases:
What happens if all slaves crash—which is not unlikely if
you have only a single server—and therefore no slave acknowledges
that it has stored the transaction to the relay log?
What happens if all slaves disconnect gracefully? In this
case, the master has no slave to which the transaction can be sent
for safekeeping.
In addition to rpl-semi-sync-master-enabled and rpl-semi-sync-slave-enabled, there are two
options that you can use to handle the situations given above:
rpl-semi-sync-master-timeout= milliseconds
To prevent semisynchronous replication from blocking if it
does not receive an acknowledgment, it is possible to set a
timeout using the rpl-semi-sync-master-timeout=milliseconds
option.
If the master does not receive any acknowledgment before
the timeout expires, it will revert to normal asynchronous
replication and continue operating without semisynchronous
replication.
This option is also available as a server variable and can
be set without bringing the server down. Note, however, that as
with every server variable, the value will not be saved between
restarts.
rpl-semi-sync-master-wait-no-slave= {ON|OFF}
If a transaction is committed but the master does not
have any slaves connected, it is not possible for the master to
send the transaction anywhere for safekeeping. By default, the
master will then wait for a slave to connect—as long as it is
within the timeout limit—and acknowledge that the transaction
has been properly written to disk.
You can use the rpl-semi-sync-master-wait-no-slave={ON|OFF}
option to turn off this behavior, in which case the master
reverts to asynchronous replication if there are no connected
slaves.
3.2. Monitoring semisynchronous replication
Both plug-ins install a number of status variables that allow you to monitor
semisynchronous replication.
rpl_semi_sync_master_clients
This status variable reports the number of connected slaves that
support and have registered for semisynchronous
replication.
rpl_semi_sync_master_status
The status of semisynchronous replication on the master is 1 if
it is active, and 0 if it is inactive—either because it has not
been enabled or because it was enabled but has reverted to
asynchronous replication.
rpl_semi_sync_slave_status
The status of semisynchronous replication on the slave is 1
if active—that is, it has been enabled and the I/O thread is
running—and 0 if it is inactive.
You can read the values of these variables either using
the SHOW STATUS command
or through the information schema table GLOBAL_STATUS. If you want to use the values
for other purposes, the SHOW STATUS
command is hard to use and a query as shown in Example 5 uses SELECT on the
information schema to extract the value and store it in a user-defined
variable.
Example 5. Retrieving values using the information schema
master> SELECT Variable_value INTO @value
-> FROM INFORMATION_SCHEMA.GLOBAL_STATUS
-> WHERE Variable_name = 'Rpl_semi_sync_master_status';
Query OK, 1 row affected (0.00 sec)