Skip to content
Permalink
Browse files

Implement START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT

Summary:
Provide better alternative to FLUSH TABLES WITH READ LOCK
for use to take backups. See
mysqlha.blogspot.com/2009/10/be-careful-with-flush-tables-with-read.html
for context. This diff adds START TRANSACTION WITH CONSISTENT
INNODB SNAPSHOT which starts an InnoDB transaction and returns
the master binlog filename and offset which corresponds to all
transactions visible to the InnoDB view for the transaction.
When this is used for backups, replication should be restarted
at those return values. The new START command returns 1 row with
2 columns (File, Position) on success.

In order to get the correct binlog filename and position for the
view it is required to block all commit progress, which is
accomplished by acquring all the locks which are also acquired
by MYSQL_BIN_LOG::ordered_commit.

As group commit was implemented, this could result in missing transactions:

Group commit works with 3 mutexes, one for each stage:

  Flush transaction caches to the bin log
  Sync the bin log to disk
  Commit the transactions to the storage engines (i.e., InnoDB)

Previously, each stage of group commit worked by:

  release mutex for previous stage
  acquire mutex for current stage
  do the work for the stage

The consistent snapshot code works by

  acquire all 3 group commit mutexes
  create innodb view
  get current bin log file and position
  release all 3 group commit mutexes

Missing transactions could result when:

  Group commit flushes and syncs one or more transactions to the bin log
  Group commit releases LOCK_sync
  Context switch to consistent snapshot thread
  Snapshot thread acquires all 3 group commit mutexes
  Snapshot creates InnoDB view (does not include transactions from step 1)
  Snapshot gets bin log file and position, (includes transactions from step 1)
  Snapshot releases group commit mutexes
  Group commit commits transactions to InnoDB

The fix, suggested by Santosh, was to change group commit to:

  acquire mutex for current stage
  release mutex for previous stage
  do the work for the stage

which prevents the consistent snapshot thread from acquiring all group commit
mutexes until any pending group commit is complete.

Test Plan:
Added new mtr tests:

mysqltestrun.sh --repeat=32 --parallel=16 --valgrind rpl.rpl_innodb_snapshot ...

I also plan to add a stress test, which tests this feature in
combination with binlog group commits happening concurrently, in a
follow-up diff.

Reviewers: steaphan, hfisk, santoshb

Reviewed By: steaphan, santoshb
  • Loading branch information...
jtolmer committed Mar 18, 2013
1 parent 54087fb commit c2b8ced16e255ffdbcc1e638ac8dd4f398eb1a93
@@ -676,6 +676,35 @@ void *thd_get_ha_data(const MYSQL_THD thd, const struct handlerton *hton);
*/
void thd_set_ha_data(MYSQL_THD thd, const struct handlerton *hton,
const void *ha_data);

/**
Determine if the bin log is open.
*/
char mysql_bin_log_is_open(void);

/**
Block MYSQL_BIN_LOG::ordered_commit
@details
Acquires the necessary mysql_bin_log locks in order to block commits so that
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT can return the correct
bin log filename and pos.
*/
void mysql_bin_log_lock_commits(void);

/**
Unblock MYSQL_BIN_LOG::ordered_commit
@details
Releases the mysql_bin_log locks which blocked commits so that
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT can return the correct
bin log filename and pos.
@param binlog_file the filename of the binlog
@param binlog_pos the pos in the binlog
*/
void mysql_bin_log_unlock_commits(char* binlog_file,
unsigned long long* binlog_pos);

#ifdef __cplusplus
}
#endif
@@ -250,6 +250,10 @@
void *thd_get_ha_data(const void* thd, const struct handlerton *hton);
void thd_set_ha_data(void* thd, const struct handlerton *hton,
const void *ha_data);
char mysql_bin_log_is_open(void);
void mysql_bin_log_lock_commits(void);
void mysql_bin_log_unlock_commits(char* binlog_file,
unsigned long long* binlog_pos);
struct mysql_event_general
{
unsigned int event_subclass;
@@ -250,6 +250,10 @@
void *thd_get_ha_data(const void* thd, const struct handlerton *hton);
void thd_set_ha_data(void* thd, const struct handlerton *hton,
const void *ha_data);
char mysql_bin_log_is_open(void);
void mysql_bin_log_lock_commits(void);
void mysql_bin_log_unlock_commits(char* binlog_file,
unsigned long long* binlog_pos);
#include <mysql/plugin_auth_common.h>
typedef struct st_plugin_vio_info
{
@@ -203,6 +203,10 @@
void *thd_get_ha_data(const void* thd, const struct handlerton *hton);
void thd_set_ha_data(void* thd, const struct handlerton *hton,
const void *ha_data);
char mysql_bin_log_is_open(void);
void mysql_bin_log_lock_commits(void);
void mysql_bin_log_unlock_commits(char* binlog_file,
unsigned long long* binlog_pos);
enum enum_ftparser_mode
{
MYSQL_FTPARSER_SIMPLE_MODE= 0,
@@ -0,0 +1,6 @@
SHOW MASTER STATUS;
File Position Binlog_Do_DB Binlog_Ignore_DB Executed_Gtid_Set
START TRANSACTION;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
ERROR HY000: Cannot start InnoDB transaction or binlog disabled
@@ -0,0 +1,6 @@
call mtr.add_suppression("Info table is not ready to be used.");
START TRANSACTION;
START TRANSACTION WITH CONSISTENT SNAPSHOT;
ERROR HY000: InnoDB disabled
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
ERROR 42000: Unknown storage engine 'INNODB'
@@ -0,0 +1,121 @@
include/master-slave.inc
Warnings:
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
[connection master]
DROP TABLE IF EXISTS t1;
# Establish connection con1 (user=root)
# Establish connection con2 (user=root)
# Establish connection con3 (user=root)
# Establish connection con4 (user=root)
# reset replication to guarantee that master-bin.000001 is used
STOP SLAVE;
RESET SLAVE;
RESET MASTER;
CHANGE MASTER TO master_host="127.0.0.1",master_port=MASTER_PORT,master_user="root";
Warnings:
Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
Note 1760 Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
START SLAVE;
# Switch to connection con1
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
INSERT INTO t1 VALUES(1);
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
File Position
master-bin.000001 451
# Switch to connection con2
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
# Switch to connection con1
SELECT * FROM t1;
a
1
COMMIT;
SELECT * FROM t1;
a
1
2
3
DROP TABLE t1;
# Switch to connection con1
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
INSERT INTO t1 VALUES(1);
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
File Position
master-bin.000001 1313
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
File Position
master-bin.000001 1313
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
File Position
master-bin.000001 1313
START TRANSACTION WITH CONSISTENT INNODB SNAPSHOT;
File Position
master-bin.000001 1313
# Switch to connection con2
INSERT INTO t1 VALUES(2);
INSERT INTO t1 VALUES(3);
# Switch to connection con1
SELECT * FROM t1;
a
1
SELECT * INTO OUTFILE '<MYSQLTEST_VARDIR>/tmp/rpl_innodb_snapshot.out.file' FROM t1;
COMMIT;
# Switch to slave
CREATE TABLE t1_backup AS SELECT * FROM t1;
STOP SLAVE;
RESET SLAVE;
DELETE FROM t1;
LOAD DATA INFILE '<MYSQLTEST_VARDIR>/tmp/rpl_innodb_snapshot.out.file' INTO TABLE t1;
SELECT * FROM t1;
a
1
CHANGE MASTER TO master_host="127.0.0.1",master_port=MASTER_PORT,master_user="root",master_log_file="master-bin.000001",master_log_pos=binlog_pos;
Warnings:
Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
Note 1760 Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
START SLAVE;
SELECT * FROM t1;
a
1
2
3
SELECT * FROM t1_backup;
a
1
2
3
DROP TABLE t1_backup;
DROP TABLE t1;
# Switch to connection con1
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
INSERT INTO t1 VALUES(1);
# async queries from con2
INSERT INTO t1 VALUES(2);
# async queries from con3
INSERT INTO t1 VALUES(21);
# Switch to connection con1
# Switch to connection con4
INSERT INTO t1 VALUES(9);
# Switch to connection con1
SELECT * INTO OUTFILE '<MYSQLTEST_VARDIR>/tmp/rpl_innodb_snapshot.out.file' FROM t1;
COMMIT;
# reap async statements
# Switch to slave
CREATE TABLE t1_backup AS SELECT * FROM t1;
STOP SLAVE;
RESET SLAVE;
DELETE FROM t1;
LOAD DATA INFILE '<MYSQLTEST_VARDIR>/tmp/rpl_innodb_snapshot.out.file' INTO TABLE t1;
CHANGE MASTER TO master_host="127.0.0.1",master_port=MASTER_PORT,master_user="root",master_log_file="master-bin.000001",master_log_pos=binlog_pos;
Warnings:
Note 1759 Sending passwords in plain text without SSL/TLS is extremely insecure.
Note 1760 Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
START SLAVE;
# sync and then query slave
ShouldBeZero
0
DROP TABLE t1_backup;
DROP TABLE t1;
# Switch to connection default + close connections con1 and con2
include/rpl_end.inc
@@ -0,0 +1 @@
--innodb

0 comments on commit c2b8ced

Please sign in to comment.
You can’t perform that action at this time.