SQL Server 为大量插入调整表

AK_*_*AK_ 7 sql-server-2008 sql-server

我正在尝试解决性能问题,我需要建议。

我有一张桌子,每天大约有两次插入大量插入的内容,同时每个人都试图从中读取。所以我有锁,一切都变得非常缓慢。

我的性能要求如下:

  1. 许多作家,来自不同的来源,我更喜欢每一个写作都是原子的。
  2. 我不关心写入的顺序,数字 7 可以在数字 3 之前出现。
  3. 写应该尽快结束,我希望作者等待尽可能少的时间。
  4. 数据永远不会更新,只会插入。
  5. 我有很多很多读者。
  6. 读者希望在阅读时看到最新的快照。
  7. 我可以忍受读者看到延迟数据(10-20 秒)
  8. 我偶尔需要对这些数据进行复杂的大型查询,所以我需要几个索引。

您将如何解决这个问题,您将如何使用 SQL Server 实现这一点?


我正在考虑将其拆分为 2 个表......一个用于插入,一个用于读取,一个工人移动数据。

大型查询只会在第二个上运行,需要更新数据的读者可以在第二个上运行,并从第一个完成他们需要的内容。

Aar*_*and 5

我使用的一种解决方案是将写入线程化到多个临时表,然后批量收集数据。我们将暂存表放在它们自己的文件组中以尝试尽可能地隔离 I/O,尽管我们最终在每个可用的 LUN 上有两个表,而不是每个表都有自己的专用 I/O。

由于您说写入顺序无关紧要,这可能是可行的,但我不知道 20 秒是否足以使这项工作正常工作并且仍然不会遇到一堆阻塞。

因此,假设我们有一个像这样的基表,每个人都从这里读取(并且当前正在写入):

CREATE TABLE dbo.StockData
(
  StockID INT,
  ...other info...
);
Run Code Online (Sandbox Code Playgroud)

然后我们有这样的临时表,我们将在其中编写:

CREATE TABLE staging.StockData0
(
  StockID INT,
  ...other info...
);

CREATE TABLE staging.StockData1
(
  StockID INT,
  ...other info...
);

... up to StockData9
Run Code Online (Sandbox Code Playgroud)

*不要费心模仿这些临时表上的所有非聚集索引、约束等,

然后假设您通过存储过程进行单例插入,将存储过程更改为使用动态 SQL 并根据StockID值插入 10 个表之一:

DECLARE @sql NVARCHAR(MAX) = N'INSERT staging.StockData'
  + CONVERT(CHAR(1), @StockID % 10 + '(StockID, cols...)
    SELECT @StockID, @params...;';

EXEC sp_executesql @sql, N'@StockID INT, @params...', @StockID, @params...;
Run Code Online (Sandbox Code Playgroud)

然后有一个后台作业执行非常快速的元数据操作,将这些表移动到一个虚拟模式中 - 这最大限度地减少了在您将这些数据移动到真实表中时阻止用户写入的时间(您现在可以控制比来自各地的零星写入要好得多)。因此,首先在不同的模式中创建表的第二个副本:

CREATE SCHEMA shadow  AUTHORIZATION dbo;
CREATE SCHEMA holding AUTHORIZATION dbo;
GO

CREATE TABLE shadow.StockData0
(
  StockID INT,
  ...other info...
);

-- repeat for 1-9
Run Code Online (Sandbox Code Playgroud)

现在工作将这样做:

TRUNCATE TABLE shadow.StockData0;

BEGIN TRANSACTION;

  ALTER SCHEMA holding TRANSFER staging.StockData0;
  ALTER SCHEMA staging TRANSFER shadow.StockData0;

COMMIT TRANSACTION;

ALTER SCHEMA shadow TRANSFER holding.StockData0;

-- repeat for 1-9 (I actually used dynamic SQL in a loop
-- so I wouldn't have to repeat myself ten times...
Run Code Online (Sandbox Code Playgroud)

我在这里这里写了关于这部分的博客。

然后,该作业将继续向主表中插入批次,中间有延迟,以允许进行一些读取。

BEGIN TRANSACTION;

  INSERT dbo.StockData(StockID, ...cols...)
    SELECT StockID, ...cols...
    FROM holding.StockData0;

COMMIT TRANSACTION;

WAITFOR DELAY '00:00:01';

-- repeat for 1-9
Run Code Online (Sandbox Code Playgroud)

这是几年前的事情,当时我没有想到的事情(我现在也记得,在看到 wBob 发布的另一个链接后)是TABLOCK在这些事务中使用可能有助于加快插入速度(尽管您' 将要根据音量、隔离级别等进行测试)。

启用Read Committed Snapshot Isolation和/或如果您使用企业版考虑分区(并将临时表与这些分区对齐)也可能很有用(即使完成所有这些工作)。