sql:
select id from sbtest1 where id >=300000 order by id asc limit 1; // poor
select id,pad from sbtest1 where id >=300000 order by id asc limit 1; // ok
innodb has no problem
the relational code is:for rocksdb
// for rocksdb, the cost of range scan in ha_rocksdb.cc
double ha_rocksdb::read_time(uint index, uint ranges, ha_rows rows) {
DBUG_ENTER_FUNC();
if (index != table->s->primary_key) {
/* Non covering index range scan */
DBUG_RETURN(handler::read_time(index, ranges, rows));
}
DBUG_RETURN((rows / 20.0) + 1);
}
for sql optimizer:
in sql/range_optimizer/range_optimizer.cc
// calc the cost of range scan, compare full index cost and range scan cost
int test_quick_select()
{
...
/* Calculate cost of full index read for the shortest covering index */
if (!table->covering_keys.is_clear_all() &&
/*
If optimizer_force_index_for_range is on and force index is used,
then skip calculating index scan cost.
*/
!(thd->variables.optimizer_force_index_for_range && table->force_index)) {
int key_for_use = find_shortest_key(table, &table->covering_keys);
// find_shortest_key() should return a valid key:
assert(key_for_use != MAX_KEY);
Cost_estimate key_read_time = param.table->file->index_scan_cost(
key_for_use, 1, static_cast<double>(records));
key_read_time.add_cpu(
cost_model->row_evaluate_cost(static_cast<double>(records)));
bool chosen = false;
if (key_read_time < cost_est) {
cost_est = key_read_time;
chosen = true;
}
Opt_trace_object trace_cov(trace, "best_covering_index_scan",
Opt_trace_context::RANGE_OPTIMIZER);
trace_cov.add_utf8("index", table->key_info[key_for_use].name)
.add("cost", key_read_time)
.add("chosen", chosen);
if (!chosen) trace_cov.add_alnum("cause", "cost");
}
AccessPath *best_path = nullptr;
double best_cost = cost_est.total_cost();
...
}
I have tried two different approaches to solve this problem, But I'm not sure if these two directions are suitable or if they will bring other problems. I hope the official can come up with a solution or help evaluate these two solutions.
the first way: Do not use full index read to eliminate range scan
--- a/sql/range_optimizer/range_optimizer.cc
+++ b/sql/range_optimizer/range_optimizer.cc
@@ -616,7 +616,6 @@ int test_quick_select(THD *thd, MEM_ROOT *return_mem_root,
const bool index_merge_intersect_allowed =
index_merge_allowed &&
thd->optimizer_switch_flag(OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT);
/* Calculate cost of full index read for the shortest covering index */
if (!table->covering_keys.is_clear_all() &&
/*
@@ -624,7 +623,7 @@ int test_quick_select(THD *thd, MEM_ROOT *return_mem_root,
then skip calculating index scan cost.
*/
!(thd->variables.optimizer_force_index_for_range && table->force_index)) {
- int key_for_use = find_shortest_key(table, &table->covering_keys);
+/* int key_for_use = find_shortest_key(table, &table->covering_keys);
// find_shortest_key() should return a valid key:
assert(key_for_use != MAX_KEY);
@@ -645,8 +644,8 @@ int test_quick_select(THD *thd, MEM_ROOT *return_mem_root,
.add("cost", key_read_time)
.add("chosen", chosen);
if (!chosen) trace_cov.add_alnum("cause", "cost");
+*/
}
the problem phenomenon:
Reproduction method:
use sysbench create one table, insert 100w rows
sql: select id from sbtest1 where id >=300000 order by id asc limit 1; // poor select id,pad from sbtest1 where id >=300000 order by id asc limit 1; // ok
innodb has no problem
the relational code is: for rocksdb
for sql optimizer: in sql/range_optimizer/range_optimizer.cc
I have tried two different approaches to solve this problem, But I'm not sure if these two directions are suitable or if they will bring other problems. I hope the official can come up with a solution or help evaluate these two solutions.
the first way: Do not use full index read to eliminate range scan
the second way: change the cost of range scan
The above are just two simple ways to represent two solutions, after testing, both of them can solve the above problem