Han*_*urg 6 sql-server olap columnstore
我正在使用 SQL Server 2014 设置一个 OLAP 数据库。核心事实表有大约 40,000,000 行、225 列,平均行大小为 181 字节。我一直在玩聚簇列存储索引,但运气不佳。一般来说,我发现使用新技术时查询性能要慢 4 倍以上。
一个特殊的例子 - 使用 int32 主键选择单行现在需要 12 秒......这是对行存储表的亚秒操作(当然它在 PK 上有一个唯一索引,这不允许与聚集列存储索引)。
我试图找出我做错了什么 - 从 MS 文档中听起来这是完成这项任务的理想技术;也许我错过了一些东西。
我正在 Windows 8.1 64 位上运行 SQL 2014 Enterprise,具有 128GB RAM 和 SSD 用于数据存储。此应用程序的数据是只读的。
如果您可以发布您正在使用的特定数据和查询,这可能是我们在您的特定案例中帮助回答问题的唯一方法。您可以使用生成匿名数据的脚本,其规模与您的真实示例大致相同。
但是,我继续自己创建了类似类型的脚本。为简单起见,我使用的列少于 225 列。但是我使用了相同数量的行和随机数据(这对列存储不利)并且我看到的结果与您的大不相同。所以我最初的想法是,是的,您的配置或测试查询确实存在某种问题。
一些关键要点:
BIT
列吗?这可能需要进一步研究。我确实在一个简单的BIT
列列存储上看到了非常好的压缩率(超过 99%),但可能大部分原因是由于没有行开销,BIT
当单行中有许多列时,这种优势会消失。现在来看看细节:
创建行存储数据集
这里没有什么太令人兴奋的了;我们创建了 40MM 行的伪随机数据。
SELECT @@VERSION
--Microsoft SQL Server 2014 - 12.0.4213.0 (X64)
-- Jun 9 2015 12:06:16
-- Copyright (c) Microsoft Corporation
-- Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
GO
-- Create a rowstore table with 40MM rows of pseudorandom data
;WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
, E2(N) AS (SELECT 1 FROM E1 a CROSS JOIN E1 b)
, E4(N) AS (SELECT 1 FROM E2 a CROSS JOIN E2 b)
, E8(N) AS (SELECT 1 FROM E4 a CROSS JOIN E4 b)
SELECT TOP 40000000 ISNULL(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), 0) AS id
, ISNULL((ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) % 5) + 1, 0) AS col1
, ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * RAND(), 0) AS col2
, ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * RAND(), 0) AS col3
, ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * RAND(), 0) AS col4
, ISNULL(ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) * RAND(), 0) AS col5
INTO dbo.test_row
FROM E8
GO
ALTER TABLE test_row
ADD CONSTRAINT PK_test_row PRIMARY KEY (id)
GO
Run Code Online (Sandbox Code Playgroud)
创建列存储数据集
让我们创建与 相同的数据集CLUSTERED COLUMNSTORE
,使用Niko 博客上描述的加载数据以更好地消除分段的技术。
-- Create a columnstore table with the same 40MM rows
-- The data is first ordered by id and then a single thread
-- use to build the columnstore for optimal segment elimination
SELECT *
INTO dbo.test_column
FROM dbo.test_row
GO
CREATE CLUSTERED INDEX cs_test_column
ON dbo.test_column (id)
GO
CREATE CLUSTERED COLUMNSTORE INDEX cs_test_column
ON dbo.test_column WITH (DROP_EXISTING = ON, MAXDOP = 1)
GO
Run Code Online (Sandbox Code Playgroud)
尺寸对比
因为我们正在加载随机数据,所以列存储只能适度减少表大小。如果数据不是随机的,列存储压缩将显着减小列存储索引的大小。这个特定的测试用例实际上对列存储非常不利,但很高兴看到我们得到了一点压缩。
-- Check the sizes of the two tables
SELECT t.name, ps.row_count, (ps.reserved_page_count*8.0) / (1024.0) AS sizeMb
FROM sys.tables t WITH (NOLOCK)
JOIN sys.dm_db_partition_stats ps WITH (NOLOCK)
ON ps.object_id = t.object_id
WHERE t.name IN ('test_row','test_column')
--name row_count sizeMb
--test_row 40000000 2060.6328125
--test_column 40000000 1352.2734375
GO
Run Code Online (Sandbox Code Playgroud)
性能对比
在以下两个测试用例中,我尝试了两个非常不同的用例。
第一个是您的问题中提到的单身寻求。正如评论者指出的那样,这根本不是列存储的用例。因为必须为每一列读取整个段,所以我们看到冷缓存(行0ms
存储与列273ms
存储)的读取次数更多,性能更慢。但是,列存储归结为2ms
热缓存;考虑到没有要寻找的 b 树,这实际上是一个令人印象深刻的结果!
在第二个测试中,我们计算所有行中两列的聚合。这更符合列存储的设计目的,我们可以看到列存储具有更少的读取(由于压缩并且不需要访问所有列)和显着更快的性能(主要是由于批处理模式执行)。从冷缓存,列存储执行4s
VS15s
为rowstore。使用热缓存,差异在282ms
vs是一个完整的数量级2.8s
。
SET STATISTICS TIME, IO ON
GO
-- Clear cache; don't do this in production!
-- I ran this statement between each set of trials to get a fresh read
--CHECKPOINT
--DBCC DROPCLEANBUFFERS
GO
-- Trial 1: CPU time = 0 ms, elapsed time = 0 ms.
-- logical reads 4, physical reads 4, read-ahead reads 0
-- Trial 2: CPU time = 0 ms, elapsed time = 0 ms
-- logical reads 4, physical reads 0, read-ahead reads 0
SELECT *
FROM dbo.test_row
WHERE id = 12345678
GO 2
-- Trial 1: CPU time = 15 ms, elapsed time = 273 ms..
-- lob logical reads 9101, lob physical reads 1, lob read-ahead reads 25756
-- Trial 2: CPU time = 0 ms, elapsed time = 2 ms.
-- lob logical reads 9101, lob physical reads 0, lob read-ahead reads 0
SELECT *
FROM dbo.test_column
WHERE id = 12345678
GO 2
-- Trial 1: CPU time = 8441 ms, elapsed time = 14985 ms.
-- logical reads 264733, physical reads 3, read-ahead reads 263720
-- Trial 2: CPU time = 9733 ms, elapsed time = 2776 ms.
-- logical reads 264883, physical reads 0, read-ahead reads 0
SELECT AVG(id), SUM(col3)
FROM dbo.test_row
GO 2
-- Trial 1: CPU time = 1233 ms, elapsed time = 3992 ms.
-- lob logical reads 207778, lob physical reads 1, lob read-ahead reads 341196
-- Trial 2: CPU time = 1030 ms, elapsed time = 282 ms.
-- lob logical reads 207778, lob physical reads 0, lob read-ahead reads 0
SELECT AVG(id), SUM(col3)
FROM dbo.test_column
GO 2
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1045 次 |
最近记录: |