如何获得Postgres中当前的可用磁盘空间?

时间:2015-04-22 18:46:04

标签: sql postgresql api postgresql-9.3 diskspace

我需要确保在开始在我的数据库中进行一些工作之前,我至少有1Gb的可用磁盘空间。我正在寻找这样的事情:

select pg_get_free_disk_space();

有可能吗? (我在docs中找不到任何内容)。

PG:9.3&操作系统:Linux / Windows

4 个答案:

答案 0 :(得分:12)

PostgreSQL目前没有直接公开磁盘空间的功能。

首先,哪个磁盘?生产PostgreSQL实例通常如下所示:

  • /pg/pg94/:WB模式下BBU RAID控制器上的快速可靠存储RAID6,用于目录和最重要的数据
  • /pg/pg94/pg_xlog:用于事务日志的快速可靠的RAID1
  • /pg/tablespace-lowredundancy:快速廉价存储的RAID10,用于索引和UNLOGGED表等您不关心丢失的内容,以便您可以使用低冗余存储
  • /pg/tablespace-bulkdata:RAID6或类似的慢速近线磁存储,用于旧审核日志,历史数据,写入数据以及其他可能较慢访问的内容。
  • postgreSQL日志通常会在其他地方再次出现,但如果填满,系统可能仍会停止。哪里取决于许多配置设置,其中一些根本不能从PostgreSQL中看到,比如syslog选项。

然后事实是“免费”空间并不一定意味着PostgreSQL可以使用它(想想:磁盘配额,系统保留磁盘空间),以及免​​费阻止 / bytes 不是唯一的约束,因为许多文件系统也对文件数量(inode)有限制。

SELECT pg_get_free_disk_space()如何举报?

了解可用磁盘空间可能是一个安全问题。如果支持,它至少只会暴露给超级用户。

可以做的是使用不受信任的过程语言(如plpythonu)来进行操作系统调用,以使用针对pg_catalog.pg_tablespacedata_directory的查询来询问主机操作系统的磁盘空间信息使用pg_settings中的pg_xlog设置来发现PostgreSQL在主机操作系统上保存内容的位置。您还必须检查挂载点(unix / Mac)/交接点(Windows)以发现SELECT * FROM pg_get_free_diskspace等是否在单独的存储上。但是,这仍然无法真正帮助您获取日志空间。

我非常希望有pg_xlog报告主数据库空间,以及其中的任何挂载点或交接点,如pg_clogCREATE LANGUAGE plpython3u;,并且每个表空间及其中的任何挂载点。这是一个回归功能。关心足够的人不得不费心去为所有目标平台实现 ,而且现在,没有人希望它足以完成这项工作。

与此同时,如果您愿意将您的需求简化为:

  • 一个文件系统
  • 目标操作系统与UNIX / POSIX兼容,如Linux
  • 没有启用配额系统
  • 没有根保留块百分比
  • inode耗尽不是问题

然后您可以CREATE FUNCTIONLANGUAGE plpython3u import os st = os.statvfs(datadir_path) return st.f_bavail * st.f_frsize 函数执行以下操作:

returns bigint

datadir_path并以SELECT setting FROM pg_settings WHERE name = 'data_directory'作为参数的函数中,或通过从PL / Python中执行df之类的SPI查询来发现它。

如果您也想支持Windows,请参阅Cross-platform space remaining on volume using python。我使用Windows管理界面(WMI)查询而不是使用ctypes来调用Windows API。

或者你可以use this function someone wrote in PL/Perlu使用mountfunction cursorSelect(element){ element.css("cursor","pointer"); } function cursorNormal(element){ element.css("cursor","default"); } function colourSelect(element){ element.css("color","blue"); } function colourNormal(element){ element.css("color","black"); } function variation1(){ //variation 1 code $("td").mouseover(cursorSelect($(this))); $("td").mouseout(cursorNormal($(this))); } function variation2(){ //variation 2 code $("td").mouseover(colourSelect($(this))); $("td").mouseout(colourNormal($(this))); } var pageVariations = [ function() {}, //Original; Mostlikely this will not need changing function() { //Variation 1 goes in here variation1(); }, function (){ //Variation 2 goes in here variation2(); }, function (){ //Variation 3 goes in here variation1(); variation2(); } ] $(document).ready(pageVariations[chosenVariation]);//jQuery launching selected variation 命令输出解析来完成它,这可能只适用于Linux,但是嘿,它是预先编写的。

答案 1 :(得分:2)

这里有一种无需任何扩展语言即可获取可用磁盘空间的简单方法,只需使用pgsql定义一个函数即可。

CREATE OR REPLACE FUNCTION sys_df() RETURNS SETOF text[]
LANGUAGE plpgsql $$
BEGIN
    CREATE TEMP TABLE IF NOT EXISTS tmp_sys_df (content text) ON COMMIT DROP;
    COPY tmp_sys_df FROM PROGRAM 'df | tail -n +2';
    RETURN QUERY SELECT regexp_split_to_array(content, '\s+') FROM tmp_sys_df;
END;
$$;

用法:

select * from sys_df();
                          sys_df                               
-------------------------------------------------------------------
 {overlay,15148428,6660248,7695656,46%,/}
 {overlay,15148428,6660248,7695656,46%,/}
 {tmpfs,65536,0,65536,0%,/dev}
 {tmpfs,768284,0,768284,0%,/sys/fs/cgroup}
 {/dev/sda2,15148428,6660248,7695656,46%,/etc/resolv.conf}
 {/dev/sda2,15148428,6660248,7695656,46%,/etc/hostname}
 {/dev/sda2,15148428,6660248,7695656,46%,/etc/hosts}
 {shm,65536,8,65528,0%,/dev/shm}
 {/dev/sda2,15148428,6660248,7695656,46%,/var/lib/postgresql/data}
 {tmpfs,65536,0,65536,0%,/proc/kcore}
 {tmpfs,65536,0,65536,0%,/proc/timer_list}
 {tmpfs,65536,0,65536,0%,/proc/sched_debug}
 {tmpfs,768284,0,768284,0%,/sys/firmware}
(13 rows)

在将所有数据保存在磁盘上的同一路径中时,使用df $PGDATA | tail -n +2而不是df | tail -n +2。在这种情况下,该函数仅返回$ PGDATA路径的一行磁盘使用情况。

安全注意事项

PROGRAM 可以由shell运行任何命令,就像两刃剑一样。最好使用固定的命令字符串,或者至少避免在其中传递任何用户输入。 See detail on document

答案 2 :(得分:1)

这是我们已经使用了一段时间的plpython2u实现。

-- NOTE this function is a security definer, so it carries the superuser permissions
-- even when called by the plebs.
-- (required so we can access the data_directory setting.)
CREATE OR REPLACE FUNCTION get_tablespace_disk_usage()
    RETURNS TABLE (
        path VARCHAR,
        bytes_free BIGINT,
        total_bytes BIGINT
    )
AS $$
import os

data_directory = plpy.execute("select setting from pg_settings where name='data_directory';")[0]['setting']
records = []

for t in plpy.execute("select spcname, spcacl, pg_tablespace_location(oid) as path from pg_tablespace"):
    if t['spcacl']:
        # TODO handle ACLs. For now only show public tablespaces.
        continue

    name = t['spcname']
    if name == 'pg_default':
        path = os.path.join(data_directory, 'default')
    elif name == 'pg_global':
        path = os.path.join(data_directory, 'global')
    else:
        path = t['path']

    # not all tablespaces actually seem to exist(?) in particular, pg_default.
    if os.path.exists(path):
        s = os.statvfs(path)
        total_bytes = s.f_blocks * s.f_frsize
        bytes_free = s.f_bavail * s.f_frsize

        records.append((path, bytes_free, total_bytes))

return records

$$ LANGUAGE plpython2u STABLE SECURITY DEFINER;

用法类似于:

SELECT path, bytes_free, total_bytes FROM get_tablespace_disk_usage();

答案 3 :(得分:0)

C版本适用于那些仍需要一个工具来检查postgresql服务器上的可用空间的人。 目前仅适用于Linux和FreeBSD,需要为其他操作系统添加适当的标头和定义。

#if defined __FreeBSD__
# include <sys/param.h>
# include <sys/mount.h>
#elif defined __linux__
# define _XOPEN_SOURCE
# define _BSD_SOURCE
# include <sys/vfs.h>
#else
# error Unsupported OS
#endif
#include <postgres.h>
#include <catalog/pg_type.h>
#include <funcapi.h>
#include <utils/builtins.h>

/* Registration:
CREATE FUNCTION disk_free(path TEXT) RETURNS TABLE (
  size BIGINT, free BIGINT, available BIGINT, inodes INTEGER, ifree INTEGER, blksize INTEGER
) AS '$pglib/pg_df.so', 'df' LANGUAGE c STRICT;
*/

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

PG_FUNCTION_INFO_V1(df);

Datum df(PG_FUNCTION_ARGS)
{
  TupleDesc tupdesc;
  AttInMetadata *attinmeta;
  HeapTuple tuple;
  Datum result;
  char **values;
  struct statfs sfs;
  const char* path = text_to_cstring(PG_GETARG_TEXT_P(0));

  if(get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
    ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context that cannot accept type record")));
  attinmeta = TupleDescGetAttInMetadata(tupdesc);

  if(0 != statfs(path, &sfs))
    ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("statfs() system call failed: %m")));

  values = (char **) palloc(6 * sizeof(char *));
  values[0] = (char *) palloc(20 * sizeof(char));
  values[1] = (char *) palloc(20 * sizeof(char));
  values[2] = (char *) palloc(20 * sizeof(char));
  values[3] = (char *) palloc(10 * sizeof(char));
  values[4] = (char *) palloc(10 * sizeof(char));
  values[5] = (char *) palloc(10 * sizeof(char));

  int64 df_total_bytes = sfs.f_blocks * sfs.f_bsize;
  int64 df_free_bytes  = sfs.f_bfree  * sfs.f_bsize;
  int64 df_avail_bytes = sfs.f_bavail * sfs.f_bsize;
  snprintf(values[0], 20, "%lld", df_total_bytes);
  snprintf(values[1], 20, "%lld", df_free_bytes);
  snprintf(values[2], 20, "%lld", df_avail_bytes);
  snprintf(values[3], 10, "%d", sfs.f_files);
  snprintf(values[4], 10, "%d", sfs.f_ffree);
  snprintf(values[5], 10, "%d", sfs.f_bsize);

  tuple = BuildTupleFromCStrings(attinmeta, values);
  return HeapTupleGetDatum(tuple);
}