小编典典

SQL Server有效地删除具有数百万行的行组

sql

我有一张表的以下布局:

表格:Star
starId bigint
类别
Id 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企业超出了他们的预算。


阅读 193

收藏
2021-04-15

共1个答案

小编典典

我的问题是,SQL Server中有成千上万个表是否有问题?

是的。在SQL Server中拥有这么多的表是一个 巨大的 问题。SQL
Server必须将每个对象作为元数据进行跟踪,并且一旦包含索引,引用约束,主键,默认值等,那么您正在谈论 数百万 个数据库对象。

尽管从理论上讲SQL Server可能能够处理2 32个对象,但是请放心,它会比负载下更快地开始在负载下屈曲。

如果数据库没有崩溃,那么您的开发人员和IT员工几乎肯定会崩溃。当我看到一千多张桌子时,我会感到紧张;给我看一个有 成千上万
个数据库的数据库,我会大喊大叫。

创建数十万张表作为穷人的分区策略将消除您执行以下任一操作的能力:

  • 编写有效的查询(您如何选择SELECT多个类别?)
  • 维护唯一身份(如您所知)
  • 保持引用完整性(除非您喜欢管理300,000个外键)
  • 执行远程更新
  • 编写干净的应用程序代码
  • 保持任何历史
  • 加强适当的安全性(似乎很明显,用户必须能够启动这些创建/删除操作-非常危险)
  • 正确缓存-100,000个表意味着100,000个不同的执行计划都在争用同一内存,而您可能没有足够的内存。
  • 雇用一个DBA(因为放心,他们将在看到您的数据库后立即退出)。

另一方面,在单个 表中 包含数十万 甚至 数百万完全不是 问题,这是设计使用SQL Server和其他SQL
RDBMS的方式,它们非常好-针对这种情况进行了优化。 __

O(1)的下降对我来说是非常理想的。也许我没有想到一个完全不同的解决方案?

按优先顺序,数据库中性能问题的典型解决方案是:

  • 运行探查器以确定查询中最慢的部分;
  • 如有可能,改进查询(即通过消除不可保留的谓词);
  • 规范化或添加索引以消除这些瓶颈;
  • 必要时进行非规范化(通常不适用于删除);
  • 如果涉及级联约束或触发器,请在事务期间禁用那些约束或触发器,然后手动断开级联。

但这里的现实是,你 并不 需要 一个“解决方案”。

在SQL Server数据库中,“数百万行”不是很多。这是 非常快 ,只需你希望从删除的列索引中删除从数以百万计的表几千行-
在这种情况下CategoryID。SQL Server可以毫不费力地做到这一点。

实际上,删除通常具有O(M log N)复杂度(N =行数,M =要删除的行数)。为了获得O(1)删除时间,您首先要牺牲SQL
Server提供的几乎所有好处。

O(M log N)可能不如O(1)快,但是您正在谈论的减速类型(需要删除几分钟) 必须
有第二个原因。这些数字并不累加,为了证明这一点,我继续进行了工作,并制定了一个基准:


表格架构:

CREATE TABLE Stars
(
    StarID int NOT NULL IDENTITY(1, 1)
        CONSTRAINT PK_Stars PRIMARY KEY CLUSTERED,
    CategoryID smallint NOT NULL,
    StarName varchar(200)
)

CREATE INDEX IX_Stars_Category
ON Stars (CategoryID)

请注意,该架构甚至没有真正针对DELETE操作进行优化,它是您在SQL
Server中可能看到的相当普通的表架构。如果该表没有关系,则我们不需要代理键或聚簇索引(或者我们可以将聚簇索引放在类别上)。我稍后再讲。

样本数据:

这将使用500个类别填充1000万行的表格(即每个类别的基数为1:20,000)。您可以调整参数以更改数据量和/或基数。

SET NOCOUNT ON

DECLARE
    @BatchSize int,
    @BatchNum int,
    @BatchCount int,
    @StatusMsg nvarchar(100)

SET @BatchSize = 1000
SET @BatchCount = 10000
SET @BatchNum = 1

WHILE (@BatchNum <= @BatchCount)
BEGIN
    SET @StatusMsg =
        N'Inserting rows - batch #' + CAST(@BatchNum AS nvarchar(5))
    RAISERROR(@StatusMsg, 0, 1) WITH NOWAIT

    INSERT Stars2 (CategoryID, StarName)
        SELECT
            v.number % 500,
            CAST(RAND() * v.number AS varchar(200))
        FROM master.dbo.spt_values v
        WHERE v.type = 'P'
        AND v.number >= 1
        AND v.number <= @BatchSize

    SET @BatchNum = @BatchNum + 1
END

设定档指令码

他们中最简单的…

DELETE FROM Stars
WHERE CategoryID = 50

结果:

这是在运行 5年的旧工作站计算机 ,IIRC,32位双核AMD Athlon和便宜的7200 RPM SATA驱动器上进行的测试。

我使用不同的CategoryID运行了10次测试。最慢的时间(冷高速缓存)约为5秒。最快的时间是1秒。

也许不如简单地删除表那样快,但是与您提到的多分钟删除时间相去甚远。请记住,这甚至还不是在一台像样的机器上!

但是我们可以做得更好…

关于您问题的一切都暗示这些数据 是不相关的 。如果没有关系,则不需要代理键,并且可以摆脱其中一个索引,将聚簇索引移至该CategoryID列。

现在,通常,非唯一/非顺序列上的聚集索引不是一个好习惯。但是我们只是在这里进行基准测试,因此无论如何我们都会这样做:

CREATE TABLE Stars
(
    CategoryID smallint NOT NULL,
    StarName varchar(200)
)

CREATE CLUSTERED INDEX IX_Stars_Category
ON Stars (CategoryID)

在此上运行相同的测试数据生成器(导致令人难以置信的页面拆分数量),并且相同的删除平均仅花费 62毫秒
,而从冷缓存(异常值)则花费190。作为参考,如果将索引设置为非聚集索引(根本没有聚集索引),则删除时间最多只能平均达到606毫秒。

结论:

如果您看到 几分钟 甚至 几秒钟的 删除时间,那么这是 非常非常错误的

可能的因素有:

  • 统计信息不是最新的(这里不应该是问题,但如果是,请运行sp_updatestats);

  • 缺乏索引(尽管奇怪的是,IX_Stars_Category在第一个示例中删除索引实际上导致 更快的 整体删除,因为聚集索引扫描比非聚集索引删除要快);

  • 选择的数据类型不正确。如果您只有 数百万 行,而不是 数十亿 行,则您不需要在bigintStarID。您 绝对 不需要CategoryID-如果您的类别少于32,768,则甚至可以使用smallint。每行中不必要数据的每个字节都会增加I / O成本。

  • 锁定争用。也许问题根本就不是删除速度;也许其他一些脚本或进程Star在行上持有锁,而DELETE只是坐在那里等待它们放开。

  • 极其 硬件差。我能够在一台非常糟糕的计算机上运行此数据库,而没有任何问题,但是如果您在90年代的Presario或其他类似的计算机上运行此数据库,而该计算机以前根本不适合托管SQL Server实例,并且该数据库负载很重,那么您显然会遇到问题。

  • 您的示例中未包含的非常昂贵的外键,触发器,约束或其他数据库对象,可能会增加很高的成本。您的执行计划应该清楚地表明这一点(在上面的优化示例中,它只是一个“聚簇索引删除”)。

老实说,我认为没有其他可能性。在SQL Server中删除 并不慢


如果您能够运行这些基准测试并看到与我所见(或更好)大致相同的性能,则意味着问题出在数据库设计和优化策略上,而不是SQL
Server或删除的渐进复杂性上。
如果 仍然 不能解决您的问题,那么我可以提供以下其他建议:

  • 升级到SQL Server 2008,这将为您提供大量压缩选项,可以极大地提高I / O性能。

  • 考虑将每个类别的Star数据预压缩为紧凑的序列化列表(使用BinaryWriter.NET中的类),并将其存储在varbinary列中。这样,每个类别可以有一行。这违反了1NF规则,但是由于无论如何您似乎都不会对Star数据库中的单个数据进行任何处理,因此我怀疑您会损失很多。

  • 考虑使用非关系数据库或存储格式,例如db4oCassandra。而不是实施已知的数据库反模式(臭名昭著的“数据转储”),而应使用专门为这种存储和访问模式设计的工具。

2021-04-15