Commit 8964dbd5 authored by Itagaki Takahiro's avatar Itagaki Takahiro

Add buffer access counters to pg_stat_statements.

This uses the same infrastructure with EXPLAIN BUFFERS to support
{shared|local}_blks_{hit|read|written} andtemp_blks_{read|written}
columns in the pg_stat_statements view. The dumped file format
also updated.

Thanks to Robert Haas for the review.
parent b1ee6daf
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* Copyright (c) 2008-2010, PostgreSQL Global Development Group * Copyright (c) 2008-2010, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.c,v 1.11 2010/01/02 16:57:32 momjian Exp $ * $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.c,v 1.12 2010/01/08 00:38:19 itagaki Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/instrument.h" #include "executor/instrument.h"
#include "funcapi.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
...@@ -44,7 +45,7 @@ PG_MODULE_MAGIC; ...@@ -44,7 +45,7 @@ PG_MODULE_MAGIC;
#define PGSS_DUMP_FILE "global/pg_stat_statements.stat" #define PGSS_DUMP_FILE "global/pg_stat_statements.stat"
/* This constant defines the magic number in the stats file header */ /* This constant defines the magic number in the stats file header */
static const uint32 PGSS_FILE_HEADER = 0x20081202; static const uint32 PGSS_FILE_HEADER = 0x20100108;
/* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
#define USAGE_EXEC(duration) (1.0) #define USAGE_EXEC(duration) (1.0)
...@@ -75,10 +76,18 @@ typedef struct pgssHashKey ...@@ -75,10 +76,18 @@ typedef struct pgssHashKey
*/ */
typedef struct Counters typedef struct Counters
{ {
int64 calls; /* # of times executed */ int64 calls; /* # of times executed */
double total_time; /* total execution time in seconds */ double total_time; /* total execution time in seconds */
int64 rows; /* total # of retrieved or affected rows */ int64 rows; /* total # of retrieved or affected rows */
double usage; /* usage factor */ int64 shared_blks_hit; /* # of shared buffer hits */
int64 shared_blks_read; /* # of shared disk blocks read */
int64 shared_blks_written;/* # of shared disk blocks written */
int64 local_blks_hit; /* # of local buffer hits */
int64 local_blks_read; /* # of local disk blocks read */
int64 local_blks_written; /* # of local disk blocks written */
int64 temp_blks_read; /* # of temp blocks read */
int64 temp_blks_written; /* # of temp blocks written */
double usage; /* usage factor */
} Counters; } Counters;
/* /*
...@@ -129,7 +138,8 @@ typedef enum ...@@ -129,7 +138,8 @@ typedef enum
PGSS_TRACK_ALL /* all statements, including nested ones */ PGSS_TRACK_ALL /* all statements, including nested ones */
} PGSSTrackLevel; } PGSSTrackLevel;
static const struct config_enum_entry track_options[] = { static const struct config_enum_entry track_options[] =
{
{"none", PGSS_TRACK_NONE, false}, {"none", PGSS_TRACK_NONE, false},
{"top", PGSS_TRACK_TOP, false}, {"top", PGSS_TRACK_TOP, false},
{"all", PGSS_TRACK_ALL, false}, {"all", PGSS_TRACK_ALL, false},
...@@ -169,7 +179,8 @@ static void pgss_ProcessUtility(Node *parsetree, ...@@ -169,7 +179,8 @@ static void pgss_ProcessUtility(Node *parsetree,
DestReceiver *dest, char *completionTag); DestReceiver *dest, char *completionTag);
static uint32 pgss_hash_fn(const void *key, Size keysize); static uint32 pgss_hash_fn(const void *key, Size keysize);
static int pgss_match_fn(const void *key1, const void *key2, Size keysize); static int pgss_match_fn(const void *key1, const void *key2, Size keysize);
static void pgss_store(const char *query, double total_time, uint64 rows); static void pgss_store(const char *query, double total_time, uint64 rows,
const BufferUsage *bufusage);
static Size pgss_memsize(void); static Size pgss_memsize(void);
static pgssEntry *entry_alloc(pgssHashKey *key); static pgssEntry *entry_alloc(pgssHashKey *key);
static void entry_dealloc(void); static void entry_dealloc(void);
...@@ -558,7 +569,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc) ...@@ -558,7 +569,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
pgss_store(queryDesc->sourceText, pgss_store(queryDesc->sourceText,
queryDesc->totaltime->total, queryDesc->totaltime->total,
queryDesc->estate->es_processed); queryDesc->estate->es_processed,
&queryDesc->totaltime->bufusage);
} }
if (prev_ExecutorEnd) if (prev_ExecutorEnd)
...@@ -580,7 +592,9 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, ...@@ -580,7 +592,9 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
instr_time start; instr_time start;
instr_time duration; instr_time duration;
uint64 rows = 0; uint64 rows = 0;
BufferUsage bufusage;
bufusage = pgBufferUsage;
INSTR_TIME_SET_CURRENT(start); INSTR_TIME_SET_CURRENT(start);
nested_level++; nested_level++;
...@@ -609,7 +623,26 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, ...@@ -609,7 +623,26 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
sscanf(completionTag, "COPY " UINT64_FORMAT, &rows) != 1) sscanf(completionTag, "COPY " UINT64_FORMAT, &rows) != 1)
rows = 0; rows = 0;
pgss_store(queryString, INSTR_TIME_GET_DOUBLE(duration), rows); /* calc differences of buffer counters. */
bufusage.shared_blks_hit =
pgBufferUsage.shared_blks_hit - bufusage.shared_blks_hit;
bufusage.shared_blks_read =
pgBufferUsage.shared_blks_read - bufusage.shared_blks_read;
bufusage.shared_blks_written =
pgBufferUsage.shared_blks_written - bufusage.shared_blks_written;
bufusage.local_blks_hit =
pgBufferUsage.local_blks_hit - bufusage.local_blks_hit;
bufusage.local_blks_read =
pgBufferUsage.local_blks_read - bufusage.local_blks_read;
bufusage.local_blks_written =
pgBufferUsage.local_blks_written - bufusage.local_blks_written;
bufusage.temp_blks_read =
pgBufferUsage.temp_blks_read - bufusage.temp_blks_read;
bufusage.temp_blks_written =
pgBufferUsage.temp_blks_written - bufusage.temp_blks_written;
pgss_store(queryString, INSTR_TIME_GET_DOUBLE(duration), rows,
&bufusage);
} }
else else
{ {
...@@ -660,7 +693,8 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize) ...@@ -660,7 +693,8 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
* Store some statistics for a statement. * Store some statistics for a statement.
*/ */
static void static void
pgss_store(const char *query, double total_time, uint64 rows) pgss_store(const char *query, double total_time, uint64 rows,
const BufferUsage *bufusage)
{ {
pgssHashKey key; pgssHashKey key;
double usage; double usage;
...@@ -706,6 +740,14 @@ pgss_store(const char *query, double total_time, uint64 rows) ...@@ -706,6 +740,14 @@ pgss_store(const char *query, double total_time, uint64 rows)
e->counters.calls += 1; e->counters.calls += 1;
e->counters.total_time += total_time; e->counters.total_time += total_time;
e->counters.rows += rows; e->counters.rows += rows;
e->counters.shared_blks_hit += bufusage->shared_blks_hit;
e->counters.shared_blks_read += bufusage->shared_blks_read;
e->counters.shared_blks_written += bufusage->shared_blks_written;
e->counters.local_blks_hit += bufusage->local_blks_hit;
e->counters.local_blks_read += bufusage->local_blks_read;
e->counters.local_blks_written += bufusage->local_blks_written;
e->counters.temp_blks_read += bufusage->temp_blks_read;
e->counters.temp_blks_written += bufusage->temp_blks_written;
e->counters.usage += usage; e->counters.usage += usage;
SpinLockRelease(&e->mutex); SpinLockRelease(&e->mutex);
} }
...@@ -727,7 +769,7 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS) ...@@ -727,7 +769,7 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
PG_RETURN_VOID(); PG_RETURN_VOID();
} }
#define PG_STAT_STATEMENTS_COLS 6 #define PG_STAT_STATEMENTS_COLS 14
/* /*
* Retrieve statement statistics. * Retrieve statement statistics.
...@@ -761,23 +803,13 @@ pg_stat_statements(PG_FUNCTION_ARGS) ...@@ -761,23 +803,13 @@ pg_stat_statements(PG_FUNCTION_ARGS)
errmsg("materialize mode required, but it is not " \ errmsg("materialize mode required, but it is not " \
"allowed in this context"))); "allowed in this context")));
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
oldcontext = MemoryContextSwitchTo(per_query_ctx); oldcontext = MemoryContextSwitchTo(per_query_ctx);
tupdesc = CreateTemplateTupleDesc(PG_STAT_STATEMENTS_COLS, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "userid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "dbid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "query",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "calls",
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "total_time",
FLOAT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "rows",
INT8OID, -1, 0);
tupstore = tuplestore_begin_heap(true, false, work_mem); tupstore = tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode = SFRM_Materialize; rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tupstore; rsinfo->setResult = tupstore;
...@@ -829,6 +861,14 @@ pg_stat_statements(PG_FUNCTION_ARGS) ...@@ -829,6 +861,14 @@ pg_stat_statements(PG_FUNCTION_ARGS)
values[i++] = Int64GetDatumFast(tmp.calls); values[i++] = Int64GetDatumFast(tmp.calls);
values[i++] = Float8GetDatumFast(tmp.total_time); values[i++] = Float8GetDatumFast(tmp.total_time);
values[i++] = Int64GetDatumFast(tmp.rows); values[i++] = Int64GetDatumFast(tmp.rows);
values[i++] = Int64GetDatumFast(tmp.shared_blks_hit);
values[i++] = Int64GetDatumFast(tmp.shared_blks_read);
values[i++] = Int64GetDatumFast(tmp.shared_blks_written);
values[i++] = Int64GetDatumFast(tmp.local_blks_hit);
values[i++] = Int64GetDatumFast(tmp.local_blks_read);
values[i++] = Int64GetDatumFast(tmp.local_blks_written);
values[i++] = Int64GetDatumFast(tmp.temp_blks_read);
values[i++] = Int64GetDatumFast(tmp.temp_blks_written);
Assert(i == PG_STAT_STATEMENTS_COLS); Assert(i == PG_STAT_STATEMENTS_COLS);
......
/* $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.sql.in,v 1.1 2009/01/04 22:19:59 tgl Exp $ */ /* $PostgreSQL: pgsql/contrib/pg_stat_statements/pg_stat_statements.sql.in,v 1.2 2010/01/08 00:38:19 itagaki Exp $ */
-- Adjust this setting to control where the objects get created. -- Adjust this setting to control where the objects get created.
SET search_path = public; SET search_path = public;
...@@ -15,7 +15,15 @@ CREATE FUNCTION pg_stat_statements( ...@@ -15,7 +15,15 @@ CREATE FUNCTION pg_stat_statements(
OUT query text, OUT query text,
OUT calls int8, OUT calls int8,
OUT total_time float8, OUT total_time float8,
OUT rows int8 OUT rows int8,
OUT shared_blks_hit int8,
OUT shared_blks_read int8,
OUT shared_blks_written int8,
OUT local_blks_hit int8,
OUT local_blks_read int8,
OUT local_blks_written int8,
OUT temp_blks_read int8,
OUT temp_blks_written int8
) )
RETURNS SETOF record RETURNS SETOF record
AS 'MODULE_PATHNAME' AS 'MODULE_PATHNAME'
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/pgstatstatements.sgml,v 1.5 2009/12/15 20:04:49 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/pgstatstatements.sgml,v 1.6 2010/01/08 00:38:20 itagaki Exp $ -->
<sect1 id="pgstatstatements"> <sect1 id="pgstatstatements">
<title>pg_stat_statements</title> <title>pg_stat_statements</title>
...@@ -85,6 +85,62 @@ ...@@ -85,6 +85,62 @@
<entry>Total number of rows retrieved or affected by the statement</entry> <entry>Total number of rows retrieved or affected by the statement</entry>
</row> </row>
<row>
<entry><structfield>shared_blks_hit</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of shared blocks hits by the statement</entry>
</row>
<row>
<entry><structfield>shared_blks_read</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of shared blocks reads by the statement</entry>
</row>
<row>
<entry><structfield>shared_blks_written</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of shared blocks writes by the statement</entry>
</row>
<row>
<entry><structfield>local_blks_hit</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of local blocks hits by the statement</entry>
</row>
<row>
<entry><structfield>local_blks_read</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of local blocks reads by the statement</entry>
</row>
<row>
<entry><structfield>local_blks_written</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of local blocks writes by the statement</entry>
</row>
<row>
<entry><structfield>temp_blks_read</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of temp blocks reads by the statement</entry>
</row>
<row>
<entry><structfield>temp_blks_written</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Total number of temp blocks writes by the statement</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
...@@ -239,35 +295,45 @@ pg_stat_statements.track = all ...@@ -239,35 +295,45 @@ pg_stat_statements.track = all
<title>Sample output</title> <title>Sample output</title>
<programlisting> <programlisting>
$ pgbench -i bench bench=# SELECT pg_stat_statements_reset();
postgres=# SELECT pg_stat_statements_reset();
$ pgbench -i bench
$ pgbench -c10 -t300 -M prepared bench $ pgbench -c10 -t300 -M prepared bench
postgres=# \x bench=# \x
postgres=# SELECT * FROM pg_stat_statements ORDER BY total_time DESC LIMIT 3; bench=# SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit /
-[ RECORD 1 ]------------------------------------------------------------ nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
userid | 10 FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
dbid | 63781 -[ RECORD 1 ]---------------------------------------------------------------------
query | UPDATE branches SET bbalance = bbalance + $1 WHERE bid = $2; query | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2;
calls | 3000 calls | 3000
total_time | 20.716706 total_time | 9.60900100000002
rows | 3000 rows | 2836
-[ RECORD 2 ]------------------------------------------------------------ hit_percent | 99.9778970000200936
userid | 10 -[ RECORD 2 ]---------------------------------------------------------------------
dbid | 63781 query | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2;
query | UPDATE tellers SET tbalance = tbalance + $1 WHERE tid = $2; calls | 3000
calls | 3000 total_time | 8.015156
total_time | 17.1107649999999 rows | 2990
rows | 3000 hit_percent | 99.9731126579631345
-[ RECORD 3 ]------------------------------------------------------------ -[ RECORD 3 ]---------------------------------------------------------------------
userid | 10 query | copy pgbench_accounts from stdin
dbid | 63781 calls | 1
query | UPDATE accounts SET abalance = abalance + $1 WHERE aid = $2; total_time | 0.310624
calls | 3000 rows | 100000
total_time | 0.645601 hit_percent | 0.30395136778115501520
rows | 3000 -[ RECORD 4 ]---------------------------------------------------------------------
query | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2;
calls | 3000
total_time | 0.271741999999997
rows | 3000
hit_percent | 93.7968855088209426
-[ RECORD 5 ]---------------------------------------------------------------------
query | alter table pgbench_accounts add primary key (aid)
calls | 1
total_time | 0.08142
rows | 0
hit_percent | 34.4947735191637631
</programlisting> </programlisting>
</sect2> </sect2>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment