I’m lazy when I’m speakin’
I’m lazy when I walk
I’m lazy when I’m dancin’
I’m lazy when I talk

 

X-Press 2 Feat. David Byrne – Lazy

While preparing a blog post to compare how PostgreSQL and MySQL handle locks, as part of a series covering the different approaches to MVCC for these databases, I found an interesting behavior regarding no-op updates. While it is not a common pattern to issue no-op updates, application logic that is not fully under the developers’ control often leads to such situations. Understanding how different databases handle these scenarios provides insight into their internal mechanisms and performance characteristics.

And what is a no-op update? A no-op update in SQL is an update operation that doesn’t actually change any data: an UPDATE statement that sets columns to their current values, resulting in no actual changes to the values stored in the database.

Update in PostgreSQL

We create the table and insert a row.

As expected, the table has one live row and no dead rows. And yes, the database name is Batman:

Now we perform a no-op update, setting the sample_text column to the same value it already has.

The client reports that one row was updated. But what is the internal state of the table?

The result is interesting: PostgreSQL creates a new version of the row, marking the previous version as dead, even though the data hasn’t changed. This behavior is due to PostgreSQL’s MVCC implementation, which treats any update as a modification that requires a new row version. If we repeat the no-op update, the dead row count increases again.

Update in MySQL

Let’s start by creating the same table and inserting a row.

Unfortunately, there is no straightforward way to check the number of rows in the table and in the rollback segments in InnoDB. However, we can monitor the number of undo log pages written to disk, which gives us an idea of whether any changes were made to the data. Obviously, this will not work if there is activity from other sessions or if we don’t have the corresponding metrics enabled. Therefore, ensure that you run this in isolation and with metrics enabled. There are other methods that could be used, for example, executing FLUSH TABLES and checking if the corresponding InnoDB file has changed, but this one is simple enough for our purposes.

First, we reset the buffer page metrics, buffer_page_written_undo_log belongs to this module.

As expected, the metric shows zero pages written to the undo log. Now we perform a no-op update, setting the sample_text column to its current value.

We see that MySQL tells us that one row matched, but no rows were modified. And now we recheck the metric.

And no pages were written to the undo log, indicating that no changes were made to the data. Now we will run the update, but we will actually change one row.

As we can see now, two pages were written to the undo log, indicating that changes were made to the data.

Bonus track

If the no-op update in MySQL does not modify any data, does it lock the row? Let’s find out by opening two sessions. In the first session, we start a transaction and perform the no-op update.

In the second session, we attempt to update the same row.

So, even though the no-op update did not modify any data, it still acquired a lock on the row, preventing other transactions from modifying it until the transaction is committed or rolled back. And how does this happen? Well, you’ll have to wait till I finish the blog post I mentioned at the beginning!

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments