This topic explains how to create and use the clustered columnar index (CCI) feature in PolarDB-X to accelerate analytical queries on your data.
Prerequisites
Before you begin, make sure your instance meets the following requirements:
-
Edition: Enterprise Edition with an
AUTOmode database -
Version: 5.4.19-16989811 or later
For version naming rules, see Release notes. To check your current version, see View and update the version of an instance.
Usage notes
-
Prefix indexes cannot be used on a CCI.
-
You must specify an index name when creating a CCI.
-
A CCI includes all columns of the primary table by default and is automatically updated when columns are added or dropped. You cannot manually change which columns are included.
-
Creating a CCI does not create any additional local indexes.
-
The
LENGTHparameter of the sort key is ignored in the index definition. -
Primary instances, read-only instances, and columnstore read-only instances all support
SHOW INDEXand related query commands. See SHOW COLUMNAR INDEX, SHOW COLUMNAR OFFSET, and SHOW COLUMNAR STATUS. -
Only columnstore read-only instances support CCI-based query execution.
Syntax
PolarDB-X extends MySQL Data Definition Language (DDL) syntax. Use the following syntax to create a CCI the same way you create a regular index in MySQL:
CREATE
CLUSTERED COLUMNAR INDEX index_name
ON tbl_name (index_sort_key_name,...)
[partition_options]
-- Define a partitioning policy
partition_options:
PARTITION BY
HASH({column_name | partition_func(column_name)})
| KEY(column_list)
| RANGE({column_name | partition_func(column_name)})
| RANGE COLUMNS(column_list)
| LIST({column_name | partition_func(column_name)})
| LIST COLUMNS(column_list)
partition_list_spec
-- Define a partitioning function
partition_func:
YEAR
| TO_DAYS
| TO_SECOND
| UNIX_TIMESTAMP
| MONTH
-- Define a partition list
partition_list_spec:
hash_partition_list
| range_partition_list
| list_partition_list
-- For HASH/KEY partitioned tables
hash_partition_list:
PARTITIONS partition_count
-- For RANGE/RANGE COLUMNS partitioned tables
range_partition_list:
range_partition [, range_partition ...]
range_partition:
PARTITION partition_name VALUES LESS THAN {(expr | value_list)} [partition_spec_options]
-- For LIST/LIST COLUMNS partitioned tables
list_partition_list:
list_partition [, list_partition ...]
list_partition:
PARTITION partition_name VALUES IN (value_list) [partition_spec_options]
Create a CCI
The following example creates a table named t_order and a CCI named cc_i_seller on it.
Step 1: Create the primary table.
CREATE TABLE t_order (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`order_id` varchar(20) DEFAULT NULL,
`buyer_id` varchar(20) DEFAULT NULL,
`seller_id` varchar(20) DEFAULT NULL,
`order_snapshot` longtext DEFAULT NULL,
`order_detail` longtext DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `l_i_order` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 partition by hash(`order_id`) partitions 16;
Step 2: Create the CCI.
CREATE CLUSTERED COLUMNAR INDEX `cc_i_seller` ON t_order (`seller_id`) partition by hash(`order_id`) partitions 16;
The annotated diagram below shows each component of the CCI definition:
| Component | Description |
|---|---|
CLUSTERED COLUMNAR |
Keyword that specifies the index type as a CCI |
| Primary table | t_order, partitioned by HASH on order_id with 16 partitions |
| Index name | cc_i_seller — used to reference the index in SQL statements |
| Sort key | seller_id — data in the index file is stored in order by this column |
| Index partition clause | partition by hash(order_id) partitions 16 — uses the same syntax as CREATE TABLE |
Verify CCI creation status
After you issue the CREATE CLUSTERED COLUMNAR INDEX statement, check the build status:
SHOW COLUMNAR INDEX FROM t_order;
To monitor the DDL task progress:
SHOW DDL;
The CCI is ready to use when the status shows as complete. For a description of the output fields, see SHOW COLUMNAR INDEX and SHOW DDL.
Use a CCI in queries
After you create a CCI, you can specify the index table to use for queries in the following ways:
| Level | Mechanism | Behavior |
|---|---|---|
| 1 (default) | Automatic (cost-based optimizer) | The optimizer picks the lowest-cost index automatically. No hints required. |
| 2 | USE INDEX / IGNORE INDEX |
Advise the optimizer to prefer or skip a specific index. |
| 3 | FORCE INDEX |
Override the optimizer and force the query to use the specified index. |
Start with automatic selection. Use hints only when you need to override the optimizer's decision.
Automatic selection
For queries on a primary table that contains a CCI, PolarDB-X automatically selects the index table that the optimizer determines has the lowest cost. Currently, only columnstore read-only instances support CCI-based queries. No additional configuration is required.
USE INDEX
Advise the optimizer to use the specified index:
USE INDEX({index_name},...)
Example — prefer cc_i_seller:
SELECT t_order.id, t_order.order_snapshot
FROM t_order USE INDEX(cc_i_seller)
WHERE t_order.seller_id = 's1';
IGNORE INDEX
Advise the optimizer to skip the specified index:
IGNORE INDEX({index_name},...)
Example — exclude cc_i_seller from consideration:
SELECT t_order.id, t_order.order_snapshot
FROM t_order IGNORE INDEX(cc_i_seller)
WHERE t_order.seller_id = 's1';
FORCE INDEX
Override the optimizer and force use of the specified index:
FORCE INDEX({index_name})
Example — force cc_i_seller in a join:
SELECT a.*, b.order_id
FROM t_seller a
JOIN t_order b FORCE INDEX(cc_i_seller) ON a.seller_id = b.seller_id
WHERE a.seller_nick = 'abc';
Limitations
Supported data types
The following table shows the data types supported for the primary key of the primary table and the sort and partition keys of the CCI:
| Data type | Primary key | Sort key | Partition key |
|---|---|---|---|
| Numeric: BIT (UNSIGNED) | Supported | Supported | Not supported |
| Numeric: TINYINT (UNSIGNED) | Supported | Supported | Supported |
| Numeric: SMALLINT (UNSIGNED) | Supported | Supported | Supported |
| Numeric: MEDIUMINT (UNSIGNED) | Supported | Supported | Supported |
| Numeric: INT (UNSIGNED) | Supported | Supported | Supported |
| Numeric: BIGINT (UNSIGNED) | Supported | Supported | Supported |
| Time: DATE | Supported | Supported | Supported |
| Time: DATETIME | Supported | Supported | Supported |
| Time: TIMESTAMP | Supported | Supported | Supported |
| Time: TIME | Supported | Supported | Not supported |
| Time: YEAR | Supported | Supported | Not supported |
| String: CHAR | Supported | Supported | Supported |
| String: VARCHAR | Supported | Supported | Supported |
| String: TEXT | Supported | Supported | Not supported |
| String: BINARY | Supported | Supported | Supported |
| String: VARBINARY | Supported | Supported | Supported |
| String: BLOB | Supported | Supported | Not supported |
| Floating-point: FLOAT | Not supported | Not supported | Not supported |
| Floating-point: DOUBLE | Not supported | Not supported | Not supported |
| Floating-point: DECIMAL | Not supported | Not supported | Not supported |
| Floating-point: NUMERIC | Not supported | Not supported | Not supported |
| Special: JSON | Not supported | Not supported | Not supported |
| Special: ENUM | Not supported | Not supported | Not supported |
| Special: SET | Not supported | Not supported | Not supported |
| Special: POINT | Not supported | Not supported | Not supported |
| Special: GEOMETRY | Not supported | Not supported | Not supported |
Partitioning algorithms support different data types. For more information, see Data types.
DDL statement limits
To control whether DDL statements can run on a table that has a CCI, set the following parameter (true allows DDL; false blocks it):
SET [GLOBAL] forbid_ddl_with_cci = [true | false];
Supported DDL operations on primary tables with a CCI:
| Category | Action | Sample SQL | Supported |
|---|---|---|---|
| Primary table | Delete table | DROP TABLE tbl_name; |
Yes |
| Primary table | Truncate table | TRUNCATE TABLE tbl_name; |
Yes |
| Primary table | Rename table | ALTER TABLE old_tbl_name RENAME TO new_tbl_name; or RENAME TABLE old_tbl_name TO new_tbl_name; |
Yes |
| Primary table | Rename multiple tables | RENAME TABLE tbl_name_a TO tbl_name_b, tbl_name_c TO tbl_name_d; |
Yes |
| Primary table | Add column | ALTER TABLE tbl_name ADD col_name TYPE; |
Yes |
| Primary table | Delete column | ALTER TABLE tbl_name DROP COLUMN col_name; |
Yes |
| Primary table | Modify column type | ALTER TABLE tbl_name MODIFY col_name TYPE; |
Yes |
| Primary table | Rename column | ALTER TABLE tbl_name CHANGE old_col new_col TYPE; |
Yes |
| Primary table | Modify column default value | ALTER TABLE tbl_name ALTER COLUMN col_name SET DEFAULT default_value; or ALTER TABLE tbl_name ALTER COLUMN col_name DROP DEFAULT; |
Yes |
| Primary table | Lock-free column type change | ALTER TABLE tbl_name MODIFY col_name TYPE, ALGORITHM = omc; |
Yes |
| Primary table | Multiple ALTER TABLE operations | ALTER TABLE tbl_name MODIFY col_name_a, DROP COLUMN col_name_b; |
Yes |
| Primary table | Generated column | — | No |
| Primary table | Partition change | — | No |
| CCI | Create CCI | CREATE CLUSTERED COLUMNAR INDEX cci_name; or ALTER TABLE tbl_name ADD CLUSTERED COLUMNAR INDEX cci_name; |
Yes |
| CCI | Delete CCI | DROP INDEX cci_name ON TABLE tbl_name; or ALTER TABLE tbl_name DROP INDEX cci_name; |
Yes |
| CCI | Rename CCI | ALTER TABLE tbl_name RENAME INDEX cci_name_a TO cci_name_b; |
Yes |
| CCI | Add a RANGE partition | ALTER TABLE tbl_name.cci_name ADD PARTITION; |
Yes |
| CCI | Other partition changes | — | No |
Column change constraints with `ALTER TABLE`:
| Statement | Can modify primary key? | Can modify index partition key? | Can modify sort key? |
|---|---|---|---|
MODIFY COLUMN |
Yes | Yes | Yes |
ALTER COLUMN SET DEFAULT / ALTER COLUMN DROP DEFAULT |
Yes | Yes | Yes |
ADD COLUMN |
No | N/A | N/A |
CHANGE COLUMN |
No | No | No |
DROP COLUMN |
No | No | No |
In versions 5.4.20-20250714 and later, MODIFY COLUMN and column default value changes are supported for some primary keys, CCI partition keys, and CCI sort keys.
Modifying a critical column (primary key, CCI partition key, or CCI sort key) may trigger a full CCI rebuild, which can be a long-running operation on large tables. This capability is disabled by default. We recommend that you use asynchronous execution. To enable it, set the following parameters before running the ALTER statement:
-- Allow modifying critical columns of a CCI
SET ENABLE_MODIFY_CCI_CRITICAL_COLUMN = TRUE;
-- Rebuild strategy: 0 lets the system select automatically
SET REBUILD_CCI_STRATEGY = 0;
-- Maximum number of CCIs per table; increase if the table already has multiple CCIs
SET MAX_CCI_COUNT = 2;
Supported data types for `MODIFY COLUMN` / `CHANGE COLUMN`:
| Supported | Not supported |
|---|---|
| Numeric: BIT (UNSIGNED), TINYINT (UNSIGNED), SMALLINT (UNSIGNED), MEDIUMINT (UNSIGNED), INT (UNSIGNED), BIGINT (UNSIGNED) | POINT, GEOMETRY |
| Time: DATETIME, TIMESTAMP, TIME, YEAR | |
| Floating-point: FLOAT, DOUBLE, DECIMAL, NUMERIC | |
| String: CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB | |
| Special: JSON, ENUM, SET |
If you need to change a column to POINT or GEOMETRY, drop the CCI with DROP INDEX, change the data type, and recreate the CCI.
Supported index changes with `ALTER TABLE`:
| Statement | Supported |
|---|---|
ALTER TABLE ADD PRIMARY KEY |
Yes |
ALTER TABLE ADD [UNIQUE/FULLTEXT/SPATIAL/FOREIGN] KEY |
Yes |
ALTER TABLE DROP INDEX |
Yes |
ALTER TABLE DROP FOREIGN KEY fk_symbol |
Yes |
ALTER TABLE DROP PRIMARY KEY |
No (prohibited) |
ALTER TABLE RENAME INDEX |
Yes — can rename a CCI |
| ALTER TABLE ALTER INDEX index_name {VISIBLE | INVISIBLE} | No — cannot modify a CCI |
| ALTER TABLE {DISABLE | ENABLE} KEYS | No — cannot modify a CCI |
FAQ
Can I create a CCI without specifying a sort key?
No. You must specify the sort key explicitly in the CREATE CLUSTERED COLUMNAR INDEX statement. The sort key and partition key can be different columns — for example, you can use seller_id as the sort key and order_id as the partition key on the same table.
Can I create a CCI without specifying a partition key?
Yes. If you omit the partition key, PolarDB-X uses the primary key as the partition key with HASH partitioning by default.
How do I check CCI creation progress?
Run SHOW COLUMNAR INDEX to see the current status and SHOW DDL to track DDL task progress. For details on reading the output, see SHOW COLUMNAR INDEX and SHOW DDL.
How do I delete a CCI?
Use the DROP INDEX statement.