pg_stat_database的stats_reset是什么时间

pg_stat_database的stats_reset是什么时间

pg_stat_database记录数据库的一些统计信息,通常用于业务监控。
例如:
监控事务提交数量/数据库插入数据量等等

在我的工作中遇到一个监控事务提交数量的需求,如果你也有这样的需求或是你已经完成了这个需求,我非常强烈的建议你阅读完本章的内容。

监控数据库业务流量

监控数据库业务流量会用到pg_stat_database

  • pg_stat_database

pg_stat_database是什么?

test=# \d pg_stat_database
                     View "pg_catalog.pg_stat_database"

test=# select definition from pg_views where viewname='pg_stat_database'; 
                                         definition 
--------------------------------------------------------------------------------------------- 
  SELECT d.oid AS datid, 
     d.datname, 
     pg_stat_get_db_numbackends(d.oid) AS numbackends, 
     pg_stat_get_db_xact_commit(d.oid) AS xact_commit, 
     pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, 
     (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, 
     pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, 
     pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, 
     pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, 
     pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, 
     pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, 
     pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, 
     pg_stat_get_db_conflict_all(d.oid) AS conflicts, 
     pg_stat_get_db_temp_files(d.oid) AS temp_files, 
     pg_stat_get_db_temp_bytes(d.oid) AS temp_bytes, 
     pg_stat_get_db_deadlocks(d.oid) AS deadlocks, 
     pg_stat_get_db_blk_read_time(d.oid) AS blk_read_time, 
     pg_stat_get_db_blk_write_time(d.oid) AS blk_write_time, 
     pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset 
    FROM pg_database d; 
(1 row) 

pg_stat_database视图的描述

类型 描述
datid oid 数据库的 OID
datname name 数据库的名称
numbackends integer 当前连接到这个数据库的后端数量。这是在这个视图中唯一一个返回反映当前状态值的列。所有其他列返回从上次重置以来积累的值。
xact_commit bigint 在这个数据库中已经被提交的事务的数量
xact_rollback bigint 在这个数据库中已经被回滚的事务的数量
blks_read bigint 在这个数据库中被读取的磁盘块的数量
blks_hit bigint 磁盘块被发现已经在缓冲区中的次数,这样不需要一次读取(这只包括 PostgreSQL 缓冲区中的命中,而不包括在操作系统文件系统缓冲区中的命中)
tup_returned bigint 在这个数据库中被查询返回的行数
tup_fetched bigint 在这个数据库中被查询取出的行数
tup_inserted bigint 在这个数据库中插入的行数
tup_updated bigint 在这个数据库中更新的行数
tup_deleted bigint 在这个数据库中删除的行数
conflicts bigint 由于与恢复冲突而在这个数据库中被取消的查询的数目(冲突只发生在后备服务器上,详见pg_stat_database_conflicts)。
temp_files bigint 在这个数据库中被查询创建的临时文件的数量。所有临时文件都被统计,不管为什么创建这些临时文件(如排序或哈希),并且不管log_temp_files设置。
temp_bytes bigint 在这个数据库中被查询写到临时文件中的数据总量。所有临时文件都被统计,不管为什么创建这些临时文件(如排序或哈希),并且不管log_temp_files设置。
deadlock bigint 在这个数据库中被检测到的死锁数
blk_read_time double precision 在这个数据库中后端花费在读取数据文件块的时间,以毫秒计
blk_write_time double precision 在这个数据库中后端花费在写数据文件块的时间,以毫秒计
stats_reset timestamp with time zone 这些统计信息上次被重置的时间(Time at which these statistics were last reset)

stats_reset本章的主角并引用了未翻译的原英文注释

  • 监控数据库每5分钟commit事务数量
    pg_stat_database的统计值是累加的,只需每隔5分钟计算xact_commit的差值便可以。

这会在很长一的段时间都完美的运行并且不会有任何问题,因为bigint可以存储很大的值。但是这样就真的可以了么?

我的问题

  1. 数据库内累加达到数据类型的最大值后,如何处理?
  2. 数据何时更新?
  3. 视图并非来至表数据,数据如何并且何时进行持久化?
  4. 如何保障其高性能?
  5. stat_reset时间何时修改?
  6. stat_reset的时间修改意味着什么?
  • 监控数据不准确的影响
    我们都知道监控数据是允许有偏差的,但是数据数值也要合理并保持尽量准确
    老板在全国直播过程中看到业务数据变成负数,尴尬么?
    或是一个平常日10万交易的流量,突然出现百亿级的业务流量呢?
    又或是一个平常日百万交易的流量,突然出现仅有几百的业务流量呢?

寻找答案的代码

其中会涉及到大量的代码,本章将列出主要代码并对代码进行简化方便阅读和理解,并以xact_commit和stats_reset进行分析。

累加到最大值的处理

  • 结论
    xact_commit值由bigint类型存储并且在不停的进行累加,并不会做任何处理,注意bigint是有符号的允许出现负数。

  • 源码分析(不感兴趣可以跳过)
    通过视图得知xact_commit由pg_stat_get_db_xact_commit函数得出。

Datum
pg_stat_get_db_xact_commit(PG_FUNCTION_ARGS)
{
    result = (int64) (dbentry->n_xact_commit);
    PG_RETURN_INT64(result);
} // 该函数的值为pg_stat_database看到的值

dbentry结构:
typedef struct PgStat_StatDBEntry
{
    PgStat_Counter n_xact_commit;

    TimestampTz stat_reset_timestamp;
    TimestampTz stats_timestamp;    /* time of db stats file update */

    HTAB       *tables; //表的统计信息,reset修改时间时会用到
    HTAB       *functions; //函数的统计信息,reset修改时间时会用到
} PgStat_StatDBEntry;

static void
pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
{
    dbentry->n_xact_commit += (PgStat_Counter) (msg->m_xact_commit);
} //计算xact_commit的值,只有累加过程

数据何时进行更新

  • 结论
    xact_commit通常在事务处理完毕后进行更新。

  • 源码分析(不感兴趣可以跳过)

void
PostgresMain(int argc, char *argv[],
             const char *dbname,
             const char *username)
{
                pgstat_report_stat(false); //事务处理完毕
}

worker_spi_main(Datum main_arg)
{
        CommitTransactionCommand();
        pgstat_report_stat(false);  //事务处理完毕
}

static void
pgstat_beshutdown_hook(int code, Datum arg)
{
        pgstat_report_stat(true); //统计信息退出时候,注意此处参数为true
}

数据进行持久化

  • 结论
    数据存储在pg_stat_tmp/db_数据库ID.stat中。
    数据库启动时会通过backend_read_statsfile读取该文件并加载至内存。
    注意流复制不处理此数据

  • 源码分析(不感兴趣可以跳过)

static void
pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
{
    get_dbstat_filename(permanent, false, dbid, statfile, MAXPGPATH);
    elog(DEBUG2, "writing stats file \"%s\"", statfile);
}

如何保持高性能

  • 统计信息进程处理
    需要更新的内容发送至统计信息进程,采用异步方式提高性能。

  • 非实时统计
    收到的信息并不会实时统计当pgstat_report_stat(false)传入false参数时会采用500毫秒刷新一次的方式

  • 数据持久化
    在统计信息进程退出后刷新或是达到一定时间后刷新。

test=# create table lzzhang(id int);
CREATE TABLE
test=# insert into lzzhang values(1);
INSERT 0 1
test=# select xact_commit from pg_stat_database where datname='test';
 xact_commit 
-------------
        3082
(1 row)

kill -9 杀死数据库

test=# select xact_commit from pg_stat_database where datname='test';
FATAL:  terminating connection due to unexpected postmaster exit
server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
test=# select xact_commit from pg_stat_database where datname='test';
 xact_commit 
-------------
           1
(1 row)

stat_reset时间何时修改

  • 结论
    手动调用pg_stat_reset(会清零pg_stat_database下该数据库的所用内容),pg_stat_reset_single_table_counters,pg_stat_reset_single_function_counters三个函数时才会修改,系统并不会自动修改该时间。

  • 源码分析(不感兴趣可以跳过)

static void
reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
{
    dbentry->stat_reset_timestamp = GetCurrentTimestamp();
}

void
pgstat_reset_counters(void)
{
    pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETCOUNTER);
} //用户调用该函数,给统计信息进程发送PGSTAT_MTYPE_RESETCOUNTER信号,统计信息进程调用reset_dbentry_counters变重新修改了所有数据。


static void
pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
{
    dbentry->stat_reset_timestamp = GetCurrentTimestamp();
    if (msg->m_resettype == RESET_TABLE)
    else if (msg->m_resettype == RESET_FUNCTION)
}

Datum
pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS)
{
    pgstat_reset_single_counter(taboid, RESET_TABLE);}
} //修改PgStat_StatDBEntry的tables信息

Datum
pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS)
{
    pgstat_reset_single_counter(funcoid, RESET_FUNCTION);
} //修改PgStat_StatDBEntry的functions信息

stat_reset修意味着什么

  • pg_stat_reset
test=# create table lzzhang(id int);
CREATE TABLE
test=# insert into lzzhang values(1);
INSERT 0 1
test=# insert into lzzhang values(1);
INSERT 0 1

test=# select xact_commit, tup_inserted, stats_reset from pg_stat_database where datname='test';
 xact_commit | tup_inserted |          stats_reset          
-------------+--------------+-------------------------------
          60 |            2 | 2019-05-11 18:56:55.135682+08
(1 row)

test=# select pg_stat_reset();
 pg_stat_reset 
---------------

(1 row)

test=# select xact_commit, tup_inserted, stats_reset from pg_stat_database where datname='test';
 xact_commit | tup_inserted |          stats_reset          
-------------+--------------+-------------------------------
           1 |            0 | 2019-05-11 19:24:56.594342+08
(1 row)

stat_reset的时间时间改变了,pg_stat_database的信息也进行了清零处理。

  • pg_stat_reset_single_table_counters
test=# insert into lzzhang values(1);
INSERT 0 1
test=# insert into lzzhang values(1);
INSERT 0 1
test=# select n_tup_ins from pg_stat_all_tables where relname='lzzhang';
 n_tup_ins 
-----------
         2
(1 row)

test=# select pg_stat_reset_single_table_counters('lzzhang'::regclass::oid);
 pg_stat_reset_single_table_counters 
-------------------------------------

(1 row)

test=# select xact_commit, tup_inserted, stats_reset from pg_stat_database where datname='test';
 xact_commit | tup_inserted |          stats_reset          
-------------+--------------+-------------------------------
          10 |            2 | 2019-05-11 19:26:59.299965+08
(1 row)

重置表的信息时stat_reset时间改变了,但是其他信息没有改变。
stat_reset并不能作为pg_stat_database的数据的重置时间使用

  • pg_stat_reset_single_function_counters
test=# CREATE OR REPLACE FUNCTION lzzhang_func()
RETURNS int AS $$ 
DECLARE 
    result int;
BEGIN
    return 100;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
test=# CREATE OR REPLACE FUNCTION lzzhang_func_1()
RETURNS int AS $$ 
DECLARE 
    result int;
BEGIN
    return 100;
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION
test=# set track_functions='all';
SET
test=# select lzzhang_func();
 lzzhang_func 
--------------
          100
(1 row)

test=# select lzzhang_func();
 lzzhang_func 
--------------
          100
(1 row)

test=# select lzzhang_func_1();
 lzzhang_func_1 
----------------
            100
(1 row)

test=# select lzzhang_func_1();
 lzzhang_func_1 
----------------
            100
(1 row)

test=# select * from pg_stat_user_functions;
 funcid | schemaname |        funcname        | calls | total_time | self_time 
--------+------------+------------------------+-------+------------+-----------
  35011 | public     | lzzhang_func           |     2 |       0.07 |      0.07
  35012 | public     | lzzhang_func_1         |     2 |      0.061 |     0.061
(2 rows)

test=# select pg_stat_reset_single_function_counters('lzzhang_func'::regproc::oid);
 pg_stat_reset_single_function_counters 
----------------------------------------

(1 row)

test=# select * from pg_stat_user_functions;
 funcid | schemaname |        funcname        | calls | total_time | self_time 
--------+------------+------------------------+-------+------------+-----------
  35012 | public     | lzzhang_func_1         |     2 |      0.061 |     0.061
(1 rows)

test=# select xact_commit, tup_inserted, stats_reset from pg_stat_database where datname='test';
 xact_commit | tup_inserted |          stats_reset          
-------------+--------------+-------------------------------
          30 |            8 | 2019-05-11 19:31:20.385167+08
(1 row)

重置函数的统计信息时stat_reset时间改变了,但是其他信息没有改变。
stat_reset并不能作为pg_stat_database的数据的重置时间使用

解答我的问题

  1. bigint是有符号的,注意不要出现负数的情况,可以进行最大值监控并在适当的时候执行执行pg_stat_reset重置。
  2. 通过stat_reset并不能作为数据重置的条件使用,根据递增的属性可以通过数据大小对比来确定。
  3. 如果你有流复制的高可用,切机后需要注意两端数据是分开计算的。

我的建议

pg_stat_*包含很多其他视图,如果您有使用到其他视图的话,最好也做一个分析。
再多想一点,再细心一点,再做好一点…

文章浏览总量 828 次

要发表评论,您必须先登录