[MDEV-11215] Several locks taken to same record inside a transaction. Created: 2016-11-02 Updated: 2020-02-17 |
|
| Status: | Stalled |
| Project: | MariaDB Server |
| Component/s: | Storage Engine - InnoDB, Storage Engine - XtraDB |
| Fix Version/s: | None |
| Type: | Task | Priority: | Major |
| Reporter: | Jan Lindström (Inactive) | Assignee: | Marko Mäkelä |
| Resolution: | Unresolved | Votes: | 1 |
| Labels: | upstream | ||
| Issue Links: |
|
||||||||||||||||||||||||||||||||||||
| Sprint: | 5.5.55 | ||||||||||||||||||||||||||||||||||||
| Description |
|
In the output you'll see:
Normal and expected so far, record X locks on the unique index and primary key, we deleted and could delete one and only one row. Now, repeat the same delete in the same transaction:
and you'll see this beautiful set of locks:
Suggested fix: Alternatively, if this is just the way locks are reported, please, fix it so the output makes sense. In any case, please, document in the manual what locks this kind of DELETE sets when it encountered a record already marked as deleted, and why. |
| Comments |
| Comment by Jan Lindström (Inactive) [ 2016-11-02 ] | |||||||||||||||||||||||||||||||||||||||
| Comment by Jan Lindström (Inactive) [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
Let's start from transaction:
Last line will output (if lock monitor is on) following :
Lets investigate taken locks one by one:
Now there will be no more lock records even if you add more delete-clauses to open transaction. Now first and second lock are trivially necessary and these are taken by the first delete. Rest of the locks are taken the second delete-clause. Let's first assume that second delete-clause would be from different transaction. Finally, third lock is needed because we need to serialize after transaction marked the index-record to be deleted so we need to wait until it is committed. Now, lets get back to original case where both delete-clauses are in the same transaction. Firstly, as secondary indexes do not contain TRX_ID system field we do not easily know which transaction has delete marked the secondary index record. Secondly, requested lock modes are either stronger (X-lock > X-lock rec not gap) or on different record (X-lock heap 3, gap lock heap 4). Can we do better, absolutely we could add TRX_ID system field, note that we have already taken necessary index-record locks in the same transaction and not take additional locks. However, this is not feasible on GA-release. Additionally, we could investigate at second delete is there a possibility to insert a record that would cause result set to change, that would naturally decrease the performance of operations and decrease the concurrency as this investigation takes time and is done holding page latches. Again, not feasible on GA-release. | |||||||||||||||||||||||||||||||||||||||
| Comment by Jan Lindström (Inactive) [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
Both lock acquire and lock printout work's as designed, thus closing this issue. You may open a new bug for documenting the used lock modes in different SQL-clauses but doing that exhaustively while maintaining "easy" reading might not be very easy. | |||||||||||||||||||||||||||||||||||||||
| Comment by Jan Lindström (Inactive) [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
valerii please review my comment, is that clear enough? | |||||||||||||||||||||||||||||||||||||||
| Comment by Jan Lindström (Inactive) [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
greenman Can you check the documentation if we could improve it. | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
In addition to explicit locks, there are also implicit locks. A record is implicitly locked by a transaction if it was written or modified by that transaction. And if a conflicting lock request arrives from another transaction, that transaction may convert the implicit lock request to an explicit one (on behalf of the lock-owner transaction) before creating its own lock object for starting the lock wait. For clustered index records, determining whether a record is implicitly locked is easy: the hidden DB_TRX_ID column will belong to an active (or XA PREPARE; not committed) transaction. For secondary index records it is very expensive because there is no per-record DB_TRX_ID but only a PAGE_MAX_TRX_ID. Therefore the function row_vers_impl_x_locked() has to look up the matching version of the record in the clustered index before we can determine whether the secondary index record is implicitly locked. This and the similarly slow MVCC logic can lead to a ‘death spiral’ of a busy server. These operations slow down the purge of transaction history, and they become slower as the history grows. In my response to a blog post 2 years ago I mentioned Bug#14704286 SECONDARY INDEX UPDATES MAKE CONSISTENT READS DO O(N^2) UNDO PAGE LOOKUPS. As far as I can tell, fixing the bug would require introducing a per-record transaction ID, which would require some data dictionary changes, which would best wait for MDEV-11655. | |||||||||||||||||||||||||||||||||||||||
| Comment by Valerii Kravchuk [ 2017-02-16 ] | |||||||||||||||||||||||||||||||||||||||
|
Thank you for detailed explanation. I still miss some details about that heap no 4 vs heap no 3 for the last gap lock. Why do we need to lock different gap for second DELETE? | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2017-11-23 ] | |||||||||||||||||||||||||||||||||||||||
|
valerii, let me answer by quoting the previously pasted output:
Page 3 is the clustered index root page in an .ibd file, and page 4 in this case is a secondary index root page. Because the table is so small, these root pages are also leaf pages (only one page in each index tree). Locks can only exist on leaf pages. And InnoDB does not have a concept of 'row lock'; it always is an 'index record lock'. There may exist a performance problem that is demonstrated by the output. For page 4, there are 3 different lock bitmaps. The first bitmap for page 4 "locks rec but not gap" would be for record-only locks (locking only the records whose heap_numbers correspond to the set bits in the bitmap). Here is the first potential problem: If we have the same heap numbers in both bitmaps, then the "locks rec but not gap" bitmap is redundant and should have been removed. If it started as a single-record bitmap, it could even have been converted to the normal rec+gap bitmap when the gap lock was placed. The third and last bitmap for page 4 ("locks gap before rec") is for gap-only locks. In this one, we could easily have the page supremum pseudo-record, which we should never have in the other bitmaps. I think that we should consider the following alternatives:
| |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2017-11-23 ] | |||||||||||||||||||||||||||||||||||||||
|
I am reopening this, because there certainly is some room for improvement. | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-01-10 ] | |||||||||||||||||||||||||||||||||||||||
|
During the development of | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-03-08 ] | |||||||||||||||||||||||||||||||||||||||
|
Another area for improvement is that InnoDB is unnecessarily creating explicit record locks when covering table locks already exist. That is, if the entire table is locked for reads, InnoDB would still acquire record locks for locking reads (such as INSERT…SELECT). Or if the entire table is locked for writes, UPDATE or SELECT…FOR UPDATE would still create record locks. For large transactions, the unnecessary record locks can fill the buffer pool and cause InnoDB to commit suicide:
| |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-07-03 ] | |||||||||||||||||||||||||||||||||||||||
|
In MDEV-16406 we could refactor the multiple record lock bitmaps per page into a single bitmap (multiple bits per record). After MDEV-16232, also UPDATE and DELETE could mainly rely on implicit locks (page latch held between the lookup and the modification of a record), so the explicit locks would mostly be used when locking conflicts exist. | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-07-03 ] | |||||||||||||||||||||||||||||||||||||||
|
The unnecessarily created explicit lock objects can be causing warning messages in the error log, of the form "difficult to find free blocks". | |||||||||||||||||||||||||||||||||||||||
| Comment by Marko Mäkelä [ 2018-07-03 ] | |||||||||||||||||||||||||||||||||||||||
|
Fixing |