我们有一个广泛的表格,我们目前正在尝试优化。该表有50列(统计数据),我们最终希望按降序排列。目前有超过500万行。
我们正在寻找在降低复杂性和提高读取速度方面优化此表的方法。写入速度对我们来说也很重要,但读取更为关键。这些统计数据的排名应该尽可能接近实时,最佳解决方案是在每个请求的基础上快速排名(新行一直在增加,我们希望尽快显示这些行的排名) 。)
我们目前正在评估垂直表格布局是否会更高效。)更容易使用。
因为正在插入的统计数据不一定定义得很好,如果它们没有硬编码到表中(因此优先选择垂直表结构),对我们来说更容易。)
以下是我们当前的表结构和查询:
CREATE TABLE Stats
(
Id BIGINT PRIMARY KEY NOT NULL,
UserId INT,
Name VARCHAR(32) NOT NULL,
Value DECIMAL(10,4) DEFAULT ((0)) NOT NULL,
UpdatedAt DATETIME
);
CREATE INDEX Leaderboard__index ON Stats (Name, Value DESC);
SELECT
Id,
Name,
Value,
RANK() OVER (PARTITION BY Name ORDER BY Value DESC) AS Rank
FROM
Stats
ORDER BY
Value DESC
通常我们要么搜索任何给定统计数据的前N行(如排行榜),要么我们选择单个UserId并获取与该UserId相关的所有统计数据的等级。
数据相当大(正如我上面提到的,因为有很多行和很多列,垂直表结构可能会在2.5亿行的范围内并且会继续增长。)
我们希望在需要的任何硬件上尽快获取此数据,秒是我们的目标,因为我们目前处于分钟范围内。
在垂直表结构的测试中,我们插入了超过400,000行的数据,上面的查询花了不到3分钟(尽管它也只花了大约18秒来排序10,000行。)
我很乐意听到任何建议。谢谢你的时间!
答案 0 :(得分:9)
您拥有的索引对您的窗口函数没用,因为
1.要获取ID列值,SQL可能最终会进行密钥查找,甚至在跨越 Tipping point 时最终扫描整个其他索引。因此,您的索引可能不会用于所有。
2.你是通过val desc订购的,这需要一个没有合适索引的排序,甚至可能会结束spilling to TEMPDB
3.对于另一个有趣的碎片方面,见下文
通常,要使Window函数表现良好,您需要 POC 索引,这意味着
P,O - 按列分区和排序应在关键条款中 C - 覆盖 - 您在select中包含的列应包括在内
因此,以下查询可以最佳地运作。
SELECT
Id,
Name,
Value,
RANK() OVER (PARTITION BY Name ORDER BY Value DESC) AS Rank
FROM
Stats
ORDER BY
Value DESC
您需要以下索引
create index nci_test on dbo.table(name,value desc)
include(id)
使用"创建的索引还有一个问题。 value desc
&#34 ;.
通常在索引中,默认情况下所有值都将以升序顺序存储,但是使用此索引时,您要求以相反的方式存储,这可能导致逻辑碎片,这可以从answer Martin Smith看出这里..从答案中提取相关术语......
如果使用降序键创建索引但新行附加了升序键值,那么您最终可能会出现逻辑顺序中的每个页面。扫描表时,这会严重影响IO读取的大小,而不在缓存中
这么少的选择..
1.根据您的频率运行索引重建以查看是否有帮助
2.将查询更改为按分区子句排序将消除使用" val desc"创建索引的需要。选项
SELECT
Id,
Name,
Value,
RANK() OVER (PARTITION BY Name ORDER BY Value DESC) AS Rank
FROM
Stats
ORDER BY
name DESC
上面的查询不需要像你创建的那样创建一个索引。你可以像下面那样改变它。它还会处理上面提到的碎片方面
CREATE INDEX Leaderboard__index ON Stats (Name, Value)
include(id);
<强>参考文献:强>
Microsoft SQL Server 2012 High-Performance T-SQL Using Window Functions