In large Zabbix deployments, the raw history tables (history, history_uint, history_str, history_log, and history_text) frequently grow to tens or hundreds of gigabytes. Running a DELETE FROM history_uint WHERE clock < ... statement against a massive InnoDB table is both slow (holding row locks and generating huge undo logs) and does not reclaim disk space on the filesystem without a subsequent OPTIMIZE TABLE.
A much faster way to prune an oversized Zabbix history table and immediately reclaim disk space (when MySQL is configured with innodb_file_per_table=ON) is the shadow-table swap pattern:
- Stop
zabbix-server. - Back up the target table (
mysqldump) as a safety precaution. - Create a new empty table (
CREATE TABLE ... LIKE history_uint). - Copy only the recent rows you want to keep (for example, the last 3 months based on Unix
clockepoch) into the new table (INSERT INTO ... SELECT). - Atomically rename
history_uinttohistory_uint_oldand rename the new table into place (history_uint). - Drop
history_uint_oldto immediately release the old.ibdfile back to the OS filesystem. - Start
zabbix-server.
- Companion Script on GitHub:
history_table_clean_up_db_script.sh
Prerequisites & Warnings:
- Verify
innodb_file_per_table=ON(SHOW VARIABLES LIKE 'innodb_file_per_table';) before starting. Ifinnodb_file_per_tableisOFF, droppinghistory_uint_oldfrees pages inside the globalibdata1file rather than shrinkingibdata1on disk.- Ensure you have enough free disk space to hold the recent 3-month subset of rows during Step 4 before Step 7 drops the old table.
- For long-term high-volume Zabbix installations, consider MySQL/MariaDB table partitioning (or TimescaleDB on PostgreSQL) and disable Zabbix’s internal Housekeeper for
history*andtrends*.
Step-by-Step Procedure
Step 1: Stop the Zabbix Server
Stop zabbix-server so no new metrics are written while the table swap is in progress:
sudo service zabbix-server stop
Step 2: Back Up the Target Table (Optional Safety Dump)
If you have sufficient disk space on a backup volume, dump the existing history_uint table first:
mysqldump -u zabbix -p zabbix history_uint > /tmp/history_uint.sql
Step 3: Create the New Empty Table (CREATE TABLE ... LIKE)
CREATE TABLE ... LIKE clones the exact column definitions and indexes of history_uint:
CREATE TABLE history_uint_new_20161007 LIKE history_uint;
Step 4: Copy Only Recent Records Using a Unix Epoch Cutoff
Zabbix stores timestamps in the clock column as Unix epoch seconds. You can calculate the epoch timestamp for 3 months ago using GNU date:
# Example: specific date to Unix epoch
date --date "20160707" +%s
# 1467829800
# Dynamic: 3 months ago from right now
date -d "now -3 months" +%s
Insert only rows newer than your cutoff timestamp into the new table:
INSERT INTO history_uint_new_20161007
SELECT * FROM history_uint
WHERE clock > 1467829800;
Step 5: Rename the Old Table and Swap in the New Table
Rename the original table to history_uint_old and rename history_uint_new_20161007 to history_uint:
ALTER TABLE history_uint RENAME history_uint_old;
ALTER TABLE history_uint_new_20161007 RENAME history_uint;
(Tip: You can also perform both renames in a single atomic statement with RENAME TABLE history_uint TO history_uint_old, history_uint_new_20161007 TO history_uint;.)
Step 6: Drop the Old Table to Reclaim Disk Space
Once you confirm history_uint contains the expected row count, drop the old table:
DROP TABLE history_uint_old;
Step 7: Restart the Zabbix Server
sudo service zabbix-server start
Step 8: Reduce Item History Retention to Prevent Future Bloat (Optional)
To prevent history* tables from growing excessively again, review item history retention intervals. In Zabbix 2.x/3.0, items.history is stored as an integer number of days:
-- Zabbix 2.x / 3.0 (integer days): cap item history retention at 15 days where > 30 days
UPDATE items SET history = 15 WHERE history > 30;
(Note: In Zabbix 3.4+, the history column uses time-unit suffixes such as '15d' or '90d' and can be overridden globally under Administration → General → Housekeeping.)
Complete Automation Script
Below is the complete automation script (history_table_clean_up_db_script.sh):
#!/bin/bash
set -euo pipefail
THREE_MONTH_BACK_DATE=$(date -d "now -3 months" +%Y-%m-%d)
CURRENT_DATE=$(date -d "now" +%Y-%m-%d)
EPOCH_3MONTHS_BACK=$(date -d "$THREE_MONTH_BACK_DATE" +%s)
EPOCH_NOW=$(date -d "$CURRENT_DATE" +%s)
ZABBIX_DATABASE="zabbix"
ZABBIX_USER="zabbix"
ZABBIX_PASSWD="REPLACE_WITH_DB_PASSWORD"
ZABBIX_TABLE_NAME="history_uint"
BACKUP_DIR_PATH="/tmp/zabbix/zabbix_table_backup_${ZABBIX_TABLE_NAME}"
BACKUP_FILE_PATH="${BACKUP_DIR_PATH}/${ZABBIX_TABLE_NAME}_${CURRENT_DATE}_${EPOCH_NOW}.sql"
echo "------------------------------------------"
echo "Date to Keep From : $THREE_MONTH_BACK_DATE"
echo "Epoch to Keep From : $EPOCH_3MONTHS_BACK"
echo "Today's Date : $CURRENT_DATE"
echo "Epoch For Today's Date : $EPOCH_NOW"
echo "------------------------------------------"
echo "------------------------------------------"
echo " 1. Stopping Zabbix Server"
echo "------------------------------------------"
sudo service zabbix-server stop
sleep 1
echo "------------------------------------------"
echo " Display Tables"
echo "------------------------------------------"
echo "SHOW TABLES;" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------"
echo " 2. Backing up ${ZABBIX_TABLE_NAME} Table"
echo " Location : ${BACKUP_FILE_PATH}"
echo "------------------------------------------"
mkdir -p "${BACKUP_DIR_PATH}"
mysqldump --single-transaction -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE" "${ZABBIX_TABLE_NAME}" > "${BACKUP_FILE_PATH}"
sleep 1
echo "------------------------------------------------------------------"
echo " 3. Create Temp (${ZABBIX_TABLE_NAME}_${EPOCH_NOW}) Table"
echo "------------------------------------------------------------------"
echo "CREATE TABLE ${ZABBIX_TABLE_NAME}_${EPOCH_NOW} LIKE ${ZABBIX_TABLE_NAME};" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------------------------------"
echo " 4. Copy Recent Rows from ${ZABBIX_TABLE_NAME} to Temp Table"
echo "------------------------------------------------------------------"
echo "INSERT INTO ${ZABBIX_TABLE_NAME}_${EPOCH_NOW} SELECT * FROM ${ZABBIX_TABLE_NAME} WHERE clock > ${EPOCH_3MONTHS_BACK};" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------------------------------"
echo " 5. Rename ${ZABBIX_TABLE_NAME} to ${ZABBIX_TABLE_NAME}_${EPOCH_NOW}_old"
echo "------------------------------------------------------------------"
echo "ALTER TABLE ${ZABBIX_TABLE_NAME} RENAME ${ZABBIX_TABLE_NAME}_${EPOCH_NOW}_old;" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------"
echo " 6. Rename Temp Table (${ZABBIX_TABLE_NAME}_${EPOCH_NOW}) to ${ZABBIX_TABLE_NAME}"
echo "------------------------------------------"
echo "ALTER TABLE ${ZABBIX_TABLE_NAME}_${EPOCH_NOW} RENAME ${ZABBIX_TABLE_NAME};" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------"
echo " 7. Drop Old Table (${ZABBIX_TABLE_NAME}_${EPOCH_NOW}_old)"
echo "------------------------------------------"
echo "DROP TABLE ${ZABBIX_TABLE_NAME}_${EPOCH_NOW}_old;" | mysql -u"$ZABBIX_USER" -p"$ZABBIX_PASSWD" "$ZABBIX_DATABASE"
sleep 1
echo "------------------------------------------"
echo " 8. Starting Zabbix Server"
echo "------------------------------------------"
sudo service zabbix-server start