MySQL Tools Comparison.

  • Increase font size
  • Default font size
  • Decrease font size
Home Managing Transactions in MySQL
Managing Transactions in MySQL
In this lesson of the MySQL tutorial, you will learn...
  1. To understand Transaction Control in MySQL.
  2. To understand data problems.
  3. To use savepoints, commit and rollback.

Managing Transactions

Print PDF

We have so far interactively executed several SQL statements that have performed various actions in your MySQL database. The statements were run in an isolated environment one statement at a time, with no concurrency or contention for data access with other users.

In the real world, numerous applications and users attempt to access and manipulate data at the same time. MySQL supports the use of transactions to ensure safe data access by multiple users.

This lesson explains use of transactions to manage the concurrent execution of SQL statements.

  • Understanding what a transaction is and the characteristics of a transaction
  • Performing basic transactions and adding savepoints to your transactions
  • Setting the autocommit mode for your session and setting the transaction isolation levels
  • Locking and unlocking nontransactional tables

Introducing Transactions

In almost all applications that access MySQL databases, multiple users concurrently attempt to view and modify data. The simultaneous operations may result in data that is inconsistent and inaccurate. Using transactions avoid these problems by isolating each operation.

In the context of SQL and relational databases, a transaction is a set of one or more SQL statements that perform a set of related actions. The statements are grouped together and treated as a single unit whose success or failure depends on the successful execution of each statement in the transaction.

In addition to ensuring the proper execution of a set of SQL statements, a transaction locks the tables involved in the transaction so that other users cannot modify any rows that are involved in the transaction.

Working Without Transactions

For example, suppose you have an application that does not use transactions, and two users attempt to rent the same film, but there is only one in stock. When the first user initiates the rental, an INSERT statement is issued against the rental table. The application then experiences a delay before trying to issue an UPDATE statement against the inventory table to mark the film rented.

During the delay between the INSERT statement and the UPDATE statement, a second user initiates a rental for the same film copy. As a result, a second INSERT statement is issued against the rental table for that film, and a second UPDATE statement is issued against the inventory table, marking that film as rented before the first user completes the renting process.

The first user then attempts to complete the order process by issuing an UPDATE statement against the inventory table, which may behave strange depending on how the inventory table and the application is designed.

If the application were to use transactions, the film inventory involved in the first transaction would have been locked, so the second transaction could not have been initiated until the first transaction had completed. As a result, the second user may not have seen the film as being available.

Transactional Databases

In order for an operation in a relational database to qualify as a transaction, it must pass what is referred to as the ACID (Atomic, Consistent, Isolated, and Durable) test:

  • Atomic: The statements in a transaction are treated as a single unit. Either all the designated operations are performed, or none are performed. Only when all operations are performed successfully are the results of that transaction applied to the database.
  • Consistent: Each transaction must leave the database in a consistent state at the end of the transaction. If only some of the statements are executed and other fail, the database may wind up in an inconsistent state.
  • Isolated: A transaction must be isolated from other transactions and not be affected by them. Users outside the transaction should not be affected by data that becomes temporarily inconsistent during the life of the transaction.
  • Durable: Once any changes that result from a transaction are committed to the database, those changes must be preserved permanently in the database, despite any errors encountered.

Transactions in MySQL are supported for only InnoDB and BDB table types. All other MySQL table types are nontransactional.

Performing a Basic Transaction

In a basic transaction, SQL statements are executed as a unit. If one of the statements fails, the entire transaction should be rolled back; If they succeed, any changes made by the statements are permanently saved to the database.

MySQL provides the START TRANSACTION to create a transaction, and COMMIT, ROLLBACK statements to end it. The COMMIT statement saves changes to the database, and the ROLLBACK statement will undo any changes made during the transaction and database is reverted to the pre-transaction state.

Usually, these three transaction-control statements are used in programmed applications which are able to conditionally check if an SQL statement returns a certain result. The figure below provides an overview of the logic used to create a basic transaction. The transaction-related statements are written in bold:



The START TRANSACTION statement requires no clauses or options:


START TRANSACTION statement notifies MySQL that the statements that follow should be treated as a unit, until the transaction ends, successfully or otherwise.

Note: A BEGIN statement can also be used to start a transaction.

The COMMIT Statement

The COMMIT statement is used to terminate a transaction and to save all changes made by the transaction to the database. There are no additional clauses or options:


The following transaction is made of two INSERT statements, followed by a COMMIT statement:

INSERT INTO Studio VALUES (101, 'MGM Studios');
INSERT INTO Studio VALUES (102, 'Wannabe Studios');

The START TRANSACTION initiates the transaction. If both INSERT statements are successfully completed, the COMMIT statement saves the changes to the database.

The ROLLBACK Statement

The ROLLBACK statement aborts any changes made in the transaction and does not save any of the changes to the database:


The following transaction uses a ROLLBACK statement to undo the UPDATE statements that are part of the transaction:

UPDATE Studio SET studio_name = 'Temporary Studios' WHERE studio_id = 101;
UPDATE Studio SET studio_name = 'Studio with no buildings' WHERE studio_id = 102;

To confirm the operation, a SELECT on Studio executed as part of the transaction shows updated data. After the ROLLBACK, the SELECT shows original (unchanged) data.

Note: If a transaction ends before it is explicitly terminated - the connection is ended or if there is a hardware or software failure, the transaction is rolled back automatically.

Adding Savepoints to Your Transaction

The SAVEPOINT and ROLLBACK TO SAVEPOINT statements isolate portions of a transaction. The SAVEPOINT statement defines a marker in a transaction, and the ROLLBACK TO SAVEPOINT statement allows you to roll back a transaction to a pre- determined marker (savepoint).

In figure below, you can see an example of how a savepoint can be added to a transaction. As with any transaction, you should use the START TRANSACTION statement to start the statement, the ROLLBACK statement to terminate the transaction and roll back the database to its original state, and the COMMIT statement to commit changes to the database: Savepoint

The picture above demonstrates the savepoint process. The SAVEPOINT statement is added to the transaction after the first two INSERT statements. If the statements are executed successfully, the savepoint is defined. If the statements fail, the database is rolled back to its original statement. Once the savepoint is defined, two more INSERT statements are executed. If the statements are executed successfully, the changes are committed to the database. If either of the INSERT statements fails, however, the database is rolled back to the savepoint, undoing the changes made by the second set of INSERT statements, but preserving the changes made by the first two INSERT statements. Any changes made before the savepoint are saved. Now take a closer look at the SAVEPOINT and ROLLBACK TO SAVEPOINT statements.

The SAVEPOINT Statement

You can add a savepoint anywhere in a transaction. When you roll back to that savepoint, any changes made to the database after the savepoint are discarded, and any changes made prior to the savepoint are saved. To create a savepoint, you must use the SAVEPOINT statement, which is shown in the following syntax:

SAVEPOINT <savepoint-name>

In the transaction below, a SAVEPOINT statement is used to define a savepoint with the name savepoint1. That savepoint then becomes a permanent bookmark in the transaction.

INSERT INTO Studio VALUES (103, 'Hell\'s Angels Horror Shows');
INSERT INTO Studio VALUES (104, 'Black Dog Entertainment');
SAVEPOINT savepoint1;


In order to use a savepoint, it must be used along with with one or more ROLLBACK TO SAVEPOINT statements. When using a ROLLBACK TO SAVEPOINT statement, you must provide a save-point name, as shown in the following syntax:

ROLLBACK TO SAVEPOINT <savepoint name>

In this transaction, a SAVEPOINT named savepoint1 is defined after two INSERT statements. We have an additional two INSERT statements, a ROLLBACK TO SAVEPOINT statement is used to roll back the transaction to savepoint savepoint1. Following the ROLLBACK TO SAVEPOINT statement, two more INSERT statements are included in the transaction, followed by a COMMIT statement. If you were now to use a SELECT statement to view the contents of the film table, you would see a result set similar to the following:

INSERT INTO Studio VALUES (105, 'Noncomformant Studios');
INSERT INTO Studio VALUES (106, 'Studio Cartel');


INSERT INTO Studio VALUES (105, 'Moneymaking Studios');
INSERT INTO Studio VALUES (106, 'Studio Mob');


Data Problems in a Transaction

As you saw in the syntax for the SET TRANSACTION statement, you must specify one of the four isolation levels for the statement to be complete. To understand the differences among the isolation levels, though, you should first have an overview of some of the problems that can arise in a transaction, depending on how isolated that transaction is from other transactions. Depending on the level of isolation, you can experience one of several problems when multiple transactions are initiated at the same time. These problems include dirty reads, nonrepeatable reads, and phantom reads.

Dirty Reads

A dirty read can take place when:

  • Transaction A modifies data in a table.
  • Around the same time, another Transaction B reads the table, before those modifications are committed to the database.
  • Transaction A rolls back (cancels) the changes, returning the database to its original state.
  • Transaction B now has data inconsistent with the database.
  • Worse, Transaction B may modify the data based on its initial read, which is incorrect or dirty read.

You will need to be running these three scripts in two separate sessions, as discussed before.

UPDATE film_category SET category_id = 3 WHERE film_id = 998;
Code Explanation

In session one, update the category of a Horror film "ZHIVAGO CORE" to "Children".

SELECT * FROM film f, film_category cat
WHERE f.film_id = cat.film_id
AND cat.category_id = 3;
Code Explanation

In session two, SELECT children movies, also change the default isolation level to READ UNCOMMITTED.

Rollback the UPDATE in session one.

SELECT * FROM film WHERE film_id = 998;
Code Explanation

Rollback the UPDATE in session one.

The rental agency is likely to get a bad rap if a customer in Session two lands up ordering that Horror movie for their children on a friday night !

Here is a diagram to show the progression of two transactions to a dirty read.

Dirty Read Flow

Nonrepeatable Reads

Concurrent transactions can also encounter a nonrepeatable read that can occur as shown:

  • Transaction A reads from a table.
  • Then Transaction B updates the table.
  • Transaction A tries to read from the table again after the update.
  • Transaction A sees data different from what was originally viewed.

You will need to be running these scripts in two separate sessions, as discussed before.

SELECT i.inventory_id, r.rental_id, f.title, r.rental_date, return_date
FROM inventory i JOIN film f USING (film_id) LEFT JOIN rental r USING (inventory_id)
WHERE f.film_id = 998
AND ((r.rental_date = (SELECT MAX(r2.rental_date) FROM rental r2
 WHERE r2.inventory_id = i.inventory_id)
AND r.return_date IS NOT NULL)
OR r.rental_date IS NULL);
Code Explanation

This script is executed in both sessions.

SELECT available inventory level for the same movie "ZHIVAGO CORE", you should see two available.

--DELETE FROM rental WHERE inventory_id = 4568 AND return_date IS NULL;
INSERT INTO rental SET rental_date = now(), inventory_id = 4567, return_date = NULL,
customer_id = 101, staff_id = 1;
INSERT INTO rental SET rental_date = now(), inventory_id = 4568, return_date = NULL,
customer_id = 102, staff_id = 1;
Code Explanation

In session one, add two rentals for both copies of film "ZHIVAGO CORE", marking the film as not available in the inventory.

In session two, SELECT available inventory level for the same movie again, the result is an Empty Set. If customer was expecting to rent the available copies, the customer is in for an unpleasant surprise !


Phantom Reads

A phantom read occurs when a user sees data in transaction B that where inserted in transaction A. Subsequently, transaction A may rollback thereby not saving the new rows to the database. User in transaction B is now looking at rows that do not exist in the database.

You will need to be running these scripts in three separate sessions this time.

INSERT INTO Studio VALUES (105, 'Noncomformant Studios');
INSERT INTO Studio VALUES (106, 'Studio Cartel');
Code Explanation

In session one, add two new studios.

SELECT * FROM studio;
Code Explanation

In session two, change the isolation level and do a Select from studio.

SELECT * FROM studio;
Code Explanation

In session one, rollback the Inserts in session one and issue a SELECT to verify.

The user in session two is seeing rows that do not exist anymore or are Phantom. In this situation, user may be very pleased to see one's favorite studio is part of the rental, while those studio relationships did not actually materialize.

SELECT * FROM studio;
Code Explanation

In a separate session three, change the isolation level and do a Select from studio.

You will not see the newly added studios after the rollback.

Here is another pictorial example of a phantom read: A different set of values has been returned, which indicates that a phantom read has occurred: PhantomRead

Transaction Isolation Levels

We can set transaction isolation levels that determine which anomalies are prevented. Use the SET TRANSACTION statement allows you to specify one of four transaction isolation levels.

The following table summarizes which data anomalies are permitted by which isolation levels.

Isolation level Description Dirty reads Nonrepeatable reads Phantom reads
READ UNCOMMITTED Least restrictive. Use for read only situations or where data exactness is not important, such as statistical data. Yes Yes Yes
READ COMMITTED Prevents dirty reads No Yes Yes
REPEATABLE READ default transaction isolation level for InnoDB tables No No Yes
SERIALIZABLE Most restrictive. Transactions are fully isolated from one another and are processed sequentially. No No No

Note: To determine which isolation level to use, you must balance the need to ensure the accuracy of the data retrieved by a transaction against the trade-offs in performance. The more restrictive the isolation level, the greater the impact on performance. For data whose precision is always critical, such as in financial transactions, you should use the SERIALIZABLE isolation level.

Setting the Isolation Level

We can set the default isolation level for all connections by using the --transaction-isolation option on the command line or in an option file.

transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED

We can also use the SET TRANSACTION statement from within a mysql session to set the isolation level as needed as shown:


If no scope is specified, the default behavior is to set the isolation level for the next transaction that starts in the current session. The GLOBAL keyword will cause the default transaction level to be set globally for all new connections created after but not for any existing connections. This change will require the SUPER privilege. Using the SESSION keyword sets the default transaction level for all future transactions performed on the current connection.

You can view the current setting for the global transaction isolation level by using the @@global.tx_isolation system variable, as shown in the following SELECT statement:

Locking Nontransactional Tables

MySQL supports the use of transactions only for InnoDB and BDB tables. There might be times, though, when you want to lock other types of tables that are included in your database. By locking nontransactional tables manually, you can group SQL statements together and set up a transaction-like operation in order to prevent anyone from changing the tables that participate in your operation. To lock a nontransactional table, you must use the LOCK TABLES statement. Once you've completed updating the tables, you should use the UNLOCK TABLES statement to release them. In this section, you learn how to both lock and unlock tables.

The LOCK TABLES Statement

To lock a table in a MySQL database, you should use the LOCK TABLES statement, as shown in the following syntax:

<table name> [AS <alias>] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[{, <table name> [AS <alias>] {READ [LOCAL] | [LOW_PRIORITY] WRITE}}...]

To use the statement, you must specify the LOCK keyword, the TABLE or TABLES keyword followed by one or more table names, and the type of lock for each table.

For each table that you specify, you have the option of providing an alias for the table. In addition, you must specify a READ or a WRITE lock type. If you specify READ, any connection can read from the table, but no connection can write to the table. If you specify READ LOCAL, nonconflicting INSERT statements can be executed by any connection. If you specify WRITE, the current connection can read or write to the table, but no other connections can access the table until the lock has been removed. If LOW_PRIORITY WRITE lock is chosen, other connections can get READ locks while the current session is waiting for the WRITE lock.

Note:Once you lock a table, it remains locked until you explicitly unlock the table with the UNLOCK TABLES statement or end your current session.

The following statement places a lock on the category table:


With this lock, only read access is available to all connections on category table. The following LOCK TABLES statement places locks on the film and the rental tables:


In this case, a READ lock is placed on the film table, and a WRITE lock is placed on the rental table. As a result, other connections can read from the film table, but they will wait to access the rental table.


Once done with a locked table, we should explicitly unlock the table or end your current session. To unlock one or more locked tables, you must use the UNLOCK TABLES statement, shown in the following syntax:


There is no need to specify any tables with UNLOCK TABLES statement, as this statement will unlock all locked tables in the current session.


In session one, create a new table and lock it with READ. By default, the table will be created as MyISAM. We will also issue an INSERT which will fail due to the READ lock, though the statement has been issued in the same session with the lock. Due to the READ lock, sessions can only read from table1, but no session can update the table. In INSERT statement we issued, we received an error indicating that we could not update this table due to a READ lock.


In session two, we will simply read from this table, which is expected to go through, but will return an empty set, as the Insert had failed.

SELECT * FROM table1;

Continuing in session one, we will change the lock to write and successfully insert a row.


In session two, we will try to SELECT again using query above, which will now block due to the write lock.

In session one, we will switch back the lock to read. The Select in session two will now go through.

--DROP TABLE table1;

In session two, we will now try to insert a row, which will now wait for the table to be unlocked.

SELECT * FROM table1;

In session one, we will unlock the table. The insert in session two will now go through and SELECT will show both the rows.

--DROP TABLE table1;

Note: Changing the LOCK to READ may cause the session two to complete. Simply run the Lock-Tables-2-Continue.sql script above again.

--DROP TABLE table1;

The insert will now complete and SELECT will show the appropriate results.

Note: The session one with the lock can drop the table, even with just a READ lock. The other sessions will have to wait for the table to be unlocked before dropping the table locked in session one.

Setting the Autocommit Mode

By default, MySQL begins each client connection with autocommit mode enabled. When autocommit is enabled, MySQL does a commit after each SQL statement if that statement did not return an error. If an SQL statement returns an error, the commit or rollback behavior depends on the error. Some of the error handling is covered under "Table Types" lesson.

If you have the autocommit mode off and close a connection without explicitly committing the final transaction, MySQL rolls back that transaction. To prevent individual statements from committing automatically, the autocommit mode must be set to off.

Warning: The autocommit mode applies only to transactional tables. All statement that are issued against a non-transactional table are committed automatically, and you cannot override that behavior.

If the autocommit mode is set to off, all statements that follow are considered part of a transaction. You must then manually commit your statements by using the COMMIT statement or roll back your statements by using the ROLLBACK statement. If you fail to commit your statements before you end the session, they are automatically rolled back and you lose your changes.

To set the autocommit mode, you must use a SET statement along with the AUTOCOMMIT argument. The following SET statement sets the autocommit mode to off:


Once you execute this statement, all statements that follow must be explicitly committed to the database. If you fail to commit them before ending a session, the statements will be rolled back.

To set the mode to on, you should use the following SET statement:


When you set the autocommit mode to on, all statements that precede the SET clause are committed to the database, as if you had executed a COMMIT statement, and individual statements that follow are each committed automatically.

Warning: If the autocommit mode has been set to off in a session and you end that session, the autocommit mode is automatically set to on when you start a new session.

In addition to allowing you to set the autocommit mode, MySQL provides the @@autocommit system variable that allows you to view the current autocommit mode setting. To use the system variable, simply include it in a SELECT statement, as shown in the following statement:

SELECT @@autocommit;

When you execute this statement, MySQL returns the current autocommit mode. For example, if your autocommit mode is set to on, you receive results similar to the following:

| @@autocommit |
| 1 |
1 row in set (0.00 sec)

If the autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts new connections with autocommit enabled.

If the connection has autocommit enabled, the user can still perform a multiple-statement transaction by starting it with an explicit START TRANSACTION or BEGIN statement and ending it with COMMIT or ROLLBACK.

Statements that Automatically Commit Transactions

MySQL includes a number of statements that should not be included in a transaction. If you issue one of these statements in a transaction, the statement automatically or implicitly commits that transaction; then the statement itself is executed. The following list provides a brief description of the statements that should not be included in an expression:

  • ALTER TABLE: Modifies a table definition.
  • CREATE INDEX: Creates an index on a table.
  • DROP DATABASE: Removes a database from a MySQL server.
  • DROP INDEX: Removes an index on a table.
  • DROP TABLE: Removes a table from a database.
  • LOCK TABLES: Prevents concurrent access to tables.
  • RENAME TABLES: Renames a table.
  • SET AUTOCOMMIT=1: Sets the autocommit mode to on.
  • START TRANSACTION: Begins a transaction.
  • TRUNCATE TABLE: Removes data from a table.
  • UNLOCK TABLES: Unlocks locked tables.

Issuing one of these statements has the same effect as issuing a COMMIT statement: The transaction is terminated, and all applicable changes are saved to the database. Only then is the statement executed. In addition, none of these statements can be rolled back.


Performing a read in share mode means that we read the latest available data, and set a shared mode lock on the rows we read. A shared mode lock prevents others from updating or deleting the row we have read. Also, if the latest data belongs to a yet uncommitted transaction of another client connection, we wait until that transaction commits.

In this demo, we will lock data in shared mode, to prevent external updates to a table till the transaction completes.

Select a row from studio in lock mode.

SELECT * FROM studio WHERE studio_id = 201 LOCK IN SHARE MODE;

In session two, we will update two rows. The first update will go through as it is not locked, but the second one will block.

SELECT * FROM studio;
UPDATE studio SET studio_name = 'Oddly Placed Studio' WHERE studio_id = 103;
SELECT * FROM studio;
UPDATE studio SET studio_name = 'Nasty Studio XYZABC' WHERE studio_id = 201;

Now release locks via a rollback or a commit.

SELECT * FROM studio;

session two's updates will now go through and you will see latest changes reflected on the table.

This strategy is effective when we want to add a new row into a child table while ensuring that the child has a parent in table parent for referential integrity. If we simply use a consistent read for parent table, some other user may delete the parent row from the table.

The solution is to perform the SELECT in a locking mode using LOCK IN SHARE MODE:

SELECT * FROM category WHERE name = 'Horror' LOCK IN SHARE MODE;

Locking via SELECT ... FOR UPDATE

In another case, we have an integer counter field in a table category_count that we use to maintain film counts for each category in category table. Using a consistent read or a shared mode read to read the present value of the counter are not the best options here because two users of the database may see the same value for the counter, causing incorrect updates.

Alternately, LOCK IN SHARE MODE is not a good solution because if two users read (lock) the counter at the same time, at least one of them ends up in deadlock when attempting to update the counter.

A good approach here is to read the counter first with a FOR UPDATE lock and increment the counter as shown:

Create a table to maintain category counters.


DROP TABLE category_count;
CREATE TABLE category_count(category_id INT NOT NULL PRIMARY KEY, category_counter
INSERT INTO category_count VALUES(1,0);
INSERT INTO category_count VALUES(2,0);

SELECT category_id,category_counter FROM category_count
 WHERE category_id = 2 FOR UPDATE;

In session two, try to select that counter for update. A normal select (without update lock) will go through showing pre-committed value.

SELECT category_id,category_counter FROM category_count
 WHERE category_id = 2;
SELECT category_counter FROM category_count
 WHERE category_id = 2 FOR UPDATE;
UPDATE category_count SET category_counter = category_counter + 1
WHERE category_id = 2;
SELECT * FROM category_count;

Now update the counter commit changes in session one.

UPDATE category_count SET category_counter = category_counter + 1
 WHERE category_id = 2;
SELECT * FROM category_count;
SELECT * FROM category_count;

session two will now be able to obtain the lock, complete its own update and read the counters, correctly showing the counter to be 2.

A SELECT ... FOR UPDATE reads the latest available data, setting exclusive locks on each row it reads. Thus, it sets the same locks a searched SQL UPDATE would set on the rows.

In both locking read cases above, locks set by IN SHARE MODE and FOR UPDATE reads are released when the transaction is committed or rolled back.

Warning: Locking of rows for update using SELECT FOR UPDATE only applies when AUTOCOMMIT is switched off. If AUTOCOMMIT is on, then the rows matching the specification are not locked.

Managing Transactions in MySQL Conclusion

Transactions effectively ensure the safe execution of your SQL statements towards integrity of your data. Transactions allow multiple statements to be treated as a unit so that data in the process of being modified is isolated in such a way as to prevent data inconsistencies and inaccuracies. In this lesson, you learned how to use transactions to control how statements are executed in order to protect your data. We also learned how to lock and unlock nontransactional tables. Specifically, the lesson described how to use the following SQL statements to perform transactions and lock tables:

  • The START TRANSACTIONS statement, in order to begin a transaction
  • The COMMIT and ROLLBACK statements, in order to end a transaction
  • The SAVEPOINT and ROLLBACK TO SAVEPOINT statements, in order to use savepoints in your transactions
  • The SET AUTOCOMMIT statement, in order to set the autocommit mode
  • The SET TRANSACTION statement, in order to set the transaction isolation levels
  • The LOCK TABLES and UNLOCK TABLES statements, in order lock nontransactional tables
To continue to learn MySQL go to the top of this page and click on the next lesson in this MySQL Tutorial's Table of Contents.