postgresql 常用运维语句。

张彤 2022年07月11日 737次浏览

Postgresql 运维常用查询

ALL COLUMNS

显示所有的列的信息

SELECT c.table_schema,
       c.table_name       as table,
       c.ordinal_position as order,
       c.column_name      as column,
--	 c.data_type as type,
       CASE
           WHEN c.data_type IN ('character', 'varchar') THEN c.data_type || '(' || c.character_maximum_length || ')'
           WHEN c.data_type IN ('numeric')
               THEN c.data_type || '(' || c.numeric_precision || ',' || c.numeric_scale || ')'
           ELSE c.data_type
           END,
--	 c.character_maximum_length as max_len,
       c.is_nullable      as null,
       col_description(t.oid, c.ordinal_position)
FROM information_schema.columns c
         JOIN pg_class t ON (t.relname = c.table_name)
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2, 3;

ALL CONSTRAINTS

显示所有的键信息

SELECT table_schema,
       table_name,
       constraint_name as constraint,
       constraint_type as type
FROM information_schema.table_constraints
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;

ALL TABLES

显示所有的表信息

SELECT table_schema as schema,
       table_name   as table
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
  AND table_schema NOT IN ('pg_catalog', 'information_schema')
ORDER BY 1, 2;

BAD INDEX

失效索引查找

SELECT n.nspname,
       i.relname,
       i.indexrelname,
       CASE
           WHEN idx.indisprimary
               THEN 'pkey'
           WHEN idx.indisunique
               THEN 'uidx'
           ELSE 'idx'
           END AS type,
       'INVALID'
FROM pg_stat_all_indexes i
         JOIN pg_class c ON (c.oid = i.relid)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_index idx ON (idx.indexrelid = i.indexrelid)
WHERE idx.indisvalid = FALSE
ORDER BY 1, 2;

TABLE PRIMARY KEY

表主键查找

SELECT n.nspname,
       t.relname as table,
       c.conname as pk_name
FROM pg_class t
         JOIN pg_constraint c ON (c.conrelid = t.OID AND c.contype = 'p')
         JOIN pg_namespace n ON (n.oid = t.relnamespace)
WHERE relkind = 'r'
  AND t.relname NOT LIKE 'pg_%'
  AND t.relname NOT LIKE 'sql_%'
ORDER BY n.nspname, t.relname, c.conname;

BLOCK TRANSACTION

块交换

SELECT w.query                          as waiting_query,
       w.pid                            as w_pid,
       w.usename                        as w_user,
       l.query                          as locking_query,
       l.pid                            as l_pid,
       l.usename                        as l_user,
       t.schemaname || '.' || t.relname as tablename
FROM pg_stat_activity w
         JOIN pg_locks l1 ON (w.pid = l1.pid and not l1.granted)
         JOIN pg_locks l2 on (l1.relation = l2.relation and l2.granted)
         JOIN pg_stat_activity l ON (l2.pid = l.pid)
         JOIN pg_stat_user_tables t ON (l1.relation = t.relid)

CACHE HIT RATIO

每个数据库的缓存命中率

SELECT pg_stat_database.datname,
       pg_stat_database.blks_read,
       pg_stat_database.blks_hit,
       round((pg_stat_database.blks_hit::double precision
                  / (pg_stat_database.blks_read
               + pg_stat_database.blks_hit
               + 1)::double precision * 100::double precision)::numeric, 2) AS cachehitratio
FROM pg_stat_database
WHERE pg_stat_database.datname !~ '^(template(0|1)|postgres)$'::text
ORDER BY round((pg_stat_database.blks_hit::double precision
                    / (pg_stat_database.blks_read
        + pg_stat_database.blks_hit
        + 1)::double precision * 100::double precision)::numeric, 2) DESC;

COLUMN DIFFERENCE

列名相同,类型不同

SELECT sa.nspname as schemaa,
       ca.relname as tablea,
       a.attname as columna, ta.typname,
       sb.nspname as schemab,
       cb.relname as tableb,
       a.attname as columnb, tb.typname
  FROM pg_attribute a
       JOIN pg_type ta ON (ta.oid = a.atttypid)
       JOIN pg_class ca ON (ca.oid = a.attrelid)
       JOIN pg_catalog.pg_namespace sa ON (ca.relnamespace = sa.oid),
       pg_attribute b
       JOIN pg_type tb ON (tb.oid = b.atttypid)
       JOIN pg_class cb ON (cb.oid = b.attrelid)
       JOIN pg_catalog.pg_namespace sb ON (cb.relnamespace = sb.oid)
 WHERE a.attname = b.attname
   AND ta.typname <> tb.typname
   AND ca.relname NOT LIKE 'pg_%'
   AND ca.relname NOT LIKE 'information%'
   AND ca.relname NOT LIKE 'sql_%'
   AND ca.relkind = 'r'
   AND cb.relname NOT LIKE 'pg_%'
   AND cb.relname NOT LIKE 'information%'
   AND cb.relname NOT LIKE 'sql_%'
   AND cb.relkind = 'r'
   AND 'rollback' NOT IN (sa.nspname, sb.nspname)
   AND 'id' NOT IN (a.attname, b.attname);

CURRENT LOCKS

当前锁信息,注意,不是死锁,事务的隔离级别和锁类型可以参考pg官方文档

SELECT pd.datname,
       l.relation,
       n.nspname,
       c.relname,
       l.pid,
       a.usename,
       locktype,
       mode,
       granted,
       tuple
FROM pg_locks l
         JOIN pg_class c ON (c.oid = l.relation)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_stat_activity a ON (a.pid = l.pid)
         left join pg_database pd on a.datname = pd.datname
ORDER BY database,
         relation,
         pid;

CURRENT QUERY

当前查询

SELECT a.datname,
       a.pid as pid,
       CASE WHEN a.client_addr IS NULL
            THEN 'local'
            ELSE a.client_addr::text
       END as client_addr,
       a.usename as user,
       a.wait_event,
       l.pid as blocking_pid,
       l.usename as blicking_user,
       a.query,
       a.query_start,
       current_timestamp - a.query_start as duration
  FROM pg_stat_activity a
LEFT  JOIN pg_locks l1 ON (a.pid = l1.pid )
LEFT  JOIN pg_locks l2 on (l1.relation = l2.relation )
LEFT  JOIN pg_stat_activity l ON (l2.pid = l.pid)
LEFT  JOIN pg_stat_user_tables t ON (l1.relation = t.relid)
 WHERE pg_backend_pid() <> a.pid
ORDER BY a.datname,
         a.query_start;

DB INFO

数据库信息

SELECT db.datname,
       au.rolname                       as datdba,
       pg_encoding_to_char(db.encoding) as encoding,
       db.datallowconn,
       db.datconnlimit,
       db.datfrozenxid,
       tb.spcname                       as tblspc,
--	 db.datconfig,
       db.datacl
FROM pg_database db
         JOIN pg_authid au ON au.oid = db.datdba
         JOIN pg_tablespace tb ON tb.oid = db.dattablespace
ORDER BY 1;

DB SIZE

数据库大小

SELECT datname,
       pg_size_pretty(pg_database_size(datname))                               as size_pretty,
       pg_database_size(datname)                                               as size,
       (SELECT pg_size_pretty(SUM(pg_database_size(datname))::bigint)
        FROM pg_database)                                                      AS total,
       ((pg_database_size(datname) / (SELECT SUM(pg_database_size(datname))
                                      FROM pg_database)) * 100)::numeric(6, 3) AS pct
FROM pg_database
ORDER BY datname;

DISTINCT DATA TYPE

数据库中的所有数据类型

SELECT DISTINCT t.typname
FROM pg_class c
         JOIN pg_attribute a ON (a.attrelid = c.oid)
         JOIN pg_type t ON (t.oid = a.atttypid)
         JOIN pg_catalog.pg_namespace s ON (c.relnamespace = s.oid)
WHERE c.relname NOT LIKE 'pg_%'
  AND c.relname NOT LIKE 'information%'
  AND c.relname NOT LIKE 'sql_%'
  AND c.relkind = 'r'
ORDER BY t.typname;

FILE NAME AND TABLE NAME

找到表名对应的物理存储文件名称

SELECT (SELECT oid
        FROM pg_database
        WHERE datname = current_database()) AS database,
       c.relfilenode                        as filename,
       n.nspname                            as schema,
       c.relname                            as table
FROM pg_class c
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE relname NOT LIKE 'pg_%'
  AND relname NOT LIKE 'information%'
  AND relname NOT LIKE 'sql_%'
  AND relkind = 'r'
ORDER BY relfilenode;

GEN INDEX CREATE

显示当前库内所有索引创建语句

SELECT pg_get_indexdef(idx.indexrelid) || ';'
FROM pg_stat_all_indexes i
         JOIN pg_class c ON (c.oid = i.relid)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_index idx ON (idx.indexrelid = i.indexrelid)
WHERE NOT idx.indisprimary
  AND NOT idx.indisunique
  AND i.relname NOT LIKE 'pg_%'
  AND i.idx_scan = 0
ORDER BY n.nspname,
         i.relname;

INDEX BLOAT

索引信息

SELECT N.nspname,
       T.relname                                                                    AS "table",
       C.relname                                                                    AS idx_name,
       round(100 * pg_relation_size(indexrelid) / pg_relation_size(indrelid)) / 100 AS "index_ratio %",
       pg_size_pretty(pg_relation_size(indexrelid))                                 AS index_size,
       pg_size_pretty(pg_relation_size(indrelid))                                   AS table_size_size
FROM pg_index I
         LEFT JOIN pg_class C ON (c.oid = I.indexrelid)
         LEFT JOIN pg_class T ON (T.oid = I.indrelid)
         LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
  AND c.relkind = 'i'
  AND pg_relation_size(indrelid) > 0
ORDER BY 4 desc, 1;
SELECT n.nspname                                                                                 as schema,
       i.relname                                                                                 as table,
       i.indexrelname                                                                            as index,
       i.idx_scan,
       i.idx_tup_read,
       i.idx_tup_fetch,
       CASE
           WHEN idx.indisprimary
               THEN 'pkey'
           WHEN idx.indisunique
               THEN 'uidx'
           ELSE 'idx'
           END                                                                                   AS type,
/*
       pg_get_indexdef(idx.indexrelid, 1, FALSE) as idxcol1,
       pg_get_indexdef(idx.indexrelid, 2, FALSE) as idxcol2,
       pg_get_indexdef(idx.indexrelid, 3, FALSE) as idxcol3,
       pg_get_indexdef(idx.indexrelid, 4, FALSE) as idxcol4,
*/
       CASE
           WHEN idx.indisvalid
               THEN 'valid'
           ELSE 'INVALID'
           END                                                                                   as statusi,
       pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(i.relname))                 as size_in_bytes,
       pg_size_pretty(pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(i.relname))) as size
FROM pg_stat_all_indexes i
         JOIN pg_class c ON (c.oid = i.relid)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_index idx ON (idx.indexrelid = i.indexrelid)
WHERE i.relname LIKE '%%'
  AND n.nspname NOT LIKE 'pg_%'
--   AND idx.indisunique = TRUE
--   AND NOT idx.indisprimary
--   AND i.indexrelname LIKE 'tmp%'
--   AND idx.indisvalid IS false
/*   AND NOT idx.indisprimary
   AND NOT idx.indisunique
   AND idx_scan = 0
*/
ORDER BY 1, 2, 3;

INDEX SIZE

SELECT n.nspname as schema,
       c.relname as index,
       a.rolname as owner,
       c.relfilenode as filename,
       CASE WHEN indisprimary
	    THEN 'pkey'
	    WHEN indisunique
	    THEN 'uidx'
	    ELSE'idx'
	END as type,
       pg_size_pretty(pg_total_relation_size(n.nspname|| '.' || c.relname)) as total_size,
       pg_total_relation_size(n.nspname|| '.' || c.relname) as total_size_bytes
  FROM pg_index i
  JOIN pg_class c     ON (c.oid = i.indexrelid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_authid a ON ( a.oid = c.relowner )
  WHERE relname NOT LIKE 'pg_%' AND
relname NOT LIKE 'information%' AND
relname NOT LIKE 'sql_%' AND
relkind IN ('i')
ORDER BY 7 DESC, 1, 2;

ALTER INDEX SPACE

将索引迁移至其他表空间的生成语句

SELECT 'ALTER INDEX ' || i.indexrelname
           || ' SET TABLESPACE <new_table_space>;'
FROM pg_stat_all_indexes i
         JOIN pg_class c ON (c.oid = i.relid)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_index idx ON (idx.indexrelid = i.indexrelid)
--  WHERE n.nspname = 'public'
ORDER BY 1;

ALTER TABLE SPACE

将表移至其他表空间的生成语句

SELECT 'ALTER TABLE ' || c.relname || ' SET TABLESPACE <new_table_space>;'
FROM pg_class c
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE relname NOT LIKE 'pg_%'
  AND relname NOT LIKE 'information%'
  AND relname NOT LIKE 'sql_%'
  AND n.nspname = 'public'
  AND relkind = 'r'
ORDER BY relname;

MOST ACTIVE TABLES

最活跃的前10位表,主要是读这方面的

SELECT schemaname,
       relname,
       idx_tup_fetch + seq_tup_read as TotalReads
  FROM pg_stat_all_tables
WHERE idx_tup_fetch + seq_tup_read != 0
  AND schemaname NOT IN ( 'pg_catalog', 'pg_toast' )
order by TotalReads desc
LIMIT 10;

PG RUNTIME

postgresql 数据库运行时长(主进程)

SELECT pg_postmaster_start_time()                     as pg_start,
       current_timestamp - pg_postmaster_start_time() as runtime;

PG STAT ALL TABLE

表信息统计,包括最近一次vacuum

SELECT n.nspname,
       s.relname,
       c.reltuples::bigint,
--	 n_live_tup,
       n_tup_ins,
       n_tup_upd,
       n_tup_del,
       date_trunc('second', last_vacuum) as last_vacuum,
       date_trunc('second', last_autovacuum) as last_autovacuum,
       date_trunc('second', last_analyze) as last_analyze,
       date_trunc('second', last_autoanalyze) as last_autoanalyze
       ,
       round( current_setting('autovacuum_vacuum_threshold')::integer + current_setting('autovacuum_vacuum_scale_factor')::numeric * C.reltuples) AS av_threshold
/*	 ,CASE WHEN reltuples > 0
	      THEN round(100.0 * n_dead_tup / (reltuples))
	    ELSE 0
       END AS pct_dead,
       CASE WHEN n_dead_tup > round( current_setting('autovacuum_vacuum_threshold')::integer + current_setting('autovacuum_vacuum_scale_factor')::numeric * C.reltuples)
	      THEN 'VACUUM'
	    ELSE 'ok'
	END AS "av_needed"
*/
  FROM pg_stat_all_tables s
  JOIN pg_class c ON c.oid = s.relid
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
 WHERE s.relname NOT LIKE 'pg_%'
   AND s.relname NOT LIKE 'sql_%'
--   AND s.relname LIKE '%TBL%'
 ORDER by 1, 2;

DEAD TUPLE

按关系对象统计死元组,需要vacuum的对象

SELECT *,
       n_dead_tup > av_threshold AS "av_needed",
       CASE
           WHEN reltuples > 0
               THEN round(100.0 * n_dead_tup / (reltuples))
           ELSE 0
           END                   AS pct_dead
FROM (SELECT N.nspname || '.' || C.relname                                                                      AS "relation",
             pg_stat_get_tuples_inserted(C.oid)                                                                 AS n_tup_ins,
             pg_stat_get_tuples_updated(C.oid)                                                                  AS n_tup_upd,
             pg_stat_get_tuples_deleted(C.oid)                                                                  AS n_tup_del,
             CASE
                 WHEN pg_stat_get_tuples_updated(C.oid) > 0
                     THEN pg_stat_get_tuples_hot_updated(C.oid)::real / pg_stat_get_tuples_updated(C.oid)
                 ELSE 0
                 END                                                                                            AS HOT_update_ratio,
             pg_stat_get_live_tuples(C.oid)                                                                     AS n_live_tup,
             pg_stat_get_dead_tuples(C.oid)                                                                     AS n_dead_tup,
             C.reltuples                                                                                        AS reltuples,
             round(current_setting('autovacuum_vacuum_threshold')::integer +
                   current_setting('autovacuum_vacuum_scale_factor')::numeric *
                   C.reltuples)                                                                                 AS av_threshold,
             date_trunc('minute', greatest(pg_stat_get_last_vacuum_time(C.oid),
                                           pg_stat_get_last_autovacuum_time(C.oid)))                            AS last_vacuum,
             date_trunc('minute', greatest(pg_stat_get_last_analyze_time(C.oid),
                                           pg_stat_get_last_analyze_time(C.oid)))                               AS last_analyze
      FROM pg_class C
               LEFT JOIN pg_index I ON C.oid = I.indrelid
               LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
      WHERE C.relkind IN ('r', 't')
        AND N.nspname NOT IN ('pg_catalog', 'information_schema')
        AND N.nspname !~ '^pg_toast'
     ) AS av
ORDER BY av_needed DESC,
         n_dead_tup DESC;

TABLE INDEX

表上所有索引

SELECT cc.relname as table,
       ci.relname as index
FROM pg_index i
         JOIN pg_class cc ON (cc.oid = i.indrelid)
         JOIN pg_class ci ON (ci.oid = i.indexrelid)
-- WHERE ci.relname = 'idx_normal_distro_name'
ORDER BY 1;

TABLE NO PRIMARY KEY

没有主键的表

SELECT n.nspname as schema,
       c.relname as table
FROM pg_class c
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE relkind = 'r'
  AND relname NOT LIKE 'pg_%'
  AND relname NOT LIKE 'sql_%'
  AND relhaspkey = FALSE
ORDER BY n.nspname, c.relname;

USELESS INDEX

使用次数较少的索引

SELECT idstat.schemaname                                                                                      AS schema,
       idstat.relname                                                                                         AS table_name,
       indexrelname                                                                                           AS index_name,
       idstat.idx_scan                                                                                        AS times_used,
       pg_size_pretty(pg_relation_size(
               quote_ident(idstat.schemaname) || '.' || quote_ident(idstat.relname)))                         AS table_size,
       pg_size_pretty(pg_relation_size(
               quote_ident(idstat.schemaname) || '.' || quote_ident(indexrelname)))                           AS index_size,
       n_tup_upd + n_tup_ins + n_tup_del                                                                      as num_writes,
       indexdef                                                                                               AS definition
FROM pg_stat_user_indexes AS idstat
         JOIN pg_indexes ON indexrelname = indexname
         JOIN pg_stat_user_tables AS tabstat ON idstat.relname = tabstat.relname
WHERE idstat.idx_scan < 200
  AND indexdef !~* 'unique'
ORDER BY idstat.schemaname,
         idstat.relname,
         indexrelname;
SELECT n.nspname as schema,
       i.relname as table,
       i.indexrelname as index,
       i.idx_scan,
       i.idx_tup_read,
       i.idx_tup_fetch,
       pg_size_pretty(pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(i.relname))) AS table_size,
       pg_size_pretty(pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(i.indexrelname))) AS index_size,
       pg_get_indexdef(idx.indexrelid) as idx_definition
  FROM pg_stat_all_indexes i
  JOIN pg_class c ON (c.oid = i.relid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_index idx ON (idx.indexrelid =  i.indexrelid )
 WHERE i.idx_scan < 200
   AND NOT idx.indisprimary
   AND NOT idx.indisunique
 ORDER BY 1, 2, 3;

USES

用户及权限

SELECT rolname as user,
       CASE
           WHEN rolcanlogin THEN 'user'
           ELSE 'group'
           END,
       CASE
           WHEN rolsuper THEN 'SUPERUSER'
           ELSE 'normal'
           END AS super
FROM pg_authid
ORDER BY rolcanlogin,
         rolname;

WASTE TABLESPACE

WITH s AS (
    SELECT SUM(pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(c.relname))::bigint)                  AS table_size,
           pg_size_pretty(SUM(pg_relation_size(
                   quote_ident(n.nspname) || '.' || quote_ident(c.relname))::bigint)::bigint)                      AS table_size_pretty
    FROM pg_class c
             JOIN pg_namespace n ON (n.oid = c.relnamespace)
    WHERE c.relkind = 'r'
      AND c.relname NOT LIKE 'pg_%'
      AND c.relname NOT LIKE 'sql%'
)
SELECT s.table_size,
       s.table_size_pretty,
       SUM(pg_relation_size(quote_ident(n.nspname) || '.' || quote_ident(i.indexrelname))::bigint)                  AS unused_idx_size,
       pg_size_pretty(SUM(pg_relation_size(
               quote_ident(n.nspname) || '.' || quote_ident(i.indexrelname))::bigint)::bigint)                      AS unused_idx_size_pretty,
       pg_database_size(current_database())                                                                         as db_size,
       pg_size_pretty(pg_database_size(current_database()))                                                         as db_size_pretty,
       pg_size_pretty(pg_database_size(current_database()) - SUM(pg_relation_size(
               quote_ident(n.nspname) || '.' || quote_ident(i.indexrelname))::bigint)::bigint)                      as db_minus_wasted_space

FROM s,
     pg_stat_all_indexes i
         JOIN pg_class c ON (c.oid = i.relid)
         JOIN pg_namespace n ON (n.oid = c.relnamespace)
         JOIN pg_index idx ON (idx.indexrelid = i.indexrelid)
WHERE i.idx_scan = 0
  AND NOT idx.indisprimary
  AND NOT idx.indisunique
GROUP BY table_size, table_size_pretty;