Bug #99924 | The record per key value from InnoDB is not suitable when n_diff is zero | ||
---|---|---|---|
Submitted: | 18 Jun 2020 3:27 | Modified: | 23 Jun 2020 12:11 |
Reporter: | Ze Yang (OCA) | Email Updates: | |
Status: | Verified | Impact on me: | |
Category: | MySQL Server: InnoDB storage engine | Severity: | S5 (Performance) |
Version: | 8.0 | OS: | Any |
Assigned to: | CPU Architecture: | Any |
[18 Jun 2020 3:27]
Ze Yang
[18 Jun 2020 12:27]
MySQL Verification Team
Hi Mr. Yang, Thank you for your bug report. Let me see if I understood you correctly. You would like to see a change where number of records per key is 1 or 10 depending on whether index is unique or not. However, in our optimiser, dives into indices are made which come up which much precise estimates then those. Hence, I do not see a need for this small change. Also, you have seen this small performance problem in InnoDB SE, while the relevant part of the calculus is performed in the Optimiser. Can you clear out your report further in order to make it more precise. Thanks in advance.
[18 Jun 2020 12:29]
MySQL Verification Team
Hi, Only one more question. With your patch, what were exactly the benefits that you have observed ??? Have you made any measurements ?? What was number of rows matched and total number of rows ????
[19 Jun 2020 3:28]
Ze Yang
Sorry that I do not express my point clearly. This problem will cause optimizer choose full table scan rather than index read with small range. As server read dict_table statistics from InnoDB not thread safe. It may read stat_n_rows is not zero and n_diff is zero. When n_diff is zero, the rec_per_key is stat_n_rows. This will cause the index read records estimated by optimizer is very large(larger than the table records). The optimizer will choose full table scan rather index. explain select * from t1 where b in (1, 2, 3, 4); id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t1 NULL ALL i_b NULL NULL NULL 49970 100.00 Using where
[19 Jun 2020 12:25]
MySQL Verification Team
Hi Mr. Yang, We still do not understand your report. You know, it is irrelevant whether number of rows returned is one or 10. Depending on the average record width, it is much faster to scan rows than to search by index on the small tables. Which means if you have only 50 or 100 rows, in many cases scanning is much faster then index search. Also, will you be so kind and prove to us that when server reads dict_table statistics from InnoDB , those reads are not thread safe. Please use 8.0.20 code base to prove your point.
[20 Jun 2020 2:32]
Øystein Grøvlen
I will try to summarize my understanding of this: 1. The server call handler::info_low() to get updated statistics. To update its rec_per_key statistics, it sets HA_STATUS_CONST flag. This happens, for example, when a new table object is created. This will access dict_index_t::stat_n_diff_key_vals without any form of thread synchronization. (see innodb_rec_per_key()). 2. When the background thread refreshes its statistics, it starts with setting dict_index_t::stat_n_diff_key_vals to 0. See dict_stats_empty_index() called from dict_stats_analyze_index(). 3. In innodb_rec_per_key() if stat_n_diff_key_vals is 0, it will return the total number of records instead. 4. In other words, if a table object is opened during the recalculation of statistics, the rec_per_key for a column/index may be quite misleading. It will be interpreted as all rows have the same value, and the index will probably not be chosen for any non-covering scans. 5. Note also that there is no guarantee that the servers misleading rec_per_key data will be refreshed when the background thread has finished the recalculation. Unless an explicit ANALYZE TABLE or FLUSH TABLE is performed, a refresh basically depends on a new table object being created. If there are cached table objects available for reuse, this may not happen for quite some time. 6. The particular use case in this bug report, is a query with an IN expression with a long list of values. If the size of the list is greater than eq_range_index_dive_limit, rec_per_key statistics will be used instead of index dives. If the number of rows in the table is n_rows, and we have the scenario described above, the optimizer will think that there is n_rows per range, and prefer table scan instead. 7. The suggestion in this request is to use another default than n_rows when info_low() is called and stat_n_diff_key_vals is 0. The server will, if rec_per_key is not provided, use 1 for an unique index and 10 for a non-unique index (See guess_rec_per_key()). The proposal is to do the same when stat_n_diff_key_vals is (temporarily) 0.
[22 Jun 2020 12:16]
MySQL Verification Team
Hi Øystein, I think that you are correct and what you described is exactly my understanding. However, as I wrote above, there is no proof that this change will render faster execution in all situations, when this part of code has to be executed.
[23 Jun 2020 2:29]
Øystein Grøvlen
No change will "render faster execution in all situations". However, when there is lack of information, the optimizer will need to make some assumptions that can cover a large part of the cases. Assuming that all rows have the same value, is not a good general assumption.
[23 Jun 2020 2:37]
Øystein Grøvlen
I think the best would be if InnoDB could just set the value REC_PER_KEY_UNKNOWN in such cases. Then, the optimizer could decide itself how to deal with it.
[23 Jun 2020 12:11]
MySQL Verification Team
HI Øystein Grøvlen, I agree with you. However, I do not think that this is a bug, but a performance improvement report. Verified as performance improvement.