Jag*_*SWE 6 sql-server optimization t-sql sql-server-2014
我有一个内部 Web 应用程序正在运行,每次用户转到“搜索”视图时,它都会查询数据库中的三个不同表,以生成视图中三个下拉列表的值。
它基本上运行一个
SELECT DISTINCT (PortName)
FROM Ports
ORDER BY PortName ASC
Run Code Online (Sandbox Code Playgroud)
但是该表包含约 10'000'000 行,并且负载非常重,这意味着页面的加载时间(由于加载数据下拉列表)可能会超过 10-15 秒。
那么,有没有更好的方法来做到这一点,例如以特定时间间隔运行一些脚本并在不同位置创建一个表/视图/任何内容,以便卸载查询大表,只是为了从 10'000 返回 80 行'000 在主表中?
RBa*_*ung 11
我假设DISTINCT
PortNames 在您的表中重复,并且没有返回 1000 万个不同的 portname。
最简单的解决方案是在该列上放置一个索引:
CREATE INDEX IX_Ports_PortName ON Ports(PortName);
Run Code Online (Sandbox Code Playgroud)
当然,这个和存储开销仍然有一些数据库负载,所以你可能需要一个更复杂的解决方案,比如缓存,Aaron Bertrand 在他的回答中很好地涵盖了这一点。
您还可以使用更多的规范化:如果端口名称重复并且清楚地了解它们很重要,那么您可以制作一个 [PortNames] 表,并PortNameID
在 [Ports] 表中使用 a 。这样你就可以扫描 [PortNames] 表,它可能会更小更快。当然,这可能有其自身的额外成本和考虑因素。
Aar*_*and 10
对于不经常更改的数据,您可以在这些查询所在的位置使用缓存层。有很多替代方案,例如[memcached],并且已经存在许多讨论:
您也可以自己轻松完成此操作,并且根据数据的范围和大小,您可以以低廉的价格完成此操作。我以前做过这种事情,我在每个应用程序/Web 服务器上放置了一个 SQL Server Express 实例,并编写了自己的脚本,以最小的中断定期交换这些实例中的数据。这使所有繁重的读取活动远离主实例,并且还提供了这些缓存数据副本可能变得多么陈旧的灵活性(只需更改刷新作业的频率)。我在这里写了这个过程:
您可以做的另一件事是使用日志传送来实现穷人的可用性组。基本上,您有一组日志传送目标,在它们之间循环,按计划恢复最新日志,以及一个动态应用程序,它知道下一个读取请求使用哪个目标。我在这里写了这个过程:
如果您的数据大于 10GB,或者将来会超过,那么 Express 将无法使用,您必须至少使用标准版。但是,这种类型的操作,你的规模OUT读取到的商品硬件,比在主服务器上增加核心/内存/硬盘规模便宜得多UP。
如果隔离读取和写入不是主要目标,那么对于这种非常特殊的情况,您可以使用其他本地解决方案,例如索引视图。请记住,它们会产生开销,并且您无法灵活处理这些开销,例如调整数据复制的频率(以及读取副本的陈旧程度)。其他查询场景不适合索引视图。
不知何故,没有人提到索引视图。索引视图的简要介绍可以在您可以(和不能)对索引视图执行的操作中找到。
本质上它是一个缓存,由引擎在后台自动维护。索引视图存储在磁盘上,并在基础表更改时自动更新。
因此,主表的更新、删除和插入会变得有些慢,但查询索引视图将是即时的,因为它不会扫描主表的 10M 行。在任何情况下,引擎都足够聪明,不会在更新时扫描整个 10M 行表以调整存储在索引视图中的值。
此外,问题标题说“对很少更改的数据运行查询的替代方法”,所以我假设这个大表无论如何都不会经常更改。我认为,索引视图在这里会很完美。
你不能DISTINCT
在索引视图中,但你的查询可以在没有它的情况下被重写,如下所示:
SELECT PortName, COUNT_BIG(*) AS cc
FROM Ports
GROUP BY PortName
Run Code Online (Sandbox Code Playgroud)
如果索引视图包含GROUP BY
它需要COUNT_BIG(*)
,所以我添加了它。