Bug #115378 force index not effective,because calculate_scan_cost use index=4294967
Submitted: 19 Jun 2024 6:38 Modified: 19 Jun 2024 9:47
Reporter: XIAOJING LI Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:8.0.18 OS:Any
Assigned to: CPU Architecture:Any
Tags: cost, sql optimizer

[19 Jun 2024 6:38] XIAOJING LI
Description:
the cost of K1 is calculated as the same as primary key

the sample stack:
 ha_innobase::read_time(index=4294967)
 handler::read_cost(index=4294967)
 Optimize_table_order::calculate_scan_cost(idx=0, best_ref=0x0, prefix_rowcount=1)
 Optimize_table_order::best_access_path()

 
best_ref=0x0 in Optimize_table_order::calculate_scan_cost is because it it didn't
call find_best_ref()

 in Optimize_table_order::best_access_path() function

 tab->keyuse()  is nullptr, best_ref is not find_best_ref, is 0X0

  // Look for the best ref access if the storage engine supports index access.
  if (tab->keyuse() != nullptr &&
      (table->file->ha_table_flags() & HA_NO_INDEX_ACCESS) == 0)
    best_ref =
        find_best_ref(tab, remaining_tables, idx, prefix_rowcount,
                      &found_condition, &ref_depend_map, &used_key_parts);

in the function Optimize_table_order::calculate_scan_cost()
tab->ref().key is not set before ,its values is -1, that is 4294967 for uint.

    trace_access_scan->add_alnum("access_type", "scan");

    // Cost of scanning the table once
    Cost_estimate scan_cost;
    if (table->force_index && !best_ref)  // index scan
      scan_cost = table->file->read_cost(tab->ref().key, 1,
                                         static_cast<double>(tab->records()));
    else
      scan_cost = table->file->table_scan_cost();  // table scan
    const double single_scan_read_cost = scan_cost.total_cost();

How to repeat:
create table t1(a int primary key, b int, c text);

DELIMITER |;
CREATE PROCEDURE populate_t1(total_num INT)
BEGIN
        DECLARE i int DEFAULT 1;

        START TRANSACTION;
        WHILE (i <= total_num) DO
                INSERT INTO t1 VALUES (i, i, REPEAT(CONCAT('a', i),1000));
                SET i = i + 1;
        END WHILE;
        COMMIT;
END|;
DELIMITER ;

CALL populate_t1(20000);

alter table t1 add index k1(b);
alter table t1 add index k2(a, b), add index k3(c(150));

analyze table t1;

## not choose index k1, choose table_scan instead;
mysql> explain select * from t1 force index(k1);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 11732 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
1 row in set, 1 warning (0.00 sec)
[19 Jun 2024 9:47] MySQL Verification Team
Hi Mr. LI,

Thank you for your bug report.

First of all, let us inform you that current release of 8.0 is 8.0.37.

However, that is not important. 

What is important is that our calculus clearly shows that when the entire table has to be scanned in order to resolve a query, then it is always faster to scan the rows instead of the index.

Our interanl speed experiments and measurements proved that.

Not a bug.