从Oracle到PostgreSQL:动态性能视图 vs 标准统计视图
Oracle数据库的性能视图几乎可以说是最引以为骄傲的功能,在那样细粒度的采样统计强度下,依然保持卓越的性能,基于这些性能数据采样之后形成的AWR,更是Oracle DBA分析数据库性能问题的最重要手段之一。
那么在誉为最接近Oracle的开源数据库PostgreSQL中,如果要诊断性能问题,又有哪些视图可以使用呢?作为Oracle DBA,在学习PostgreSQL的时候,不可避免地会将PostgreSQL和Oracle进行比较。
以下SQL命令,在mydb=#提示符下的均为在PostgreSQL中执行的,在SQL>提示符下的均为在Oracle中执行的。
先看一下在PostgreSQL中存在那些统计信息视图。PostgreSQL中数据字典的命名还是很规范的,所有统计信息基本上都以pg_stat_开头。
mydb=# select relname from pg_class where relname like 'pg_stat_%'; relname
----------------------------------
pg_statistic
pg_stats
pg_stat_all_tables
pg_stat_xact_all_tables
pg_stat_sys_tables
pg_stat_xact_sys_tables
pg_stat_user_tables
pg_stat_xact_user_tables
pg_statio_all_tables
pg_statio_sys_tables
pg_statio_user_tables
pg_statio_all_indexes
pg_statio_sys_indexes
pg_statio_user_indexes
pg_statio_all_sequences
pg_statio_sys_sequences
pg_statio_user_sequences
pg_stat_activity
pg_stat_replication
pg_stat_database
pg_stat_database_conflicts
pg_stat_user_functions
pg_stat_xact_user_functions
pg_stat_archiver
pg_stat_bgwriter
pg_stat_all_indexes
pg_stat_sys_indexes
pg_stat_user_indexes
pg_statistic_relid_att_inh_index
(29 rows)
pg_stat_activity
该视图显示了连接入一个Cluster下所有数据库的会话的统计信息,每个会话一行记录,类似于Oracle中的V$SESSION视图。
pg_stat_activity.query字段直接显示了该会话正在执行的SQL或者上次执行的SQL语句文本。在Oracle中检查一个会话正在执行的SQL语句文本,则需要通过V$SESSION和V$SQL视图Join才可以。
pg_stat_activity.pid字段直接显示了该会话在操作系统上的进程ID,这样通过top命令看到的繁忙操作系统进程,可以很简单地通过该字段定位,来作进一步的诊断。在Oracle中则需要通过V$SESSION和V$PROCESS视图Join才可以。
pg_stat_archiver
该视图始终只有一条记录,显示了负责一个cluster下所有数据库的重做日志(PostgreSQL中称为WAL file)归档进程的统计信息,记录项比较简单。last_archived_wal和last_archived_time分别显示了最近一次归档的文件名和最近一次归档时间。类似于Oracle中的V$ARCHIVE_DEST_STATUS。由于PostgreSQL中的归档实现实在是太简单了,所以几乎跟Oracle没有太多可比性。
pg_stat_bgwriter
该视图始终只有一条记录,显示了负责一个cluster下所有数据库的后台写进程的统计信息,也就是在操作系统中看到的postgres: writer process。该进程每隔bgwriter_delay初始化参数定义的间隔(默认200ms)会唤醒,将Buffer Pool中修改过的页写入到磁盘。跟Oracle的后台进程DBWR非常相仿。
在Oracle中没有专门记录DBWR进程的性能视图,V$BGPROCESS视图也同样没有提供类似的信息,但是在V$SYSSTAT却记录了DBWR的统计信息,这部分跟pg_stat_bgwriter中记录的信息相仿。Oracle 11gR2中有超过600项的统计信息记录在V$SYSSTAT视图中。
SQL> select NAME,VALUE from v$sysstat where upper(name) like '%DBWR%';
NAME VALUE
-----------------------------------------------------------------------------------------
flash cache insert skip: DBWR overloaded 0
DBWR checkpoint buffers written 1564210
DBWR thread checkpoint buffers written 0
DBWR tablespace checkpoint buffers written 2852
DBWR parallel query checkpoint buffers written 0
DBWR object drop buffers written 324
DBWR transaction table writes 81619
DBWR undo block writes 485016
DBWR revisited being-written buffer 0
DBWR lru scans 0
DBWR checkpoints 4158
DBWR fusion writes 0
12 rows selected.
pg_stat_database
该视图对于每个database显示一行记录,PostgreSQL中的Cluster类似于Oracle的一个Instance,一个Cluster下可以创建多个database。
该视图中记录了每个数据库提交了多少事务,回滚了多少事务,读了多少数据块,查询、插入、更新、删除了多少记录(在PostgreSQL中用Tuple这个奇怪的词表示跟Row相同的概念),产生过多少死锁。总之这是一个数据库级别相对很简单的统计信息。
但是,在Oracle中还真没有与此类似的性能视图,实际上Oracle没有一个视图简单地记录了一个Schema下面总共查询或者DML了多少条记录,但是却有DBA_TAB_MODIFICATIONS这样的视图详细记录每一张表的DML数量。查询了多少数据?可能Oracle认为这个数字是太不重要了,或者说实在是太大了,完全没必要记录。
对于事务级别的统计,同样可以在Oracle的V$SYSSTAT视图中查询包含“ROLLBACK”和“COMMIT”字样的统计值,远比PostgreSQL中记录地要更多样。
pg_stat_all_tables/pg_stat_sys_tables/pg_stat_user_tables
在PostgreSQL的统计信息视图中,all表示一个数据库下所有的表,sys表示所有的系统表,user表示所有用户创建的表,这三个配套的视图我们放在一起看。以下类似的也相同。
该视图对于每张表显示一条记录,显示了一张表上进行过多少全表扫描,多少索引扫描,查询、插入、更新、删除过多少记录,表中现在有多少记录,表的分析时间等。
在Oracle中表的分析信息存储在DBA_TABLES中,而对于每个表上DML的信息如前所述,可以从DBA_TAB_MODIFICATIONS视图中查询,而经历过怎样的IO则又可以从V$SEGSTAT视图中查询。好吧,实际上,在Oracle中根本也不关注一个表上读取过多少记录这样的数字,所以在PostgreSQL中但凡跟Tuple相关的统计值在Oracle中都找不到对应的记录。对于Oracle来说,IO都以Block为单位,所以读取一条记录还是读取一个块,在IO消耗上没有区别。而至于对于返回记录数等的优化,则归结到SQL层面,那则可以通过V$SQLSTAT等一系列视图作更详细的分析。
Oracle在视图层面从Table概念和Segment概念上做了详细的区分,看似复杂,实际清晰而且详尽,而在PostgreSQL中则混为一谈了,当然在PostgreSQL中通过后面会谈到的pg_statio_系列视图又对表和索引上的IO统计信息进行了记录。
pg_stat_xact_all_tables/pg_stat_xact_sys_tables/pg_stat_xact_user_tables
该系列视图与上述相仿,只是增加了xact前缀,xact表示transaction,统计的是当前会话对于表操作的信息,这部分信息通常还没有更新到pg_stat_all_tables视图中。
在Oracle中由于性能数据的抓取粒度是如此之细,所以并未区分当前会话还是已经结束的会话,要知道V$SEGSTAT中的信息几乎是real-time在更新的。所以,在Oracle中无需此类视图。
pg_stat_all_indexes/pg_stat_sys_indexes/pg_stat_user_indexes
该视图对于每个索引显示一条记录,显示的信息如下:
mydb=# select * from pg_stat_all_indexes where relname='t1';
relid | indexrelid | schemaname | relname | indexrelname | idx_scan | idx_tup_read | idx_tup_fetch -------+------------+------------+---------+--------------+----------+--------------+---------------
24604 | 24613 | public | t1 | t1_index | 3 | 58960 | 47817
(1 row)
可见记录的信息非常简单,就是一个索引上进行过多少次扫描,通过这个索引扫描读取了多少记录,返回了多少记录。
在Oracle中,由于索引是Segment的一种,因此类似的统计信息都可以从V$SEGSTAT中获取。
pg_statio_all_tables/pg_statio_sys_tables/pg_statio_user_tables
pg_statio_all_indexes/pg_statio_sys_indexes/pg_statio_user_indexes
这两部分放在一起描述,具有statio前缀的视图显示的是表或索引在数据块级别的IO统计信息,而stat前缀的视图(如前面看到的)则显示的是表或索引在记录级别的IO统计信息。以pg_statio_all_indexes为例:
mydb=# select * from pg_statio_all_indexes where relname='t1';
relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit -------+------------+------------+---------+--------------+---------------+--------------
24604 | 24613 | public | t1 | t1_index | 150 | 453
(1 row)
显示了读取过多少个数据块,这些读取中有多少数据块是直接命中缓存的。
在Oracle中是我们提到了多次的V$SEGSTAT视图。
pg_statio_all_sequences/pg_statio_sys_sequences/pg_statio_user_sequences
PostgreSQL对sequence上的IO独立给出了一系列视图,PostgreSQL中的sequence跟Oracle中的sequence概念基本一致,为存储序列号等的字段生成序列值。
该视图对于每个序列显示一条记录,显示的信息如下:
mydb=# select * from pg_statio_all_sequences;
relid | schemaname | relname | blks_read | blks_hit
-------+------------+--------------+-----------+----------
24614 | public | users_id_seq | 1 | 3
(1 row)
非常简单,显示了读取过多少个数据块,多少数据块的读取是直接命中缓存的。
在Oracle中,由于序列是系统自身对象的一部分,因此如果要诊断跟序列相关的问题,通常要依赖等待事件,比如“enq: SQ – contention”或者“row cache lock”,另外在V$ROWCACHE视图中存储了与序列相关的整体统计值。
SQL> select PARAMETER,GETS,GETMISSES from v$rowcache where PARAMETER='dc_sequences';
PARAMETER GETS GETMISSES
----------------------------------------------------
dc_sequences 2145 54
pg_stat_user_functions/pg_stat_xact_user_functions
有xact前缀和没有前缀的区别在前面描述pg_stat_xact_all_tables系列视图时已经提过,因此放在一起描述。
该视图对于每个指定要跟踪的用户自定义函数显示一条记录,这通过初始化参数track_functions来控制,默认不开启任何跟踪,视图结构如下:
mydb=# \d pg_stat_user_functions
View "pg_catalog.pg_stat_user_functions"
Column | Type | Modifiers
------------------+----------------------+-----------
funcid | oid |
schemaname | name |
funcname | name |
calls | bigint |
total_time | double precision |
self_time | double precision |
calls字段记录了对于用户函数进行过多少次调用;
total_time字段记录了运行这个函数总共消耗了多长时间(毫秒为单位),包括调用其它函数的时间;
self_time字段记录了运行这个函数本身消耗了多长时间(毫秒为单位),不包括调用其它函数的时间。
Oracle中没有类似的视图,Oracle的关于函数或者存储过程的执行统计信息,都是详细到其中每一条SQL语句的,实际上如果像PostgreSQL这样能有一个函数或者存储过程级别的性能统计值,也是极好的。
pg_stat_replication
在设置了复制的环境中,该视图对于每个WAL sender进程(WAL sender进程负责将本机的重做日志发送到远端复制环境)显示一条记录,显示内容大致如下:
postgres=# select pid,application_name,client_addr,state,sent_location,replay_location from pg_stat_replication;
pid | application_name | client_addr | state | sent_location | replay_location
-------+------------------+----------------+-----------+---------------+-----------------
27855 | walreceiver | 192.168.56.105 | streaming | 0/50188CE8 | 0/50188CE8
(1 row)
每个视图中都能直接显示操作系统进程ID,实在是很方便的事情。在操作系统上可以直接查看pid=27855的进程。
[root@pg1-enmotech-com ~]# ps -ef|grep 2785|grep postgres
postgres 27855 1119 0 00:45 ? 00:00:00 postgres: wal sender process postgres 192.168.56.105(57046) streaming 0/50188CE8
从操作系统的ps命令中看到实际上已经将视图中的这些字段内容更新到了该进程描述中,在进程描述中会更新一些很有用的信息(比如server进程的状态,是等待还是空闲等),这也是PostgreSQL非常方便的一个地方。
在Oracle中与PostgreSQL的复制相类似的功能是Physical Data Guard,在DG中重做日志的传输是通过归档路径来完成的,因此类似的信息可以从V$ARCHIVE_DEST_STATUS和V$MANAGED_STANDBY视图中获取。
pg_stat_database_conflicts
该视图仅对于Standby数据库有效,对于每个数据库显示一条记录,显示内容如下:
postgres=# select * from pg_stat_database_conflicts;
datid | datname | confl_tablespace | confl_lock | confl_snapshot | confl_bufferpin | confl_deadlock
-------+-----------+------------------+------------+----------------+-----------------+------------
1 | template1 | 0 | 0 | 0 | 0 | 0
0 13051 | template0 | 0 | 0 | 0 | 0 | 0
0 13056 | postgres | 0 | 0 | 0 | 0 | 0
0 16384 | mydb | 0 | 0 | 0 | 0 | 0
0 24587 | mydb_bak | 0 | 0 | 0 | 0 | 0
(5 rows)
由于PostgreSQL的机制,在备库上的查询会跟一些诸如删除表空间、删除数据库、vacuum cleanup的操作相冲突,为了不让备库的WAL replay操作延时太久,PostgreSQL内建了强制取消当前备库上运行的查询以避免跟应用重做日志这样更重要的动作相冲突的机制。而该视图则是记录由于不同原因取消掉的查询的次数。对于每个数据库显示一条记录。
Oracle中不会出现这样的问题,因此也没有相应的视图。
总结
当然,PostgreSQL中除了这些统计信息视图之外,还有不少类似于pg_tables,pg_users这样与Oracle中的数据字典视图相仿的视图,另外还有比如pg_locks这样用于记录锁信息的诊断视图。但是仅仅用一篇文章的长度就可以将所有的统计信息视图全部介绍完毕,PostgreSQL确实是很简洁的数据库。