{"id":256,"date":"2026-10-20T09:53:00","date_gmt":"2026-10-20T07:53:00","guid":{"rendered":"https:\/\/www.cisilino.com\/?p=256"},"modified":"2026-10-29T20:00:36","modified_gmt":"2026-10-29T19:00:36","slug":"database-disk-space-script-mysql-postgresql","status":"publish","type":"post","link":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/","title":{"rendered":"Which database is using up your disk space? A script for MySQL and PostgreSQL"},"content":{"rendered":"<p>It happens to everyone sooner or later. The server disk fills up, the alarm goes off and the question is always the same. <strong>Which database is taking up all this space?<\/strong> Measuring database disk space properly turns out to be harder than it looks.<\/p>\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp\" alt=\"Database disk space by engine: ring chart split into four coloured segments, with three legend bars on the right\" width=\"1200\" height=\"630\"\/><\/figure>\n<p>I ran into it after an optimisation. I had run <code>OPTIMIZE TABLE<\/code> hoping to reclaim space, but the filesystem didn&#8217;t shrink as expected. Without a &#8220;before&#8221; to compare with the &#8220;after&#8221;, it was impossible to tell whether the optimisation had worked. Nor could I tell which database was really responsible, or whether the missing space was somewhere else entirely.<\/p>\n<p>The tools I had answered only half of the problem:<\/p>\n<ul class=\"wp-block-list\">\n<li><code>du<\/code> shows the files on disk, but doesn&#8217;t say which table they belong to;<\/li>\n<li><code>information_schema<\/code> (or <code>pg_class<\/code> on PostgreSQL) gives the logical sizes, but doesn&#8217;t see binlogs, <code>ibdata1<\/code>, undo logs or WAL;<\/li>\n<li>both photograph a single instant: comparing over time remains manual work, done with copy and paste and a spreadsheet.<\/li>\n<\/ul>\n<p>I needed something simple: <strong>take a snapshot of the state of all databases at a given moment, take another later and have it tell me what changed<\/strong>, sorting the differences from the largest to the smallest. That way the culprit jumps out immediately.<\/p>\n<p>The result is a single Bash script. It has no dependencies beyond the database client and the standard Linux tools (<code>awk<\/code>, <code>du<\/code>, <code>df<\/code>). It works with <strong>MySQL\/MariaDB<\/strong> and with <strong>PostgreSQL<\/strong>. I&#8217;m sharing it here in the hope it saves someone the same afternoon of frustration.<\/p>\n<h2 class=\"wp-block-heading\">What the database disk space script does<\/h2>\n<p>The script has four commands:<\/p>\n<ul class=\"wp-block-list\">\n<li><code>snapshot<\/code> saves the current state (databases, tables, files in the data directory, filesystem space);<\/li>\n<li><code>compare<\/code> compares two snapshots and shows the differences, the largest on top;<\/li>\n<li><code>report<\/code> shows the largest databases and tables in a single snapshot;<\/li>\n<li><code>list<\/code> lists the saved snapshots.<\/li>\n<\/ul>\n<p>The comparison is split into four sections, so as not to leave any blind spots:<\/p>\n<ul class=\"wp-block-list\">\n<li><strong>size of each database<\/strong> (data + indexes);<\/li>\n<li><strong>free or reclaimable space<\/strong> (<code>data_free<\/code> on MySQL, an estimate of dead tuples on PostgreSQL);<\/li>\n<li><strong>size of each table<\/strong>;<\/li>\n<li><strong>actual files in the data directory<\/strong>: database folders, <code>ibdata1<\/code>, binlogs, undo, <code>pg_wal<\/code>.<\/li>\n<\/ul>\n<p>The last section is often the one that solves the mystery. Many times the &#8220;anomalous&#8221; space doesn&#8217;t belong to any database, but to the binary logs or to InnoDB&#8217;s shared files.<\/p>\n<h2 class=\"wp-block-heading\">Requirements<\/h2>\n<ul class=\"wp-block-list\">\n<li>Linux with Bash 4 or later, <code>awk<\/code>, <code>du<\/code>, <code>df<\/code> (all standard);<\/li>\n<li>the <code>mysql<\/code> or <code>psql<\/code> client, with a user who can read the metadata of all databases;<\/li>\n<li>root permissions (or those of the <code>mysql<\/code>\/<code>postgres<\/code> user) if you also want to measure the files on disk; without them, that section is skipped with a warning.<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\">Installation<\/h2>\n<p>Save the script (you will find it at the end of the article) to a file and make it executable:<\/p>\n<pre class=\"wp-block-code\"><code>nano db_space_compare.sh\nchmod +x db_space_compare.sh<\/code><\/pre>\n<h3 class=\"wp-block-heading\">Credentials<\/h3>\n<p>For MySQL\/MariaDB the cleanest way is a <code>~\/.my.cnf<\/code> file with the credentials, so they don&#8217;t appear in the command history:<\/p>\n<pre class=\"wp-block-code\"><code>&#91;client]\nuser=root\npassword=LA_TUA_PASSWORD<\/code><\/pre>\n<p>Alternatively you can pass the options through an environment variable:<\/p>\n<pre class=\"wp-block-code\"><code>export MYSQL_OPTS=\"-u root -pPASSWORD -h 127.0.0.1\"<\/code><\/pre>\n<p>For PostgreSQL, <code>~\/.pgpass<\/code>, the <code>PG*<\/code> variables or <code>PG_OPTS<\/code> work:<\/p>\n<pre class=\"wp-block-code\"><code>export PG_OPTS=\"-U postgres -h localhost\"<\/code><\/pre>\n<h2 class=\"wp-block-heading\">How to use it<\/h2>\n<p>The typical flow is <em>before, optimisation, after, comparison<\/em>:<\/p>\n<pre class=\"wp-block-code\"><code># 1. fotografia prima dell'ottimizzazione\nsudo .\/db_space_compare.sh snapshot prima\n\n# 2. esegui l'ottimizzazione (OPTIMIZE TABLE, VACUUM FULL, ...)\n\n# 3. fotografia dopo\nsudo .\/db_space_compare.sh snapshot dopo\n\n# 4. confronto\nsudo .\/db_space_compare.sh compare prima dopo<\/code><\/pre>\n<p>For PostgreSQL just add the <code>-e postgres<\/code> option:<\/p>\n<pre class=\"wp-block-code\"><code>sudo .\/db_space_compare.sh -e postgres snapshot prima\nsudo .\/db_space_compare.sh -e postgres snapshot dopo\nsudo .\/db_space_compare.sh -e postgres compare prima dopo<\/code><\/pre>\n<p>The available options are:<\/p>\n<ul class=\"wp-block-list\">\n<li><code>-e mysql|postgres<\/code> chooses the engine (default: <code>mysql<\/code>);<\/li>\n<li><code>-d DIR<\/code> changes the folder where snapshots are saved (default: <code>.\/db_snapshots<\/code>);<\/li>\n<li><code>-n N<\/code> sets how many rows to show per section (default: 20).<\/li>\n<\/ul>\n<p>If you don&#8217;t give a label, the snapshot is named after the date and time. This is handy for monitoring growth over time.<\/p>\n<h2 class=\"wp-block-heading\">Example output<\/h2>\n<p>Here is a comparison on test data, built on purpose to show a typical case. The <code>shop<\/code> database shrinks after the optimisation, but the <code>log<\/code> database grows and the binlogs double.<\/p>\n<pre class=\"wp-block-code\"><code>Confronto 'prima' (2026-10-11 20:00:00)  -&gt;  'dopo' (2026-10-11 22:00:00)\nFilesystem \/var: usato 46.6 GiB -&gt; 48.4 GiB (+1.9 GiB)\n\n=== Database - dimensione totale ===\n  NOME                                                      PRIMA         DOPO    DIFFERENZA  NOTE\n  log                                                    21.4 GiB     27.5 GiB      +6.1 GiB  +28.3%\n  shop                                                    5.6 GiB      3.6 GiB      -2.0 GiB  -35.0%\n\n=== Tabelle - dimensione (dati + indici) ===\n  NOME                                                      PRIMA         DOPO    DIFFERENZA  NOTE\n  log.eventi                                             21.4 GiB     27.5 GiB      +6.1 GiB  +28.3%\n  shop.ordini                                             5.6 GiB      3.6 GiB      -2.0 GiB  -35.0%\n  log.nuova                                                   0 B      1.0 KiB      +1.0 KiB  NUOVO\n\n=== File nella data directory (cartelle DB, ibdata1, binlog, undo, WAL) ===\n  NOME                                                      PRIMA         DOPO    DIFFERENZA  NOTE\n  log                                                    21.4 GiB     27.5 GiB      +6.1 GiB  +28.3%\n  mysql-bin.*                                             3.7 GiB      8.4 GiB      +4.7 GiB  +125.0%\n  shop                                                    5.7 GiB      3.6 GiB      -2.0 GiB  -36.1%\n  ibdata1                                                11.2 GiB     11.2 GiB           0 B<\/code><\/pre>\n<p>Here the culprit is obvious: the <code>log.eventi<\/code> table has grown by more than 6 GiB. The <code>mysql-bin.*<\/code> files went from 3.7 to 8.4 GiB. The optimisation on <code>shop<\/code> worked, but the gain was eaten up by the rest.<\/p>\n<h2 class=\"wp-block-heading\">How to read the database disk space results<\/h2>\n<ul class=\"wp-block-list\">\n<li>At the top of each section are the items that changed the most. A database or table that <strong>doesn&#8217;t shrink<\/strong> (or even grows) after the optimisation is the first suspect.<\/li>\n<li>If the tables shrink but the filesystem doesn&#8217;t, the space is elsewhere. Look at the section on files in the data directory (binlogs, <code>ibdata1<\/code>, undo, <code>pg_wal<\/code>).<\/li>\n<li><strong>MySQL\/MariaDB:<\/strong> <code>ibdata1<\/code> never shrinks, not even with <code>OPTIMIZE TABLE<\/code>. With <code>innodb_file_per_table=OFF<\/code> the value of <code>data_free<\/code> is shared and not very informative.<\/li>\n<li><strong>PostgreSQL:<\/strong> a plain <code>VACUUM<\/code> does not give space back to the operating system, you need <code>VACUUM FULL<\/code> (or <code>pg_repack<\/code>). The &#8220;reclaimable&#8221; space shown is an estimate based on dead tuples. The PostgreSQL documentation on <a href=\"https:\/\/www.postgresql.org\/docs\/current\/diskusage.html\" target=\"_blank\" rel=\"noopener\">monitoring disk usage<\/a> covers the underlying functions.<\/li>\n<\/ul>\n<h2 class=\"wp-block-heading\">Monitoring database disk space over time<\/h2>\n<p>If the problem isn&#8217;t tied to a single optimisation but to gradual growth, it is worth automating snapshots with cron, one an hour, and then comparing any two moments:<\/p>\n<pre class=\"wp-block-code\"><code># crontab -e (come root)\n0 * * * * \/usr\/local\/bin\/db_space_compare.sh -d \/var\/lib\/db_snapshots snapshot &gt;\/dev\/null 2&gt;&amp;1<\/code><\/pre>\n<p>Without a label, every snapshot is named after the date and time (for example <code>20261011-140000<\/code>). With <code>list<\/code> you see the available ones and with <code>compare<\/code> you compare two. Within a few days it becomes clear which database is growing abnormally.<\/p>\n<pre class=\"wp-block-code\"><code>.\/db_space_compare.sh -d \/var\/lib\/db_snapshots list\n.\/db_space_compare.sh -d \/var\/lib\/db_snapshots compare 20261011-140000 20261012-140000<\/code><\/pre>\n<h2 class=\"wp-block-heading\">The complete script<\/h2>\n<p>Here is the code. Copy it into a file called <code>db_space_compare.sh<\/code>, make it executable and you are ready.<\/p>\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/env bash\n#\n# db_space_compare.sh\n# Fotografa l'occupazione di spazio di tutti i database (MySQL\/MariaDB o\n# PostgreSQL) e confronta due fotografie (es. prima\/dopo l'ottimizzazione)\n# per capire quale database, tabella o file occupa spazio in modo anomalo.\n#\n# Uso:\n#   db_space_compare.sh &#91;opzioni] snapshot &#91;etichetta]   (default: data-ora)\n#   db_space_compare.sh &#91;opzioni] compare  &lt;prima&gt; &lt;dopo&gt;\n#   db_space_compare.sh &#91;opzioni] report   &lt;etichetta&gt;\n#   db_space_compare.sh &#91;opzioni] list\n#\n# Opzioni:\n#   -e mysql|postgres   motore del database (default: mysql)\n#   -d DIR              cartella delle fotografie (default: .\/db_snapshots)\n#   -n N                righe mostrate per sezione (default: 20)\n#\n# Credenziali:\n#   MySQL\/MariaDB: ~\/.my.cnf oppure MYSQL_OPTS=\"-u root -pPASSWORD -h 127.0.0.1\"\n#   PostgreSQL:    ~\/.pgpass, variabili PG* oppure PG_OPTS=\"-U postgres -h localhost\"\n#                  (non mettere -d in PG_OPTS: lo script si collega a ogni database)\n#\n# Per misurare anche i file su disco (cartelle dei DB, ibdata1, binlog, undo,\n# pg_wal) va eseguito come root o come utente mysql\/postgres; altrimenti\n# quella parte viene saltata con un avviso.\n#\n# Esempio:\n#   sudo .\/db_space_compare.sh snapshot prima\n#   ... ottimizzazione (OPTIMIZE TABLE \/ VACUUM FULL \/ ...) ...\n#   sudo .\/db_space_compare.sh snapshot dopo\n#   sudo .\/db_space_compare.sh compare prima dopo\n\nset -euo pipefail\nexport LC_ALL=C\n\nENGINE=mysql\nOUTDIR=.\/db_snapshots\nTOPN=20\nSNAP_IN_PROGRESS=\"\"\n\ndie()  { echo \"ERRORE: $*\" &gt;&amp;2; exit 1; }\nwarn() { echo \"ATTENZIONE: $*\" &gt;&amp;2; }\nusage() { sed -n '2,\/^$\/{s\/^# \\{0,1\\}\/\/;p}' \"$0\"; exit \"${1:-0}\"; }\n\ncleanup() {\n  if &#91;&#91; -n $SNAP_IN_PROGRESS ]]; then\n    rm -rf -- \"$SNAP_IN_PROGRESS\"\n    echo \"Fotografia incompleta rimossa: $SNAP_IN_PROGRESS\" &gt;&amp;2\n  fi\n}\ntrap cleanup EXIT\n\nwhile getopts \":e:d:n:h\" opt; do\n  case $opt in\n    e) ENGINE=$OPTARG ;;\n    d) OUTDIR=$OPTARG ;;\n    n) TOPN=$OPTARG ;;\n    h) usage 0 ;;\n    *) usage 1 ;;\n  esac\ndone\nshift $((OPTIND - 1))\n\n&#91;&#91; $ENGINE == mysql || $ENGINE == postgres ]] || die \"motore non valido: '$ENGINE' (usa mysql o postgres)\"\n&#91;&#91; $TOPN =~ ^&#91;0-9]+$ ]] || die \"-n deve essere un numero\"\n\nread -r -a MYSQL_ARGS &lt;&lt;&lt; \"${MYSQL_OPTS:-}\"\nread -r -a PG_ARGS    &lt;&lt;&lt; \"${PG_OPTS:-}\"\n\nmy() { mysql \"${MYSQL_ARGS&#91;@]+\"${MYSQL_ARGS&#91;@]}\"}\" --batch --skip-column-names \"$@\"; }\npg() { psql  \"${PG_ARGS&#91;@]+\"${PG_ARGS&#91;@]}\"}\" -X -q -A -t -F $'\\t' -v ON_ERROR_STOP=1 \"$@\"; }\n\n# Funzioni awk comuni: formattazione in KiB\/MiB\/GiB, chiave e valore di riga.\n#   nk = numero di colonne che formano il nome (1 = database, 2 = db.tabella)\n#   vc = colonne da sommare per il valore (es. \"4,5\" = dati + indici)\nAWK_LIB='\nfunction h(b,   a, i, u) {\n  split(\"B KiB MiB GiB TiB PiB\", u, \" \")\n  a = (b &lt; 0) ? -b : b; i = 1\n  while (a &gt;= 1024 &amp;&amp; i &lt; 6) { a \/= 1024; i++ }\n  if (i == 1) return sprintf(\"%s%d B\", (b &lt; 0 ? \"-\" : \"\"), a)\n  return sprintf(\"%s%.1f %s\", (b &lt; 0 ? \"-\" : \"\"), a, u&#91;i])\n}\nfunction hs(b) { return (b &gt; 0 ? \"+\" : \"\") h(b) }\nfunction key(   k, i) { k = $1; for (i = 2; i &lt;= nk; i++) k = k \".\" $i; return k }\nfunction val(   n, c, i, s) { n = split(vc, c, \",\"); s = 0; for (i = 1; i &lt;= n; i++) s += $(c&#91;i]); return s }\n'\n\nmeta_get() { awk -F= -v k=\"$2\" '$1 == k { sub(\/^&#91;^=]*=\/, \"\"); print; exit }' \"$1\/meta.txt\"; }\n\n# ---------------------------------------------------------------- raccolta dati\n\n# Occupazione reale su disco delle voci di primo livello della data directory:\n# cartelle dei database, ibdata1, binlog, undo, redo log, pg_wal, base, ...\n# I file numerati (mysql-bin.000123, relay-log.000045, ...) sono sommati in una voce.\ncollect_disk() {\n  local d=$1 out=$2\n  : &gt; \"$out\"\n  if &#91;&#91; -z $d || ! -r $d || ! -x $d ]]; then\n    warn \"data directory '${d:-?}' non leggibile: salto i file su disco (esegui come root per includerli)\"\n    return 0\n  fi\n  { du -sb -- \"$d\"\/* 2&gt;\/dev\/null || true; } |\n    awk -F'\\t' -v p=\"$d\/\" '\n      { n = $2\n        if (index(n, p) == 1) n = substr(n, length(p) + 1)\n        if (n ~ \/\\.&#91;0-9]&#91;0-9]&#91;0-9]&#91;0-9]&#91;0-9]&#91;0-9]$\/) sub(\/&#91;0-9]+$\/, \"*\", n)\n        s&#91;n] += $1 }\n      END { for (n in s) printf \"%s\\t%.0f\\n\", n, s&#91;n] }' &gt; \"$out\"\n}\n\nwrite_meta() {\n  local dir=$1 datadir=$2 version=$3\n  {\n    echo \"timestamp=$(date '+%Y-%m-%d %H:%M:%S')\"\n    echo \"engine=$ENGINE\"\n    echo \"host=$(hostname)\"\n    echo \"version=$version\"\n    echo \"datadir=$datadir\"\n    if &#91;&#91; -n $datadir &amp;&amp; -e $datadir ]]; then\n      { df -P -B1 -- \"$datadir\" 2&gt;\/dev\/null || true; } |\n        awk 'NR == 2 { print \"fs_mount=\" $6; print \"fs_used=\" $3; print \"fs_avail=\" $4 }'\n    fi\n  } &gt; \"$dir\/meta.txt\"\n}\n\n# File prodotti da ogni fotografia (separati da TAB):\n#   tables.tsv     db, tabella, engine, dati, indici, libero\/recuperabile, righe\n#   databases.tsv  db, dati, indici, libero\/recuperabile, totale\n#   disk.tsv       voce della data directory, byte su disco\n#   meta.txt       data, host, versione, filesystem\n\nsnapshot_mysql() {\n  local dir=$1 pre=\"\" datadir version\n  command -v mysql &gt;\/dev\/null 2&gt;&amp;1 || die \"client 'mysql' non trovato\"\n  my -e \"SELECT 1\" &gt;\/dev\/null || die \"connessione a MySQL\/MariaDB fallita (controlla ~\/.my.cnf o MYSQL_OPTS)\"\n\n  # MySQL 8 tiene in cache le statistiche di information_schema fino a 24 ore:\n  # senza questo, i valori \"dopo\" l'ottimizzazione potrebbero essere quelli vecchi.\n  if &#91;&#91; -n $(my -e \"SHOW VARIABLES LIKE 'information_schema_stats_expiry'\") ]]; then\n    pre=\"SET SESSION information_schema_stats_expiry = 0;\"\n  fi\n\n  my -e \"$pre\n    SELECT table_schema, table_name, IFNULL(engine, '-'),\n           IFNULL(data_length, 0), IFNULL(index_length, 0), IFNULL(data_free, 0), IFNULL(table_rows, 0)\n      FROM information_schema.TABLES\n     WHERE table_type IN ('BASE TABLE', 'SYSTEM VERSIONED')\n       AND table_schema NOT IN ('information_schema', 'performance_schema', 'sys')\" &gt; \"$dir\/tables.tsv\"\n\n  my -e \"$pre\n    SELECT s.schema_name,\n           IFNULL(SUM(t.data_length), 0), IFNULL(SUM(t.index_length), 0), IFNULL(SUM(t.data_free), 0),\n           IFNULL(SUM(t.data_length), 0) + IFNULL(SUM(t.index_length), 0)\n      FROM information_schema.SCHEMATA s\n      LEFT JOIN information_schema.TABLES t\n             ON t.table_schema = s.schema_name\n            AND t.table_type IN ('BASE TABLE', 'SYSTEM VERSIONED')\n     WHERE s.schema_name NOT IN ('information_schema', 'performance_schema', 'sys')\n     GROUP BY s.schema_name\" &gt; \"$dir\/databases.tsv\"\n\n  datadir=$(my -e \"SELECT @@datadir\"); datadir=${datadir%\/}\n  version=$(my -e \"SELECT VERSION()\")\n  collect_disk \"$datadir\" \"$dir\/disk.tsv\"\n  write_meta \"$dir\" \"$datadir\" \"$version\"\n}\n\nsnapshot_postgres() {\n  local dir=$1 maindb=${PGDATABASE:-postgres} datadir version db\n  command -v psql &gt;\/dev\/null 2&gt;&amp;1 || die \"client 'psql' non trovato\"\n  pg -d \"$maindb\" -c \"SELECT 1\" &gt;\/dev\/null || die \"connessione a PostgreSQL fallita (controlla PG_OPTS, PGUSER, ~\/.pgpass)\"\n\n  pg -d \"$maindb\" -c \"SELECT datname FROM pg_database WHERE datallowconn AND NOT datistemplate ORDER BY 1\" &gt; \"$dir\/.dblist\"\n\n  # Per ogni database: dimensione di ogni tabella (catalogo incluso, es. pg_largeobject)\n  # e stima dello spazio occupato da tuple morte (bloat recuperabile con VACUUM FULL).\n  : &gt; \"$dir\/tables.tsv\"\n  while IFS= read -r db; do\n    &#91;&#91; -n $db ]] || continue\n    pg -d \"$db\" -c \"\n      SELECT current_database(), s.schemaname || '.' || s.relname, 'table',\n             pg_table_size(s.relid), pg_indexes_size(s.relid),\n             CASE WHEN s.n_live_tup + s.n_dead_tup &gt; 0\n                  THEN (pg_table_size(s.relid)::numeric * s.n_dead_tup \/ (s.n_live_tup + s.n_dead_tup))::bigint\n                  ELSE 0 END,\n             s.n_live_tup\n        FROM pg_stat_all_tables s\n       WHERE s.schemaname &lt;&gt; 'information_schema'\n         AND s.schemaname NOT LIKE 'pg_toast%'\" &lt; \/dev\/null &gt;&gt; \"$dir\/tables.tsv\" \\\n      || warn \"impossibile leggere le tabelle del database '$db'\"\n  done &lt; \"$dir\/.dblist\"\n\n  pg -d \"$maindb\" -c \"SELECT datname, pg_database_size(oid) FROM pg_database WHERE datallowconn AND NOT datistemplate\" &gt; \"$dir\/.dbsize\"\n  awk -F'\\t' '\n    FILENAME == ARGV&#91;1] { d&#91;$1] += $4; i&#91;$1] += $5; f&#91;$1] += $6; next }\n    { printf \"%s\\t%.0f\\t%.0f\\t%.0f\\t%s\\n\", $1, d&#91;$1], i&#91;$1], f&#91;$1], $2 }' \\\n    \"$dir\/tables.tsv\" \"$dir\/.dbsize\" &gt; \"$dir\/databases.tsv\"\n\n  datadir=$(pg -d \"$maindb\" -c \"SHOW data_directory\" 2&gt;\/dev\/null || true)\n  version=$(pg -d \"$maindb\" -c \"SHOW server_version\")\n  collect_disk \"$datadir\" \"$dir\/disk.tsv\"\n\n  # Dettaglio di base\/&lt;oid&gt; con il nome del database al posto dell'oid\n  if &#91;&#91; -n $datadir &amp;&amp; -r $datadir\/base &amp;&amp; -x $datadir\/base ]]; then\n    pg -d \"$maindb\" -c \"SELECT oid, datname FROM pg_database\" &gt; \"$dir\/.oidmap\"\n    { du -sb -- \"$datadir\"\/base\/* 2&gt;\/dev\/null || true; } |\n      awk -F'\\t' '\n        FILENAME == ARGV&#91;1] { name&#91;$1] = $2; next }\n        { o = $2; sub(\/.*\\\/\/, \"\", o); printf \"base\/%s\\t%s\\n\", ((o in name) ? name&#91;o] : o), $1 }' \\\n        \"$dir\/.oidmap\" - &gt;&gt; \"$dir\/disk.tsv\"\n  fi\n  write_meta \"$dir\" \"$datadir\" \"$version\"\n}\n\n# --------------------------------------------------------------- presentazione\n\nprint_fs() {\n  local d=$1 used avail mount\n  used=$(meta_get \"$d\" fs_used); avail=$(meta_get \"$d\" fs_avail); mount=$(meta_get \"$d\" fs_mount)\n  &#91;&#91; -n $used ]] || return 0\n  awk -v u=\"$used\" -v a=\"$avail\" -v m=\"$mount\" \"$AWK_LIB\"'\n    BEGIN { printf \"Filesystem %s: usato %s, libero %s\\n\", m, h(u), h(a) }'\n}\n\ntop_section() {\n  local f=$1 nk=$2 vc=$3 title=$4\n  echo\n  echo \"=== $title ===\"\n  if &#91;&#91; ! -s $f ]]; then echo \"  (nessun dato)\"; return 0; fi\n  awk -F'\\t' -v nk=\"$nk\" -v vc=\"$vc\" \"$AWK_LIB\"'\n    { v = val(); printf \"%.0f\\t%s\\t%s\\n\", v, key(), h(v) }' \"$f\" |\n    sort -t $'\\t' -k1,1nr -k2,2 |\n    awk -F'\\t' -v top=\"$TOPN\" 'NR &lt;= top &amp;&amp; $1 &gt; 0 { printf \"  %-60s %12s\\n\", $2, $3 }'\n}\n\ncompare_section() {\n  local fa=$1 fb=$2 nk=$3 vc=$4 title=$5\n  &#91;&#91; -f $fa ]] || fa=\/dev\/null\n  &#91;&#91; -f $fb ]] || fb=\/dev\/null\n  echo\n  echo \"=== $title ===\"\n  if &#91;&#91; ! -s $fa &amp;&amp; ! -s $fb ]]; then echo \"  (nessun dato)\"; return 0; fi\n  printf '  %-50s %12s %12s %13s  %s\\n' \"NOME\" \"PRIMA\" \"DOPO\" \"DIFFERENZA\" \"NOTE\"\n  awk -F'\\t' -v nk=\"$nk\" -v vc=\"$vc\" \"$AWK_LIB\"'\n    FILENAME == ARGV&#91;1] { k = key(); A&#91;k] = val(); S&#91;k] = 1; next }\n                        { k = key(); B&#91;k] = val(); S&#91;k] = 1 }\n    END {\n      for (k in S) {\n        inA = (k in A); inB = (k in B)\n        a = inA ? A&#91;k] : 0; b = inB ? B&#91;k] : 0; d = b - a\n        if (!inA)                 note = \"NUOVO\"\n        else if (!inB)            note = \"RIMOSSO\"\n        else if (a &gt; 0 &amp;&amp; d != 0) note = sprintf(\"%+.1f%%\", 100 * d \/ a)\n        else                      note = \"\"\n        printf \"%.0f\\t%s\\t%s\\t%s\\t%s\\t%s\\n\", (d &lt; 0 ? -d : d), k, h(a), h(b), hs(d), note\n      }\n    }' \"$fa\" \"$fb\" |\n    sort -t $'\\t' -k1,1nr -k2,2 |\n    awk -F'\\t' -v top=\"$TOPN\" 'NR &lt;= top { printf \"  %-50s %12s %12s %13s  %s\\n\", $2, $3, $4, $5, $6 }'\n}\n\n# -------------------------------------------------------------------- comandi\n\ncmd_snapshot() {\n  local label=${1:-$(date '+%Y%m%d-%H%M%S')}\n  &#91;&#91; $label =~ ^&#91;A-Za-z0-9]&#91;A-Za-z0-9._-]*$ ]] || die \"etichetta non valida: usa solo lettere, numeri, . _ -\"\n  local dir=$OUTDIR\/$label\n  &#91;&#91; ! -e $dir ]] || die \"la fotografia '$label' esiste gi\u00e0 ($dir)\"\n  mkdir -p -- \"$dir\"\n  SNAP_IN_PROGRESS=$dir\n  echo \"Fotografia '$label' ($ENGINE) in corso...\"\n  \"snapshot_$ENGINE\" \"$dir\"\n  rm -f -- \"$dir\/.dblist\" \"$dir\/.dbsize\" \"$dir\/.oidmap\"\n  SNAP_IN_PROGRESS=\"\"\n  echo \"Salvata in $dir ($(wc -l &lt; \"$dir\/tables.tsv\") tabelle)\"\n  print_fs \"$dir\"\n  top_section \"$dir\/databases.tsv\" 1 5 \"Database pi\u00f9 grandi\"\n}\n\ncmd_report() {\n  local label=${1:-}\n  &#91;&#91; -n $label ]] || die \"uso: report &lt;etichetta&gt;\"\n  local d=$OUTDIR\/$label\n  &#91;&#91; -f $d\/meta.txt ]] || die \"fotografia '$label' non trovata in $OUTDIR\"\n  echo \"Fotografia '$label' del $(meta_get \"$d\" timestamp) - $(meta_get \"$d\" engine) $(meta_get \"$d\" version) su $(meta_get \"$d\" host)\"\n  print_fs \"$d\"\n  top_section \"$d\/databases.tsv\" 1 5   \"Database pi\u00f9 grandi (dati + indici)\"\n  top_section \"$d\/tables.tsv\"    2 4,5 \"Tabelle pi\u00f9 grandi (dati + indici)\"\n  top_section \"$d\/tables.tsv\"    2 6   \"Tabelle con pi\u00f9 spazio recuperabile (data_free \/ tuple morte stimate)\"\n  top_section \"$d\/disk.tsv\"      1 2   \"Voci pi\u00f9 grandi nella data directory\"\n}\n\ncmd_compare() {\n  local a=${1:-} b=${2:-}\n  &#91;&#91; -n $a &amp;&amp; -n $b ]] || die \"uso: compare &lt;etichetta_prima&gt; &lt;etichetta_dopo&gt;\"\n  &#91;&#91; $a != \"$b\" ]] || die \"le due etichette sono uguali\"\n  local da=$OUTDIR\/$a db=$OUTDIR\/$b\n  &#91;&#91; -f $da\/meta.txt ]] || die \"fotografia '$a' non trovata in $OUTDIR\"\n  &#91;&#91; -f $db\/meta.txt ]] || die \"fotografia '$b' non trovata in $OUTDIR\"\n  &#91;&#91; $(meta_get \"$da\" engine) == \"$(meta_get \"$db\" engine)\" ]] || warn \"le due fotografie sono di motori diversi\"\n\n  echo \"Confronto '$a' ($(meta_get \"$da\" timestamp))  -&gt;  '$b' ($(meta_get \"$db\" timestamp))\"\n  local ua ub m\n  ua=$(meta_get \"$da\" fs_used); ub=$(meta_get \"$db\" fs_used); m=$(meta_get \"$db\" fs_mount)\n  if &#91;&#91; -n $ua &amp;&amp; -n $ub ]]; then\n    awk -v a=\"$ua\" -v b=\"$ub\" -v m=\"$m\" \"$AWK_LIB\"'\n      BEGIN { printf \"Filesystem %s: usato %s -&gt; %s (%s)\\n\", m, h(a), h(b), hs(b - a) }'\n  fi\n\n  compare_section \"$da\/databases.tsv\" \"$db\/databases.tsv\" 1 5   \"Database - dimensione totale\"\n  compare_section \"$da\/databases.tsv\" \"$db\/databases.tsv\" 1 4   \"Database - spazio libero\/recuperabile\"\n  compare_section \"$da\/tables.tsv\"    \"$db\/tables.tsv\"    2 4,5 \"Tabelle - dimensione (dati + indici)\"\n  compare_section \"$da\/disk.tsv\"      \"$db\/disk.tsv\"      1 2   \"File nella data directory (cartelle DB, ibdata1, binlog, undo, WAL)\"\n\n  cat &lt;&lt;'EOF'\n\nCome leggere il risultato:\n  - In cima a ogni sezione ci sono le voci cambiate di pi\u00f9: un database o una\n    tabella che dopo l'ottimizzazione non cala, o addirittura cresce, \u00e8 il\n    primo sospetto.\n  - Se le tabelle calano ma il filesystem no, lo spazio sta altrove: guarda la\n    sezione \"File nella data directory\" (binlog, ibdata1, undo, pg_wal).\n  - MySQL\/MariaDB: ibdata1 non si restringe mai, nemmeno con OPTIMIZE; con\n    innodb_file_per_table=OFF il data_free \u00e8 condiviso e poco indicativo.\n  - PostgreSQL: VACUUM semplice non restituisce spazio al disco, serve\n    VACUUM FULL (o pg_repack); lo spazio \"recuperabile\" \u00e8 una stima.\nEOF\n}\n\ncmd_list() {\n  if &#91;&#91; ! -d $OUTDIR ]]; then echo \"Nessuna fotografia in $OUTDIR\"; return 0; fi\n  local d found=0\n  for d in \"$OUTDIR\"\/*\/; do\n    &#91;&#91; -f $d\/meta.txt ]] || continue\n    found=1\n    printf '%-24s %s  %-8s  %s\\n' \"$(basename \"$d\")\" \"$(meta_get \"$d\" timestamp)\" \\\n      \"$(meta_get \"$d\" engine)\" \"$(meta_get \"$d\" host)\"\n  done\n  &#91;&#91; $found -eq 1 ]] || echo \"Nessuna fotografia in $OUTDIR\"\n}\n\ncmd=${1:-}\nshift || true\ncase $cmd in\n  snapshot) cmd_snapshot \"$@\" ;;\n  compare)  cmd_compare \"$@\" ;;\n  report)   cmd_report \"$@\" ;;\n  list)     cmd_list ;;\n  \"\"|help)  usage 0 ;;\n  *)        die \"comando sconosciuto: '$cmd' (usa snapshot, compare, report, list)\" ;;\nesac<\/code><\/pre>\n<h2 class=\"wp-block-heading\">Final notes on database disk space<\/h2>\n<ul class=\"wp-block-list\">\n<li>The script is <strong>read-only<\/strong> on the databases: it only runs <code>SELECT<\/code> on the metadata. It writes only to the snapshots folder.<\/li>\n<li>On MySQL 8 it sets <code>information_schema_stats_expiry = 0<\/code> in the session. Otherwise the cached statistics (up to 24 hours) could show &#8220;old&#8221; values even after the optimisation.<\/li>\n<li>If a snapshot fails halfway, it is removed automatically so as not to leave incomplete data.<\/li>\n<li>Before using it in production, try it in a test environment and check the database user&#8217;s permissions.<\/li>\n<\/ul>\n<p>If you use it and find a case it doesn&#8217;t cover, write to me in the comments. The same goes for ideas to extend it (other engines, CSV export, email alerts). If the problem is a MySQL that won&#8217;t start rather than a full disk, see <a href=\"https:\/\/www.cisilino.com\/en\/2026\/10\/13\/mysql-wont-start-debian-12\/\">MySQL won&#8217;t start after a Debian 12 upgrade<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The server disk is filling up and you don&#8217;t know which database is to blame? A script to measure the space used by MySQL and PostgreSQL and compare it over time.<\/p>\n","protected":false},"author":1,"featured_media":752,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpai_generated_summary":"","footnotes":""},"categories":[361],"tags":[340,336,292,304],"class_list":["post-256","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-servers-databases","tag-bash-script","tag-disk-space","tag-mysql-en","tag-postgresql-en"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"Database disk space running out and you don&#039;t know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"matteocisilino\"\/>\n\t<meta name=\"google-site-verification\" content=\"_A-1f0dO30IDVHfP9muflI8kHQs-H4YOi8WLUkYzuLE\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_GB\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Matteo Cisilino - Guide su WordPress, server e sicurezza\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Database disk space: a MySQL and PostgreSQL script\" \/>\n\t\t<meta property=\"og:description\" content=\"Database disk space running out and you don&#039;t know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp\" \/>\n\t\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-20T07:53:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-29T19:00:36+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Database disk space: a MySQL and PostgreSQL script\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Database disk space running out and you don&#039;t know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#blogposting\",\"name\":\"Database disk space: a MySQL and PostgreSQL script\",\"headline\":\"Which database is using up your disk space? A script for MySQL and PostgreSQL\",\"author\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/author\\\/matteocisilino\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/mysql-postgresql-database-disk-usage-script.webp\",\"width\":1200,\"height\":630,\"caption\":\"Database disk space by engine: ring chart split into four coloured segments, with three legend bars on the right\"},\"datePublished\":\"2026-10-20T09:53:00+02:00\",\"dateModified\":\"2026-10-29T20:00:36+01:00\",\"inLanguage\":\"en-GB\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#webpage\"},\"articleSection\":\"Servers and databases, bash script, disk space, MySQL, PostgreSQL\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/category\\\/servers-databases\\\/#listItem\",\"name\":\"Servers and databases\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/category\\\/servers-databases\\\/#listItem\",\"position\":2,\"name\":\"Servers and databases\",\"item\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/category\\\/servers-databases\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#listItem\",\"name\":\"Which database is using up your disk space? A script for MySQL and PostgreSQL\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#listItem\",\"position\":3,\"name\":\"Which database is using up your disk space? A script for MySQL and PostgreSQL\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/category\\\/servers-databases\\\/#listItem\",\"name\":\"Servers and databases\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#organization\",\"name\":\"Matteo Cisilino\",\"description\":\"Guide su WordPress, server e sicurezza\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/logo-cisilino-512.png\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#organizationLogo\",\"width\":512,\"height\":512,\"caption\":\"Logo: un cerchio a sinistra che si divide in cerchi pi\\u00f9 piccoli\"},\"image\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#organizationLogo\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/author\\\/matteocisilino\\\/#author\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/author\\\/matteocisilino\\\/\",\"name\":\"matteocisilino\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/639872265f0661e8aee8924ba9d756b00ea816f8286fab99a5116c729d49618f?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"matteocisilino\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#webpage\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/\",\"name\":\"Database disk space: a MySQL and PostgreSQL script\",\"description\":\"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.\",\"inLanguage\":\"en-GB\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/author\\\/matteocisilino\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/author\\\/matteocisilino\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/mysql-postgresql-database-disk-usage-script.webp\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#mainImage\",\"width\":1200,\"height\":630,\"caption\":\"Database disk space by engine: ring chart split into four coloured segments, with three legend bars on the right\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/2026\\\/10\\\/20\\\/database-disk-space-script-mysql-postgresql\\\/#mainImage\"},\"datePublished\":\"2026-10-20T09:53:00+02:00\",\"dateModified\":\"2026-10-29T20:00:36+01:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/\",\"name\":\"Matteo Cisilino\",\"description\":\"Guide su WordPress, server e sicurezza\",\"inLanguage\":\"en-GB\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.cisilino.com\\\/en\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Database disk space: a MySQL and PostgreSQL script","description":"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.","canonical_url":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"google-site-verification":"_A-1f0dO30IDVHfP9muflI8kHQs-H4YOi8WLUkYzuLE","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#blogposting","name":"Database disk space: a MySQL and PostgreSQL script","headline":"Which database is using up your disk space? A script for MySQL and PostgreSQL","author":{"@id":"https:\/\/www.cisilino.com\/en\/author\/matteocisilino\/#author"},"publisher":{"@id":"https:\/\/www.cisilino.com\/en\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp","width":1200,"height":630,"caption":"Database disk space by engine: ring chart split into four coloured segments, with three legend bars on the right"},"datePublished":"2026-10-20T09:53:00+02:00","dateModified":"2026-10-29T20:00:36+01:00","inLanguage":"en-GB","mainEntityOfPage":{"@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#webpage"},"isPartOf":{"@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#webpage"},"articleSection":"Servers and databases, bash script, disk space, MySQL, PostgreSQL"},{"@type":"BreadcrumbList","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/#listItem","position":1,"name":"Home","item":"https:\/\/www.cisilino.com\/en\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/#listItem","name":"Servers and databases"}},{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/#listItem","position":2,"name":"Servers and databases","item":"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#listItem","name":"Which database is using up your disk space? A script for MySQL and PostgreSQL"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#listItem","position":3,"name":"Which database is using up your disk space? A script for MySQL and PostgreSQL","previousItem":{"@type":"ListItem","@id":"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/#listItem","name":"Servers and databases"}}]},{"@type":"Organization","@id":"https:\/\/www.cisilino.com\/en\/#organization","name":"Matteo Cisilino","description":"Guide su WordPress, server e sicurezza","url":"https:\/\/www.cisilino.com\/en\/","logo":{"@type":"ImageObject","url":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/logo-cisilino-512.png","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#organizationLogo","width":512,"height":512,"caption":"Logo: un cerchio a sinistra che si divide in cerchi pi\u00f9 piccoli"},"image":{"@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#organizationLogo"}},{"@type":"Person","@id":"https:\/\/www.cisilino.com\/en\/author\/matteocisilino\/#author","url":"https:\/\/www.cisilino.com\/en\/author\/matteocisilino\/","name":"matteocisilino","image":{"@type":"ImageObject","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/639872265f0661e8aee8924ba9d756b00ea816f8286fab99a5116c729d49618f?s=96&d=mm&r=g","width":96,"height":96,"caption":"matteocisilino"}},{"@type":"WebPage","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#webpage","url":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/","name":"Database disk space: a MySQL and PostgreSQL script","description":"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.","inLanguage":"en-GB","isPartOf":{"@id":"https:\/\/www.cisilino.com\/en\/#website"},"breadcrumb":{"@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#breadcrumblist"},"author":{"@id":"https:\/\/www.cisilino.com\/en\/author\/matteocisilino\/#author"},"creator":{"@id":"https:\/\/www.cisilino.com\/en\/author\/matteocisilino\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp","@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#mainImage","width":1200,"height":630,"caption":"Database disk space by engine: ring chart split into four coloured segments, with three legend bars on the right"},"primaryImageOfPage":{"@id":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/#mainImage"},"datePublished":"2026-10-20T09:53:00+02:00","dateModified":"2026-10-29T20:00:36+01:00"},{"@type":"WebSite","@id":"https:\/\/www.cisilino.com\/en\/#website","url":"https:\/\/www.cisilino.com\/en\/","name":"Matteo Cisilino","description":"Guide su WordPress, server e sicurezza","inLanguage":"en-GB","publisher":{"@id":"https:\/\/www.cisilino.com\/en\/#organization"}}]},"og:locale":"en_GB","og:site_name":"Matteo Cisilino - Guide su WordPress, server e sicurezza","og:type":"article","og:title":"Database disk space: a MySQL and PostgreSQL script","og:description":"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.","og:url":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/","og:image":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp","og:image:secure_url":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp","og:image:width":1200,"og:image:height":630,"article:published_time":"2026-10-20T07:53:00+00:00","article:modified_time":"2026-10-29T19:00:36+00:00","twitter:card":"summary_large_image","twitter:title":"Database disk space: a MySQL and PostgreSQL script","twitter:description":"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.","twitter:image":"https:\/\/www.cisilino.com\/wp-content\/uploads\/2026\/10\/mysql-postgresql-database-disk-usage-script.webp"},"aioseo_meta_data":{"post_id":"256","title":"Database disk space: a MySQL and PostgreSQL script","description":"Database disk space running out and you don't know which database is to blame? A script to measure MySQL and PostgreSQL usage and compare it over time.","keywords":null,"keyphrases":{"focus":{"keyphrase":"database disk space"}},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":null,"twitter_description":null,"schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-10-29 09:22:02","updated":"2026-10-29 21:57:55","focus_keyword":"database disk space mysql","additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.cisilino.com\/en\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/\" title=\"Servers and databases\">Servers and databases<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tWhich database is using up your disk space? A script for MySQL and PostgreSQL\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.cisilino.com\/en\/"},{"label":"Servers and databases","link":"https:\/\/www.cisilino.com\/en\/category\/servers-databases\/"},{"label":"Which database is using up your disk space? A script for MySQL and PostgreSQL","link":"https:\/\/www.cisilino.com\/en\/2026\/10\/20\/database-disk-space-script-mysql-postgresql\/"}],"_links":{"self":[{"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/posts\/256","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/comments?post=256"}],"version-history":[{"count":2,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/posts\/256\/revisions"}],"predecessor-version":[{"id":898,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/posts\/256\/revisions\/898"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/media\/752"}],"wp:attachment":[{"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/media?parent=256"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/categories?post=256"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cisilino.com\/en\/wp-json\/wp\/v2\/tags?post=256"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}