MDEV-7811: EXPLAIN/ANALYZE FORMAT=JSON should show subquery cache
This commit is contained in:
parent
498a264d19
commit
c6aee27b73
@ -39,6 +39,240 @@ Handler_read_prev 0
|
||||
Handler_read_rnd 0
|
||||
Handler_read_rnd_deleted 0
|
||||
Handler_read_rnd_next 31
|
||||
analyze format=json
|
||||
select a, (select d from t2 where b=c) from t1;
|
||||
ANALYZE
|
||||
{
|
||||
"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
|
||||
},
|
||||
"subqueries": [
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"r_loops": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"expression_cache": {
|
||||
"state": "ENABLED",
|
||||
"r_hit": 6,
|
||||
"r_miss": 4,
|
||||
"r_loops": 10,
|
||||
"r_hit_ratio": 60
|
||||
},
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 4,
|
||||
"rows": 4,
|
||||
"r_rows": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 18.75,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
analyze format=json
|
||||
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
|
||||
ANALYZE
|
||||
{
|
||||
"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
|
||||
},
|
||||
"subqueries": [
|
||||
{
|
||||
"query_block": {
|
||||
"union_result": {
|
||||
"table_name": "<union3,4>",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 4,
|
||||
"r_rows": 1,
|
||||
"expression_cache": {
|
||||
"state": "ENABLED",
|
||||
"r_hit": 6,
|
||||
"r_miss": 4,
|
||||
"r_loops": 10,
|
||||
"r_hit_ratio": 60
|
||||
},
|
||||
"query_specifications": [
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 3,
|
||||
"r_loops": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 4,
|
||||
"rows": 4,
|
||||
"r_rows": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 18.75,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 4,
|
||||
"table": {
|
||||
"message": "No tables used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"r_loops": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"expression_cache": {
|
||||
"state": "ENABLED",
|
||||
"r_hit": 6,
|
||||
"r_miss": 4,
|
||||
"r_loops": 10,
|
||||
"r_hit_ratio": 60
|
||||
},
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"r_loops": 4,
|
||||
"rows": 4,
|
||||
"r_rows": 4,
|
||||
"r_total_time_ms": "REPLACED",
|
||||
"filtered": 100,
|
||||
"r_filtered": 18.75,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
explain format=json
|
||||
select a, (select d from t2 where b=c) from t1;
|
||||
EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
"rows": 10,
|
||||
"filtered": 100
|
||||
},
|
||||
"subqueries": [
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"expression_cache": {
|
||||
"state": "UNINITIALYZED"
|
||||
},
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"rows": 4,
|
||||
"filtered": 100,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
explain format=json
|
||||
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
|
||||
EXPLAIN
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 1,
|
||||
"table": {
|
||||
"table_name": "t1",
|
||||
"access_type": "ALL",
|
||||
"rows": 10,
|
||||
"filtered": 100
|
||||
},
|
||||
"subqueries": [
|
||||
{
|
||||
"query_block": {
|
||||
"union_result": {
|
||||
"table_name": "<union3,4>",
|
||||
"access_type": "ALL",
|
||||
"expression_cache": {
|
||||
"state": "UNINITIALYZED"
|
||||
},
|
||||
"query_specifications": [
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 3,
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"rows": 4,
|
||||
"filtered": 100,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 4,
|
||||
"table": {
|
||||
"message": "No tables used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_block": {
|
||||
"select_id": 2,
|
||||
"expression_cache": {
|
||||
"state": "UNINITIALYZED"
|
||||
},
|
||||
"table": {
|
||||
"table_name": "t2",
|
||||
"access_type": "ALL",
|
||||
"rows": 4,
|
||||
"filtered": 100,
|
||||
"attached_condition": "(t1.b = t2.c)"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
set optimizer_switch='subquery_cache=off';
|
||||
flush status;
|
||||
select a, (select d from t2 where b=c) from t1;
|
||||
|
@ -24,6 +24,16 @@ select a, (select d from t2 where b=c) from t1;
|
||||
show status like "subquery_cache%";
|
||||
show status like '%Handler_read%';
|
||||
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select a, (select d from t2 where b=c) from t1;
|
||||
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
|
||||
analyze format=json
|
||||
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
|
||||
explain format=json
|
||||
select a, (select d from t2 where b=c) from t1;
|
||||
explain format=json
|
||||
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
|
||||
set optimizer_switch='subquery_cache=off';
|
||||
flush status;
|
||||
|
||||
|
13
sql/item.cc
13
sql/item.cc
@ -7554,6 +7554,19 @@ bool Item_cache_wrapper::set_cache(THD *thd)
|
||||
DBUG_RETURN(expr_cache == NULL);
|
||||
}
|
||||
|
||||
Expression_cache_stat* Item_cache_wrapper::set_stat(MEM_ROOT *mem_root)
|
||||
{
|
||||
if (expr_cache)
|
||||
{
|
||||
Expression_cache_stat* stat=
|
||||
new(mem_root) Expression_cache_stat(expr_cache);
|
||||
if (stat)
|
||||
((Expression_cache_tmptable *)expr_cache)->set_stat(stat);
|
||||
return stat;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check if the current values of the parameters are in the expression cache
|
||||
|
@ -3804,6 +3804,7 @@ public:
|
||||
|
||||
class Item_cache;
|
||||
class Expression_cache;
|
||||
class Expression_cache_stat;
|
||||
|
||||
/**
|
||||
The objects of this class can store its values in an expression cache.
|
||||
@ -3838,6 +3839,7 @@ public:
|
||||
enum Type real_type() const { return orig_item->type(); }
|
||||
|
||||
bool set_cache(THD *thd);
|
||||
Expression_cache_stat* set_stat(MEM_ROOT *mem_root);
|
||||
|
||||
bool fix_fields(THD *thd, Item **it);
|
||||
void cleanup();
|
||||
|
@ -1201,7 +1201,10 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(uchar *thd_arg)
|
||||
|
||||
if (expr_cache_is_needed(thd) &&
|
||||
(expr_cache= set_expr_cache(thd)))
|
||||
{
|
||||
set_expr_cache_stat(thd);
|
||||
DBUG_RETURN(expr_cache);
|
||||
}
|
||||
DBUG_RETURN(this);
|
||||
}
|
||||
|
||||
@ -1497,7 +1500,10 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg)
|
||||
|
||||
if (substype() == EXISTS_SUBS && expr_cache_is_needed(thd) &&
|
||||
(expr_cache= set_expr_cache(thd)))
|
||||
{
|
||||
set_expr_cache_stat(thd);
|
||||
DBUG_RETURN(expr_cache);
|
||||
}
|
||||
DBUG_RETURN(this);
|
||||
}
|
||||
|
||||
@ -6556,3 +6562,16 @@ void subselect_table_scan_engine::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void Item_subselect::set_expr_cache_stat(THD *thd)
|
||||
{
|
||||
if(!expr_cache)
|
||||
return;
|
||||
|
||||
Explain_query *qw= thd->lex->explain;
|
||||
DBUG_ASSERT(qw);
|
||||
Explain_node *node= qw->get_node(unit->first_select()->select_number);
|
||||
if (!node)
|
||||
return;
|
||||
DBUG_ASSERT(expr_cache->type() == Item::EXPR_CACHE_ITEM);
|
||||
node->cache_stat= ((Item_cache_wrapper *)expr_cache)->set_stat(qw->mem_root);
|
||||
}
|
||||
|
@ -244,7 +244,10 @@ public:
|
||||
bool limit_index_condition_pushdown_processor(uchar *opt_arg)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void set_expr_cache_stat(THD *thd);
|
||||
|
||||
|
||||
friend class select_result_interceptor;
|
||||
friend class Item_in_optimizer;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "sql_select.h"
|
||||
#include "my_json_writer.h"
|
||||
#include "opt_range.h"
|
||||
#include "sql_expression_cache.h"
|
||||
|
||||
const char * STR_DELETING_ALL_ROWS= "Deleting all rows";
|
||||
const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE";
|
||||
@ -543,6 +544,7 @@ void Explain_union::print_explain_json(Explain_query *query,
|
||||
writer->add_null();
|
||||
}
|
||||
|
||||
print_explain_json_cache(writer, is_analyze);
|
||||
writer->add_member("query_specifications").start_array();
|
||||
|
||||
for (int i= 0; i < (int) union_members.elements(); i++)
|
||||
@ -640,6 +642,30 @@ void Explain_node::print_explain_json_for_children(Explain_query *query,
|
||||
}
|
||||
|
||||
|
||||
void Explain_node::print_explain_json_cache(Json_writer *writer,
|
||||
bool is_analyze)
|
||||
{
|
||||
if (cache_stat)
|
||||
{
|
||||
cache_stat->flush_stat();
|
||||
writer->add_member("expression_cache").start_object();
|
||||
writer->add_member("state").
|
||||
add_str(Expression_cache_stat::state_str[cache_stat->state]);
|
||||
if (is_analyze)
|
||||
{
|
||||
writer->add_member("r_hit").add_ll(cache_stat->hit);
|
||||
writer->add_member("r_miss").add_ll(cache_stat->miss);
|
||||
writer->add_member("r_loops").add_ll(cache_stat->hit +
|
||||
cache_stat->miss);
|
||||
writer->add_member("r_hit_ratio").add_ll(((double)cache_stat->hit)/
|
||||
((double)(cache_stat->hit +
|
||||
cache_stat->miss)) * 100.0);
|
||||
}
|
||||
writer->end_object();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
|
||||
{
|
||||
delete join_tabs[idx];
|
||||
@ -757,17 +783,19 @@ 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");
|
||||
write_item(writer, exec_const_cond);
|
||||
}
|
||||
print_explain_json_cache(writer, is_analyze);
|
||||
|
||||
Filesort_tracker *first_table_sort= NULL;
|
||||
bool first_table_sort_used= false;
|
||||
|
@ -84,9 +84,10 @@ class Explain_query;
|
||||
class Explain_node : public Sql_alloc
|
||||
{
|
||||
public:
|
||||
Explain_node(MEM_ROOT *root) :
|
||||
connection_type(EXPLAIN_NODE_OTHER),
|
||||
children(root)
|
||||
Explain_node(MEM_ROOT *root) :
|
||||
cache_stat(NULL),
|
||||
connection_type(EXPLAIN_NODE_OTHER),
|
||||
children(root)
|
||||
{}
|
||||
/* A type specifying what kind of node this is */
|
||||
enum explain_node_type
|
||||
@ -106,10 +107,14 @@ public:
|
||||
EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */
|
||||
};
|
||||
|
||||
|
||||
virtual enum explain_node_type get_type()= 0;
|
||||
virtual int get_select_id()= 0;
|
||||
|
||||
/**
|
||||
expression cache statistics
|
||||
*/
|
||||
Expression_cache_stat* cache_stat;
|
||||
|
||||
/*
|
||||
How this node is connected to its parent.
|
||||
(NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently)
|
||||
@ -135,6 +140,7 @@ public:
|
||||
uint8 explain_flags, bool is_analyze);
|
||||
void print_explain_json_for_children(Explain_query *query,
|
||||
Json_writer *writer, bool is_analyze);
|
||||
void print_explain_json_cache(Json_writer *writer, bool is_analyze);
|
||||
virtual ~Explain_node(){}
|
||||
};
|
||||
|
||||
@ -221,7 +227,7 @@ public:
|
||||
members have no info
|
||||
*/
|
||||
const char *message;
|
||||
|
||||
|
||||
/* Expensive constant condition */
|
||||
Item *exec_const_cond;
|
||||
|
||||
|
@ -43,7 +43,7 @@ ulong subquery_cache_miss, subquery_cache_hit;
|
||||
Expression_cache_tmptable::Expression_cache_tmptable(THD *thd,
|
||||
List<Item> &dependants,
|
||||
Item *value)
|
||||
:cache_table(NULL), table_thd(thd), items(dependants), val(value),
|
||||
:cache_table(NULL), table_thd(thd), stat(NULL), items(dependants), val(value),
|
||||
hit(0), miss(0), inited (0)
|
||||
{
|
||||
DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable");
|
||||
@ -61,6 +61,9 @@ void Expression_cache_tmptable::disable_cache()
|
||||
cache_table->file->ha_index_end();
|
||||
free_tmp_table(table_thd, cache_table);
|
||||
cache_table= NULL;
|
||||
flush_stat();
|
||||
if (stat)
|
||||
stat->cache= NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -164,6 +167,7 @@ void Expression_cache_tmptable::init()
|
||||
goto error;
|
||||
}
|
||||
|
||||
flush_stat();
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
error:
|
||||
@ -180,6 +184,11 @@ Expression_cache_tmptable::~Expression_cache_tmptable()
|
||||
|
||||
if (cache_table)
|
||||
disable_cache();
|
||||
else
|
||||
{
|
||||
flush_stat();
|
||||
stat= NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -323,3 +332,7 @@ void Expression_cache_tmptable::print(String *str, enum_query_type query_type)
|
||||
}
|
||||
str->append('>');
|
||||
}
|
||||
|
||||
|
||||
const char *Expression_cache_stat::state_str[3]=
|
||||
{"UNINITIALYZED", "DISABLED", "ENABLED"};
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "sql_select.h"
|
||||
|
||||
|
||||
/**
|
||||
Interface for expression cache
|
||||
|
||||
@ -62,6 +63,11 @@ public:
|
||||
Initialize this cache
|
||||
*/
|
||||
virtual void init()= 0;
|
||||
|
||||
/**
|
||||
Save this object's statistics into Expression_cache_stat object
|
||||
*/
|
||||
virtual void flush_stat()= 0;
|
||||
};
|
||||
|
||||
struct st_table_ref;
|
||||
@ -69,6 +75,30 @@ struct st_join_table;
|
||||
class Item_field;
|
||||
|
||||
|
||||
class Expression_cache_stat :public Sql_alloc
|
||||
{
|
||||
public:
|
||||
enum expr_cache_state {UNINITED, STOPPED, OK};
|
||||
Expression_cache_stat(Expression_cache *c) :
|
||||
cache(c), hit(0), miss(0), state(UNINITED)
|
||||
{}
|
||||
|
||||
Expression_cache *cache;
|
||||
ulong hit, miss;
|
||||
enum expr_cache_state state;
|
||||
|
||||
static const char* state_str[3];
|
||||
void set(ulong h, ulong m, enum expr_cache_state s)
|
||||
{hit= h; miss= m; state= s;}
|
||||
|
||||
void flush_stat()
|
||||
{
|
||||
if (cache)
|
||||
cache->flush_stat();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Implementation of expression cache over a temporary table
|
||||
*/
|
||||
@ -85,6 +115,20 @@ public:
|
||||
bool is_inited() { return inited; };
|
||||
void init();
|
||||
|
||||
void set_stat(Expression_cache_stat *st)
|
||||
{
|
||||
stat= st;
|
||||
flush_stat();
|
||||
}
|
||||
virtual void flush_stat()
|
||||
{
|
||||
if (stat)
|
||||
stat->set(hit, miss, (inited ? (cache_table ?
|
||||
Expression_cache_stat::OK :
|
||||
Expression_cache_stat::STOPPED) :
|
||||
Expression_cache_stat::UNINITED));
|
||||
}
|
||||
|
||||
private:
|
||||
void disable_cache();
|
||||
|
||||
@ -94,6 +138,8 @@ private:
|
||||
TABLE *cache_table;
|
||||
/* Thread handle for the temporary table */
|
||||
THD *table_thd;
|
||||
/* EXPALIN/ANALYZE statistics */
|
||||
Expression_cache_stat *stat;
|
||||
/* TABLE_REF for index lookup */
|
||||
struct st_table_ref ref;
|
||||
/* Cached result */
|
||||
@ -103,7 +149,7 @@ private:
|
||||
/* Value Item example */
|
||||
Item *val;
|
||||
/* hit/miss counters */
|
||||
uint hit, miss;
|
||||
ulong hit, miss;
|
||||
/* Set on if the object has been succesfully initialized with init() */
|
||||
bool inited;
|
||||
};
|
||||
|
@ -3505,6 +3505,8 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
|
||||
|
||||
bool empty_union_result= true;
|
||||
bool is_correlated_unit= false;
|
||||
bool first= true;
|
||||
bool union_plan_saved= false;
|
||||
/*
|
||||
If the subquery is a UNION, optimize all the subqueries in the UNION. If
|
||||
there is no UNION, then the loop will execute once for the subquery.
|
||||
@ -3512,6 +3514,17 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
|
||||
for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
|
||||
{
|
||||
JOIN *inner_join= sl->join;
|
||||
if (first)
|
||||
first= false;
|
||||
else
|
||||
{
|
||||
if (!union_plan_saved)
|
||||
{
|
||||
union_plan_saved= true;
|
||||
if (un->save_union_explain(un->thd->lex->explain))
|
||||
return true; /* Failure */
|
||||
}
|
||||
}
|
||||
if (!inner_join)
|
||||
continue;
|
||||
SELECT_LEX *save_select= un->thd->lex->current_select;
|
||||
@ -4365,10 +4378,15 @@ void LEX::restore_set_statement_var()
|
||||
int st_select_lex_unit::save_union_explain(Explain_query *output)
|
||||
{
|
||||
SELECT_LEX *first= first_select();
|
||||
|
||||
if (output->get_union(first->select_number))
|
||||
return 0; /* Already added */
|
||||
|
||||
Explain_union *eu=
|
||||
new (output->mem_root) Explain_union(output->mem_root,
|
||||
thd->lex->analyze_stmt);
|
||||
|
||||
|
||||
if (derived)
|
||||
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user