enhance plan expiration, optimize index prune for small row count

This commit is contained in:
zs0
2021-11-11 19:49:35 +08:00
committed by LINxiansheng
parent f2f2aed95c
commit c84b7de59c
17 changed files with 488 additions and 259 deletions

View File

@ -1418,9 +1418,9 @@ int ObJoinOrder::cal_dimension_info(const uint64_t table_id, // alias table id
return ret;
}
int ObJoinOrder::prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
int ObJoinOrder::skyline_prunning_index(const uint64_t table_id, const uint64_t base_table_id, const ObDMLStmt* stmt,
const bool do_prunning, const ObIndexInfoCache& index_info_cache, const ObIArray<uint64_t>& valid_index_ids,
ObIArray<uint64_t>& skyline_index_ids, ObIArray<ObRawExpr*>& restrict_infos)
ObIArray<uint64_t>& skyline_index_ids, ObIArray<ObRawExpr *>& restrict_infos)
{
int ret = OB_SUCCESS;
if (!do_prunning) {
@ -1610,7 +1610,7 @@ int ObJoinOrder::add_table(
LOG_WARN("failed to add table by heuristics", K(ret));
} else if (heuristics_used) {
LOG_TRACE("table added using heuristics", K(table_id));
} else if (OB_FAIL(prunning_index(table_id,
} else if (OB_FAIL(skyline_prunning_index(table_id,
ref_table_id,
stmt,
true,
@ -1619,8 +1619,6 @@ int ObJoinOrder::add_table(
skyline_index_ids,
helper.filters_))) {
LOG_WARN("failed to pruning_index", K(table_id), K(ref_table_id), K(ret));
} else if (OB_FAIL(compute_pruned_index(table_id, ref_table_id, skyline_index_ids, helper))) {
LOG_WARN("failed to compute pruned index", K(table_id), K(ref_table_id), K(skyline_index_ids), K(ret));
} else {
LOG_TRACE("table added not using heuristics", K(table_id), K(skyline_index_ids));
helper.table_opt_info_->optimization_method_ = OptimizationMethod::COST_BASED;
@ -2478,72 +2476,67 @@ int ObJoinOrder::revise_output_rows_after_creating_path(PathHelper& helper, ObIA
return ret;
}
int ObJoinOrder::compute_pruned_index(
const uint64_t table_id, const uint64_t base_table_id, ObIArray<uint64_t>& available_index_id, PathHelper& helper)
int ObJoinOrder::fill_opt_info_index_name(const uint64_t base_table_id, ObIArray<uint64_t> &available_index_id,
ObIArray<uint64_t> &unstable_index_id, BaseTableOptInfo *table_opt_info)
{
int ret = OB_SUCCESS;
const ObTableSchema* table_schema = NULL;
uint64_t index_ids[OB_MAX_INDEX_PER_TABLE + 1];
int64_t index_count = OB_MAX_INDEX_PER_TABLE + 1;
ObSqlSchemaGuard* schema_guard = NULL;
if (OB_ISNULL(get_plan()) || OB_ISNULL(schema_guard = OPT_CTX.get_sql_schema_guard()) ||
OB_ISNULL(helper.table_opt_info_)) {
const ObTableSchema *table_schema = NULL;
uint64_t index_ids[OB_MAX_INDEX_PER_TABLE + 3];
int64_t index_count = OB_MAX_INDEX_PER_TABLE + 3;
ObSqlSchemaGuard *schema_guard = NULL;
if (OB_ISNULL(table_opt_info) || OB_ISNULL(get_plan()) || OB_ISNULL(schema_guard = OPT_CTX.get_sql_schema_guard())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("get unexpected null", K(get_plan()), K(schema_guard), K(helper.table_opt_info_), K(ret));
} else if (OB_UNLIKELY(OB_INVALID_ID == table_id) || OB_UNLIKELY(OB_INVALID_ID == base_table_id)) {
LOG_WARN("get unexpected null", K(get_plan()), K(schema_guard), K(table_opt_info), K(ret));
} else if (OB_UNLIKELY(OB_INVALID_ID == base_table_id)) {
ret = OB_INVALID_ARGUMENT;
LOG_WARN("Invalid table id", K(table_id), K(base_table_id), K(ret));
LOG_WARN("Invalid table id", K(base_table_id), K(ret));
} else if (OB_FAIL(schema_guard->get_can_read_index_array(
base_table_id, index_ids, index_count, false, true /*global index*/, false /*domain index*/))) {
LOG_WARN("failed to get can read index", K(base_table_id), K(ret));
} else if (index_count > OB_MAX_INDEX_PER_TABLE + 1) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("Invalid index count", K(base_table_id), K(index_count), K(ret));
} else if (OB_FAIL(helper.table_opt_info_->available_index_id_.assign(available_index_id))) {
} else if (OB_FAIL(table_opt_info->available_index_id_.assign(available_index_id))) {
LOG_WARN("failed to assign available index id", K(ret));
} else {
const uint64_t rowid_index_id = ObSQLMockSchemaUtils::get_rowid_index_table_id(base_table_id);
index_ids[index_count++] = base_table_id;
index_ids[index_count++] = rowid_index_id;
// i == -1 represents primary key, other value of i represent index
for (int64_t i = -1; OB_SUCC(ret) && i < index_count; i++) {
for (int64_t i = 0; OB_SUCC(ret) && i < index_count; ++i) {
ObString name;
bool is_find = false;
uint64_t index_id = (i == -1) ? base_table_id : index_ids[i];
if (OB_FAIL(schema_guard->get_table_schema(index_id, table_schema))) {
uint64_t index_id = index_ids[i];
if (rowid_index_id == index_id) {
name = ObString::make_string(ObSQLMockSchemaUtils::get_rowid_index_name());
} else if (OB_FAIL(schema_guard->get_table_schema(index_id, table_schema))) {
LOG_WARN("fail to get table schema", K(index_id), K(ret));
} else if (OB_ISNULL(table_schema)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("index schema should not be null", K(ret), K(index_id));
} else if (i == -1) {
} else if (base_table_id == index_id) {
name = table_schema->get_table_name_str();
} else if (OB_FAIL(table_schema->get_index_name(name))) {
LOG_WARN("failed to get index name", K(ret));
} else { /*do nothing*/
}
for (int64_t j = 0; OB_SUCC(ret) && (!is_find) && j < available_index_id.count(); j++) {
if (index_id == available_index_id.at(j)) {
is_find = true;
}
}
if (OB_SUCC(ret)) {
if (is_find) {
if (OB_FAIL(helper.table_opt_info_->available_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else {
if (OB_FAIL(helper.table_opt_info_->pruned_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
}
}
}
if (OB_FAIL(ret)) {
// do nothing
} else if (ObSQLMockSchemaUtils::contain_mock_index(base_table_id) &&
OB_FAIL(helper.table_opt_info_->available_index_name_.push_back(
ObString::make_string(ObSQLMockSchemaUtils::get_rowid_index_name())))) {
LOG_WARN("failed to push back rowid index name", K(ret));
if (OB_FAIL(ret)) {
} else if (ObOptimizerUtil::find_item(available_index_id, index_id)) {
if (OB_FAIL(table_opt_info->available_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else if (ObOptimizerUtil::find_item(unstable_index_id, index_id)) {
if (OB_FAIL(table_opt_info->unstable_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
} else if (rowid_index_id == index_id) {
/* do nothing */
} else if (OB_FAIL(table_opt_info->pruned_index_name_.push_back(name))) {
LOG_WARN("failed to push back index name", K(name), K(ret));
} else { /* do nothing */
}
}
}
return ret;
@ -3005,6 +2998,83 @@ int ObJoinOrder::estimate_size_and_width_for_access(PathHelper& helper, ObIArray
return ret;
}
int ObJoinOrder::pruning_unstable_access_path(BaseTableOptInfo *table_opt_info, ObIArray<AccessPath *> &access_paths)
{
int ret = OB_SUCCESS;
ObSQLSessionInfo *session_info = NULL;
bool use_acs = false;
ObSEArray<uint64_t, 4> unstable_index_id;
if (OB_UNLIKELY(access_paths.empty()) || OB_ISNULL(get_plan()) ||
OB_ISNULL(session_info = get_plan()->get_optimizer_context().get_session_info())) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret), K(access_paths.count()), K(get_plan()), K(session_info));
} else if (OB_FAIL(session_info->get_adaptive_cursor_sharing(use_acs))) {
LOG_WARN("failed to check is acs enabled", K(ret));
} else if (use_acs || access_paths.count() <= 1 || OB_DEFAULT_STAT_EST == table_meta_info_.cost_est_type_) {
/* do not pruning access path */
} else if (OB_FAIL(try_pruning_base_table_access_path(access_paths, unstable_index_id))) {
LOG_WARN("failed to pruning base table access path", K(ret));
}
if (OB_SUCC(ret)) {
ObSEArray<uint64_t, 4> available_index_id;
uint64_t base_table_id = OB_INVALID_ID;
AccessPath *ap = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < access_paths.count(); ++i) {
if (OB_ISNULL(ap = access_paths.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (OB_FAIL(available_index_id.push_back(ap->index_id_))) {
LOG_WARN("failed to push back index id", K(ret));
} else if (0 == i) {
base_table_id = ap->ref_table_id_;
}
}
if (OB_SUCC(ret) &&
OB_FAIL(fill_opt_info_index_name(base_table_id, available_index_id, unstable_index_id, table_opt_info))) {
LOG_WARN(
"failed to fill opt info index name", K(ret), K(base_table_id), K(available_index_id), K(unstable_index_id));
}
}
return ret;
}
int ObJoinOrder::try_pruning_base_table_access_path(
ObIArray<AccessPath *> &access_paths, ObIArray<uint64_t> &unstable_index_id)
{
int ret = OB_SUCCESS;
bool need_prune = false;
int64_t base_path_pos = OB_INVALID_INDEX;
AccessPath *ap = NULL;
for (int64_t i = 0; OB_SUCC(ret) && i < access_paths.count(); ++i) {
if (OB_ISNULL(ap = access_paths.at(i))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected null", K(ret));
} else if (ap->ref_table_id_ == ap->index_id_) {
base_path_pos = i;
} else {
need_prune |= ap->range_prefix_count_ > 0 && ap->query_range_row_count_ < PRUNING_ROW_COUNT_THRESHOLD;
}
}
if (OB_SUCC(ret) && need_prune && OB_INVALID_INDEX != base_path_pos) {
if (OB_UNLIKELY(base_path_pos < 0 || base_path_pos > access_paths.count()) ||
OB_ISNULL(ap = access_paths.at(base_path_pos))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("unexpected pos or access path", K(ret), K(base_path_pos), K(access_paths.count()), K(ap));
} else if (ap->range_prefix_count_ > 0) {
/* do nothing */
} else if (OB_FAIL(access_paths.remove(base_path_pos))) {
LOG_WARN("failed to remove access path", K(ret), K(base_path_pos));
} else if (OB_FAIL(unstable_index_id.push_back(ap->index_id_))) {
LOG_WARN("failed to push back index id", K(ret));
} else {
LOG_TRACE("pruned base table access paths", K(*ap));
}
}
return ret;
}
int ObJoinOrder::estimate_join_width(
const ObJoinOrder* left_tree, const ObJoinOrder* right_tree, const ObJoinType join_type)
{
@ -3493,6 +3563,25 @@ int ObJoinOrder::compute_path_relationship(const sql::Path* first_path, const sq
}
}
// relation is EQUAL now, check index column count when both not index back
// remove this if adjusted estimate cost for table scan
if (OB_SUCC(ret) && PathType::ACCESS == first_path->path_type_ && PathType::ACCESS == second_path->path_type_ &&
left_dominated_count == 0 && right_dominated_count == 0 && uncompareable_count == 0) {
const ObIndexMetaInfo &first_index_info =
static_cast<const AccessPath *>(first_path)->get_cost_table_scan_info().index_meta_info_;
const ObIndexMetaInfo &second_index_info =
static_cast<const AccessPath *>(second_path)->get_cost_table_scan_info().index_meta_info_;
if (first_index_info.is_index_back_ || second_index_info.is_index_back_) {
// do nothing for this, will return EQUAL final
} else if (first_index_info.index_column_count_ < second_index_info.index_column_count_) {
++left_dominated_count;
} else if (first_index_info.index_column_count_ > second_index_info.index_column_count_) {
++right_dominated_count;
} else {
// do nothing
}
}
// compute final result
if (OB_SUCC(ret)) {
if (left_dominated_count > 0 && right_dominated_count == 0 && uncompareable_count == 0) {
@ -3996,6 +4085,9 @@ int ObJoinOrder::generate_access_paths(PathHelper& helper)
LOG_WARN("failed to calc table location", K(ret));
} else if (OB_FAIL(estimate_size_and_width_for_access(helper, access_paths))) {
LOG_WARN("failed to estimate_size", K(ret));
} else if (!access_paths.empty() && // when generate inner path, access_paths may be empty
OB_FAIL(pruning_unstable_access_path(helper.table_opt_info_, access_paths))) {
LOG_WARN("failed to pruning unstable access path", K(ret));
} else if (!helper.is_inner_path_) {
if (OB_FAIL(compute_const_exprs(NULL, NULL, type_, UNKNOWN_JOIN))) {
LOG_WARN("failed to compute const exprs", K(ret));