我有一个看似简单的SQL Server查询,这比我预期的要长很多。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT COUNT(DISTINCT(guid)) FROM listens WHERE url='http://www.sample.com/'
'guid'是varchar(64)NULL
'url'是varchar(900)NULL
guid和url上有一个索引。
'listens'表中有超过700万行,其中17,000行与相关网址相匹配,查询结果为5,500。
在一台相当空闲的双核AMD Opteron 2GHz和1GB RAM上运行SQL Server 2008上的查询需要1分钟以上。
有关如何缩短执行时间的任何想法?理想情况下应该不到1秒!
答案 0 :(得分:5)
在网址上创建一个涵盖GUID
:
CREATE INDEX ix_listens_url__guid ON listens (url) INCLUDE (guid)
当将url作为标识符处理时,最好存储和索引URL
哈希而不是整个URL
。
答案 1 :(得分:2)
请注意,校验和不是唯一的,但它足够独特。 这是一个完整的代码示例,说明如何执行此操作。我已经包含了两列的校验和,但它可能只需要一个。您还可以自行计算插入或更新的校验和,并删除触发器。
CREATE TABLE MyTable
(
ID INT IDENTITY(1,1) PRIMARY KEY,
[Guid] varchar(64),
Url varchar(900),
GuidChecksum int,
UrlChecksum int
)
GO
CREATE TRIGGER trgMyTableCheckSumCalculation ON MyTable
FOR UPDATE, INSERT
as
UPDATE t1
SET GuidChecksum = checksum(I.[Guid]),
UrlChecksum = checksum(I.Url)
FROM MyTable t1
join inserted I on t1.ID = I.ID
GO
CREATE NONCLUSTERED INDEX NCI_MyTable_GuidChecksum ON MyTable(GuidChecksum)
CREATE NONCLUSTERED INDEX NCI_MyTable_UrlChecksum ON MyTable(UrlChecksum)
INSERT INTO MyTable([Guid], Url)
select NEWID(), 'my url 1' union all
select NEWID(), 'my url 2' union all
select null, 'my url 3' union all
select null, 'my url 4'
SELECT *
FROM MyTable
SELECT COUNT(GuidChecksum)
FROM MyTable
WHERE Url = 'my url 3'
GO
DROP TABLE MyTable
答案 2 :(得分:2)
我知道这篇文章有点晚了。我正在寻找另一个优化问题。
注意到:
我的建议:
Column = URLHash AS UNIQUEIDENTIFIER
创造新纪录。 URLHash = CONVERT( UNIQUEIDENTIFIER, HASHBYTES('MD5', url) )
然后在你的查询中:
SELECT COUNT(DISTINCT(guid)) FROM listens WHERE URLHash = CONVERT( UNIQUEIDENTIFIER, HASHBYTES( 'MD5', 'http://www.sample.com/' ) )
这将为您提供一种非常快速的独特搜索特定网址的方法,同时保持非常小的索引大小。
如果您需要进一步优化,您可能希望在guid上执行相同的哈希。在16byte uniqueidentifier上执行distinct更快比varchar(64)更快。
以上假设是您没有在监听表中添加大量新行;也就是说,新的记录率并不那么重。原因是MD5算法虽然提供了完美的色散;是出了名的慢。如果要以每秒数千的速度添加新记录;然后在创建记录时计算MD5哈希会降低服务器的速度(除非你有一个非常快的服务器)。另一种方法是实现您自己的FNV1a散列算法版本,该算法不是内置的。与MD5相比,FNV1a快得多,但提供了非常好的色散/低截留率。
希望以上帮助将来遇到这类问题的人。
答案 3 :(得分:0)
您的GUID
专栏本质上会比bigint
占用更多的空间(16 bytes)。您是否可以使用自动递增的数字列替换GUID
列,或者如果失败,则引入类型为bigint
/ int
的新列,该列会针对{{的每个新值递增1}}列(然后您可以使用GUID
确保全局唯一性,并使用GUID
t进行索引编制)?
从上面的链接:
16字节,uniqueidentifier数据 类型相对较大 其他数据类型,如4字节 整数。这意味着建立了索引 使用uniqueidentifier键可能是 相对慢于实施 使用int键的索引。
您使用bigint/in
作为guid列而不是varchar
是否有任何特殊原因?
答案 4 :(得分:0)
一些提示......
1)重构您的查询,例如使用with
条款......
with url_entries as ( select guid from listens where url='http://www.sample.com/' ) select count(distinct(enries.guid)) as distinct_guid_count from url_entries entries
2)告诉确切的SQL Serever在执行查询时必须扫描哪个索引(当然,按url
字段索引)。另一种方法 - 由guid
简单地删除索引并仅由url
留下索引。查看here以获取有关提示的更多信息。特别是对于像select ... from listens with (index(index_name_for_url_field) )
3)验证listens
表上的索引状态并更新index statistics。
答案 5 :(得分:0)
我敢打赌,如果你的机器内存超过1GB,它的性能会更好(我所遇到的所有DBA在生产SQL服务器中都至少有4GB。)
我不知道这是否重要,但如果你做了
SELECT DISTINCT(guid) FROM listens WHERE url='http://www.sample.com/'
@rowcount
不会包含您想要的结果吗?
答案 6 :(得分:0)
你最好的计划是寻求获得17k候选网址的范围,并且计数明显依赖于保证的输入顺序,因此它不必排序。能够满足这两个要求的正确数据结构是(url, guid)
:
CREATE INDEX idxListensURLGuid on listens(url, guid);
您已经对所使用的密钥的广泛性有了大量的反馈,您可以明确地寻求改进它们,如果可以的话,还可以增加1Gb的RAM。
如果可以在SQL 2008 EE上部署,那么请确保为这样一个高度重复且广泛的索引打开page compression。由于降低了IO,它将在性能方面创造奇迹。