Transparent page compression (TPC) reduces storage usage and improves read performance on ApsaraDB RDS for PostgreSQL without requiring an instance type change. TPC compresses page data when writing to disk and decompresses it when reading — transparently, with no changes to your application. The trade-off is higher CPU utilization, particularly during compression.
Prerequisites
Before you begin, make sure that you have:
An ApsaraDB RDS for PostgreSQL instance running PostgreSQL 14 or later
A minor engine version of 20240530 or later. To update, see Update the minor engine version
When to use TPC
TPC is a good fit when your instance has low CPU utilization (under 50%) and IOPS or I/O throughput frequently hits a bottleneck. In these conditions, TPC shifts the bottleneck from I/O to CPU — which has headroom — and overall throughput improves.
TPC is most effective when:
The dataset is large (the larger the dataset relative to the buffer pool, the more I/O TPC saves)
Workloads are read-heavy
CPU resources are underutilized
Avoid TPC when:
The workload is write-intensive — write transactions per second (TPS) drops with TPC enabled
Most data is in TOAST columns — TPC has little effect on TOAST data and adds CPU overhead without storage benefit
CPU is already near capacity
How it works
TPC works through tablespaces. Data stored in a TPC tablespace is compressed using the zstd algorithm before being written to disk. When read back into the buffer pool, pages are decompressed automatically. This process is transparent to queries and applications.
Expected impact:
| Metric | With TPC enabled |
|---|---|
| Storage usage | Reduced by ~50% on average |
| I/O usage | Reduced by ~50% on average |
| Read TPS | Increased; up to +100% in I/O-saturated read workloads |
| Write TPS | Decreased |
| CPU utilization (compression) | Increased by ~260% |
| CPU utilization (decompression) | Increased by ~40% |
Enable TPC
TPC works at the tablespace level. Assign a table or index to the TPC tablespace to compress it.
Step 1: Create the TPC tablespace
Run the following statement to create the required tablespace:
CREATE TABLESPACE rds_compress LOCATION '/data/postgresql/rds_compress' WITH(COMPRESSION='zstd');Do not modify the tablespace name (rds_compress), the location path, or the compression algorithm. These values are fixed by the system.
Step 2: Enable TPC for tables and indexes
Assign existing or new tables and indexes to the rds_compress tablespace.
For a new table:
CREATE TABLE <tablename> ... TABLESPACE rds_compress;For an existing table:
ALTER TABLE <tablename> SET TABLESPACE rds_compress;For a new index:
CREATE INDEX <indexname> ... TABLESPACE rds_compress;For an existing index:
ALTER INDEX <indexname> SET TABLESPACE rds_compress;(Optional) Set TPC as the default tablespace
To apply TPC automatically to all new tables and indexes without specifying the tablespace each time, set the session default:
SET default_tablespace TO 'rds_compress';After this, any CREATE TABLE or CREATE INDEX statement without an explicit TABLESPACE clause uses TPC:
CREATE TABLE <tablename> ...;
CREATE INDEX <indexname> ...;Verify compression
Check whether a table uses TPC
In the psql command line, run:
\d+ <tablename>If the table and its indexes are in the rds_compress tablespace, TPC is active. The following is an example output for the sbtest1 table:
Table "public.sbtest1"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+----------------+-----------+----------+-------------------------------------+----------+-------------+--------------+-------------
id | integer | | not null | nextval('sbtest1_id_seq'::regclass) | plain | | |
k | integer | | not null | 0 | plain | | |
c | character(120) | | not null | ''::bpchar | extended | | |
pad | character(60) | | not null | ''::bpchar | extended | | |
Indexes:
"sbtest1_pkey" PRIMARY KEY, btree (id), tablespace "rds_compress"
"k_1" btree (k), tablespace "rds_compress"
Tablespace: "rds_compress"
Access method: heapMonitor storage savings
Use the following PostgreSQL functions to check the actual size of compressed data:
| Function | What it reports |
|---|---|
pg_relation_size('<tablename>') | On-disk size of a table (in bytes) |
pg_table_size('<tablename>') | Table size including TOAST and free-space map |
pg_index_size('<indexname>') | On-disk size of an index |
pg_tablespace_size('rds_compress') | Total size of all data in the TPC tablespace |
pg_database_size(current_database()) | Total database size |
pg_total_relation_size('<tablename>') | Table size including indexes and TOAST |
To validate savings, insert the same volume of test data into a compressed and a non-compressed table using sysbench. The compressed table typically uses about 50% of the disk space of the non-compressed table.
Calculate the compression ratio
SELECT pg_relation_size('<tablename>')::float / (relpages * 8192)
FROM pg_class
WHERE relname = '<tablename>';This query divides the actual on-disk size (from pg_relation_size) by the uncompressed logical size (relpages × 8192 bytes, where 8192 is PostgreSQL's default page size). A lower result means higher compression — for example, 0.5 means pages are compressed to half their original size.
Disable TPC
To remove TPC from a table or index, move it back to the default tablespace:
ALTER TABLE <tablename> SET TABLESPACE pg_default;
ALTER INDEX <indexname> SET TABLESPACE pg_default;Performance benchmarks
The following results use sysbench on an instance with these specs:
| Spec | Value |
|---|---|
| CPU and memory | 8 cores, 32 GB |
| Storage | Enterprise SSD (ESSD) PL1, max 350 MB/s I/O bandwidth |
| Buffer pool | 8 GB (default) |
Test 1: I/O-saturated conditions (64 threads)
Sysbench parameters: --tables=100 --table-size=<400000, 4000000, or 32000000> --report-interval=1 --time=100 --threads=64
Table size per dataset: 400,000 rows (8 GB), 4,000,000 rows (80 GB), 32,000,000 rows (640 GB).
Set --table-size to 400000 for the 8 GB dataset, 4000000 for the 80 GB dataset, and 32000000 for the 640 GB dataset.
Read-only workload (oltp_read_only)
| Dataset | TPC | TPS | Read bandwidth | CPU |
|---|---|---|---|---|
| 8 GB | No | 6,878 | 0 MB/s | 100% |
| 8 GB | Yes | 6,914 | 0 MB/s | 100% |
| 80 GB | No | 5,939 | 280 MB/s | 100% |
| 80 GB | Yes | 5,945 | 15 MB/s | 100% |
| 640 GB | No | 2,222 | 350 MB/s | 44% |
| 640 GB | Yes | 4,508 | 320 MB/s | 100% |
Write-only workload (oltp_write_only)
| Dataset | TPC | TPS | Read bandwidth | Write bandwidth | CPU |
|---|---|---|---|---|---|
| 8 GB | No | 22,151 | 0 MB/s | 100 MB/s | 100% |
| 8 GB | Yes | 22,314 | 0 MB/s | 50 MB/s | 100% |
| 80 GB | No | 7,044 | 80 MB/s | 270 MB/s | 30% |
| 80 GB | Yes | 5,493 | 10 MB/s | 180 MB/s | 100% |
| 640 GB | No | 2,375 | 80 MB/s | 270 MB/s | 20% |
| 640 GB | Yes | 1,245 | 25 MB/s | 210 MB/s | 100% |
Test 2: Normal conditions (4 threads, 640 GB dataset)
Sysbench parameters: --tables=100 --table-size=32000000 --report-interval=1 --time=100 --threads=4
| Workload | TPC | TPS | Read bandwidth | Write bandwidth | CPU |
|---|---|---|---|---|---|
| oltp_read_only | No | 720 | 112 MB/s | — | 7.5% |
| oltp_read_only | Yes | 795 | 56 MB/s | — | 10.6% |
| oltp_write_only | No | 1,497 | 60 MB/s | 180 MB/s | 6.8% |
| oltp_write_only | Yes | 1,000 | 30 MB/s | 90 MB/s | 25% |
What the results show
Enable TPC when your dataset is large and I/O is the bottleneck:
At 640 GB with saturated I/O and a read workload, TPS doubled (2,222 to 4,508) after enabling TPC.
Even in normal (non-saturated) conditions, read TPS improved modestly (720 to 795) at the cost of slightly higher CPU.
Hold off on TPC when write performance is critical:
At 80 GB and 640 GB, write TPS dropped significantly with TPC (7,044 to 5,493 and 2,375 to 1,245 respectively).
The 8 GB dataset showed no write penalty — the entire dataset fits in the buffer pool, so compression saves I/O without bottlenecking writes.
FAQ
After enabling TPC, do `pg_dump` and `pg_basebackup` work as expected?
Yes. Both tools work without modification. If you restore a pg_basebackup to a different RDS instance, enable TPC on the destination instance first — otherwise the compressed tablespace cannot be attached correctly.