我有一个单列MySQL数据库表ids
(id INTEGER PRIMARY KEY AUTOINCREMENT),我按升序存储预先生成的唯一ID。为了从该表中获取随机ID,我使用此查询:
SELECT id FROM ids ORDER BY RAND() LIMIT 1;
现在我想知道如何确保我获得的id永远不会被再次使用。我看到两个选择。一种是从表中删除id,另一种是添加跟踪该id的使用的列:
DELETE FROM ids WHERE id=?; //where id is the one I got from the previous query
或
SELECT id FROM ids WHERE used=0 ORDER BY RAND() LIMIT 1;
UPDATE ids SET used=1 WHERE id=?; //where used is new column with 0 as default value
这两个问题只有轻微的问题。如果服务器负载很重,那么对于随机id的两个查询可能会在从列表中删除之前返回相同的id(或者使用已使用的列禁用)。
交易有帮助吗?
答案 0 :(得分:1)
在交易中包装您的选择和更新将有效。如果您想要避免交易以及选择项目并将其标记为无法使用之间的竞争条件,您可以先运行UPDATE
。您需要一种方法让每个进程将自己标识为声明它和删除之间的行的所有者。例如,假设您的ids
架构是
id
(整数)owner
(字符串)让每个进程选择一个UUID(或其他适当的唯一)并运行以下命令:
UPDATE ids SET owner = $process_id WHERE owner IS NULL ORDER BY RAND() LIMIT 1
SELECT id FROM ids WHERE owner = $process_id
DELETE FROM ids WHERE id = $selected_id
(或以其他方式标记使用)步骤1原子地声明流程的行,以便其他流程无法声明它。第2步提取所声明的ID。步骤3从可用集中删除ID以获得良好。如果第3步没有删除该行,只需将其标记为已使用,请确保同时清除owner
,以便稍后您的流程不会再次选择。
答案 1 :(得分:1)
你可以尝试在选择之前更新id 1st。试试这个。
-- get firstID
SELECT @MinID:=id FROM ids ORDER by id ASC LIMIT 1;
-- get last id
SELECT @MaxID:=id FROM ids ORDER by id DESC LIMIT 1;
-- get random id
SELECT @rndomID :=ROUND((RAND() * (@MaxID-@MinID))+@MinID);
-- update first
UPDATE ids SET used=1 WHERE id=@rndomID;
-- select the id
SELECT id FROM ids WHERE used=0 WHERE id=@rndomID;
答案 2 :(得分:0)
在ids
表格中添加一个额外的列。我们先说selected
。在随机查询中生成此列时,您将更新此列。
1
表示已选中,0
表示尚未。
例如
SELECT id FROM ids ORDER BY RAND() WHERE selected = 0 LIMIT 1;
$id = $row['id']; /* STORE THE SELECTED ID TO A VARIABLE */
然后用
更新表格UPDATE ids SET selected = 1 WHERE id = $id
因此,您下次运行的随机查询只会获得所选0
值的行,而不是1
值的行。