[MDEV-24813] Locking full table scan fails to use table-level locking Created: 2021-02-09 Updated: 2024-01-31 |
|
| Status: | Confirmed |
| Project: | MariaDB Server |
| Component/s: | Locking, Optimizer, Storage Engine - InnoDB |
| Affects Version/s: | 10.5, 10.11 |
| Fix Version/s: | 10.5 |
| Type: | Bug | Priority: | Critical |
| Reporter: | Marko Mäkelä | Assignee: | Yuchen Pei |
| Resolution: | Unresolved | Votes: | 2 |
| Labels: | optimizer-feature, performance | ||
| Issue Links: |
|
||||||||||||||||||||||||||||||||||||||||||||||||
| Description |
|
The following test case shows that table locks are not being passed to the storage engine in any of the cases of locking read.
I would have expected that an explicit LOCK TABLE would be passed to the storage engine, like it seems to have been the case during the development of I would also expect the optimizer to automatically pass information to the storage engine so that the table can be locked upfront, if we know that the entire table will be scanned in a locking operation. This should include the following:
If no WHERE or JOIN condition applies to the table, nor a LIMIT is present, then we should expect the operation to scan the entire table, and the storage engine should be requested to lock the entire table.
Note: type_mode == 17 == LOCK_TABLE | LOCK_IX. For the LOCK IN SHARE MODE, it would be 16 == LOCK_TABLE | LOCK_IS. We would expect the table lock to exist in LOCK_X or LOCK_S mode, so that no locks will have to be allocated for each individual visited row. If we change the CREATE TABLE to CREATE TEMPORARY TABLE, we can get an accurate estimate of how fast the execution would be without the row-level locking. On my system, with locking, each full-table-scan consumes between 0.49 and 0.66 seconds (the first run being the slowest). This can probably be attributed to dynamic memory allocation. With a temporary table, each locking full-table scan consumes between 0.28 and 0.30 seconds. The memory operations related to row-level locking are roughly doubling the execution time (and seriously increasing the memory footprint)! |
| Comments |
| Comment by Sergei Petrunia [ 2021-02-10 ] | ||||||||||||||||||||
|
I think this should be implemented in steps. How about defining the first step as follows: If InnoDB already knows about whether #1 is true. #2 can be implemented as follows: (TODO: check if this can be unified with Table Condition Pushdown). | ||||||||||||||||||||
| Comment by Sergei Petrunia [ 2021-02-10 ] | ||||||||||||||||||||
|
Implementation suggestion is below. Need to think this through. == Use Table Condition Pushdown for WHERE ==There is already handler::cond_push() call which informs the storage engine Possible issues: Issue 2: That is, we'll have:
what should be in handler::pushed_cond, then? The code checks this member and seeing NULL interprets it as "nothing is pushed". Is this ok? If it is not, should we put "Item(1)" there? == Introduce push_limit ==To inform the storage engine about the LIMIT clause, we introduce
Which serves as a hint to the storage engine that the SQL layer is not going to read more than soft_limit rows in a scan. The idea is that this is generally useful for storage engines that do batching - they should not batch much more than soft_limit rows. | ||||||||||||||||||||
| Comment by Roel Van de Paar [ 2022-08-30 ] | ||||||||||||||||||||
|
Marko informed me that this is the same bug:
Leads to:
And
If it would help, I can reduce this testcase further. | ||||||||||||||||||||
| Comment by Sergei Golubchik [ 2023-08-31 ] | ||||||||||||||||||||
|
May be InnoDB should automatically upgrade row locks to a table lock, when there're too many of them? |