我有一个大表,我想优化.我正在使用MS-SQL 2005服务器.我将尝试描述它是如何使用的,如果有人有任何建议我会非常感激.
该表约为400GB,每天插入1亿行和100万行.该表有8列,1列数据和7列用于查找/排序.
k1 k2 k3 k4 k5 k6 k7 d1
哪里
k1: varchar(3), primary key - clustered index, 10 possible values k2: bigint, primary key - clustered index, total rows/10 possible values k3: int, 10 possible values k4: money, 100 possible values k5: bool k6: bool k7: DateTime
只运行一个选择查询,如下所示:
SELECT TOP(g) d1 FROM table WITH(NOLOCK) WHERE k1 = a AND k3 = c AND k4 = d AND k5 = e AND k6 = f ORDER BY k7
其中g =大约1百万这个查询我们每天运行大约10次(通常在插入时发生)并且大约需要5-30分钟.
所以我目前在两个主键列上只有一个聚簇索引.我的问题是:我应该添加哪些索引来改善此查询的性能?
每列的单独索引是一个不错的选择吗?我认为单个索引会占用大约5-8GB.数据库服务器总共有8GB RAM.
请不要说最好的事情就是试验.这类似于'我不知道,自己动手':)
任何提示非常感谢!
你已经在这里引起了过早优化的爆发,如果不是直接的建议,"最好的事情就是试验".如果您需要有用的帮助,您需要澄清一些问题.
- doofledorfer
你可能是I/O绑定的
是的,它不受CPU限制.磁盘访问量很高.似乎使用了所有可用的RAM.是否明智地使用还有待观察.
您说您无法拆分数据,因为使用了所有数据:不可能
I mean that all data is used at some point - not that all data is used by each user in each query. I can certainly split the data but, so far, I don't understand why partitioning the table is any better than using a clustered index.
Why did you choose these types VARCHAR probably should have been INT as it can only be a few values. The rest are sensible enough, Money represents a money value in real life and bigint is an ID, and the bools are onny, offy type things :)
By any chance we could get have a look the insert statement, or TSQL or the bulkinsert
TSQL.它基本上是INSERT INTO表VALUES(k1,k2,k3,k4,k5,k6,d1).唯一有趣的是,尝试了许多重复插入,并使用k1和k2 PK约束来防止重复数据进入数据库.我相信在设计时(现在)这是一个快速的方式来找出重复的数据.
你能说出插件发生的频率吗?每隔10分钟左右,插件运行(ADO.NET)可能一次10K,需要几分钟.我估计目前整整一天的插入占当天40%的时间.
DateTime字段是否包含插入号的日期.实际上有另一个DateTime列,但是在任何SELECT查询中都没有检索它,所以为了简单起见我没有提到它.
你是怎么来到这个更多的人一天思考.
if you're interested only in the last data, deleting/archiving the useless data could make sense (start from scratch every morning)
I am not interested in recent data only. A query may select some of the very first data that was inserted into the table all the way up to data inserted minutes ago. But as the data is filtered this does not mean that all the data in the DB is requested in that query.
if there is only one "inserter" and only one "reader", you may want to switch to a specialised type (hashmap/list/deque/stack) or something more elaborated, in a programming language.
I will probably stick with MSSQL for the moment. It's not broke yet, just a little slow.
liggett78, do you suggest a clustered index on columns k1,k4,k5,k6,k3 or a non-clustered index on those columns?
My main question right now is should I extend the current clustered index to contain k4 also (this is the col with next most possible values) or should I just add a non-clustered index to k4.
Would adding all k1-k6 to a clustered index be an option? Then have a separate non-clustered index on the DateTime column for the ORDER BY? Am I correct in thinking that this would not cause any major increase in DB size but will only affect insert times. Can anyone guesstimate the effect this will have on inserts?
I think that if adding indexes to all the columns will double the DB size then it is not viable without large (ie. hardware) changes.
The following plan was run with an index (non clustered) on the DATE column.
EDIT: Not sure if you can see the XML below so here is a link to it: http://conormccarthy.com/box/queryplan.sqlplan.txt
Rob Williams.. 17
正如我在评论中暗示的那样,我用一个接近8 TB的Oracle表来完成这项工作,其中包含超过20亿行,每天以4千万行的速度增长.但是,在我的情况下,用户是200万(并且不断增长)的客户通过网络全天候访问这些数据,并且字面上任何行都可以被访问.哦,必须在两分钟内实时添加新行.
您可能受I/O限制,而不是CPU或内存限制,因此优化磁盘访问至关重要.你的RAM很好 - 绰绰有余.使用多个内核会很有帮助,但如果I/O没有并行化,则会受到限制.
有几个人建议拆分数据,应该认真对待,因为它比任何其他解决方案都要好得多,也更有效(没有什么比完全不接触数据更快).
你说你不能拆分数据,因为使用了所有数据:不可能!您的用户无法每天分页一百万行或总共一亿行.因此,了解您的用户如何实际使用数据 - 在这种情况下查看每个查询.
更重要的是,我们并不是说你应该删除数据,而是说要分割数据.将表结构克隆为多个命名相似的表,可能基于时间(可能每个表一个月).将数据复制到相关表中并删除原始表.创建一个在新表上执行联合的视图,其名称与原始表相同.更改您的插入处理以定位最新的表(假设它是合适的),并且您的查询仍然可以对新视图起作用.
您的精明用户现在可以开始针对表的一部分发出查询,甚至可能是最新的表.您不满意的用户可以继续使用所有表格的视图.
您现在有一个数据管理策略,其形式是归档最旧的表并删除它(当然,更新视图定义).同样,您需要定期创建一个新表,并更新该数据末尾的视图定义.
期望无法使用唯一索引:它们的扩展速度不会超过大约一百到二百万行.您可能还需要修改其他一些策略/建议.在一亿行和400 GB,您已进入另一个处理领域.
除此之外,使用其他建议 - 使用SQL Server和操作系统中已有的许多工具分析实际性能.应用许多众所周知的调整技术,这些技术可以在网上或书籍中找到.
但是,不要试验!有了那么多数据,你就没有时间进行实验,而且风险太大了.仔细研究可用的技术和您的实际性能细节,然后一次选择一个步骤,并给每个步骤几个小时,以显示其影响.
正如我在评论中暗示的那样,我用一个接近8 TB的Oracle表来完成这项工作,其中包含超过20亿行,每天以4千万行的速度增长.但是,在我的情况下,用户是200万(并且不断增长)的客户通过网络全天候访问这些数据,并且字面上任何行都可以被访问.哦,必须在两分钟内实时添加新行.
您可能受I/O限制,而不是CPU或内存限制,因此优化磁盘访问至关重要.你的RAM很好 - 绰绰有余.使用多个内核会很有帮助,但如果I/O没有并行化,则会受到限制.
有几个人建议拆分数据,应该认真对待,因为它比任何其他解决方案都要好得多,也更有效(没有什么比完全不接触数据更快).
你说你不能拆分数据,因为使用了所有数据:不可能!您的用户无法每天分页一百万行或总共一亿行.因此,了解您的用户如何实际使用数据 - 在这种情况下查看每个查询.
更重要的是,我们并不是说你应该删除数据,而是说要分割数据.将表结构克隆为多个命名相似的表,可能基于时间(可能每个表一个月).将数据复制到相关表中并删除原始表.创建一个在新表上执行联合的视图,其名称与原始表相同.更改您的插入处理以定位最新的表(假设它是合适的),并且您的查询仍然可以对新视图起作用.
您的精明用户现在可以开始针对表的一部分发出查询,甚至可能是最新的表.您不满意的用户可以继续使用所有表格的视图.
您现在有一个数据管理策略,其形式是归档最旧的表并删除它(当然,更新视图定义).同样,您需要定期创建一个新表,并更新该数据末尾的视图定义.
期望无法使用唯一索引:它们的扩展速度不会超过大约一百到二百万行.您可能还需要修改其他一些策略/建议.在一亿行和400 GB,您已进入另一个处理领域.
除此之外,使用其他建议 - 使用SQL Server和操作系统中已有的许多工具分析实际性能.应用许多众所周知的调整技术,这些技术可以在网上或书籍中找到.
但是,不要试验!有了那么多数据,你就没有时间进行实验,而且风险太大了.仔细研究可用的技术和您的实际性能细节,然后一次选择一个步骤,并给每个步骤几个小时,以显示其影响.
帮助我了解更多关于桌子的信息.如果您的PK是k1,k2,您不必选择任何其他列来获得完全唯一的记录.
你的意思是说k1到7是PK吗?如果是这样,请将其声明为此类,并且它将是聚簇索引.查询性能应该会大大提高.
订单增加了很多开销.考虑找一个可以返回较小数据集的更好选项.了解为什么需要返回大约一百万条记录可能会帮助我提供更好的解决方案.
编辑:我觉得我并不孤单,我怀疑开始优化的最佳位置是你的物理表设计.你对此有什么控制权吗?不知道每个列存储的内容,我不能提供非常具体的想法,但是下面是一个非常通用的方法:将K1,3,4,5和6(k2似乎与表中的值直接相关)放在自己的表中使用单个唯一的int作为PK.然后创建一个返回此表的FK关系.然后,主表上的PK将包含此字段k2和k7.现在您的查询将优化器将在您的新表中执行相当便宜的查找,返回单个记录,然后仅通过PK执行索引搜索到主表.
看起来你只想要最早的"g"记录?也许只有最近的"g"记录?
基本上,您希望查询只读取最新/最旧的记录.你不想查询整个400GB吗?如果是这种情况,您可以考虑归档400GB的大部分,或者将最近插入的记录保存在您可以查询的"当前"表中.您可以通过双重插入或通过表格上的触发器(颤抖)将当前表中的记录保持为当前状态.但基本前提是您对尽可能小的表运行查询.这基本上是穷人的表分区.
这是我要做的:
不要在每列上创建单个索引.你会浪费空间,他们不会帮助你(如果有的话)
保留主键,但在日期列上创建聚簇索引,因为这是您在ORDER BY中使用的.这样,数据库引擎将开始扫描聚簇键,将列与您提供的值进行比较,并输出满足条件的行.
您不需要任何其他索引.我相信,对于k4来说,即使100个值中的100个值也会被优化器视为选择性差(尽管你至少可以尝试这个).
如果您根据某些日期范围进行选择,例如仅选择上个月,周,年等的数据,您可能希望根据日期列将大表分区为"较小的".这些10值列也是分区键的好选择.
顺便说一句,你在查询中指定了整个PK - 假设在WHERE中为AND,它将精确地选择1行.
好,
让我们尝试用统计数据来解决这个问题.在尝试创建任何索引之前,您应该问一下哪些组合键给了我更好的选择性:
K1:10个不同的值
K3:100个不同的值
k4:10个不同的值
k5:2个不同的值
k6:2个不同的值
如果我们制作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的索引与它一样好.
希望这可以帮助,