【问题标题】:Insert a row into a SQL Server table only if total count is less than a threshold仅当总计数小于阈值时才将行插入 SQL Server 表
【发布时间】:2014-10-19 16:53:10
【问题描述】:

我使用的是 SQL Server 2012。

我有存储用户的表。允许的最大用户数为 100 万。我有一个 sproc,我在其中注册用户。当我插入时,我想确保 users 表中的总行数不会超过 100 万。我更喜欢使用允许最大并发的方法。

我相信我可以使用隔离级别为 SERIALIZABLE 的事务,然后首先计算行数,如果总计数小于 100 万则插入。我的理解是 SERIALIZABLE 限制性很大,会随着并发的增加而导致性能下降。

IF(SELECT COUNT(*) FROM Users) < 100000
BEGIN
    INSERT INTO Users VALUES (@Name, @Email, @Password)
END

如何以原子方式执行此操作,以确保总行数少于 100 万,但同时进行最小锁定以防止阻塞其他事务?

有针对这种情况的解决方案/最佳做法吗?

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    您可以创建SELECT 来检查用户数并检索新用户的数据并使用该选择插入表中:

    INSERT INTO users (
      name, email, [password]
    )
    SELECT
      'newUser' AS name
      , 'email@example.com' AS email
      , 'fsfsfs' AS [password]
    FROM
      users
    HAVING
      COUNT(*) < 100000;
    

    SELECT 语句在数据库中用户少于 100000 时返回一行,在用户超过 100000 时返回 0 条记录。

    整个语句(INSERT..SELECT)在每种情况下都有效,但是当SELECT部分返回0行时,INSERT不会插入任何内容。

    使用SERIALIZABLE事务级别,保证并发写入不会互相干扰。 COUNT(*) 使用最有效的索引/键来计算行数,这意味着锁定时间将是最短的。

    由于整个操作在一个语句中完成,这可以防止在 SELECT 和 INSERT 的执行之间插入

    SQL Fiddle demo

    【讨论】:

    • 哇。这是对这种形式的一种非常聪明的“滥用”——我印象深刻。标记它以在我需要时记住它。很好的答案。
    • @Pred - 我是否需要使用 SERIALIZABLE 隔离级别才能正常工作?
    • 如果可序列化过于严格,REPEATABLE READ 可能就足够了。在现实世界的场景中,当用户长时间注册时,这并不重要,所以它可能是READ COMMITTED,但是如果你的应用程序的这部分需要这个功能并且它应该尽可能严格地工作,比 DBE 在运行此代码时应该以最严格的模式工作。
    • 我想知道SERIALIZABLE 是否不是必要的,以“防止在SELECT 和INSERT 的执行之间插入”。这个问答似乎矛盾:stackoverflow.com/questions/1994771/…
    • 好吧,TABLOCKX 将在表上放置一个排他锁,SERIALIZABLE 也会这样做,因为我们没有过滤器,因此整个表将被使用并锁定直到事务结束.这类似于TABLOCKX、HOLDLOCK 对。此外,如果我没记错的话,HOLDLOCK 已被弃用。
    猜你喜欢
    • 1970-01-01
    • 2019-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多