Skip to content

Commit 63244f4

Browse files
Merge pull request #214651 from WilliamDAssafMSFT/20221014-sql-db-ordered-cci
20221014 sql db ordered cci
2 parents 3d21eef + eba78f1 commit 63244f4

File tree

1 file changed

+53
-47
lines changed

1 file changed

+53
-47
lines changed
Lines changed: 53 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,39 @@
11
---
2-
title: Performance tuning with ordered clustered columnstore index
2+
title: Performance tuning with ordered clustered columnstore index
33
description: Recommendations and considerations you should know as you use ordered clustered columnstore index to improve your query performance in dedicated SQL pools.
44
author: XiaoyuMSFT
5+
ms.author: xiaoyul
56
manager: craigg
7+
ms.reviewer: nibruno; wiassaf
8+
ms.date: 10/17/2022
69
ms.service: synapse-analytics
10+
ms.subservice: sql-dw
711
ms.topic: conceptual
8-
ms.subservice: sql-dw
9-
ms.date: 07/14/2022
10-
ms.author: xiaoyul
11-
ms.reviewer: nibruno; wiassaf
12-
ms.custom: seo-lt-2019, azure-synapse
12+
ms.custom:
13+
- seo-lt-2019
14+
- azure-synapse
1315
---
1416

15-
# Performance tuning with ordered clustered columnstore index
17+
# Performance tuning with ordered clustered columnstore index
1618

1719
**Applies to:** Azure Synapse Analytics dedicated SQL pools, SQL Server 2022 (16.x) and later
1820

19-
When users query a columnstore table in dedicated SQL pool, the optimizer checks the minimum and maximum values stored in each segment. Segments that are outside the bounds of the query predicate aren't read from disk to memory. A query can finish faster if the number of segments to read and their total size are small.
21+
When users query a columnstore table in dedicated SQL pool, the optimizer checks the minimum and maximum values stored in each segment. Segments that are outside the bounds of the query predicate aren't read from disk to memory. A query can finish faster if the number of segments to read and their total size are small.
2022

2123
## Ordered vs. non-ordered clustered columnstore index
2224

23-
By default, for each table created without an index option, an internal component (index builder) creates a non-ordered clustered columnstore index (CCI) on it. Data in each column is compressed into a separate CCI rowgroup segment. There's metadata on each segment's value range, so segments that are outside the bounds of the query predicate aren't read from disk during query execution. CCI offers the highest level of data compression and reduces the size of segments to read so queries can run faster. However, because the index builder doesn't sort data before compressing them into segments, segments with overlapping value ranges could occur, causing queries to read more segments from disk and take longer to finish.
25+
By default, for each table created without an index option, an internal component (index builder) creates a non-ordered clustered columnstore index (CCI) on it. Data in each column is compressed into a separate CCI rowgroup segment. There's metadata on each segment's value range, so segments that are outside the bounds of the query predicate aren't read from disk during query execution. CCI offers the highest level of data compression and reduces the size of segments to read so queries can run faster. However, because the index builder doesn't sort data before compressing them into segments, segments with overlapping value ranges could occur, causing queries to read more segments from disk and take longer to finish.
2426

25-
When creating an ordered CCI, the dedicated SQL pool engine sorts the existing data in memory by the order key(s) before the index builder compresses them into index segments. With sorted data, segment overlapping is reduced allowing queries to have a more efficient segment elimination and thus faster performance because the number of segments to read from disk is smaller. If all data can be sorted in memory at once, then segment overlapping can be avoided. Due to large tables in data warehouses, this scenario doesn't happen often.
27+
When creating an ordered CCI, the dedicated SQL pool engine sorts the existing data in memory by the order key(s) before the index builder compresses them into index segments. With sorted data, segment overlapping is reduced allowing queries to have a more efficient segment elimination and thus faster performance because the number of segments to read from disk is smaller. If all data can be sorted in memory at once, then segment overlapping can be avoided. Due to large tables in data warehouses, this scenario doesn't happen often.
2628

2729
To check the segment ranges for a column, run the following command with your table name and column name:
2830

2931
```sql
30-
SELECT o.name, pnp.index_id,
31-
cls.row_count, pnp.data_compression_desc,
32-
pnp.pdw_node_id, pnp.distribution_id, cls.segment_id,
33-
cls.column_id,
34-
cls.min_data_id, cls.max_data_id,
32+
SELECT o.name, pnp.index_id,
33+
cls.row_count, pnp.data_compression_desc,
34+
pnp.pdw_node_id, pnp.distribution_id, cls.segment_id,
35+
cls.column_id,
36+
cls.min_data_id, cls.max_data_id,
3537
cls.max_data_id-cls.min_data_id as difference
3638
FROM sys.pdw_nodes_partitions AS pnp
3739
JOIN sys.pdw_nodes_tables AS Ntables ON pnp.object_id = NTables.object_id AND pnp.pdw_node_id = NTables.pdw_node_id
@@ -43,28 +45,28 @@ WHERE o.name = '<Table Name>' and cols.name = '<Column Name>' and TMap.physical
4345
ORDER BY o.name, pnp.distribution_id, cls.min_data_id;
4446
```
4547

46-
> [!NOTE]
47-
> In an ordered CCI table, the new data resulting from the same batch of DML or data loading operations are sorted within that batch, there is no global sorting across all data in the table. Users can REBUILD the ordered CCI to sort all data in the table. In dedicated SQL pool, the columnstore index REBUILD is an offline operation. For a partitioned table, the REBUILD is done one partition at a time. Data in the partition that is being rebuilt is "offline" and unavailable until the REBUILD is complete for that partition.
48+
> [!NOTE]
49+
> In an ordered CCI table, the new data resulting from the same batch of DML or data loading operations are sorted within that batch, there is no global sorting across all data in the table. Users can REBUILD the ordered CCI to sort all data in the table. In dedicated SQL pool, the columnstore index REBUILD is an offline operation. For a partitioned table, the REBUILD is done one partition at a time. Data in the partition that is being rebuilt is "offline" and unavailable until the REBUILD is complete for that partition.
4850
4951
## Query performance
5052

5153
A query's performance gain from an ordered CCI depends on the query patterns, the size of data, how well the data is sorted, the physical structure of segments, and the DWU and resource class chosen for the query execution. Users should review all these factors before choosing the ordering columns when designing an ordered CCI table.
5254

5355
Queries with all these patterns typically run faster with ordered CCI.
5456
1. The queries have equality, inequality, or range predicates
55-
1. The predicate columns and the ordered CCI columns are the same.
56-
57+
1. The predicate columns and the ordered CCI columns are the same.
58+
5759
In this example, table T1 has a clustered columnstore index ordered in the sequence of Col_C, Col_B, and Col_A.
5860

5961
```sql
6062
CREATE CLUSTERED COLUMNSTORE INDEX MyOrderedCCI ON T1
6163
ORDER (Col_C, Col_B, Col_A);
6264
```
6365

64-
The performance of query 1 and query 2 can benefit most from ordered CCI than the other queries as they reference all the ordered CCI columns.
66+
The performance of query 1 and query 2 can benefit more from ordered CCI than the other queries, as they reference all the ordered CCI columns.
6567

6668
```sql
67-
-- Query #1:
69+
-- Query #1:
6870

6971
SELECT * FROM T1 WHERE Col_C = 'c' AND Col_B = 'b' AND Col_A = 'a';
7072

@@ -77,55 +79,55 @@ SELECT * FROM T1 WHERE Col_B = 'b' AND Col_A = 'a';
7779

7880
-- Query #4
7981
SELECT * FROM T1 WHERE Col_A = 'a' AND Col_C = 'c';
80-
8182
```
8283

8384
## Data loading performance
8485

85-
The performance of data loading into an ordered CCI table is similar to a partitioned table. Loading data into an ordered CCI table can take longer than a non-ordered CCI table because of the data sorting operation, however queries can run faster afterwards with ordered CCI.
86+
The performance of data loading into an ordered CCI table is similar to a partitioned table. Loading data into an ordered CCI table can take longer than a non-ordered CCI table because of the data sorting operation, however queries can run faster afterwards with ordered CCI.
8687

87-
Here is an example performance comparison of loading data into tables with different schemas.
88+
Here's an example performance comparison of loading data into tables with different schemas.
8889

89-
![Bar graph that shows the performance comparison of loading data into tables with different schemas.](./media/performance-tuning-ordered-cci/cci-data-loading-performance.png)
90+
:::image type="content" source="./media/performance-tuning-ordered-cci/cci-data-loading-performance.png" alt-text="Bar graph that shows the performance comparison of loading data into tables with different schemas.":::
9091

92+
Here's an example query performance comparison between CCI and ordered CCI.
9193

92-
Here is an example query performance comparison between CCI and ordered CCI.
94+
:::image type="content" source="./media/performance-tuning-ordered-cci/occi_query_performance.png" alt-text="Bar graph comparing performance during data_loading. An ordered clustered columnstore index has lower duration.":::
9395

94-
![Performance_comparison_data_loading](./media/performance-tuning-ordered-cci/occi_query_performance.png)
95-
96-
9796
## Reduce segment overlapping
9897

9998
The number of overlapping segments depends on the size of data to sort, the available memory, and the maximum degree of parallelism (MAXDOP) setting during ordered CCI creation. Below are options to reduce segment overlapping when creating ordered CCI.
10099

101-
- Use `xlargerc` resource class on a higher DWU to allow more memory for data sorting before the index builder compresses the data into segments. Once in an index segment, the physical location of the data cannot be changed. There's no data sorting within a segment or across segments.
100+
- Use `xlargerc` resource class on a higher DWU to allow more memory for data sorting before the index builder compresses the data into segments. Once in an index segment, the physical location of the data cannot be changed. There's no data sorting within a segment or across segments.
102101

103-
- Create ordered CCI with `OPTION (MAXDOP = 1)`. Each thread used for ordered CCI creation works on a subset of data and sorts it locally. There's no global sorting across data sorted by different threads. Using parallel threads can reduce the time to create an ordered CCI but will generate more overlapping segments than using a single thread. Currently, the MAXDOP option is only supported in creating an ordered CCI table using CREATE TABLE AS SELECT command. Creating an ordered CCI via CREATE INDEX or CREATE TABLE commands does not support the MAXDOP option. For example:
102+
- Create ordered CCI with `OPTION (MAXDOP = 1)`. Each thread used for ordered CCI creation works on a subset of data and sorts it locally. There's no global sorting across data sorted by different threads. Using parallel threads can reduce the time to create an ordered CCI but will generate more overlapping segments than using a single thread. Using a single threaded operation delivers the highest compression quality. For example:
104103

105104
```sql
106105
CREATE TABLE Table1 WITH (DISTRIBUTION = HASH(c1), CLUSTERED COLUMNSTORE INDEX ORDER(c1) )
107106
AS SELECT * FROM ExampleTable
108107
OPTION (MAXDOP 1);
109108
```
110109

110+
> [!NOTE]
111+
> Currently, in dedicated SQL pools in Azure Synapse Analytics, the MAXDOP option is only supported in creating an ordered CCI table using `CREATE TABLE AS SELECT` command. Creating an ordered CCI via `CREATE INDEX` or `CREATE TABLE` commands doesn't support the MAXDOP option. This limitation does not apply to SQL Server 2022 and later versions, where you can specify MAXDOP with the `CREATE INDEX` or `CREATE TABLE` commands.
112+
111113
- Pre-sort the data by the sort key(s) before loading them into tables.
112114

113-
Here is an example of an ordered CCI table distribution that has zero segment overlapping following above recommendations. The ordered CCI table is created in a DWU1000c database via CTAS from a 20-GB heap table using MAXDOP 1 and `xlargerc`. The CCI is ordered on a BIGINT column with no duplicates.
115+
Here's an example of an ordered CCI table distribution that has zero segment overlapping following above recommendations. The ordered CCI table is created in a DWU1000c database via CTAS from a 20-GB heap table using MAXDOP 1 and `xlargerc`. The CCI is ordered on a BIGINT column with no duplicates.
114116

115-
![Segment_No_Overlapping](./media/performance-tuning-ordered-cci/perfect-sorting-example.png)
117+
:::image type="content" source="./media/performance-tuning-ordered-cci/perfect-sorting-example.png" alt-text="A screenshot of text data showing no segment overlapping.":::
116118

117119
## Create ordered CCI on large tables
118120

119-
Creating an ordered CCI is an offline operation. For tables with no partitions, the data won't be accessible to users until the ordered CCI creation process completes. For partitioned tables, since the engine creates the ordered CCI partition by partition, users can still access the data in partitions where ordered CCI creation isn't in process. You can use this option to minimize the downtime during ordered CCI creation on large tables:
121+
Creating an ordered CCI is an offline operation. For tables with no partitions, the data won't be accessible to users until the ordered CCI creation process completes. For partitioned tables, since the engine creates the ordered CCI partition by partition, users can still access the data in partitions where ordered CCI creation isn't in process. You can use this option to minimize the downtime during ordered CCI creation on large tables:
120122

121-
1. Create partitions on the target large table (called `Table_A`).
122-
2. Create an empty ordered CCI table (called `Table_B`) with the same table and partition schema as `Table_A`.
123-
3. Switch one partition from `Table_A` to `Table_B`.
124-
4. Run `ALTER INDEX <Ordered_CCI_Index> ON <Table_B> REBUILD PARTITION = <Partition_ID>` to rebuild the switched-in partition on `Table_B`.
125-
5. Repeat step 3 and 4 for each partition in `Table_A`.
126-
6. Once all partitions are switched from `Table_A` to `Table_B` and have been rebuilt, drop `Table_A`, and rename `Table_B` to `Table_A`.
123+
1. Create partitions on the target large table (called `Table_A`).
124+
1. Create an empty ordered CCI table (called `Table_B`) with the same table and partition schema as `Table_A`.
125+
1. Switch one partition from `Table_A` to `Table_B`.
126+
1. Run `ALTER INDEX <Ordered_CCI_Index> ON <Table_B> REBUILD PARTITION = <Partition_ID>` to rebuild the switched-in partition on `Table_B`.
127+
1. Repeat step 3 and 4 for each partition in `Table_A`.
128+
1. Once all partitions are switched from `Table_A` to `Table_B` and have been rebuilt, drop `Table_A`, and rename `Table_B` to `Table_A`.
127129

128-
>[!TIP]
130+
> [!TIP]
129131
> For a dedicated SQL pool table with an ordered CCI, ALTER INDEX REBUILD will re-sort the data using `tempdb`. Monitor `tempdb` during rebuild operations. If you need more `tempdb` space, scale up the pool. Scale back down once the index rebuild is complete.
130132
>
131133
> For a dedicated SQL pool table with an ordered CCI, ALTER INDEX REORGANIZE does not re-sort the data. To resort data, use ALTER INDEX REBUILD.
@@ -134,10 +136,10 @@ Creating an ordered CCI is an offline operation. For tables with no partitions,
134136
135137
## Feature differences in SQL Server 2022 capabilities
136138

137-
SQL Server 2022 (16.x) introduced ordered clustered columnstore indexes similar to the feature in Azure Synapse dedicated SQL pools.
139+
SQL Server 2022 (16.x) introduced ordered clustered columnstore indexes similar to the feature in Azure Synapse dedicated SQL pools.
138140

139-
- Currently, only SQL Server 2022 (16.x) and later support clustered columnstore enhanced rowgroup elimination capabilities for string, binary, and guid data types, and the datetimeoffset data type for scale greater than two.
140-
- Currently, only SQL Server 2022 (16.x) supports clustered columnstore rowgroup elimination for the prefix of `LIKE` predicates.
141+
- Currently, only SQL Server 2022 (16.x) and later versions support clustered columnstore enhanced [segment elimination](/sql/relational-databases/indexes/columnstore-indexes-design-guidance) capabilities for string, binary, and guid data types, and the datetimeoffset data type for scale greater than two. Previously, this segment elimination applies to numeric, date, and time data types, and the datetimeoffset data type with scale less than or equal to two.
142+
- Currently, only SQL Server 2022 (16.x) and later versions support clustered columnstore rowgroup elimination for the prefix of `LIKE` predicates, for example `column LIKE 'string%'`. Segment elimination is not supported for non-prefix use of LIKE such as `column LIKE '%string'`.
141143

142144
For more information, see [What's New in Columnstore Indexes](/sql/relational-databases/indexes/columnstore-indexes-what-s-new).
143145

@@ -146,8 +148,8 @@ For more information, see [What's New in Columnstore Indexes](/sql/relational-da
146148
**A. To check for ordered columns and order ordinal:**
147149

148150
```sql
149-
SELECT object_name(c.object_id) table_name, c.name column_name, i.column_store_order_ordinal
150-
FROM sys.index_columns i
151+
SELECT object_name(c.object_id) table_name, c.name column_name, i.column_store_order_ordinal
152+
FROM sys.index_columns i
151153
JOIN sys.columns c ON i.object_id = c.object_id AND c.column_id = i.column_id
152154
WHERE column_store_order_ordinal <>0;
153155
```
@@ -162,4 +164,8 @@ WITH (DROP_EXISTING = ON);
162164

163165
## Next steps
164166

165-
For more development tips, see [development overview](sql-data-warehouse-overview-develop.md).
167+
- For more development tips, see [development overview](sql-data-warehouse-overview-develop.md).
168+
- [Columnstore indexes: Overview](/sql/relational-databases/indexes/columnstore-indexes-overview)
169+
- [What's new in columnstore indexes](/sql/relational-databases/indexes/columnstore-indexes-what-s-new)
170+
- [Columnstore indexes - Design guidance](/sql/relational-databases/indexes/columnstore-indexes-design-guidance)
171+
- [Columnstore indexes - Query performance](/sql/relational-databases/indexes/columnstore-indexes-query-performance)

0 commit comments

Comments
 (0)