Skip to content

Commit

Permalink
Bug #23046302 COUNT(*) MUCH SLOWER ON 5.7 THAN 5.6
Browse files Browse the repository at this point in the history
ANALYSIS

This is regression caused due to worklog 6742 which
implemented ha_innobase::records() which always
uses clustered index to get the row count. Previously
optimizer chose secondary index which was smaller in
size of clustered index to scan for rows and resulted in
a quicker scan.

FIX

After discussion it was decided to remove this feature in 5.7.

[#rb14040 Approved by Kevin and Oystein ]
  • Loading branch information
Aditya A authored and dr-m committed Apr 26, 2017
1 parent 07f3311 commit 14fe6dd
Show file tree
Hide file tree
Showing 7 changed files with 6 additions and 169 deletions.
112 changes: 1 addition & 111 deletions storage/innobase/handler/ha_innodb.cc
Expand Up @@ -14230,116 +14230,6 @@ ha_innobase::rename_table(
DBUG_RETURN(convert_error_code_to_mysql(error, 0, NULL));
}

/*********************************************************************//**
Returns the exact number of records that this client can see using this
handler object.
@return Error code in case something goes wrong.
These errors will abort the current query:
case HA_ERR_LOCK_DEADLOCK:
case HA_ERR_LOCK_TABLE_FULL:
case HA_ERR_LOCK_WAIT_TIMEOUT:
case HA_ERR_QUERY_INTERRUPTED:
For other error codes, the server will fall back to counting records. */

#ifdef MYSQL_57_SELECT_COUNT_OPTIMIZATION
int
ha_innobase::records(
/*==================*/
ha_rows* num_rows) /*!< out: number of rows */
{
DBUG_ENTER("ha_innobase::records()");

dberr_t ret;
ulint n_rows = 0; /* Record count in this view */

update_thd();

if (dict_table_is_discarded(m_prebuilt->table)) {
ib_senderrf(
m_user_thd,
IB_LOG_LEVEL_ERROR,
ER_TABLESPACE_DISCARDED,
table->s->table_name.str);

*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);

} else if (m_prebuilt->table->ibd_file_missing) {
ib_senderrf(
m_user_thd, IB_LOG_LEVEL_ERROR,
ER_TABLESPACE_MISSING,
table->s->table_name.str);

*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_TABLESPACE_MISSING);

} else if (m_prebuilt->table->corrupted) {
ib_errf(m_user_thd, IB_LOG_LEVEL_WARN,
ER_INNODB_INDEX_CORRUPT,
"Table '%s' is corrupt.",
table->s->table_name.str);

*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_INDEX_CORRUPT);
}

TrxInInnoDB trx_in_innodb(m_prebuilt->trx);

m_prebuilt->trx->op_info = "counting records";

dict_index_t* index = dict_table_get_first_index(m_prebuilt->table);

ut_ad(dict_index_is_clust(index));

m_prebuilt->index_usable = row_merge_is_index_usable(
m_prebuilt->trx, index);

if (!m_prebuilt->index_usable) {
*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_TABLE_DEF_CHANGED);
}

/* (Re)Build the m_prebuilt->mysql_template if it is null to use
the clustered index and just the key, no off-record data. */
m_prebuilt->index = index;
dtuple_set_n_fields(m_prebuilt->search_tuple, 0);
m_prebuilt->read_just_key = 1;
build_template(false);

/* Count the records in the clustered index */
ret = row_scan_index_for_mysql(m_prebuilt, index, false, &n_rows);
reset_template();
switch (ret) {
case DB_SUCCESS:
break;
case DB_DEADLOCK:
case DB_LOCK_TABLE_FULL:
case DB_LOCK_WAIT_TIMEOUT:
*num_rows = HA_POS_ERROR;
DBUG_RETURN(convert_error_code_to_mysql(ret, 0, m_user_thd));
case DB_INTERRUPTED:
*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_QUERY_INTERRUPTED);
default:
/* No other error besides the three below is returned from
row_scan_index_for_mysql(). Make a debug catch. */
*num_rows = HA_POS_ERROR;
ut_ad(0);
DBUG_RETURN(-1);
}

m_prebuilt->trx->op_info = "";

if (thd_killed(m_user_thd)) {
*num_rows = HA_POS_ERROR;
DBUG_RETURN(HA_ERR_QUERY_INTERRUPTED);
}

*num_rows= n_rows;
DBUG_RETURN(0);
}
#endif /* MYSQL_57_SELECT_COUNT_OPTIMIZATION */

/*********************************************************************//**
Estimates the number of index records in a range.
@return estimated number of rows */
Expand Down Expand Up @@ -15586,7 +15476,7 @@ ha_innobase::check(
ret = row_count_rtree_recs(m_prebuilt, &n_rows);
} else {
ret = row_scan_index_for_mysql(
m_prebuilt, index, true, &n_rows);
m_prebuilt, index, &n_rows);
}

DBUG_EXECUTE_IF(
Expand Down
5 changes: 1 addition & 4 deletions storage/innobase/handler/ha_innodb.h
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2013, 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
Expand Down Expand Up @@ -219,9 +219,6 @@ class ha_innobase: public handler

ha_rows estimate_rows_upper_bound();

// JAN: TODO: MySQL 5.7
// int records(ha_rows* num_rows);

void update_create_info(HA_CREATE_INFO* create_info);

int create(
Expand Down
37 changes: 1 addition & 36 deletions storage/innobase/handler/ha_innopart.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2016, 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
Expand Down Expand Up @@ -3004,41 +3004,6 @@ ha_innopart::truncate()
DBUG_RETURN(error);
}

/** Total number of rows in all used partitions.
Returns the exact number of records that this client can see using this
handler object.
@param[out] num_rows Number of rows.
@return 0 or error number. */
int
ha_innopart::records(
ha_rows* num_rows)
{
ha_rows n_rows;
int err;
DBUG_ENTER("ha_innopart::records()");

*num_rows = 0;

/* The index scan is probably so expensive, so the overhead
of the rest of the function is neglectable for each partition.
So no current reason for optimizing this further. */

for (uint i = m_part_info->get_first_used_partition();
i < m_tot_parts;
i = m_part_info->get_next_used_partition(i)) {

set_partition(i);
err = ha_innobase::records(&n_rows);
update_partition(i);
if (err != 0) {
*num_rows = HA_POS_ERROR;
DBUG_RETURN(err);
}
*num_rows += n_rows;
}
DBUG_RETURN(0);
}

/** Estimates the number of index records in a range.
@param[in] keynr Index number.
@param[in] min_key Start key value (or NULL).
Expand Down
4 changes: 0 additions & 4 deletions storage/innobase/handler/ha_innopart.h
Expand Up @@ -1221,10 +1221,6 @@ class ha_innopart:
uchar* record,
uchar* pos);

int
records(
ha_rows* num_rows);

int
index_next(
uchar* record)
Expand Down
1 change: 0 additions & 1 deletion storage/innobase/include/ha_prototypes.h
Expand Up @@ -38,7 +38,6 @@ simple headers.
class THD;

// JAN: TODO missing features:
#undef MYSQL_57_SELECT_COUNT_OPTIMIZATION
#undef MYSQL_FT_INIT_EXT
#undef MYSQL_INNODB_PARTITIONING
#undef MYSQL_PFS
Expand Down
5 changes: 1 addition & 4 deletions storage/innobase/include/row0mysql.h
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
Expand Down Expand Up @@ -540,9 +540,6 @@ row_scan_index_for_mysql(
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct
in MySQL handle */
const dict_index_t* index, /*!< in: index */
bool check_keys, /*!< in: true=check for mis-
ordered or duplicate records,
false=count the rows only */
ulint* n_rows) /*!< out: number of entries
seen in the consistent read */
MY_ATTRIBUTE((warn_unused_result));
Expand Down
11 changes: 2 additions & 9 deletions storage/innobase/row/row0mysql.cc
@@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
Expand Down Expand Up @@ -5046,9 +5046,6 @@ row_scan_index_for_mysql(
row_prebuilt_t* prebuilt, /*!< in: prebuilt struct
in MySQL handle */
const dict_index_t* index, /*!< in: index */
bool check_keys, /*!< in: true=check for mis-
ordered or duplicate records,
false=count the rows only */
ulint* n_rows) /*!< out: number of entries
seen in the consistent read */
{
Expand Down Expand Up @@ -5115,7 +5112,7 @@ row_scan_index_for_mysql(
goto func_exit;
default:
{
const char* doing = check_keys? "CHECK TABLE" : "COUNT(*)";
const char* doing = "CHECK TABLE";
ib::warn() << doing << " on index " << index->name << " of"
" table " << index->table->name << " returned " << ret;
/* fall through (this error is ignored by CHECK TABLE) */
Expand All @@ -5131,9 +5128,6 @@ row_scan_index_for_mysql(

*n_rows = *n_rows + 1;

if (!check_keys) {
goto next_rec;
}
/* else this code is doing handler::check() for CHECK TABLE */

/* row_search... returns the index record in buf, record origin offset
Expand Down Expand Up @@ -5215,7 +5209,6 @@ row_scan_index_for_mysql(
}
}

next_rec:
ret = row_search_for_mysql(
buf, PAGE_CUR_G, prebuilt, 0, ROW_SEL_NEXT);

Expand Down

0 comments on commit 14fe6dd

Please sign in to comment.