I think I am confused with the SELECT FOR UPDATE construct.
Example:
mysql> select * from employees2;
+-------+----------+--------+-----------+
| EmpId | EmpName | DeptId | EmpSalary |
+-------+----------+--------+-----------+
| 1 | John | 1 | 5000.00 |
| 2 | Albert | 1 | 4500.00 |
| 3 | Crain | 2 | 6000.00 |
| 4 | Micheal | 2 | 5000.00 |
| 5 | David | NULL | 34.00 |
| 6 | Kelly | NULL | 457.00 |
| 7 | Rudy | 1 | 879.00 |
| 8 | Smith | 2 | 7878.00 |
| 9 | Karsen | 5 | 878.00 |
| 10 | Stringer | 5 | 345.00 |
| 11 | Cheryl | NULL | NULL |
+-------+----------+--------+-----------+
11 rows in set (0.00 sec)
I do the following in a script:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect('dbi:mysql:testdb','root','1234', {'RaiseError' => 1, 'AutoCommit' => 0}) or die "Connection Error: $DBI::errstr\n";
my $sql = "select * from employees2 where EmpId IN (2,10) for update";
my $sth = $dbh->prepare($sql);
$sth->execute or die "SQL Error: $DBI::errstr\n";
while (my @row = $sth->fetchrow_array) {
print "@row\n";
}
sleep(9000);
$dbh->commit;
I also in parallel a console and connect to the database.
So I run the script first and then in another session I do:
mysql> select * from employees2 where EmpId IN (10) for update;
The second select blocks as it refers to the same row.
This blocks either I do:
mysql> set autocommit = 0;
mysql> begin;
mysql> select * from employees2 where EmpId IN (10) for update;
mysql> commit;
or just
mysql> select * from employees2 where EmpId IN (10) for update;
So it blocks irrelevant if it is in a transaction or not.
Now if I change the script as:
my $dbh = DBI->connect('dbi:mysql:practice','root','') or die "Connection Error: $DBI::errstr\n";
I.e the script does not run within a transaction the second session does not block!
Why does it block only if the script runs within a transaction?
The SELECT FOR UPDATE statement is used to order transactions by controlling concurrent access to one or more rows of a table. It works by locking the rows returned by a selection query, such that other transactions trying to access those rows are forced to wait for the transaction that locked the rows to finish.
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.
FOR UPDATE with INSERT will work. On the default isolation level, SELECT ... FOR UPDATE on a non-existent record does not block other transactions.
The select ... for update acquires a ROW SHARE LOCK on a table. This lock conflicts with the EXCLUSIVE lock needed for an update statement, and prevents any changes that could happen concurrently. All the locks will be released when the transaction ends.
According to the documentation:
Locking of rows for update using
SELECT FOR UPDATE
only applies when autocommit is disabled (either by beginning transaction withSTART TRANSACTION
or by setting autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked.
In other words, if you don't execute your first SELECT FOR UPDATE
inside a transaction, no rows are locked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With