答案 0 :(得分:17)
正如我在评论中暗示的那样,我用一个接近8 TB的Oracle表来完成这项工作,其中包含超过20亿行,每天以4千万行的速度增长。但是,在我的情况下,用户是200万(并且不断增长)的客户通过网络全天候访问这些数据,并且字面上任何行都可以被访问。哦,必须在两分钟内实时添加新行。
您可能受I / O限制,而不是CPU或内存限制,因此优化磁盘访问至关重要。你的RAM很好 - 绰绰有余。使用多个内核会很有帮助,但如果I / O没有并行化,则会受到限制。
有些人建议拆分数据,应该认真对待,因为它比任何其他解决方案都要好得多,效果更好(没有什么比不接触数据更快)。
您说由于使用了所有数据,您无法拆分数据:不可能!您的用户无法每天分页一百万行或总共一亿行。因此,了解您的用户如何实际使用数据 - 在这种情况下查看每个查询。
更重要的是,我们并不是说您应该删除数据,我们说要分割数据。将表结构克隆为多个命名相似的表,可能基于时间(可能每个表一个月)。将数据复制到相关表中并删除原始表。创建一个在新表上执行联合的视图,其名称与原始表相同。更改您的插入处理以定位最新的表(假设它是合适的),并且您的查询仍然可以对新视图起作用。
您精明的用户现在可以开始针对表的一部分发出查询,甚至可能是最新的表。您不满意的用户可以继续使用所有表格的视图。
您现在拥有一种数据管理策略,其形式是归档最旧的表并将其删除(当然,更新视图定义)。同样,您需要定期创建一个新表,并更新该数据末尾的视图定义。
预计无法使用唯一索引:它们的扩展速度不会超过大约一百万到两百万行。您可能还需要修改其他一些策略/建议。在一亿行和400 GB,您已进入另一个处理领域。
除此之外,使用其他建议 - 使用SQL Server和操作系统中已有的许多工具分析实际性能。应用许多众所周知的调整技术,这些技术可以在网上或书中找到。
但是,不要试验!有了那么多数据,你就没有时间进行实验,而且风险太大了。仔细研究可用的技术和您的实际性能细节,然后一次选择一个步骤,并给每个步骤几个小时到几天来揭示它的影响。
答案 1 :(得分:8)
帮助我了解有关该表的更多信息。如果您的PK是k1,k2,您不必选择任何其他列来获得完全唯一的记录。
你的意思是说k1到7是PK吗?如果是这样,请将其声明为此类,并且它将是聚簇索引。查询性能应该会大幅提升。
订单增加了很多开销。考虑找一个可以返回较小数据集的更好选项。知道你需要返回大约一百万条记录的原因可能会帮助我提供更好的解决方案。
编辑:我觉得我并不孤单,我怀疑开始优化的最佳位置是你的物理表设计。你对此有什么控制权吗?不知道每个专栏的内容,我不能提供非常具体的想法,但下面是一个非常通用的方法:放K1,3,4,5& 6(k2似乎与表中的值直接相关)在其自己的表中,其中一个唯一的int作为PK。然后创建一个返回此表的FK关系。然后主表上的PK将包括这个字段,k2& K7。现在您的查询将优化器在新表中执行相当便宜的查找,返回单个记录,然后仅通过PK执行索引到主表中。
答案 2 :(得分:5)
行,
让我们尝试用统计数据来解决这个问题。在尝试创建任何索引之前,您应该问一下哪些组合键能让我获得更好的选择性:
如果我们制作k1,k3,k4,k5和k6的复合键,这意味着该键只有40,000种不同的组合(10 * 100 * 10 * 2 * 2)。这意味着如果我们有100,000,000记录除以40,000,从统计上来说,我们将有2,500个不同记录的子集,并且将使用顺序搜索来完成WHERE子句的其他限制。
如果我们推断这个结果,并将它们与当前执行时间(30分钟)进行比较,使用一个键(k1),统计生成1000万条不同记录的子集:
10,000,000 rec * X sec = 30 * 60 sec * 2,500 rec
=> X秒= 0.45秒
不错吧?更好。如果我们从compund索引中消除k5和k6怎么样?从统计上来说,我们将有10,000个不同记录的子集,其中将执行顺序搜索。从理论上讲,需要多长时间?让我们看看:
10,000,000 rec * X sec = 30 * 60 * 10,000 rec
=> X秒= 1.8秒
由于我们希望最小的索引足迹以最佳性能进行交易,我会说k1 + K3 + K4的索引与它一样好。
希望这有帮助,
答案 3 :(得分:5)
以下是我要做的事情:
顺便说一句,你在查询中指定了整个PK - 假设在WHERE中为AND,它将精确地选择1行。
答案 4 :(得分:5)
看起来你只想要最早的“g”记录?也许只有最近的“g”记录?
基本上,您希望查询仅读取最新/最旧的记录。你不想查询整个400GB吗?如果是这种情况,您可以考虑归档400GB的大部分,或将最近插入的记录保存在您可以查询的“当前”表中。您可以通过双重插入或通过表格上的触发器(颤抖)将当前表中的记录保持为当前状态。但基本前提是您对尽可能小的表运行查询。这基本上是穷人的桌子分区。
答案 5 :(得分:4)
首先,花一天时间在后台运行SQL Profiler。在一天结束时,将跟踪数据保存到文件中,然后让优化向导倒在上面并评估当前索引。这应该告诉您是否更改索引字段,排序顺序等可以为您带来任何重大收益。不要让向导进行更改。如果百分比性能增益看起来很重要(恕不通过30%恕我直言),请继续并自行进行更改。
你的指数必须偏大。您可能希望安排一份工作(一夜之间,每周几次)来执行以下操作:
一旦调整了索引,这将使其保持快速。
答案 6 :(得分:3)
很难给你一个非常有意义的答案。你看过磁盘I / O成本了吗?你在哪里保存数据库文件 - 也许是拖延的I / O?这里有很多变量会影响性能。也许是您的UI或者显示数据所花费的时间,也许是网络所花费的时间?
也许最简单的方法 - 如果您使用的是SQL Server 2005的企业版,那么您将看到最大的收益就是对表进行分区。
再次无法访问实际查询计划,perfmon统计数据很难确切地告诉您究竟是什么问题。你的问题根本不足以让我们继续下去 - 而且一切都只是猜测。
答案 7 :(得分:3)
为什么你聚集在主键上?
哪些列可以为NULL?
什么是VARCHAR长度?
查询计划现在给你什么?
您通过提供无意义的列名来阻碍我们。
即使聚集索引正确,也应首先选择更具选择性的字段。
我可以根据信息不足提出建议,但有些帮助会更好。
答案 8 :(得分:3)
使用SQL事件探查器来计算要创建的索引,它旨在为您计算出这些信息并建议改进的执行配置文件。
k3,k4上有外键吗?
尝试将k1,k2转换为int并使它们成为外键,它将使用更少的存储空间,我已经想到了,我认为它应该更快(虽然我可能错了,我猜SQL服务器缓存这些值)。更重要的是,如果您需要更新值,则更容易。您只需更改外键行的名称 - 然后您不必更新1亿个主键,或其他任何内容。
提高查询速度的一个好方法是放入一个子查询,将您的记录集大小减少到更易于管理的大小。
在:
SELECT TOP(g) d1
FROM table WITH(NOLOCK)
WHERE k1 = a WHERE k2 = b WHERE k3 = c WHERE k4 = d WHERE k5 = e WHERE k6 = f
ORDER BY k7
其中,我认为应该是
SELECT TOP(g) d1
FROM table WITH(NOLOCK)
WHERE k1 = a AND k2 = b AND k3 = c AND k4 = d AND k5 = e AND k6 = f
ORDER BY k7
可能会有一些数据立即将记录集从1000万行减少到10,000行。
e.g。
SELECT TOP(g) d1
FROM (SELECT *
FROM table k1=a AND k2=a WITH(NOLOCK))
WHERE AND k3 = c AND k4 = d AND k5 = e AND k6 = f
ORDER BY k7
这假设您可以通过一个或两个WHERE参数大量减少初始数据集 - 几乎可以肯定。
DBA可能有更多更好的解决方案!
答案 9 :(得分:1)
显示查询计划输出 - 任何未启动的调整冒险都会出现意外事故。
答案 10 :(得分:1)
您是否考虑过创建代理标识列(类型为bigint)并将其用作聚簇索引?然后将主键创建为非群集唯一索引。
使用这种大小的表,索引和页面碎片很可能是一个很大的性能问题。代理聚簇索引将确保所有插入都位于表的末尾,这几乎可以完全消除页面碎片(除非行被删除)。减少页面碎片==每个IO更多页面,这是一件非常好的事情。
这还允许您定期对要查询的唯一索引进行碎片整理,这将使其更有效。经常这样做,或至少定期监控此表上的索引碎片。
这些性能改进可能非常引人注目 - 如果您当前的PK高度分散,索引搜索可能涉及的IO比它应该多得多。
一旦实现了这一点,请考虑(也就是说,尝试并测量;-)在第k7列添加非聚集索引。
答案 11 :(得分:1)
看起来您没有充分利用聚集索引,并且拥有大量重复数据。
您的聚集索引似乎构造如下:
create clustered index IX_Clustered on Table(k1 ASC, k2 ASC)
但是,您的其他k *列仅代表40,000种可能的排列。
10(k1)* 10(k3)* 100(k4)* 2(k5)* 2(k6)= 40,000
您应该将这4个密钥的唯一组合拉出到一个单独的表中,并为每个密钥提供一个唯一的int(主键“newPK”)。
请原谅伪代码:
create table SurrogateKey(
newPK int -- /*primary key*/
, k1, k3, k4, k5, k6
)
constraint: newPK is primary key, clustered
constraint: k1, k3, k4, k5, k6 is unique
此表只有40,000行,并且可以非常快速地查找主键newPK。 然后,您可以在大表中查找单个整数。
您的现有表格应更改为包含以下列:
鉴于上述情况,您可以将聚集索引更改为:
create clustered index IX_Clustered on Table(newPK ASC)
你可以寻求这个。它保证比您的查询现在更快(与索引扫描+键查找相当的性能)。
declare @pk int
select @pk = newPK
from SurrogateKey
where
k1 = @k1
and k3 = @k3
and k4 = @k4
and k5 = @k5
and k6 = @k6
select top(g1) d1, k2, k7
from Table with(read uncommitted)
where newPK = @pk
order by k7
您的insert语句也需要修改以查询/插入SurrogateKey表。
答案 12 :(得分:1)
分区和并行化 - 检查查询计划,如果它没有显示查询是并行化的,那么找出它不是的原因。您可能需要将查询分解为几个步骤,然后将结果合并在一起。
如果是在多个物理磁盘上分配数据,则添加更多核心。它有很多工作要做,一旦你把它归结为原始状态,物理能力就是剩下的。
不要以为SQL Server会使用所有核心。通常,您必须正确设计查询,以便可以使用多个核心。检查查询计划中第一个节点的属性以查看DOP(并行度)。如果它是1你浪费核心......
答案 13 :(得分:0)
什么是D1,是十进制还是长字符请你详细说明一下。 我的推荐是将聚集索引创建为(K7,k2,k1,k4),然后在(k3)上创建一个附加索引(除非值分布约为30%,否则创建两个bool值的索引几乎毫无意义/ 70%值之间,或者如果你的表非常宽,如果d1)。
此更改不会对您的插入速度产生太大影响,同时为您提供聚集索引的粗略通用答案。
答案 14 :(得分:0)
您可以尝试:
alter table MyTable
add constraint PK_MyTable
primary key nonclustered (k1, k2)
create clustered index IX_MyTable
on MyTable(k4, k1, k3, k5, k6, k7)
--decreasing order of cardinality of the filter columns
这将确保您的重复插入继续出错。
这也可以指示SQL Server在(k1, k3, k4, k5, k6)
上进行过滤并在(k7 asc)
上一次性排序,允许SQL Server流式传输查询结果,而无需先对一百万个结果进行排序的中间步骤。一旦SQL Server找到匹配(k1, k3, k4, k5, k6)
的第一行,下一百万行左右的行将匹配相同的过滤器,并且已经按(k7 asc)
排序。所有过滤和排序将根据聚集索引一起完成。
如果页面是连续存储的,并且提供了SQL Server知道如何优化,那就是一些磁盘试图沿着索引向下查找第一个匹配的行,然后是一个大的顺序磁盘读取一万个左右的页面。这应该比要求SQL Server遍布各处寻找行,然后要求SQL Server在tempdb中对它们进行排序要快!
您必须保持警惕并确保聚集索引始终处于良好状态。如果插入时间减慢太多,您可能还需要减少页面填充因子。
答案 15 :(得分:0)
我认为K7上的聚集索引是唯一有价值的东西。你的where子句的其余部分具有如此低的选择性,这是浪费时间。
除非您可以利用某些特定的值知识(k5仅在k4 <0或其他情况下才有效),否则您几乎都在关注聚簇索引扫描。不妨把它作为你订购的领域。
观察k3 - k6中的低数量的不同值,您可能只需要阅读&lt; 150万行,以获得您的前100万。这可能是你要做的最好的 - 特别是因为任何其他计划都需要你通过k7订购才能评估你的TOP条款。
答案 16 :(得分:0)
这听起来很有趣。
几个问题:
一些事实对我来说很重要:
一些评论来到我身边:
答案 17 :(得分:0)
您的查询计划基本上显示以下内容:
计划建议一个指数,它应该将烫发提高81% - k1,k4,k5,k6,k3 +包括d1&amp; K7。 我不知道构建这样一个索引并查看结果需要多长时间,但正如我在这里评论的那样,它会有效地使表的大小加倍,因为几乎每个列都存在于索引中。插入也会慢一些。
正如许多人所建议的那样,分区是最好的策略,例如:例如,使一个表的k3值从1到3,另一个从4到7,第三个从8到10.使用此列上的CHECK约束完成SQL Server Enterprise分区,查询优化器将确定哪个表出来n的扫描/搜索取决于列的参数值。
答案 18 :(得分:0)
答案 19 :(得分:0)
我会说,对于400 GB的表,8 GB的内存不足。如果一个索引单独占用5-8 GB,则服务器无法将相关数据保留在内存中。因此,有很多硬盘读取会使查询变慢。
在我看来,增加RAM的数量并将数据库放在快速RAID上(可能在多个RAID上分割?)会有所帮助。
编辑:要确定您的真正瓶颈是什么,请运行Windows Performance Monitor。
答案 20 :(得分:0)
我会使用索引调整向导来获得更好的答案。
但是,如果是我,我会尝试使用K3,K4的索引(按照您最常查询的顺序)(您已经将K1和K2编入索引)并在K7上单独编制索引。我不相信布尔字段的附加内容会提高索引性能。
记住索引越多,插入速度就越慢。随着你拥有的插入数量,这是一个真正的问题。所以真正唯一真正的答案是你必须尝试自己的数据和硬件,找到最适合你个人情况的东西。事实上,它不是你想要听到的并不是真的,索引非常依赖于你的应用程序的实际工作方式和数据结构。
答案 21 :(得分:0)
添加一个包含列k1-k6的索引;这应该是最好的。
此外,如果您可以在每个查询之前运行sp_updatestats。
答案 22 :(得分:0)
这是一个想法,如果您创建一个包含所有Lookup值的第二个表,然后使用where
而不是使用{{1}},您可以加入表并在新的Lookup表上执行where子句。
此外,如果您发布了几行数据和示例查询,我认为这可能有所帮助。
答案 23 :(得分:0)
您需要创建一个索引,以尽可能快地减少返回的可能行数。
因此,最简单的创建索引将在列k4上,因为它可能具有最大数量的不同值。 只需要索引k4的初始子串,其中k4的期望值在该子串内不同。这将减少索引的大小,并加快访问速度。
k7也应该被编入索引,因为这将大大提高orderby子句的速度。
您可能还需要通过以下顺序创建多列索引来实验(我知道,我知道,您说不要进行实验,但这可能会有所帮助......):k4,k1,k2,k3。这同样是为了尽可能快地减少返回的可能行数。