我们正在使用概述here的技术生成无冲突的随机记录ID。简而言之,我们创建一个包含每个可能ID的随机排序表,并在使用时将每个记录标记为“Taken”。
我使用以下存储过程来获取ID:
$statsTable = "someTable";
$userTable = "someOtherTable";
$someData = "SELECT stats.* FROM $statsTable stats, $userTable user
WHERE user.some_status = '0'
AND (stats.some_value BETWEEN $rangeFrom AND $rangeTo)
ORDER BY stats.some_value ASC
LIMIT 0,10";
then mysqli_query and so on...
ID的检索必须是原子操作,因为可以同时插入。
对于大型插入(1000万以上),这个过程非常慢,因为我必须通过游标来传递表格。
IdMasterList有一个架构:
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;
IX_Taken索引是:
SeqNo (BIGINT, NOT NULL) (PK) -- sequence of ordered numbers
Id (BIGINT) -- sequence of random numbers
Taken (BIT, NULL) -- 1 if taken, NULL if not
我通常以这种方式用Ids填充表格:
CREATE NONCLUSTERED INDEX (IX_Taken) ON IdMasterList (Taken ASC)
问题:
答案 0 :(得分:1)
在每张桌子上放置BigInt的PK inden
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;
一个
insert into user (name) value ("justsaynotocursor");
set @PK = select select SCOPE_IDENTITY();
update user set ID = (select ID from id where PK = @PK);
答案 1 :(得分:1)
与SQL Server中的大多数内容一样,如果您使用游标,那么您做错了。
由于您使用的是SQL Server 2012,因此可以使用SEQUENCE
跟踪已使用的随机值,并有效地替换Taken
列。
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
SEQUENCE
是原子的。对NEXT VALUE FOR SeqNoSequence
的后续调用保证返回唯一值,即使对于并行进程也是如此。请注意SeqNo
可能存在差距,但对于速度的大幅提升,这是一个非常小的折衷。
答案 2 :(得分:1)
我想到了很少的想法:
尝试删除顶部,内部选择等有助于提高ID提取的性能(查看统计信息和查询计划):
UPDATE top(1) IdMasterList
SET @retVal = Id, Taken=1
WHERE Taken IS NULL
将索引更改为过滤索引,因为我假设您不需要获取已获取的数字。如果我没记错的话,你不能为NULL值做这个,所以你需要把Taken改为0/1。
你的问题究竟是什么?获取单个ID或10多万个ID?是否由光标和光盘引起的CPU / I / O等问题? ID获取逻辑,还是并行进程被其他进程阻止?
使用序列对象获取SeqNo。然后使用它返回的值从idMasterList中获取Id。如果您在IdMasterList序列中没有空白,这可能会有效。
使用READPAST提示可以帮助阻止,对于CPU / I / O问题,您应该尝试优化SQL。
如果原因纯粹是表格是热点,并且没有其他简单的解决方案似乎有帮助,请将其拆分为多个表并使用某种简单的逻辑(甚至@@ spid,rand()或类似的东西)决定从哪个表中获取ID。您需要更多检查所有表格是否都有免费号码,但它不应该那么糟糕。
创建不同的程序(甚至表格)来处理单个ID,数百个ID和数百万个ID的提取。