Sql server 有效生成唯一随机数

Sql server 有效生成唯一随机数,sql-server,tsql,sql-server-2012,Sql Server,Tsql,Sql Server 2012,我们正在使用概述的技术生成无冲突的随机记录ID。简而言之,我们创建了一个包含每个可能ID的随机排序表,并在使用时将每个记录标记为“take” 我使用以下存储过程获取ID: ALTER PROCEDURE spc_GetId @retVal BIGINT OUTPUT AS DECLARE @curUpdate TABLE (Id BIGINT); SET NOCOUNT ON; UPDATE IdMasterList SET Taken=1 OUTPUT DELETED.

我们正在使用概述的技术生成无冲突的随机记录ID。简而言之,我们创建了一个包含每个可能ID的随机排序表,并在使用时将每个记录标记为“take”

我使用以下存储过程获取ID:

ALTER PROCEDURE spc_GetId @retVal BIGINT OUTPUT
AS
  DECLARE @curUpdate TABLE (Id BIGINT);

  SET NOCOUNT ON;
  UPDATE IdMasterList SET Taken=1 
    OUTPUT DELETED.Id INTO @curUpdate
    WHERE ID=(SELECT TOP 1 ID FROM IdMasterList WITH (INDEX(IX_Taken)) WHERE Taken IS NULL ORDER BY SeqNo);

  SELECT TOP 1 @retVal=Id FROM @curUpdate;
  RETURN;
ID的检索必须是一个原子操作,因为可以同时插入

对于大的插入(1000多万),这个过程相当慢,因为我必须通过游标遍历要插入的表

IdMasterList有一个架构:

SeqNo (BIGINT, NOT NULL) (PK) -- sequence of ordered numbers
Id (BIGINT)                   -- sequence of random numbers
Taken (BIT, NULL)             -- 1 if taken, NULL if not
IX_索引为:

CREATE NONCLUSTERED INDEX (IX_Taken) ON IdMasterList (Taken ASC)
我通常以以下方式使用ID填充表:

DECLARE @recNo BIGINT;
DECLARE @newId BIGINT;
DECLARE newAdds CURSOR FOR SELECT recNo FROM Adds
OPEN newAdds;

FETCH NEXT FROM newAdds INTO @recNo;
WHILE @@FETCH_STATUS=0 BEGIN
  EXEC spc_GetId @newId OUTPUT;
  UPDATE Adds SET id=@newId WHERE recNo=@recNo;
  FETCH NEXT FROM newAdds INTO @id;
END;

CLOSE newAdds;
DEALLOCATE newAdds;
问题:

  • 是否有任何方法可以改进SP以更快地提取ID
  • 条件指数会改善绩效吗(我还没有测试,如 IdMasterList很大吗
  • 有没有更好的方法用这些ID填充表

  • 在每个表上放置一个PK inden BigInt

    insert into user (name)  
    values ()..... 
    
    update user set = user.ID = id.ID  
      from id
      left join usr 
        on usr.PK = id.PK 
     where user.ID = null;
    
    一个


    在每个表上放置一个PK inden BigInt

    insert into user (name)  
    values ()..... 
    
    update user set = user.ID = id.ID  
      from id
      left join usr 
        on usr.PK = id.PK 
     where user.ID = null;
    
    一个


    在每个表上放置一个PK inden BigInt

    insert into user (name)  
    values ()..... 
    
    update user set = user.ID = id.ID  
      from id
      left join usr 
        on usr.PK = id.PK 
     where user.ID = null;
    
    一个


    在每个表上放置一个PK inden BigInt

    insert into user (name)  
    values ()..... 
    
    update user set = user.ID = id.ID  
      from id
      left join usr 
        on usr.PK = id.PK 
     where user.ID = null;
    
    一个


    与SQLServer中的大多数内容一样,如果您使用的是游标,那么您就错了

    由于您使用的是SQL Server 2012,因此可以使用
    序列
    跟踪您已经使用的随机值,并有效地替换
    take

    CREATE SEQUENCE SeqNoSequence
        AS bigint
        START WITH 1    -- Start with the first SeqNo that is not taken yet
        CACHE 1000;     -- Increase the cache size if you regularly need large blocks
    
    用法:

    CREATE TABLE #tmp
    (
        recNo       bigint,
        SeqNo       bigint
    )
    
    INSERT INTO #tmp (recNo, SeqNo)
        SELECT      recNo,
                    NEXT VALUE FOR SeqNoSequence
        FROM        Adds
    
    UPDATE Adds
        SET         id = m.id
        FROM        Adds            a
        INNER JOIN  #tmp            tmp     ON a.recNo = tmp.recNo
        INNER JOIN  IdMasterList    m       ON tmp.SeqNo = m.SeqNo
    

    序列是原子的。后续对SeqNoSequence的
    下一个值的调用保证返回唯一值,即使对于并行进程也是如此。请注意,
    SeqNo
    中可能会有一些空白,但对于速度的大幅提升来说,这是一个非常小的折衷。

    与SQL Server中的大多数内容一样,如果您使用的是游标,那么您就错了

    由于您使用的是SQL Server 2012,因此可以使用
    序列
    跟踪您已经使用的随机值,并有效地替换
    take

    CREATE SEQUENCE SeqNoSequence
        AS bigint
        START WITH 1    -- Start with the first SeqNo that is not taken yet
        CACHE 1000;     -- Increase the cache size if you regularly need large blocks
    
    用法:

    CREATE TABLE #tmp
    (
        recNo       bigint,
        SeqNo       bigint
    )
    
    INSERT INTO #tmp (recNo, SeqNo)
        SELECT      recNo,
                    NEXT VALUE FOR SeqNoSequence
        FROM        Adds
    
    UPDATE Adds
        SET         id = m.id
        FROM        Adds            a
        INNER JOIN  #tmp            tmp     ON a.recNo = tmp.recNo
        INNER JOIN  IdMasterList    m       ON tmp.SeqNo = m.SeqNo
    

    序列是原子的。后续对SeqNoSequence的
    下一个值的调用保证返回唯一值,即使对于并行进程也是如此。请注意,
    SeqNo
    中可能会有一些空白,但对于速度的大幅提升来说,这是一个非常小的折衷。

    与SQL Server中的大多数内容一样,如果您使用的是游标,那么您就错了

    由于您使用的是SQL Server 2012,因此可以使用
    序列
    跟踪您已经使用的随机值,并有效地替换
    take

    CREATE SEQUENCE SeqNoSequence
        AS bigint
        START WITH 1    -- Start with the first SeqNo that is not taken yet
        CACHE 1000;     -- Increase the cache size if you regularly need large blocks
    
    用法:

    CREATE TABLE #tmp
    (
        recNo       bigint,
        SeqNo       bigint
    )
    
    INSERT INTO #tmp (recNo, SeqNo)
        SELECT      recNo,
                    NEXT VALUE FOR SeqNoSequence
        FROM        Adds
    
    UPDATE Adds
        SET         id = m.id
        FROM        Adds            a
        INNER JOIN  #tmp            tmp     ON a.recNo = tmp.recNo
        INNER JOIN  IdMasterList    m       ON tmp.SeqNo = m.SeqNo
    

    序列是原子的。后续对SeqNoSequence的
    下一个值的调用保证返回唯一值,即使对于并行进程也是如此。请注意,
    SeqNo
    中可能会有一些空白,但对于速度的大幅提升来说,这是一个非常小的折衷。

    与SQL Server中的大多数内容一样,如果您使用的是游标,那么您就错了

    由于您使用的是SQL Server 2012,因此可以使用
    序列
    跟踪您已经使用的随机值,并有效地替换
    take

    CREATE SEQUENCE SeqNoSequence
        AS bigint
        START WITH 1    -- Start with the first SeqNo that is not taken yet
        CACHE 1000;     -- Increase the cache size if you regularly need large blocks
    
    用法:

    CREATE TABLE #tmp
    (
        recNo       bigint,
        SeqNo       bigint
    )
    
    INSERT INTO #tmp (recNo, SeqNo)
        SELECT      recNo,
                    NEXT VALUE FOR SeqNoSequence
        FROM        Adds
    
    UPDATE Adds
        SET         id = m.id
        FROM        Adds            a
        INNER JOIN  #tmp            tmp     ON a.recNo = tmp.recNo
        INNER JOIN  IdMasterList    m       ON tmp.SeqNo = m.SeqNo
    

    序列是原子的。后续对SeqNoSequence的
    下一个值的调用保证返回唯一值,即使对于并行进程也是如此。请注意,
    SeqNo
    中可能存在差距,但对于速度的大幅提升来说,这只是一个很小的折衷。

    我想到的一些想法:

    • 如果删除顶部、内部选择等有助于提高ID获取的性能,请尝试(查看统计io和查询计划):

    • 将索引更改为过滤索引,因为我假设您不需要获取所获取的数字。如果我没记错的话,您不能对空值执行此操作,因此您需要将take更改为0/1

    • 你到底有什么问题?获取单个ID还是1000多万个ID?CPU/I/O等问题是由游标和ID获取逻辑引起的,还是并行进程被其他进程阻塞

    • 使用sequence对象获取SeqNo。然后使用idMasterList返回的值从idMasterList中获取Id。如果IdMasterList序列中没有空白,这可能会起作用

    • 使用readpass提示可以帮助阻塞,对于CPU/I/O问题,您应该尝试优化SQL

    • 如果原因纯粹是表是一个热点,而且似乎没有其他简单的解决方案可以提供帮助,请将其拆分为多个表,并使用某种简单的逻辑(甚至是@spid、rand()或类似的逻辑)来决定应从哪个表获取ID。如果所有表都有空闲的数字,您需要进行更多的检查,但情况应该不会那么糟

    • 创建不同的过程(甚至表格)来处理单个ID、数百个ID和数百万个ID的获取


      • 我脑子里有几个想法:

        • 如果删除顶部、内部选择等有助于提高ID获取的性能,请尝试(查看统计io和查询计划):

        • 将索引更改为过滤索引,因为我假设您不需要获取所获取的数字。如果我没记错的话,您不能对空值执行此操作,因此您需要将take更改为0/1

        • 你到底有什么问题?获取单个ID还是1000多万个ID?CPU/I/O等问题是由游标和ID获取逻辑引起的,还是并行进程被其他进程阻塞

        • 使用sequence对象获取SeqNo。然后使用idMasterList返回的值从idMasterList中获取Id。如果IdMasterList序列中没有空白,这可能会起作用

        • 使用readpass提示可以帮助阻塞,对于CPU/I/O问题,您应该尝试优化SQL

        • 如果原因纯粹是表格成为热点,而且似乎没有其他简单的解决方案可以提供帮助,那么拆分