Как получить текущее свободное место на диске в Postgres?

Я должен быть уверен, что у меня есть по крайней мере 1 ГБ свободного места на диске, прежде чем начать выполнять некоторую работу в моей базе данных. Я ищу что-то вроде этого:

select pg_get_free_disk_space();

это возможно? (Я ничего не нашел об этом в docs).

PG: 9.3 & OS: Linux / Windows

3 ответов


PostgreSQL в настоящее время не имеет функций для прямого предоставления дискового пространства.

во-первых, какой диск? Производственный экземпляр PostgreSQL часто выглядит следующим образом:

  • /pg/pg94/: RAID6 быстрого надежного хранения на контроллере BBU RAID в режиме WB, для каталогов и наиболее важных данных
  • /pg/pg94/pg_xlog: быстрый надежный RAID1, для журналов транзакций
  • /pg/tablespace-lowredundancy: RAID10 быстрого дешевого хранения для таких вещей, как индексы и UNLOGGED таблицы, которые вы не заботитесь о потере, поэтому вы можете использовать хранилище с меньшим резервированием
  • /pg/tablespace-bulkdata: RAID6 или аналогичное медленное магнитное хранилище, используемое для старых журналов аудита, исторических данных, данных записи и других вещей, которые могут быть медленнее для доступа.
  • журналы postgreSQL обычно находятся где-то еще, но если это заполнится, система все равно может остановиться. Где зависит от ряда параметров, некоторые из которых вы не можете видеть PostgreSQL вообще, как параметры системного журнала.

тогда есть тот факт, что" свободное " пространство не обязательно означает, что PostgreSQL может его использовать (подумайте: дисковые квоты, зарезервированное системой дисковое пространство), и тот факт, что free блоки/байт не единственным ограничением, так как многие файловые системы также имеют ограничения на количество файлов (дескрипторы).

какSELECT pg_get_free_disk_space() сообщить об этом?

знание свободного места на диске может быть проблемой безопасности. Если поддерживается, это то, что будет доступно только суперпользователю, по крайней мере.

что ты можете do использует ненадежный процедурный язык, такой как plpythonu чтобы сделать вызовы операционной системы для опроса хост-ОС для информации о дисковом пространстве, используя запросы против pg_catalog.pg_tablespace и с помощью data_directory параметр pg_settings чтобы узнать, где PostgreSQL хранит материал на хост-ОС. Вы также должны проверить точки монтирования (unix / Mac) / точки соединения (Windows), чтобы узнайте, если pg_xlog и т. д. находятся на отдельном хранилище. Однако это все равно не поможет вам с пространством для журналов.

я бы очень хотел иметь SELECT * FROM pg_get_free_diskspace это сообщило основное пространство datadir и любые точки монтирования или точки соединения внутри него, как для pg_xlog или pg_clog, а также сообщил о каждом табличном пространстве и любых точках монтирования внутри него. Это будет функция возврата набора. Кто-то, кто достаточно заботится, должен был бы потрудиться, чтобы реализовать его для всех целевых платформ хотя, и прямо сейчас, никто не хочет этого достаточно, чтобы сделать работу.


в то же время, если вы готовы упростить ваши потребности:

  • одна файловая система
  • целевая ОС совместима с UNIX / POSIX, как Linux
  • система квот не включена
  • нет корневого зарезервированного процента блока
  • inode истощение не является проблемой

тут вы можете CREATE LANGUAGE plpython3u; и CREATE FUNCTION a LANGUAGE plpython3u функция, которая делает что-то вроде:

import os
st = os.statvfs(datadir_path)
return st.f_bavail * st.f_frsize

в функции, которая returns bigint и либо принимает datadir_path в качестве аргумента или обнаруживает его, выполняя запрос SPI, например SELECT setting FROM pg_settings WHERE name = 'data_directory' изнутри PL / Python.

если вы также хотите поддерживать Windows, см. кросс-платформенное пространство, оставшееся на Томе с помощью python . Я бы использовал запросы интерфейса управления Windows (WMI), а не использовать ctypes для вызова Windows API.

или вы могли бы используйте эту функцию кто-то написал в PL / Perlu сделать это с помощью df и mount синтаксический анализ вывода команды, который, вероятно, будет работать только на Linux, но эй, он предварительно написан.


вот реализация 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();

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);
}