当前位置: 首页 > 面试题库 >

在不锁定表的情况下插入大量记录

敖子安
2023-03-14
问题内容

我试图将 1,500,000 条记录插入表中。在插入过程中遇到表锁定问题。所以我想出了下面的批处理插入。

DECLARE @BatchSize INT = 50000

WHILE 1 = 1
  BEGIN
      INSERT INTO [dbo].[Destination] 
                  (proj_details_sid,
                   period_sid,
                   sales,
                   units)
      SELECT TOP(@BatchSize) s.proj_details_sid,
                             s.period_sid,
                             s.sales,
                             s.units
      FROM   [dbo].[SOURCE] s
      WHERE  NOT EXISTS (SELECT 1
                         FROM   dbo.Destination d
                         WHERE  d.proj_details_sid = s.proj_details_sid
                                AND d.period_sid = s.period_sid)

      IF @@ROWCOUNT < @BatchSize
        BREAK
  END

我在Destination表上有聚簇索引(proj_details_sid ,period_sid )NOT EXISTS部分只是为了限制插入的记录再次插入表中

我做对了,这样可以避免表锁定吗?还是有更好的方法。

注意: 批处理和不批处理插入所花费的时间或多或少相同


问题答案:

锁升级根本不可能与SELECT语句的一部分有关。

插入大量行是自然的结果

当使用ALTER TABLE SET LOCK_ESCALATION选项未在表上禁用锁升级时,并且存在以下任一情况时,将触发锁升级:

  • 单个Transact-SQL语句在单个未分区表或索引上获取至少5,000个锁。
  • 单个Transact-SQL语句在分区表的单个分区上获取至少5,000个锁,并且ALTER TABLE SET
    LOCK_ESCALATION选项设置为AUTO。
  • 数据库引擎实例中的锁数超过了内存或配置阈值。

如果由于锁冲突而无法升级锁,则数据库引擎会在每获取1,250个新锁时定期触发锁升级。

您可以通过在Profiler中跟踪锁升级事件,或简单地尝试以下具有不同批处理大小的事件来轻松地自己查看此情况。对我来说,TOP (6228)显示了6250个锁,但是TOP (6229)随着锁升级的来临,它突然下降到1。确切的数字可能会有所不同(取决于数据库设置和当前可用的资源)。使用反复试验来找到为您显示锁升级的阈值。

CREATE TABLE [dbo].[Destination]
  (
     proj_details_sid INT,
     period_sid       INT,
     sales            INT,
     units            INT
  )

BEGIN TRAN --So locks are held for us to count in the next statement
INSERT INTO [dbo].[Destination]
SELECT TOP (6229) 1,
                  1,
                  1,
                  1
FROM   master..spt_values v1,
       master..spt_values v2

SELECT COUNT(*)
FROM   sys.dm_tran_locks
WHERE  request_session_id = @@SPID;

COMMIT

DROP TABLE [dbo].[Destination]

您要插入50,000行,因此几乎可以肯定,将尝试进行锁升级。

文章如何解决由SQL Server中的锁升级引起的阻塞问题已经很老了,但是许多建议仍然有效。

  1. 将大型批处理操作分成几个较小的操作(即使用较小的批处理大小)
  2. 如果其他SPID当前持有不兼容的表锁,则不会发生锁升级-他们给出的示例是正在执行的其他会话
BEGIN TRAN
SELECT * FROM mytable (UPDLOCK, HOLDLOCK) WHERE 1=0
WAITFOR DELAY '1:00:00'
COMMIT TRAN
  1. 通过启用跟踪标志1211来禁用锁定升级-但是,这是全局设置,可能会导致严重的问题。存在较新的选项1224,该选项的问题较少,但这仍然是全球性的。

另一个选择是,ALTER TABLE blah SET (LOCK_ESCALATION = DISABLE)但这仍然不是很针对性,因为它会影响针对该表的所有查询,而不仅仅是这里的单个方案。

因此,我会选择选项1或可能的选项2,并打折其他选项。



 类似资料:
  • 问题内容: 我想将实时生产数据库复制到本地开发数据库中。有没有一种方法可以不锁定生产数据库? 我目前正在使用: 但是它在运行时锁定每个表。 问题答案: 该选项有效吗? 根据手册页,如果要转储InnoDB表,则可以使用以下选项: 对于 innodb DB :

  • 问题内容: 在SQL(SQL Server)中,是否可以从表的标识列中检索下一个ID(整数),而实际上无需插入行?如果删除了最近的行,则不一定是最高ID加1。 我之所以这样问,是因为我们有时不得不用新行更新活动数据库。该行的ID在我们的代码中使用(例如,Switch(ID){Case ID:},并且必须相同。如果我们的开发数据库和实时数据库不同步,最好预先预测一个行ID部署之前。 我当然可以 SE

  • 我试图使用实体类和实体管理器将一条记录插入数据库(MySQL)。但是其中一个字段是自动递增的主键,所以除非我手动提供一个值,否则插入是不成功的。 如何解决这样的问题?是否有一种方法告诉实体管理器尝试不带ID字段的插入,而使用值。 更新:下面是定义的实体类的一部分

  • 下面是将作者姓名添加到表中的部分代码。 我的其余代码与我认为的问题无关,因此我没有包括它。 我有一个很长的作者名列表,每次读取一个名称时,我都会尝试将其写入作者表。但是,有些名称是重复的,这是一个问题,因为name是主键,所以我无法插入重复的名称。有人知道只插入表中不存在的名称的查询吗?我正在通过jupyter笔记本使用python 2.7和sqlite,以防有人需要知道。 我意识到这个网站上已经

  • 问题内容: 我正在使用hibernate+播放!框架在工作中,是否有使用“hibernate”插入大量记录的“最佳实践”?每个文本文件大约有6,000到10,000个,所以我不知道Hibernate是否会扼杀工作或引发异常。 任何建议都让我知道,如果需要进一步说明,请告诉我 问题答案: 在* Java Persistence and Hibernate(Manning)中,并在Pangea的评论之

  • 我有一个关于Hibernate中乐观锁定的问题。我正试图深入乐观地锁定Hibernate,但我有一个疑问。Hibernate使用版本方法(整数或时间戳)来实现乐观锁定。要进行配置,可以使用@Version注释(或xml配置)并创建版本属性。另一个选项是在不使用乐观lock=“all”属性进行版本控制的情况下进行配置。 我的问题是,如果你没有定义任何版本属性,也没有指定乐观锁属性,在这种情况下,哪种