Bug #102076
openPageSlugCandidateProvider puts a serious load on the database
0%
Description
The query in is quite inefficient and stresses the database a lot:
$statement = $queryBuilder ->select('uid', 'sys_language_uid', 'l10n_parent', 'l18n_cfg', 'pid', 'slug', 'mount_pid', 'mount_pid_ol', 't3ver_state', 'doktype', 't3ver_wsid', 't3ver_oid') ->from('pages') ->where( $queryBuilder->expr()->eq( 'sys_language_uid', $queryBuilder->createNamedParameter($languageId, Connection::PARAM_INT) ), $queryBuilder->expr()->in( 'slug', $queryBuilder->createNamedParameter( $slugCandidates, Connection::PARAM_STR_ARRAY ) ) ) // Exact match will be first, that's important ->orderBy('slug', 'desc') // versioned records should be rendered before the live records ->addOrderBy('t3ver_wsid', 'desc') // Sort pages that are not MountPoint pages before mount points ->addOrderBy('mount_pid_ol', 'asc') ->addOrderBy('mount_pid', 'asc') ->executeQuery();
MySql:
MariaDB [prod12]> explain SELECT `uid`, `sys_language_uid`, `l10n_parent`, `l18n_cfg`, `pid`, `slug`, `mount_pid`, `mount_pid_ol`, `t3ver_state`, `doktype`, `t3ver_wsid`, `t3ver_oid` FROM `pages` WHERE (`sys_language_uid` = ?) AND (`slug` IN (?)) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_wsid` = 0) AND (`t3ver_state` <> 2))))) ORDER BY `slug` desc, `t3ver_wsid` desc, `mount_pid_ol` asc, `mount_pid` asc; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?) AND (`slug` IN (?)) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_ws...' at line 1 MariaDB [prod12]> explain SELECT `uid`, `sys_language_uid`, `l10n_parent`, `l18n_cfg`, `pid`, `slug`, `mount_pid`, `mount_pid_ol`, `t3ver_state`, `doktype`, `t3ver_wsid`, `t3ver_oid` FROM `pages` WHERE (`sys_language_uid` = 0) AND (`slug` IN ('')) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_wsid` = 0) AND (`t3ver_state` <> 2))))) ORDER BY `slug` desc, `t3ver_wsid` desc, `mount_pid_ol` asc, `mount_pid` asc; +------+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+ | 1 | SIMPLE | pages | ref | slug | slug | 384 | const | 2 | Using where; Using filesort | +------+-------------+-------+------+---------------+------+---------+-------+------+-----------------------------+ 1 row in set (0.000 sec)
So where + filesort.
For now I was able to fix this by adjusting slug
key like this:
alter table pages add key slug2 (slug(127),`t3ver_wsid`,`mount_pid_ol`,`mount_pid`);
Check the result:
MariaDB [prod12]> explain SELECT `uid`, `sys_language_uid`, `l10n_parent`, `l18n_cfg`, `pid`, `slug`, `mount_pid`, `mount_pid_ol`, `t3ver_state`, `doktype`, `t3ver_wsid`, `t3ver_oid` FROM `pages` WHERE (`sys_language_uid` = 0) AND (`slug` IN ('')) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_wsid` = 0) AND (`t3ver_state` <> 2))))) ORDER BY `slug` desc, `t3ver_wsid` desc, `mount_pid_ol` asc, `mount_pid` asc; +------+-------------+-------+-------+---------------+-------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +------+-------------+-------+-------+---------------+-------+---------+------+------+-------------+ | 1 | SIMPLE | pages | range | slug,slug2 | slug2 | 388 | NULL | 2 | Using where | +------+-------------+-------+-------+---------------+-------+---------+------+------+-------------+ 1 row in set (0.001 sec)
No filesort.
Updated by Gerrit Code Review about 1 year ago
- Status changed from New to Under Review
Patch set 1 for branch main of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/c/Packages/TYPO3.CMS/+/81309
Updated by Oliver Hader about 1 year ago
Applying the change to TYPO3 v12, I'm getting the following (with MySQL 8.0.34):
mysql> EXPLAIN SELECT `uid`, `sys_language_uid`, `l10n_parent`, `l18n_cfg`, `pid`, `slug`, `mount_pid`, `mount_pid_ol`, `t3ver_state`, `doktype`, `t3ver_wsid`, `t3ver_oid` FROM `pages` WHERE (`sys_language_uid` = 0) AND (`slug` IN ('a','b','c')) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_wsid` = 0) AND (`t3ver_state` <> 2))))) ORDER BY `slug` desc, `t3ver_wsid` desc, `mount_pid_ol` asc, `mount_pid` asc; +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------------------------+ | 1 | SIMPLE | pages | NULL | range | slug | slug | 515 | NULL | 3 | 1.67 | Using index condition; Using where; Using filesort | +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+----------------------------------------------------+ 1 row in set, 1 warning (0.00 sec)
In the initial report "So where + filesort."
is mentioned - it sounds, like this would be a bad thing?
The query below "check result" in the report above is using a static `slug` IN ('')
which leads to not having to sort. However, any other real scenario with more than one given slug
item has to be sorted.
Anyway, I think there's more that can be done here in order to optimize the query any have some numbers on it.
Updated by Dmitry Dulepov about 1 year ago
It is still better and faster. Read: https://dev.mysql.com/doc/refman/8.0/en/index-condition-pushdown-optimization.html
But feel free to close the report. I guess it is easier to keep it as is. I will simply have the change locally.
Updated by Markus Klein about 1 year ago
If you preserve the original index too, does it still need to filesort for the query @Oliver Hader ?
(That would mean that MySQL is not able to pick up the new index, if it only needs slug for sorting)
Updated by Markus Klein about 1 year ago
Markus Klein wrote in #note-4:
If you preserve the original index too, does it still need to filesort for the query @Oliver Hader ?
(That would mean that MySQL is not able to pick up the new index, if it only needs slug for sorting)
Nope, it does not make a difference.
Updated by Oliver Hader about 1 year ago
Using EXPLAIN FORMAT=JSON ...
provides some more details (having the patch with the modified slug
index applied):
Query
EXPLAIN FORMAT=JSON SELECT `uid`, `sys_language_uid`, `l10n_parent`, `l18n_cfg`, `pid`, `slug`, `mount_pid`, `mount_pid_ol`, `t3ver_state`, `doktype`, `t3ver_wsid`, `t3ver_oid` FROM `pages` WHERE (`sys_language_uid` = 0) AND (`slug` IN ('/','/content-examples','/content-examples/', '/content-examples/menus', '/content-examples/menus/')) AND (((`pages`.`deleted` = 0) AND (((`pages`.`t3ver_wsid` = 0) AND (`t3ver_state` <> 2))))) ORDER BY `slug` desc, `t3ver_wsid` desc, `mount_pid_ol` asc, `mount_pid` asc \G
Result
EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "3.57" }, "ordering_operation": { "using_filesort": true, "cost_info": { "sort_cost": "0.06" }, "table": { "table_name": "pages", "access_type": "range", "possible_keys": [ "slug" ], "key": "slug", "used_key_parts": [ "slug", "t3ver_wsid" ], "key_length": "515", "rows_examined_per_scan": 5, "rows_produced_per_join": 0, "filtered": "1.18", "index_condition": "(`t3_ip13_local`.`pages`.`t3ver_wsid` = 0)", "cost_info": { "read_cost": "3.50", "eval_cost": "0.01", "prefix_cost": "3.51", "data_read_per_join": "1K" }, "used_columns": [ "uid", "pid", "deleted", "sys_language_uid", "l10n_parent", "t3ver_oid", "t3ver_wsid", "t3ver_state", "doktype", "mount_pid", "mount_pid_ol", "slug", "l18n_cfg" ], "attached_condition": "((`t3_ip13_local`.`pages`.`deleted` = 0) and (`t3_ip13_local`.`pages`.`sys_language_uid` = 0) and (`t3_ip13_local`.`pages`.`slug` in ('/','/content-examples','/content-examples/','/content-examples/menus','/content-examples/menus/')) and (`t3_ip13_local`.`pages`.`t3ver_state` <> 2))" } } } }
Comparison of what the patch changed in the EXPLAIN
statement (basically diff -u before.json after.json
):
@@ -17,12 +17,14 @@ ], "key": "slug", "used_key_parts": [ - "slug" + "slug", + "t3ver_wsid" ], - "key_length": "511", + "key_length": "515", "rows_examined_per_scan": 5, "rows_produced_per_join": 0, "filtered": "1.18", + "index_condition": "(`t3_ip13_local`.`pages`.`t3ver_wsid` = 0)", "cost_info": { "read_cost": "3.50", "eval_cost": "0.01", @@ -44,7 +46,7 @@ "slug", "l18n_cfg" ], - "attached_condition": "((`t3_ip13_local`.`pages`.`t3ver_wsid` = 0) and (`t3_ip13_local`.`pages`.`deleted` = 0) and (`t3_ip13_local`.`pages`.`sys_language_uid` = 0) and (`t3_ip13_local`.`pages`.`slug` in ('/','/content-examples','/content-examples/','/content-examples/menus','/content-examples/menus/')) and (`t3_ip13_local`.`pages`.`t3ver_state` <> 2))" + "attached_condition": "((`t3_ip13_local`.`pages`.`deleted` = 0) and (`t3_ip13_local`.`pages`.`sys_language_uid` = 0) and (`t3_ip13_local`.`pages`.`slug` in ('/','/content-examples','/content-examples/','/content-examples/menus','/content-examples/menus/')) and (`t3_ip13_local`.`pages`.`t3ver_state` <> 2))" } } }
- the query on
t3ver_wsid
can be served by index (ICP for that "partition") - key size grew by 4 bytes (that's the size of the
t3ver_wsid
field asINT
) - still having the
filesort
on the ordering part
Updated by Markus Klein about 1 year ago
Dmitry Dulepov wrote in #note-3:
It is still better and faster. Read: https://dev.mysql.com/doc/refman/8.0/en/index-condition-pushdown-optimization.html
But feel free to close the report. I guess it is easier to keep it as is. I will simply have the change locally.
Can you please verify on your systems the last query example from Olly? Is filesort gone for you on your server?
Updated by Dmitry Dulepov about 1 year ago
WIth this query filesort is there but it uses index condition. So it is still an improvement.
Updated by Tymoteusz Motylewski about 1 year ago
- Related to Epic #95690: Performance issues when hosting a large amount of websites, and optimizations propositions added
Updated by Tymoteusz Motylewski about 1 year ago
fyi, i have linked issue https://forge.typo3.org/issues/95690 which has some work done in a similar area (code changes and test environment to reproduce, and traces), maybe it's useful.
Updated by Markus Klein 2 months ago
- Status changed from Under Review to New
- Assignee deleted (
Stefan Bürk) - Complexity deleted (
easy)