如何让MS SQL Server透明地使用CHECKSUM /哈希索引?

问题描述 投票:8回答:4

看起来SQL Server不会自动使用CHECKSUM /哈希索引,除非CHECKSUM列明确包含在查询的搜索参数中。这是一个问题,因为我不控制查询表的应用程序,我可能不会破坏它们的性能。

有没有办法让SQL Server使用新的CHECKSUM /哈希索引而不修改查询以包含新的CHECKSUM /哈希列?

Repro脚本

CREATE TABLE big_table
(
    id BIGINT IDENTITY CONSTRAINT pk_big_table PRIMARY KEY,
    wide_col VARCHAR(50),
    wide_col_checksum AS CHECKSUM(wide_col),
    other_col INT
)

CREATE INDEX ix_checksum ON big_table (wide_col_checksum)

插入一些测试数据:

SET NOCOUNT ON
DECLARE @count INT = 0
BEGIN TRANSACTION
WHILE @count < 10000
BEGIN
    SET @count = @count + 1
    INSERT INTO big_table (wide_col, other_col) 
    VALUES (SUBSTRING(master.dbo.fn_varbintohexstr(CRYPT_GEN_RANDOM(25)), 3, 50), @count)
    IF @count % 1000 = 0
    BEGIN
        COMMIT TRANSACTION
        BEGIN TRANSACTION
    END
END
COMMIT TRANSACTION

INSERT INTO big_table (wide_col, other_col) 
VALUES ('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 9999999)

遗留查询。导致聚集索引扫描(BAD):

SELECT * FROM big_table 
WHERE wide_col = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'


更新的查询。导致NonClustered Index Seek(好):

SELECT * FROM big_table 
WHERE wide_col = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
AND wide_col_checksum = CHECKSUM('ABCDEFGHIJKLMNOPQRSTUVWXYZ')

背景

我的表非常大(数亿行),有几个索引(~20),所有这些都是必需的。一些索引列有点宽(~50个字节)并且具有很少的重复值。仅在相等性上搜索列。桌子不断插入。

下面是一个表格,比较上面样本表中的“普通”索引和CHECKSUM /哈希索引,包括压缩和非压缩。来自具有100万行的表的新重建索引的数据:

单独的页面压缩对样本数据非常无效(实际数据应该更好地压缩)。哈希索引实现了4X索引大小的减少。哈希索引上的页面压缩可以减少6倍的索引大小。

我使用哈希索引的目的是:

  1. 减少内存中这些索引的大小,从而允许SQL Server在RAM中缓存更大的部分,从而避免物理读取。
  2. 减少索引存储大小。
  3. 减少INSERT操作的索引I / O.
sql-server sql-server-2008 hash indexing database-performance
4个回答
1
投票

如果您的应用程序查询

SELECT * FROM big_table WHERE wide_col = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

你需要一个关于wide_col的索引,而不是wide_col_checksum

SQL Server将索引存储为B树。正如@MartinSmith建议的那样,减少索引中列的大小确实会减少内存和磁盘空间。


1
投票

SQL Server不会自动开始使用校验和/哈希索引。查询需要使用sql server的散列列来考虑使用索引。所以我不知道如何实现更改查询的目标。然而,这是一个有趣的问题,可能是SQL Server的一个很好的功能请求。


1
投票

我有一个解决方案,这是一项艰巨的任务!

您可以重命名表,然后使用表的名称创建一个视图,并在视图内部执行操作。

我们的想法是用视图捕获对表的调用,如果在wide_col上没有直接过滤器或使用ix_checksum索引对应的记录,则在视图内部返回所有记录。

我使用sys.dm_exec_requestssys.dm_exec_sql_text来获取用户想要的查询文本,然后通过一点解析我提取wide_col列及其CHECKSUM()NULL的参数(如果没有找到参数)。

之后,我用该校验和(如果存在)提取记录的id

使用UNION ALL运算符,如果查询中未请求过滤器,则将结果集添加到结果集中。

这很棘手,但它确实有效!

警告! 我只是进行了一些解析以从查询中获取参数,您应该检查您的查询以查看它是否正确并在需要时进行调整。

-- rename the table
exec sp_rename big_table, _big_table;
go

drop view big_table
go

-- create the view with the name of the table
create view big_table
as
with
q as ( -- extract the query text
    SELECT SUBSTRING(dest.text, (dem.statement_start_offset+2)/2, CASE WHEN dem.statement_end_offset=-1 THEN 8000 ELSE (dem.statement_end_offset-dem.statement_start_offset+4)/2 END) current_statement
    FROM   sys.dm_exec_requests dem CROSS APPLY sys.dm_exec_sql_text(dem.sql_handle) dest  WHERE  session_id = @@SPID
),
f as ( -- do some parsing to get WHERE condition
    select 
        REPLACE(REPLACE(REPLACE(REPLACE(
            SUBSTRING(current_statement, nullif(patindex('%WHERE%wide_col%=%''%''%', current_statement), 0)+5, 8000)
        , CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' '), ' ', '') par 
        from q 
        where current_statement like '%WHERE%wide_col%=%''%''%'
),
r as ( -- some more parsig to get wide_col filter
    select SUBSTRING(par, 1, charindex('''', par)-1) par
    from (
        select SUBSTRING(par, patindex('%wide_col=''%''%', par)+LEN('wide_col')+2, 8000) par
        from f
        where par like '%wide_col=''%''%'
    ) r
),
p as ( -- calc the checksum of the parameter
    select par, iif(par is null, null, CHECKSUM(par)) chk 
    from r
),
x as ( -- lookup the id of the searched record
    select m.id 
    from _big_table m 
    where wide_col_checksum = (select chk from p)),
z as ( -- test if a parameter was found (flag for normal operation)
    select COUNT(*) n 
    from p 
    where chk is not null
)

-- this is the fast output for searched record
select m.*
from _big_table m, x
where (m.id = x.id) --OR (x.id is null) 

union all

-- this is the normal output for all other conditions
select m.*
from _big_table m, z
where z.n = 0

请享用


0
投票

在大多数排序规则中,这两个查询可以提供不同的结果,因为'A'='a',但CHECKSUM('A')不等于CHECKSUM('a')。即使在CS_AS或BIN排序规则上,尾随空格也可能是个问题。这就是为什么SQL Server无法自动使用这样的索引。

© www.soinside.com 2019 - 2024. All rights reserved.