Bug #83470 | Reverse scan on a partitioned table does ICP check incorrectly, causing slowdown | ||
---|---|---|---|
Submitted: | 20 Oct 2016 18:24 | Modified: | 16 May 2017 2:53 |
Reporter: | Sergey Petrunya | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Partitions | Severity: | S3 (Non-critical) |
Version: | 5.7.16-debug-log | OS: | Any |
Assigned to: | CPU Architecture: | Any | |
Tags: | ICP |
[20 Oct 2016 18:24]
Sergey Petrunya
[20 Oct 2016 18:46]
Sergey Petrunya
Reproducible on 5.7.16, too.
[21 Oct 2016 5:47]
MySQL Verification Team
Hello Sergey, Thank you for the report and test case. Thanks, Umesh
[21 Oct 2016 17:25]
Sergey Petrunya
The failure scenario is: Put a breakpoint in QUICK_SELECT_DESC::get_next Observe execution to reach this call: file->set_end_range(&min_range, handler::RANGE_SCAN_DESC); Put a hardware watchpoint on file->end_range and see: Hardware watchpoint 4: *$a Old value = (key_range *) 0x7fff9401b4e8 New value = (key_range *) 0x0 (gdb) wher #0 Partition_helper::ph_index_read_map (this=0x7fff9401b6a0, buf=0x7fff9401b820 "\377\200", key=0x7fff9401f678 "\231\217N", keypart_map=1, find_flag=HA_READ_BEFORE_KEY) at /home/psergey/dev-git/mysql-5.7/sql/partitioning/partition_handler.cc:2499 #1 0x0000000001ab3c8f in ha_innopart::index_read_map (this=0x7fff9401b3b0, buf=0x7fff9401b820 "\377\200", key=0x7fff9401f678 "\231\217N", keypart_map=1, find_flag=HA_READ_BEFORE_KEY) at /home/psergey/dev-git/mysql-5.7/storage/innobase/handler/ha_innopart.h:1299 #2 0x0000000000f7a13f in handler::ha_index_read_map (this=0x7fff9401b3b0, buf=0x7fff9401b820 "\377\200", key=0x7fff9401f678 "\231\217N", keypart_map=1, find_flag=HA_READ_BEFORE_KEY) at /home/psergey/dev-git/mysql-5.7/sql/handler.cc:2998 #3 0x00000000017e50c8 in QUICK_SELECT_DESC::get_next (this=0x7fff94031da0) at /home/psergey/dev-git/mysql-5.7/sql/opt_range.cc:11495 So, Partition_helper that clears end_range, and then ICP check walks off the end of the range.
[21 Oct 2016 17:49]
Sergey Petrunya
Looking at how handler::end_range is set or cleared. It is set when reading ranges: - read_range_first() sets it - then read_range_first() and read_range_next() use it. - ICP code also uses it It is set by QUICK_SELECT_DESC: - QUICK_SELECT_DESC::get_next sets it - then it calls index_read_map() or index_last(), followed by index_prev() - ICP code inside these calls use it. It is set by Partition_helper::ph_read_range_first - it sets m_handler->end_key - Then the code in Partition_helper::handle_unordered_scan_next_partition uses it. Where is handler::end_range cleared? - it is done in handler::ha_index_end(), handler::ha_index_init() and other [de]initialization calls. - within an index scan, it is not cleared: - if the scan uses read_range_XXX functions, each read_range_first call will set end_range accordingly. - if the scan is a reverse scan done by QUICK_SELECT_DESC, then QUICK_SELECT_DESC code will call set_end_range() for each new range.
[21 Oct 2016 17:50]
Sergey Petrunya
Considering the above, there seems to be absolutely no reason to have m_handler->end_range= NULL; assignment inside these functions: - Partition_helper::ph_index_read_last_map - Partition_helper::ph_index_first - Partition_helper::ph_index_read_map
[25 Oct 2016 14:27]
Sergey Petrunya
Suggested patch
Attachment: bug83470.diff (text/x-patch), 4.83 KiB.
[25 Oct 2016 14:29]
Sergey Petrunya
Please find the patch attached. I'm offering it under BSD licence.
[21 Dec 2016 11:00]
MySQL Verification Team
see also http://bugs.mysql.com/bug.php?id=84107
[16 May 2017 2:53]
Jon Stephens
Documented fix in the 5.7.19 changelog, as follows: When a query performed a reverse-ordered range scan (to satisfy ORDER BY ... DESC) using index condition pushdown on a partitioned InnoDB table, it could take an unreasonably long time to finish. This was due to the fact that the condition pushdown check failed to get the bounds of the range, so that the scan continued to read index tuples until it reached the first value in the index. Closed.