Tablespaces in YugabyteDB give users fine-grained control over where data lives, enabling better performance tuning, fault isolation, and cost management. By mapping tables and indexes to specific storage locations or availability zones, teams can optimize for data locality, balance I/O across nodes, and separate workloads with different performance profiles. In geo-distributed deployments, tablespaces make it easier to keep data close to the applications that use it, reducing latency and improving resilience.
Once a tablespace is created, its metadata—including placement and replication details—is stored in the PostgreSQL catalog table pg_tablespace
.
Consider a YugabyteDB cluster with nodes distributed across three different regions:
yugabyte=# SELECT host, cloud, region, zone FROM yb_servers() ORDER BY cloud, region, zone;
host | cloud | region | zone
-----------+-------+-----------+------------
127.0.0.1 | aws | us-east-1 | us-east-1a
127.0.0.4 | aws | us-east-1 | us-east-1b
127.0.0.7 | aws | us-east-1 | us-east-1c
127.0.0.2 | aws | us-east-2 | us-east-2a
127.0.0.5 | aws | us-east-2 | us-east-2b
127.0.0.8 | aws | us-east-2 | us-east-2c
127.0.0.3 | aws | us-west-2 | us-west-2a
127.0.0.6 | aws | us-west-2 | us-west-2b
127.0.0.9 | aws | us-west-2 | us-west-2c
(9 rows)
We’ll create a separate tablespace for each region, allowing us to geo-locate specific tables to a single region for improved data locality and performance.
yugabyte=# CREATE TABLESPACE us_east_1_ts WITH (
yugabyte(# replica_placement='{"num_replicas": 3, "placement_blocks":
yugabyte'# [{"cloud":"aws","region":"us-east-1","zone":"us-east-1a","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-east-1","zone":"us-east-1b","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-east-1","zone":"us-east-1c","min_num_replicas":1,"leader_preference":1}]}'
yugabyte(# );
CREATE TABLESPACE
yugabyte=# CREATE TABLESPACE us_east_2_ts WITH (
yugabyte(# replica_placement='{"num_replicas": 3, "placement_blocks":
yugabyte'# [{"cloud":"aws","region":"us-east-2","zone":"us-east-2a","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-east-2","zone":"us-east-2b","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-east-2","zone":"us-east-2c","min_num_replicas":1,"leader_preference":1}]}'
yugabyte(# );
CREATE TABLESPACE
yugabyte=# CREATE TABLESPACE us_west_2_ts WITH (
yugabyte(# replica_placement='{"num_replicas": 3, "placement_blocks":
yugabyte'# [{"cloud":"aws","region":"us-west-2","zone":"us-west-2a","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-west-2","zone":"us-west-2b","min_num_replicas":1,"leader_preference":1},
yugabyte'# {"cloud":"aws","region":"us-west-2","zone":"us-west-2c","min_num_replicas":1,"leader_preference":1}]}'
yugabyte(# );
CREATE TABLESPACE
With the tablespaces created, we can query the pg_tablespace
system catalog to view the details of each one.
yugabyte=# SELECT spcname, spcoptions FROM pg_tablespace;
spcname | spcoptions
--------------+------------------------------------------------------------------------------------------------------
pg_default |
pg_global |
us_east_1_ts | {"replica_placement={\"num_replicas\": 3, \"placement_blocks\": +
| [{\"cloud\":\"aws\",\"region\":\"us-east-1\",\"zone\":\"us-east-1a\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-east-1\",\"zone\":\"us-east-1b\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-east-1\",\"zone\":\"us-east-1c\",\"min_num_replicas\":1}]}"}
us_east_2_ts | {"replica_placement={\"num_replicas\": 3, \"placement_blocks\": +
| [{\"cloud\":\"aws\",\"region\":\"us-east-2\",\"zone\":\"us-east-2a\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-east-2\",\"zone\":\"us-east-2b\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-east-2\",\"zone\":\"us-east-2c\",\"min_num_replicas\":1}]}"}
us_west_2_ts | {"replica_placement={\"num_replicas\": 3, \"placement_blocks\": +
| [{\"cloud\":\"aws\",\"region\":\"us-west-2\",\"zone\":\"us-west-2a\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-west-2\",\"zone\":\"us-west-2b\",\"min_num_replicas\":1}, +
| {\"cloud\":\"aws\",\"region\":\"us-west-2\",\"zone\":\"us-west-2c\",\"min_num_replicas\":1}]}"}
(5 rows)
That output isn’t the easiest to read. To make the details clearer and easier to understand, we can extract the relevant information into a database view.
CREATE OR REPLACE VIEW tablespace_info_vw AS
SELECT
spcname,
(placement->>'num_replicas')::int AS num_replicas,
block->>'cloud' AS cloud,
block->>'region' AS region,
block->>'zone' AS zone,
(block->>'min_num_replicas')::int AS min_replicas,
(block->>'leader_preference')::int AS leader_preference
FROM (
SELECT
spcname,
(regexp_replace(spcoptions[1], '^replica_placement=', ''))::jsonb AS placement
FROM pg_tablespace
WHERE spcname NOT IN ('pg_default','pg_global')
) t,
LATERAL jsonb_array_elements(placement->'placement_blocks') AS block
ORDER BY spcname, cloud, region, zone;
With the view in place, we can now take a look at the output it generates.
yugabyte=# SELECT * FROM tablespace_info_vw;
spcname | num_replicas | cloud | region | zone | min_replicas | leader_preference
--------------+--------------+-------+-----------+------------+--------------+-------------------
us_east_1_ts | 3 | aws | us-east-1 | us-east-1a | 1 | 1
us_east_1_ts | 3 | aws | us-east-1 | us-east-1b | 1 | 1
us_east_1_ts | 3 | aws | us-east-1 | us-east-1c | 1 | 1
us_east_2_ts | 3 | aws | us-east-2 | us-east-2a | 1 | 1
us_east_2_ts | 3 | aws | us-east-2 | us-east-2b | 1 | 1
us_east_2_ts | 3 | aws | us-east-2 | us-east-2c | 1 | 1
us_west_2_ts | 3 | aws | us-west-2 | us-west-2a | 1 | 1
us_west_2_ts | 3 | aws | us-west-2 | us-west-2b | 1 | 1
us_west_2_ts | 3 | aws | us-west-2 | us-west-2c | 1 | 1
(9 rows)
Much cleaner and easier to read… what a difference!
Have Fun!
