Details
-
Task
-
Status: Closed (View Workflow)
-
Critical
-
Resolution: Fixed
Description
InnoDB is unnecessarily acquiring record locks when the transaction is already holding a table lock that prevents any conflicts. Starting with MySQL 4.0 or 4.1, the table locks acquired by LOCK TABLES are mapped to shared or exclusive InnoDB table locks, but InnoDB is still acquiring unnecessary record locks internally.
The attached patch is only a start. It adjusts a debug check so that exclusive or shared table locks are considered to be equivalent to exclusive or shared record locks.
Attachments
Issue Links
- relates to
-
MDEV-16675 Unnecessary explicit lock acquisition during UPDATE or DELETE
-
- Closed
-
-
MDEV-24813 Locking full table scan fails to use table-level locking
-
- In Review
-
-
MDEV-11215 Several locks taken to same record inside a transaction.
-
- Stalled
-
For the record, I tried the following patch on top of
MDEV-16675, but we did not avoid acquiring all record locks when a covering exclusive table lock should exist:diff --git a/mysql-test/suite/innodb/t/monitor.test b/mysql-test/suite/innodb/t/monitor.test
index 3535c9c85ad..475eb202be6 100644
--- a/mysql-test/suite/innodb/t/monitor.test
+++ b/mysql-test/suite/innodb/t/monitor.test
@@ -439,6 +439,20 @@ SET @end = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
= 'lock_rec_lock_created');
SELECT @end - @start;
+SET @start = @end;
+
+BEGIN;
+LOCK TABLE t1 WRITE;
+SELECT * FROM t1 FOR UPDATE;
+SELECT * FROM t1 WHERE a>=10000 FOR UPDATE;
+DELETE FROM t1;
+COMMIT;
+UNLOCK TABLES;
+
+SET @end = (SELECT COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
+= 'lock_rec_lock_created');
+SELECT @end - @start;
+
DROP TABLE t1;
--disable_warnings
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index cd65a9ac8e0..bc74261257d 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -5586,6 +5586,10 @@ lock_rec_convert_impl_to_expl(
ut_ad(page_rec_is_leaf(rec));
ut_ad(!rec_is_default_row(rec, index));
+ if (lock_table_has(caller_trx, index->table, LOCK_X)) {
+ return true;
+ }
+
if (dict_index_is_clust(index)) {
trx_id_t trx_id;
The above patch was just a quick attempt at avoiding X-lock creation in a special case. In addition to preventing X-locks from being created on records when a table X-lock exists, we should avoid creating record S-locks when either X-lock or S-lock is held on the table.