8

除非 CHECKSUM 列显式包含在查询的搜索参数中,否则 SQL Server 似乎不会自动使用 CHECKSUM/哈希索引。这是一个问题,因为我不控制查询表的应用程序,并且我可能不会破坏它们的性能。

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

复制脚本

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'

聚集索引扫描 (BAD)


更新了查询。导致非聚集索引搜索(良好):

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

非聚集索引搜索(好)

背景

我的表非常大(数亿行),有几个索引(~ 20),所有这些都是必需的。一些索引列有点宽(约 50 个字节)并且几乎没有重复值。仅在相等时搜索列。表被不断地插入。

这是一个比较上面示例表上的“正常”索引和 CHECKSUM/哈希索引的表,包括压缩和非压缩。来自 100 万行表的新重建索引的数据:

哈希索引和压缩

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

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

  1. 减少这些索引在内存中的大小,从而允许 SQL Server 在 RAM 中缓存更多部分,从而避免物理读取。
  2. 减少索引存储大小。
  3. 减少 INSERT 操作的索引 I/O。
4

4 回答 4

1

如果您的应用程序查询:

SELECT * FROM big_table WHERE wide_col = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

您需要在 上wide_col而不是在 上的索引wide_col_checksum

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

于 2012-06-18T13:44:30.687 回答
1

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

于 2014-09-23T10:14:25.873 回答
1

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

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

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

我使用sys.dm_exec_requestsandsys.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

请享用

于 2018-04-12T18:25:35.813 回答
0

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

于 2019-04-29T04:11:26.080 回答