We create two connections against a MySQL server. We will call them Session Blue and Session Red (The fact that these are the colors of FC Barcelona is purely coincidental). In Session Blue we will create the database isolation and the table repeatable_read, both will be required for this test.
We start a transaction, verify current isolation level by checking the value of the variable tx_isolation, and retrieve the contents of repeatable_read table, this way we create a snapshot of that table for the whole transaction.
Now we move to Session Red and insert two rows into the table. We commit the transaction to make sure data is updated.
Next we check what data is retrieved in Session Blue.
As we can see, repeatable-read in MySQL avoids Phantom Reads, as rows are not retrieved. This is more restrictive than the standard description of the isolation level. But, what happens if we try to update the table contents? Our intuition is that we should not update any rows.
Surprise!
The update command tells us that one row matched and one row was changed. Let's select table contents to view what is happening.
We see just one row, the row that was modified by the update executed before. This is quite unexpected and counter-intuitive as the table never had one single row committed; we inserted and committed two rows. We are seeing a view of the table that never existed. As expected, when we commit, we see both rows, the one we modified and the other that was inserted before in Session Red.
Now we will begin another transaction and retrieve table contents. We retrieve table contents to create a snapshot (probably we should call it a version) of the table.
Back in Session Red, we will run a transaction to update the contents of the table. Note: Phantom reads only affect new rows, not the ones already existing.
Let's find what Session Blue retrieves and what can update.
Initially, we see the table unchanged. But no rows matched when we try to update the table using the data we retrieved in the select. We see one row with value "modified" for the text column, but the update finds no rows. When we update the table using a column value that was not modified by any transaction, in this case id, then we are able to proceed. Now we see the new value for the text column.
We will return our table to the original values and we will create an additional table required for the next test.
As usual we start a new transaction and we retrieve table contents to create the snapshot.
Now we go back to Session Red to update the contents of the whole table.
Returning to Session Blue, we "clone" the contents of the table repeatable_read to repeatable_read_copy table using an insert into ... select statement. After that we retrieve the values of both tables using a select.
The values of rows inserted into the copy table using an insert into ... as select is different than the values of rows retrieved using a regular select statement. Once we commit the transaction, as expected, we are able to see the modified data in the original table too.
After these tests, we have found about MySQL implementation of Repeatable-read isolation level:
The way MySQL implements Repeatable Read is not intuitive and, although it is required to support statement replication, can lead to some problems while running data modification and transfer to other tables in concurrent transactions. If your application can face these issues, you will need to modify your queries using select ... for update statements and thus increase the number of locks in the database.
Looking to optimize your MySQL use?