我最近问了这个问题: MS SQL share identity seed amongst tables (很多人想知道为什么)
我有一个表的以下布局:
表:星星
starId bigint
categoryId bigint
starname varchar(200)
但我的问题是我有数百万行。因此,当我想从表格中删除星星时,它在SQL Server上过于激烈。
我不能使用2005+的内置分区,因为我没有企业许可证。
当我删除时,我总是一次删除整个类别ID。
我想过做这样的设计:
表:Star_1
starId bigint
CategoryId bigint constaint rock = 1
starname varchar(200)
表:Star_2
starId bigint
CategoryId bigint constaint rock = 2
starname varchar(200)
通过这种方式,我可以通过简单的删除表删除整个类别,从而删除O(1)中的数百万行。
我的问题是,在SQL Server中拥有数十万个表是一个问题吗? O(1)的下降对我来说是非常可取的。也许我没有考虑完全不同的解决方案?
修改:
一旦插入了星星是否曾被修改过?否。
您是否需要查询星级类别?我永远不必查询星级类别。
如果您要查找特定星的数据,您会知道要查询的表吗?是的
输入数据时,应用程序将如何决定将数据放入哪个表?在创建categoryId时,将在开始时一次性完成星形数据的插入。
会有多少个类别?您可以假设将有无限的星级类别。假设每天最多100个星级类别,每天最多不超过30个星级类别。
你真的需要删除整个类别或只删除数据更改的星号吗?是全明星类别。
您是否尝试过分批删除?是的,我们今天这样做,但还不够好。 足够的。
另一种技术是将记录标记为删除?没有必要将星标记为已删除,因为我们知道整个星级类别都有资格被删除。
他们中有多少比例从未使用过?通常我们会将每个星级类别数据保留几周,但有时需要保留更多。
当你认为一个有用时是永远有用还是以后还需要删除?
不是永远,而是在发出删除类别的手动请求之前。 如果是这样的话有多少时间会发生?不常见。
您使用的是什么样的光盘安排?单个文件组存储,当前没有分区。
你能用sql enterprise吗?没有。有很多人运行这个软件,他们只有sql标准。获得ms sql企业是超出预算的。
答案 0 :(得分:34)
答案 1 :(得分:4)
你必须删除它们吗?通常最好只将IsDeleted
位列设置为1,然后在非工作时间异步执行实际删除。
修改:
这是一个黑暗中的镜头,但在CategoryId
上添加聚集索引可能会加快删除速度。它也可能会对其他查询产生负面影响。这是你能测试的东西吗?
答案 2 :(得分:2)
这是SQL 2000中的旧技术,分区视图并且仍然是SQL 2005的有效选项。问题确实来自于拥有大量表和与之相关的维护开销。
正如您所说,分区是一项企业功能,但专为此大规模数据删除/滚动窗口效果而设计。
另一个选项是运行批量删除,以避免创建一个非常大的事务,创建数百个小得多的事务,以避免锁定升级并保持每个事务的小。
答案 3 :(得分:2)
拥有单独的表是分区 - 您只需手动管理它,不获得任何管理协助或统一访问(没有视图或分区视图)。
企业版的成本是否比单独构建和维护分区方案的成本更高?
长时间运行删除的替代方法还包括使用相同的模式填充替换表,并简单地排除要删除的行,然后使用sp_rename交换表。
我不明白为什么要定期删除所有类别的明星?据推测,您正在创建新的类别,这意味着您的类别数量必须很大,并且(手动或非手动)分区将非常密集。
答案 4 :(得分:1)
也许在Stars
表上将PK设置为非群集,并在categoryid
上添加聚簇索引。
除此之外,服务器设置是否在性能最佳实践方面做得很好?这是使用单独的物理磁盘用于数据和日志,而不是使用RAID5等。
答案 5 :(得分:1)
当你说删除数百万行时“对于SQL服务器而言太强烈”,你的意思是什么?你的意思是在删除过程中日志文件增长太多了吗?
您所要做的就是批量执行批量删除:
DECLARE @i INT
SET @i = 1
WHILE @i > 0
BEGIN
DELETE TOP 10000 FROM dbo.SuperBigTable
WHERE CategoryID = 743
SELECT @i = @@ROWCOUNT
END
如果您的数据库处于完全恢复模式,则必须在此过程中运行频繁的事务日志备份,以便它可以重用日志中的空间。如果数据库处于简单模式,则不必执行任何操作。
我唯一的建议是确保在CategoryId中有适当的索引。我甚至可能会建议这是聚集索引。
答案 6 :(得分:1)
如果要优化类别,首先使用类别删除聚类复合索引可能比损坏做得更好。
你也可以描述桌子上的关系。
答案 7 :(得分:1)
听起来事务日志正在努力解决删除的问题。事务日志以单位形式增长,这需要时间,同时分配更多的磁盘空间。
虽然可以使用TRUNCATE命令截断表,但是在不登记事务的情况下无法从表中删除行。但是,这将无条件地删除表中的所有行。
我可以提出以下建议:
切换到非事务性数据库或可能是平面文件。听起来你不需要事务数据库的原子性。
尝试以下方法。每次删除x后(取决于大小)发出以下声明
使用TRUNCATE_ONLY备份日志;
这简单地截断了事务日志,剩下的空间用于重新填充日志。但是我不确定这会增加多少时间。
答案 8 :(得分:0)
你如何处理明星数据?如果您在任何给定时间只查看一个类别的数据,这可能会有效,但很难维护。每次有新类别时,都必须构建一个新表。如果您想跨类别查询,它会变得更复杂,并且在时间方面可能更昂贵。如果你这样做并且想要跨类别查询,那么视图可能是最好的(但不要在视图之上堆叠视图)。如果您正在寻找特定星的数据,您会知道要查询的表吗?如果没有,那么您将如何确定哪个表或者您要查询它们?输入数据时,应用程序将如何决定将数据放入哪个表中?有多少个类别?顺便提一下,每个人都有一个单独的身份证,使用bigint身份,并将身份与您的唯一身份识别的类别类型相结合。
你真的需要删除整个类别或只删除数据更改的星号吗? 你需要删除,也许你只需要更新信息。
您是否尝试过批量删除(循环中一次删除1000条记录)。这通常比在一个删除语句中删除一百万条记录要快得多。它通常会使表在删除期间不被锁定。
另一种技术是将记录标记为删除。然后,您可以在使用率较低时运行批处理以删除这些记录,并且您的查询可以在排除标记为删除的记录的视图上运行。
鉴于你的答案,我认为你的建议可能是合理的。
答案 9 :(得分:0)
我知道这有点像切线,但SQL Server(或任何关系数据库)真的是这项工作的好工具吗?您实际使用的是什么关系数据库功能?
如果您一次删除整个类别,则根据它不能具有很多参照完整性。数据是只读的,因此您不需要ACID进行数据更新。
听起来像是在使用基本的SELECT查询功能吗?
答案 10 :(得分:0)
只是想到了很多桌子 - 你怎么能意识到......
如何使用动态查询。
我将进行一些研究的另一个方向是使用xml类型的列来存储星星数据。这里的主要想法是,如果您需要仅按类别操作星星而不是为什么不以xml格式将所有具体类别的星星存储在表格的一个单元格中。不幸的是,我绝对无法想象出这样决定的表现。
这两种变体都与头脑风暴中的想法一样。
答案 11 :(得分:0)
正如Cade所指出的那样,为每个类别添加一个表是手动分区数据,没有统一访问的好处。
在不使用分区的情况下,数百万行的删除速度与删除表一样快,绝不会有任何删除。
因此,似乎为每个类别使用单独的表可能是一个有效的解决方案。但是,由于您已经声明保留了其中一些类别,并且删除了一些类别,因此这是一个解决方案:
这样,您将拥有有限数量的表格,具体取决于您添加类别的比率以及您决定是否需要它们的时间段。
最终,对于您保留的类别,您将工作量增加一倍,但额外的工作会随着时间的推移而分配。用户体验到聚集索引末尾的插入可能比从中间删除的插入更少。但是,对于那些你没有保留的类别,你节省了大量的时间。
即使你在技术上没有挽救工作,感知往往是更大的问题。
答案 12 :(得分:0)
我没有得到我对原帖的评论的答案,所以我会做一些假设......
这是我的想法:使用多个数据库,每个类别一个。
您可以免费使用每个Windows版本附带的managed ESE database。
使用PersistentDictionary对象,并以这种方式跟踪starid,starname对。如果需要删除类别,只需删除该类别的PersistentDictionary对象。
PersistentDictionary<int, string> starsForCategory = new PersistentDictionary<int, string>("Category1");
这将创建一个名为“Category1”的数据库,您可以在其上使用标准的.NET字典方法(add,exists,foreach等)。