All Products
Search
Document Center

PolarDB:Use a columnstore index on a partitioned table

Last Updated:Mar 28, 2026

PolarDB for PostgreSQL supports columnstore indexes on partitioned tables. If your system handles both transactional and analytical workloads and you want to run analytical queries on partitioned historical data without a separate ETL pipeline, you can add a columnstore index directly on your partitioned table.

In benchmark tests with a two-level partitioned table and 320 million rows (~16 GB), the columnstore index is over 35 times faster than native PostgreSQL parallel execution at a degree of parallelism of 4:

QueryNative PostgreSQL parallel executionColumnstore index
Q12.13 s0.05 s
Q26.42 s0.18 s
Q310.51 s0.30 s

Prerequisites

Before you begin, make sure that:

  • Your cluster runs one of the supported versions: To check your minor engine version, run SHOW polardb_version; or view the minor engine version in the console. If your version doesn't meet the requirement, upgrade the minor engine version.

    • PostgreSQL 16, minor engine version 2.0.16.8.3.0 or later

    • PostgreSQL 14, minor engine version 2.0.14.10.20.0 or later

  • The partitioned table has a primary key, and the primary key column is included when you create the columnstore index.

  • The wal_level parameter is set to logical. This adds the information required for logical replication to the write-ahead log (WAL).

    Important

    Set the wal_level parameter in the console. Changing this parameter restarts the cluster—plan your maintenance window accordingly.

Choose an enablement method

The enablement method depends on your minor engine version. Identify your version range before proceeding.

PostgreSQL 16 (2.0.16.9.8.0 or later) or PostgreSQL 14 (2.0.14.17.35.0 or later)

Two methods are available for these versions:

Add an In-Memory Column Index (IMCI) read-only node (recommended)Use the pre-installed extension
SetupAdd a node through the console.No setup required—use the extension directly.
Memory allocationThe columnstore engine uses all node resources exclusively.The columnstore engine is limited to 25% of memory; the remaining 75% goes to the row-store engine.
Workload isolationTransactional processing (TP) and analytical processing (AP) workloads run on separate nodes and don't affect each other.TP and AP workloads share the same node and may affect each other.
CostIMCI read-only nodes incur additional charges, billed at the same rate as regular compute nodes.No additional cost.

Add an IMCI read-only node if you need TP/AP isolation and can allocate dedicated resources for analytics. Use the pre-installed extension if you want to test the feature at no extra cost or if workload isolation isn't a requirement.

PostgreSQL 16 (2.0.16.8.3.0 to 2.0.16.9.8.0) or PostgreSQL 14 (2.0.14.10.20.0 to 2.0.14.17.35.0)

For these versions, the columnstore index is available only as the polar_csi extension. Install the extension in each database where you want to use the feature.

Enable the columnstore index

Follow the instructions for your version and chosen method.

Add an IMCI read-only node (recommended for newer versions)

Note

Your cluster must already have at least one read-only node. You cannot add an IMCI read-only node to a single-node cluster.

Add in the console:

  1. Log on to the PolarDB console and select the region where your cluster is located. Open the Add/Remove Node wizard using one of these paths:

    • On the Clusters page, click Add/Remove Node in the Actions column. image

    • On the Basic Information page of your cluster, click Add/Remove Node in the Database Nodes section. image

  2. Select Add Read-only IMCI Node and click OK.

  3. On the upgrade/downgrade page, configure and purchase the node:

    1. Click Add an IMCI Node and select the node specifications.

    2. Select a switchover time.

    3. (Optional) Review the Product Terms of Service and Service Level Agreement.

    4. Click Buy Now.

    image

  4. Return to the cluster details page. The IMCI node is ready when its status changes to Running.

    image

Add during purchase:

On the PolarDB purchase page, set the number of nodes for the IMCI Read-Only Nodes parameter.

image

Use the pre-installed extension (newer versions)

No additional setup is required. The extension is pre-installed and ready to use.

Install the polar_csi extension (older versions)

Note

The polar_csi extension is scoped to the database level. Install it separately in each database where you want to use the columnstore index. The database account used to install the extension must be a privileged account.

Install from the console:

  1. Log on to the PolarDB console. In the left navigation pane, click Clusters, select your region, and click your cluster ID to open the cluster details page.

  2. In the left navigation pane, choose Settings and Management > Extension Management. On the Extension Management tab, select Uninstalled Extensions.

  3. In the upper-right corner, select the target database. In the row for polar_csi, click Install in the Actions column. In the Install Extension dialog box, select the target Database Account and click OK.

    image.png

Install from the command line:

Connect to the database cluster and run the following statement in the target database:

CREATE EXTENSION polar_csi;

Set up a partitioned table and create a columnstore index

This example creates a two-level partitioned table with 320 million rows (~16 GB), partitioned by year and then by month.

Table schema:

TableDescription
salesPrimary (top-level) table
sales_20232023 data, partitioned by month
sales_2023_aMonths 1–6 of 2023
sales_2023_bMonths 7–12 of 2023
sales_20242024 data, partitioned by month
sales_2024_aMonths 1–6 of 2024
sales_2024_bMonths 7–12 of 2024
  1. Create the partitioned table. The partition key is sale_date. The table requires a composite primary key that includes the partition key.

    CREATE TABLE sales (
        sale_id serial,
        product_id int NOT NULL,
        sale_date date NOT NULL,
        amount numeric(10,2) NOT NULL,
        primary key(sale_id, sale_date)
    ) PARTITION BY RANGE (sale_date);
    
    CREATE TABLE sales_2023 PARTITION OF sales
        FOR VALUES FROM ('2023-1-1') TO ('2024-1-1')
        PARTITION BY RANGE (sale_date);
    CREATE TABLE sales_2023_a PARTITION OF sales_2023
        FOR VALUES FROM ('2023-1-1') TO ('2023-7-1');
    CREATE TABLE sales_2023_b PARTITION OF sales_2023
        FOR VALUES FROM ('2023-7-1') TO ('2024-1-1');
    
    CREATE TABLE sales_2024 PARTITION OF sales
        FOR VALUES FROM ('2024-1-1') TO ('2025-1-1')
        PARTITION BY RANGE (sale_date);
    CREATE TABLE sales_2024_a PARTITION OF sales_2024
        FOR VALUES FROM ('2024-1-1') TO ('2024-7-1');
    CREATE TABLE sales_2024_b PARTITION OF sales_2024
        FOR VALUES FROM ('2024-7-1') TO ('2025-1-1');
  2. Insert ~16 GB of test data.

    INSERT INTO sales (product_id, sale_date, amount)
    SELECT
      (random()*100)::int AS product_id,
      '2023-01-1'::date + i/3200000*7 AS sale_date,
      (random()*1000)::numeric(10,2) AS amount
    FROM
      generate_series(1, 320000000) i;
  3. Create a columnstore index on sales. Include the primary key column (sale_id) along with all columns used in analytical queries.

    CREATE INDEX ON sales USING CSI(sale_id, product_id, sale_date, amount);

Run queries with the columnstore index

The following three queries filter by different sale_date ranges to exercise partition pruning across different partition boundaries.

Using the columnstore index (degree of parallelism = 4):

-- Enable the columnstore index and set the degree of parallelism to 4.
SET polar_csi.enable_query TO on;
SET polar_csi.exec_parallel TO 4;

-- Q1: 2-month range (within a single sub-partition)
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2023-3-1' AND amount > 100 GROUP BY sale_date;

-- Q2: 8-month range (spans two sub-partitions)
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2023-9-1' AND amount > 100 GROUP BY sale_date;

-- Q3: 14-month range (spans two years)
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2024-3-1' AND amount > 100 GROUP BY sale_date;

Using the row-store engine for comparison (degree of parallelism = 4):

-- Disable the columnstore index and use native PostgreSQL parallel execution.
SET polar_csi.enable_query TO off;
SET max_parallel_workers_per_gather TO 4;

-- Q1
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2023-3-1' AND amount > 100 GROUP BY sale_date;

-- Q2
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2023-9-1' AND amount > 100 GROUP BY sale_date;

-- Q3
EXPLAIN ANALYZE SELECT sale_date, COUNT(*) FROM sales WHERE sale_date BETWEEN '2023-1-1' AND '2024-3-1' AND amount > 100 GROUP BY sale_date;