mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
Merge branch 'bb-10.1-explain-analyze' into 10.1
This commit is contained in:
@@ -7,12 +7,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t0",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 10,
|
||||
"r_rows": 10,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 30,
|
||||
"attached_condition": "(t0.a < 3)"
|
||||
@@ -32,12 +35,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t0",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 10,
|
||||
"r_rows": 10,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 0,
|
||||
"attached_condition": "((t0.a > 9) and (t0.a is not null))"
|
||||
@@ -69,12 +75,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t0",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 10,
|
||||
"r_rows": 10,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"attached_condition": "(t0.a is not null)"
|
||||
@@ -90,6 +99,7 @@ EXPLAIN
|
||||
"r_loops": 10,
|
||||
"rows": 1,
|
||||
"r_rows": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 40,
|
||||
"attached_condition": "(t1.b < 4)"
|
||||
@@ -107,12 +117,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "tbl1",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 100,
|
||||
"r_rows": 100,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 20,
|
||||
"attached_condition": "(tbl1.b < 20)"
|
||||
@@ -124,6 +137,7 @@ EXPLAIN
|
||||
"r_loops": 1,
|
||||
"rows": 100,
|
||||
"r_rows": 100,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 60,
|
||||
"attached_condition": "(tbl2.b < 60)"
|
||||
@@ -140,12 +154,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "tbl1",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 100,
|
||||
"r_rows": 100,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 20,
|
||||
"attached_condition": "(tbl1.b < 20)"
|
||||
@@ -157,6 +174,7 @@ EXPLAIN
|
||||
"r_loops": 1,
|
||||
"rows": 100,
|
||||
"r_rows": 100,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 60,
|
||||
"attached_condition": "(tbl2.b < 60)"
|
||||
@@ -182,12 +200,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 10,
|
||||
"r_rows": 10,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"attached_condition": "(t1.a is not null)"
|
||||
@@ -203,6 +224,7 @@ EXPLAIN
|
||||
"r_loops": 10,
|
||||
"rows": 2,
|
||||
"r_rows": 0.2,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 100,
|
||||
"using_index": true
|
||||
@@ -226,12 +248,15 @@ EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"r_loops": 1,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 1,
|
||||
"rows": 10,
|
||||
"r_rows": 10,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 50,
|
||||
"attached_condition": "(test.t1.a < 5)"
|
||||
|
||||
@@ -9,28 +9,34 @@ create table t0 (a int);
|
||||
INSERT INTO t0 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||
|
||||
--echo # r_filtered=30%, because 3 rows match: 0,1,2
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json select * from t0 where a<3;
|
||||
|
||||
create table t1 (a int, b int, c int, key(a));
|
||||
insert into t1 select A.a*10 + B.a, A.a*10 + B.a, A.a*10 + B.a from t0 A, t0 B;
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze
|
||||
select * from t0, t1 where t1.a=t0.a and t0.a > 9;
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select * from t0, t1 where t1.a=t0.a and t0.a > 9;
|
||||
|
||||
analyze
|
||||
select * from t0, t1 where t1.a=t0.a and t1.b<4;
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select * from t0, t1 where t1.a=t0.a and t1.b<4;
|
||||
|
||||
analyze
|
||||
select * from t1 tbl1, t1 tbl2 where tbl1.b<2 and tbl2.b>5;
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60;
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select * from t1 tbl1, t1 tbl2 where tbl1.b<20 and tbl2.b<60 and tbl1.c > tbl2.c;
|
||||
|
||||
@@ -47,6 +53,7 @@ insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
|
||||
create table t2 (a int, key(a));
|
||||
insert into t2 values (0),(1);
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json select * from t1 straight_join t2 force index(a) where t2.a=t1.a;
|
||||
|
||||
drop table t1,t2;
|
||||
@@ -62,6 +69,7 @@ select database();
|
||||
connect (con1,localhost,root,,*NO-ONE*);
|
||||
connection con1;
|
||||
select database();
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json select * from test.t1 where t1.a<5;
|
||||
disconnect con1;
|
||||
connection default;
|
||||
|
||||
@@ -105,6 +105,7 @@ SET (SQL_SOURCE
|
||||
|
||||
# added in MariaDB:
|
||||
sql_explain.h sql_explain.cc
|
||||
sql_analyze_stmt.h
|
||||
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
|
||||
create_options.cc multi_range_read.cc
|
||||
opt_index_cond_pushdown.cc opt_subselect.cc
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include "debug_sync.h" // DEBUG_SYNC
|
||||
#include "sql_audit.h"
|
||||
|
||||
#include "sql_analyze_stmt.h" // tracker in TABLE_IO_WAIT
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
#include "ha_partition.h"
|
||||
#endif
|
||||
@@ -54,6 +56,17 @@
|
||||
#include "wsrep_mysqld.h"
|
||||
#include "wsrep.h"
|
||||
|
||||
#define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \
|
||||
{ \
|
||||
if (unlikely(tracker)) \
|
||||
tracker->start_tracking(); \
|
||||
\
|
||||
MYSQL_TABLE_IO_WAIT(PSI, OP, INDEX, FLAGS, PAYLOAD); \
|
||||
\
|
||||
if (unlikely(tracker)) \
|
||||
tracker->stop_tracking(); \
|
||||
}
|
||||
|
||||
/*
|
||||
While we have legacy_db_type, we have this array to
|
||||
check for dups and to find handlerton from legacy_db_type.
|
||||
@@ -2566,12 +2579,38 @@ int handler::ha_close(void)
|
||||
status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read);
|
||||
PSI_CALL_close_table(m_psi);
|
||||
m_psi= NULL; /* instrumentation handle, invalid after close_table() */
|
||||
|
||||
/* Detach from ANALYZE tracker */
|
||||
tracker= NULL;
|
||||
|
||||
DBUG_ASSERT(m_lock_type == F_UNLCK);
|
||||
DBUG_ASSERT(inited == NONE);
|
||||
DBUG_RETURN(close());
|
||||
}
|
||||
|
||||
inline int handler::ha_write_tmp_row(uchar *buf)
|
||||
{
|
||||
int error;
|
||||
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
|
||||
increment_statistics(&SSV::ha_tmp_write_count);
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
|
||||
{ error= write_row(buf); })
|
||||
MYSQL_INSERT_ROW_DONE(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
|
||||
{
|
||||
int error;
|
||||
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
|
||||
increment_statistics(&SSV::ha_tmp_update_count);
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
|
||||
{ error= update_row(old_data, new_data);})
|
||||
MYSQL_UPDATE_ROW_DONE(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int handler::ha_rnd_next(uchar *buf)
|
||||
{
|
||||
int result;
|
||||
@@ -2580,7 +2619,7 @@ int handler::ha_rnd_next(uchar *buf)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited == RND);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
|
||||
{ result= rnd_next(buf); })
|
||||
if (!result)
|
||||
{
|
||||
@@ -2605,7 +2644,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos)
|
||||
/* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */
|
||||
/* DBUG_ASSERT(inited == RND); */
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
|
||||
{ result= rnd_pos(buf, pos); })
|
||||
increment_statistics(&SSV::ha_read_rnd_count);
|
||||
if (!result)
|
||||
@@ -2624,7 +2663,7 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key,
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_read_map(buf, key, keypart_map, find_flag); })
|
||||
increment_statistics(&SSV::ha_read_key_count);
|
||||
if (!result)
|
||||
@@ -2648,7 +2687,7 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
|
||||
DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(end_range == NULL);
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, index, 0,
|
||||
{ result= index_read_idx_map(buf, index, key, keypart_map, find_flag); })
|
||||
increment_statistics(&SSV::ha_read_key_count);
|
||||
if (!result)
|
||||
@@ -2668,7 +2707,7 @@ int handler::ha_index_next(uchar * buf)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_next(buf); })
|
||||
increment_statistics(&SSV::ha_read_next_count);
|
||||
if (!result)
|
||||
@@ -2685,7 +2724,7 @@ int handler::ha_index_prev(uchar * buf)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_prev(buf); })
|
||||
increment_statistics(&SSV::ha_read_prev_count);
|
||||
if (!result)
|
||||
@@ -2701,7 +2740,7 @@ int handler::ha_index_first(uchar * buf)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_first(buf); })
|
||||
increment_statistics(&SSV::ha_read_first_count);
|
||||
if (!result)
|
||||
@@ -2717,7 +2756,7 @@ int handler::ha_index_last(uchar * buf)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_last(buf); })
|
||||
increment_statistics(&SSV::ha_read_last_count);
|
||||
if (!result)
|
||||
@@ -2733,7 +2772,7 @@ int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
|
||||
m_lock_type != F_UNLCK);
|
||||
DBUG_ASSERT(inited==INDEX);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
|
||||
{ result= index_next_same(buf, key, keylen); })
|
||||
increment_statistics(&SSV::ha_read_next_count);
|
||||
if (!result)
|
||||
@@ -5857,7 +5896,7 @@ int handler::ha_write_row(uchar *buf)
|
||||
mark_trx_read_write();
|
||||
increment_statistics(&SSV::ha_write_count);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
|
||||
{ error= write_row(buf); })
|
||||
|
||||
MYSQL_INSERT_ROW_DONE(error);
|
||||
@@ -5890,7 +5929,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
|
||||
mark_trx_read_write();
|
||||
increment_statistics(&SSV::ha_update_count);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
|
||||
{ error= update_row(old_data, new_data);})
|
||||
|
||||
MYSQL_UPDATE_ROW_DONE(error);
|
||||
@@ -5918,7 +5957,7 @@ int handler::ha_delete_row(const uchar *buf)
|
||||
mark_trx_read_write();
|
||||
increment_statistics(&SSV::ha_delete_count);
|
||||
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
|
||||
TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
|
||||
{ error= delete_row(buf);})
|
||||
MYSQL_DELETE_ROW_DONE(error);
|
||||
if (unlikely(error))
|
||||
|
||||
@@ -1571,6 +1571,7 @@ typedef struct {
|
||||
|
||||
#define UNDEF_NODEGROUP 65535
|
||||
class Item;
|
||||
class Exec_time_tracker;
|
||||
struct st_table_log_memory_entry;
|
||||
|
||||
class partition_info;
|
||||
@@ -2594,6 +2595,9 @@ public:
|
||||
ulonglong rows_changed;
|
||||
/* One bigger than needed to avoid to test if key == MAX_KEY */
|
||||
ulonglong index_rows_read[MAX_KEY+1];
|
||||
|
||||
/* ANALYZE time tracker, if present */
|
||||
Exec_time_tracker *tracker;
|
||||
|
||||
Item *pushed_idx_cond;
|
||||
uint pushed_idx_cond_keyno; /* The index which the above condition is for */
|
||||
@@ -2648,6 +2652,7 @@ public:
|
||||
ft_handler(0), inited(NONE),
|
||||
implicit_emptied(0),
|
||||
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
|
||||
tracker(NULL),
|
||||
pushed_idx_cond(NULL),
|
||||
pushed_idx_cond_keyno(MAX_KEY),
|
||||
auto_inc_intervals_count(0),
|
||||
|
||||
@@ -314,6 +314,9 @@ arg_cmp_func Arg_comparator::comparator_matrix[6][2] =
|
||||
{&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal},
|
||||
{&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}};
|
||||
|
||||
/* Timer info to be used by the SQL layer */
|
||||
MY_TIMER_INFO sys_timer_info;
|
||||
|
||||
/* static variables */
|
||||
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
@@ -5461,6 +5464,7 @@ int mysqld_main(int argc, char **argv)
|
||||
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
|
||||
pfs_param.m_pfs_instrument= const_cast<char*>("");
|
||||
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
|
||||
my_timer_init(&sys_timer_info);
|
||||
|
||||
int ho_error __attribute__((unused))= handle_early_options();
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "sql_cmd.h"
|
||||
#include <my_rnd.h>
|
||||
#include "my_pthread.h"
|
||||
#include "my_rdtsc.h"
|
||||
|
||||
class THD;
|
||||
struct handlerton;
|
||||
@@ -60,6 +61,8 @@ typedef Bitmap<((MAX_INDEXES+7)/8*8)> key_map; /* Used for finding keys */
|
||||
#define OPT_SESSION SHOW_OPT_SESSION
|
||||
#define OPT_GLOBAL SHOW_OPT_GLOBAL
|
||||
|
||||
extern MY_TIMER_INFO sys_timer_info;
|
||||
|
||||
/*
|
||||
Values for --slave-parallel-mode
|
||||
Must match order in slave_parallel_mode_typelib in sys_vars.cc.
|
||||
|
||||
50
sql/sql_analyze_stmt.h
Normal file
50
sql/sql_analyze_stmt.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (c) 2015 MariaDB Corporation Ab
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
/*
|
||||
A class for tracking time it takes to do a certain action
|
||||
*/
|
||||
class Exec_time_tracker
|
||||
{
|
||||
ulonglong count;
|
||||
ulonglong cycles;
|
||||
ulonglong last_start;
|
||||
public:
|
||||
Exec_time_tracker() : count(0), cycles(0) {}
|
||||
|
||||
// interface for collecting time
|
||||
void start_tracking()
|
||||
{
|
||||
last_start= my_timer_cycles();
|
||||
}
|
||||
|
||||
void stop_tracking()
|
||||
{
|
||||
ulonglong last_end= my_timer_cycles();
|
||||
count++;
|
||||
cycles += last_end - last_start;
|
||||
}
|
||||
|
||||
// interface for getting the time
|
||||
ulonglong get_loops() { return count; }
|
||||
double get_time_ms()
|
||||
{
|
||||
// convert 'cycles' to milliseconds.
|
||||
return 1000 * ((double)cycles) / sys_timer_info.cycles.frequency;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5271,28 +5271,6 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
|
||||
return error;
|
||||
}
|
||||
|
||||
inline int handler::ha_write_tmp_row(uchar *buf)
|
||||
{
|
||||
int error;
|
||||
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
|
||||
increment_statistics(&SSV::ha_tmp_write_count);
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
|
||||
{ error= write_row(buf); })
|
||||
MYSQL_INSERT_ROW_DONE(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
|
||||
{
|
||||
int error;
|
||||
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
|
||||
increment_statistics(&SSV::ha_tmp_update_count);
|
||||
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
|
||||
{ error= update_row(old_data, new_data);})
|
||||
MYSQL_UPDATE_ROW_DONE(error);
|
||||
return error;
|
||||
}
|
||||
|
||||
extern pthread_attr_t *get_connection_attrib(void);
|
||||
|
||||
/**
|
||||
|
||||
@@ -732,7 +732,12 @@ void Explain_select::print_explain_json(Explain_query *query,
|
||||
*/
|
||||
writer->add_member("query_block").start_object();
|
||||
writer->add_member("select_id").add_ll(select_id);
|
||||
|
||||
|
||||
if (is_analyze && time_tracker.get_loops())
|
||||
{
|
||||
writer->add_member("r_loops").add_ll(time_tracker.get_loops());
|
||||
writer->add_member("r_total_time_ms").add_double(time_tracker.get_time_ms());
|
||||
}
|
||||
if (exec_const_cond)
|
||||
{
|
||||
writer->add_member("const_condition");
|
||||
@@ -1289,6 +1294,12 @@ void Explain_table_access::print_explain_json(Explain_query *query,
|
||||
}
|
||||
else
|
||||
writer->add_null();
|
||||
|
||||
if (op_tracker.get_loops())
|
||||
{
|
||||
writer->add_member("r_total_time_ms").
|
||||
add_double(op_tracker.get_time_ms());
|
||||
}
|
||||
}
|
||||
|
||||
/* `filtered` */
|
||||
|
||||
@@ -14,6 +14,44 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
#include "sql_analyze_stmt.h"
|
||||
|
||||
/*
|
||||
|
||||
== EXPLAIN/ANALYZE architecture ==
|
||||
|
||||
=== [SHOW] EXPLAIN data ===
|
||||
Query optimization produces two data structures:
|
||||
1. execution data structures themselves (eg. JOINs, JOIN_TAB, etc, etc)
|
||||
2. Explain data structures.
|
||||
|
||||
#2 are self contained set of data structures that has sufficient info to
|
||||
produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or
|
||||
ANALYZE [FORMAT=JSON], without accessing the execution data structures.
|
||||
|
||||
(the only exception is that Explain data structures keep Item* pointers,
|
||||
and we require that one might call item->print(QT_EXPLAIN) when printing
|
||||
FORMAT=JSON output)
|
||||
|
||||
=== ANALYZE data ===
|
||||
EXPLAIN data structures have embedded ANALYZE data structures. These are
|
||||
objects that are used to track how the parts of query plan were executed:
|
||||
how many times each part of query plan was invoked, how many rows were
|
||||
read/returned, etc.
|
||||
|
||||
Each execution data structure keeps a direct pointer to its ANALYZE data
|
||||
structure. It is needed so that execution code can quickly increment the
|
||||
counters.
|
||||
|
||||
(note that this increases the set of data that is frequently accessed
|
||||
during the execution. What is the impact of this?)
|
||||
|
||||
Since ANALYZE/EXPLAIN data structures are separated from execution data
|
||||
structures, it is easy to have them survive until the end of the query,
|
||||
where we can return ANALYZE [FORMAT=JSON] output to the user, or print
|
||||
it into the slow query log.
|
||||
|
||||
*/
|
||||
|
||||
class String_list: public List<char>
|
||||
{
|
||||
@@ -21,12 +59,16 @@ public:
|
||||
bool append_str(MEM_ROOT *mem_root, const char *str);
|
||||
};
|
||||
|
||||
class Json_writer;
|
||||
|
||||
/*
|
||||
A class for collecting read statistics.
|
||||
|
||||
The idea is that we run several scans. Each scans gets rows, and then filters
|
||||
some of them out. We count scans, rows, and rows left after filtering.
|
||||
|
||||
(note: at the moment, the class is not actually tied to a physical table.
|
||||
It can be used to track reading from files, buffers, etc).
|
||||
*/
|
||||
|
||||
class Table_access_tracker
|
||||
@@ -39,7 +81,6 @@ public:
|
||||
|
||||
ha_rows r_scans; /* How many scans were ran on this join_tab */
|
||||
ha_rows r_rows; /* How many rows we've got after that */
|
||||
// ha_rows r_rows_after_table_cond; /* Rows after applying the table condition */
|
||||
ha_rows r_rows_after_where; /* Rows after applying attached part of WHERE */
|
||||
|
||||
bool has_scans() { return (r_scans != 0); }
|
||||
@@ -65,6 +106,39 @@ public:
|
||||
inline void on_record_after_where() { r_rows_after_where++; }
|
||||
};
|
||||
|
||||
#if 0
|
||||
/*
|
||||
A class to track operations (currently, row reads) on a PSI_table.
|
||||
*/
|
||||
class Table_op_tracker
|
||||
{
|
||||
PSI_table *psi_table;
|
||||
|
||||
/* Table counter values at start. Sum is in picoseconds */
|
||||
ulonglong start_sum;
|
||||
ulonglong start_count;
|
||||
|
||||
/* Table counter values at end */
|
||||
ulonglong end_sum;
|
||||
ulonglong end_count;
|
||||
public:
|
||||
void start_tracking(TABLE *table);
|
||||
// At the moment, print_json will call end_tracking.
|
||||
void end_tracking();
|
||||
|
||||
// this may print nothing if the table was not tracked.
|
||||
void print_json(Json_writer *writer);
|
||||
};
|
||||
#endif
|
||||
|
||||
#define ANALYZE_START_TRACKING(tracker) \
|
||||
if (tracker) \
|
||||
{ tracker->start_tracking(); }
|
||||
|
||||
#define ANALYZE_STOP_TRACKING(tracker) \
|
||||
if (tracker) \
|
||||
{ tracker->stop_tracking(); }
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
|
||||
@@ -82,8 +156,6 @@ const int FAKE_SELECT_LEX_ID= (int)UINT_MAX;
|
||||
|
||||
class Explain_query;
|
||||
|
||||
class Json_writer;
|
||||
|
||||
/*
|
||||
A node can be either a SELECT, or a UNION.
|
||||
*/
|
||||
@@ -231,6 +303,9 @@ public:
|
||||
/* Global join attributes. In tabular form, they are printed on the first row */
|
||||
bool using_temporary;
|
||||
bool using_filesort;
|
||||
|
||||
/* ANALYZE members */
|
||||
Exec_time_tracker time_tracker;
|
||||
|
||||
int print_explain(Explain_query *query, select_result_sink *output,
|
||||
uint8 explain_flags, bool is_analyze);
|
||||
@@ -639,7 +714,7 @@ public:
|
||||
// Valid if ET_USING tag is present
|
||||
Explain_quick_select *quick_info;
|
||||
|
||||
/* Non-NULL values means this tab uses "range checked for each record" */
|
||||
/* Non-NULL value means this tab uses "range checked for each record" */
|
||||
Explain_range_checked_fer *range_checked_fer;
|
||||
|
||||
bool full_scan_on_null_key;
|
||||
@@ -666,6 +741,7 @@ public:
|
||||
|
||||
/* Tracker for reading the table */
|
||||
Table_access_tracker tracker;
|
||||
Exec_time_tracker op_tracker;
|
||||
Table_access_tracker jbuf_tracker;
|
||||
|
||||
int print_explain(select_result_sink *output, uint8 explain_flags,
|
||||
@@ -735,6 +811,7 @@ public:
|
||||
|
||||
/* ANALYZE members and methods */
|
||||
Table_access_tracker tracker;
|
||||
//psergey-todo: io-tracker here.
|
||||
|
||||
virtual int print_explain(Explain_query *query, select_result_sink *output,
|
||||
uint8 explain_flags, bool is_analyze);
|
||||
|
||||
@@ -2390,7 +2390,9 @@ void JOIN::exec()
|
||||
select_lex->select_number))
|
||||
dbug_serve_apcs(thd, 1);
|
||||
);
|
||||
ANALYZE_START_TRACKING(tracker);
|
||||
exec_inner();
|
||||
ANALYZE_STOP_TRACKING(tracker);
|
||||
|
||||
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
|
||||
if (dbug_user_var_equals_int(thd,
|
||||
@@ -23404,6 +23406,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
|
||||
tab->tracker= &eta->tracker;
|
||||
tab->jbuf_tracker= &eta->jbuf_tracker;
|
||||
|
||||
tab->table->file->tracker= &eta->op_tracker;
|
||||
/* id and select_type are kept in Explain_select */
|
||||
|
||||
/* table */
|
||||
@@ -23842,6 +23845,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
||||
Explain_select *xpl_sel;
|
||||
explain_node= xpl_sel= new (output->mem_root) Explain_select(output->mem_root);
|
||||
table_map used_tables=0;
|
||||
tracker= &xpl_sel->time_tracker;
|
||||
|
||||
join->select_lex->set_explain_type(true);
|
||||
xpl_sel->select_id= join->select_lex->select_number;
|
||||
|
||||
@@ -252,6 +252,7 @@ typedef struct st_join_table {
|
||||
enum explain_extra_tag info;
|
||||
|
||||
Table_access_tracker *tracker;
|
||||
|
||||
Table_access_tracker *jbuf_tracker;
|
||||
/*
|
||||
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
|
||||
@@ -1260,6 +1261,8 @@ public:
|
||||
OPTIMIZATION_DONE=2};
|
||||
bool optimized; ///< flag to avoid double optimization in EXPLAIN
|
||||
bool initialized; ///< flag to avoid double init_execution calls
|
||||
|
||||
Exec_time_tracker *tracker;
|
||||
|
||||
enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
|
||||
|
||||
@@ -1354,6 +1357,8 @@ public:
|
||||
no_rows_in_result_called= 0;
|
||||
positions= best_positions= 0;
|
||||
|
||||
tracker= NULL;
|
||||
|
||||
all_fields= fields_arg;
|
||||
if (&fields_list != &fields_arg) /* Avoid valgrind-warning */
|
||||
fields_list= fields_arg;
|
||||
|
||||
Reference in New Issue
Block a user