Databases 10 min read

How to Use MariaDB System-Versioned Tables for Easy Data Recovery

This guide explains MariaDB 10.3 system-versioned tables, showing how to create them with PERIOD FOR SYSTEM_TIME, query historical rows using FOR SYSTEM_TIME clauses, restore previous data via export/import, partition history to reduce storage, and safely manage versioning in production environments.

dbaplus Community
dbaplus Community
dbaplus Community
How to Use MariaDB System-Versioned Tables for Easy Data Recovery

Introduction

System‑versioned tables, defined in the SQL:2011 standard, let MariaDB store a complete history of row changes. This enables point‑in‑time queries and recovery from accidental updates or deletions.

1. Creating a system‑versioned table

Define two generated timestamp columns and a PERIOD clause, then enable system versioning.

CREATE TABLE t1 (
  id INT(11) NOT NULL AUTO_INCREMENT,
  name VARCHAR(100) DEFAULT NULL,
  ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,
  te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,
  PRIMARY KEY (id, te),
  PERIOD FOR SYSTEM_TIME (ts, te)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 WITH SYSTEM VERSIONING;

To convert an existing table:

ALTER TABLE t1
  ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,
  ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,
  ADD PERIOD FOR SYSTEM_TIME (ts, te),
  ADD SYSTEM VERSIONING;

2. Querying historical data

Three common forms are supported:

Rows changed within the last hour:

SELECT * FROM t1
FOR SYSTEM_TIME BETWEEN (NOW() - INTERVAL 1 HOUR) AND NOW();

Rows in a specific interval:

SELECT * FROM t1
FOR SYSTEM_TIME FROM '2018-05-15 00:00:00' TO '2018-05-15 14:00:00';

All historical rows:

SELECT * FROM t1 FOR SYSTEM_TIME ALL;

3. Restoring a previous version

Export the desired rows and reload them:

SELECT id, name
FROM t1 FOR SYSTEM_TIME ALL
WHERE id = 1 AND name = '张三'
INTO OUTFILE '/tmp/t1.sql'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"';
LOAD DATA INFILE '/tmp/t1.sql' REPLACE INTO TABLE t1
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
(id, name);

4. Storing history separately with partitions

Partition the table by system‑time intervals so that normal queries scan only the current partition.

ALTER TABLE t1
  PARTITION BY SYSTEM_TIME INTERVAL 1 MONTH (
    PARTITION p0 HISTORY,
    PARTITION p1 HISTORY,
    PARTITION p2 HISTORY,
    PARTITION p3 HISTORY,
    PARTITION p4 HISTORY,
    PARTITION p5 HISTORY,
    PARTITION p6 HISTORY,
    PARTITION pcur CURRENT
);

The pcur partition holds the current rows; each pN partition stores one month of history.

System‑time partition diagram
System‑time partition diagram

5. Deleting old history

Drop obsolete history partitions to reclaim space, e.g.:

ALTER TABLE t1 DROP PARTITION p0;

6. Correct usage tips

Set system_versioning_alter_history=KEEP in my.cnf (or at runtime) to allow DDL on versioned tables.

When adding new columns, use the AFTER clause to avoid placing them after the te column, which would break versioning.

7. Backup and recovery considerations

Standard mysqldump does not export history. Use a physical backup tool such as Percona XtraBackup, or export the schema first and then convert tables to versioned tables with a script. For high‑availability, a delayed replica (e.g., 12‑hour delay) provides point‑in‑time recovery.

Example PHP script that converts all tables in a database to system‑versioned tables:

<?php
$conn = mysqli_connect('10.10.100.11','admin','123456','test','3306')
       or die('connection error');
mysqli_query($conn, 'SET NAMES utf8');

$res = mysqli_query($conn, 'SHOW TABLES');
while ($row = mysqli_fetch_array($res)) {
    $tbl = $row[0];
    $sql = "ALTER TABLE $tbl
            ADD COLUMN ts TIMESTAMP(6) GENERATED ALWAYS AS ROW START,
            ADD COLUMN te TIMESTAMP(6) GENERATED ALWAYS AS ROW END,
            ADD PERIOD FOR SYSTEM_TIME (ts, te),
            ADD SYSTEM VERSIONING";
    if (mysqli_query($conn, $sql)) {
        echo "$tbl converted
";
    } else {
        echo "failed on $tbl: " . mysqli_error($conn) . "
";
    }
}
mysqli_close($conn);
?>

Install the PHP MySQL driver before running the script (e.g., yum install php php-mysql -y).

8. Limitations

DROP DATABASE, DROP TABLE, and TRUNCATE TABLE cannot be recovered with the export‑import method.

For production environments, configure a delayed slave (e.g., using pt‑slave‑delay) to protect against accidental destructive statements.

Reference

MariaDB documentation: https://mariadb.com/kb/en/library/system-versioned-tables/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

SQLData RecoveryMariaDBSystem-Versioned Tables
dbaplus Community
Written by

dbaplus Community

Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.