簡體   English   中英

SELECT隨機ID的SQL優化(帶有WHERE子句)

[英]SQL Optimization on SELECT random id (with WHERE clause)

我目前正在研究多線程程序(Java),該程序需要選擇數據庫中的隨機行以更新它們。 這運作良好,但是我開始遇到有關我的SELECT請求的性能問題。

在找到此網站之前,我嘗試了多種解決方案:

http://jan.kneschke.de/projects/mysql/order-by-rand/

我嘗試了以下解決方案:

SELECT * FROM Table 
JOIN (SELECT FLOOR( COUNT(*) * RAND() ) AS Random FROM Table) 
AS R ON Table.ID > R.Random 
WHERE Table.FOREIGNKEY_ID IS NULL 
LIMIT 1;

它僅在生成的隨機ID號下方選擇一行。 這工作得很好(在15萬行上,每個請求平均不到100毫秒)。 但是,在我的程序執行完之后,FOREIGNKEY_ID將不再為NULL(它將使用某些值進行更新)。

問題是,我的SELECT將“忘記”某些行,而該行的ID低於隨機生成的ID,而我將無法對其進行處理。

所以我試圖適應我的要求,這樣做:

SELECT * FROM Table 
JOIN (SELECT FLOOR( 
(SELECT COUNT(id) FROM Table WHERE FOREIGNKEY_ID IS NULL) * RAND() ) 
AS Random FROM Table) 
AS R ON Table.ID > R.Random
WHERE Table.FOREIGNKEY_ID IS NULL 
LIMIT 1;

有了該請求,不再有跳過某些行的問題,但是性能急劇下降(在15萬行中,每個請求平均1s)。

當我仍然有很多行要處理時,我可以簡單地執行快速操作,而當它只剩下幾行時,切換到慢速操作,但這將是代碼中的“臟”修復程序,我希望使用可以完成工作的優雅的SQL請求。

感謝您的幫助,如果我不清楚或需要更多詳細信息,請告訴我。

您的ID可能包含空白。 任何與COUNT(*)一起使用的東西都將無法找到所有ID。

ID為1,2,3,10,11,12,13記錄的表只有7條記錄。 COUNT(*)進行隨機操作通常會導致未命中,因為記錄4,5和6不存在,然后它將選擇最接近的ID 3 這不僅不平衡(它經常會頻繁選擇3 ),而且也永遠不會選擇10-13個記錄。

為了獲得公平統一分配的記錄隨機選擇,我建議先加載表的ID。 即使對於15萬行,加載一組integer ID也不會消耗大量內存(<1 MB):

SELECT id FROM table;

然后,您可以使用Collections.shuffle類的函數來隨機化ID的順序。 要獲取其余數據,您可以一次選擇一個記錄,例如一次選擇10個:

SELECT * FROM table WHERE id = :id

要么:

SELECT * FROM table WHERE id IN (:id1, :id2, :id3)

如果id列具有索引,這應該很快,並且它將為您提供適當的隨機分布。

為了使方法更通用,您需要max(id)而不是count(*)

SELECT t.*
FROM Table t JOIN
     (SELECT FLOOR(MAX(id) * RAND() ) AS Random FROM Table) r
     ON t.ID > R.Random 
WHERE t.FOREIGNKEY_ID IS NULL 
ORDER BY t.ID
LIMIT 1;

通常會添加ORDER BY ,以確保返回“下一個” ID。 從理論上講,MySQL總是可以返回表中的最大id。

問題是id之間的差距。 而且,創建從未獲得隨機數的分布很容易。 說,四個ID是1231000 您的方法永遠不會得到1000000 以上幾乎總是可以得到的。

可能最簡單的解決方案是多次運行第一個查詢,直到獲得有效的行。 下一個建議是(FOREIGNKEY_ID, ID)上的索引,子查詢可以使用該索引。 這可能會加快查詢速度。

我傾向於以下方面:

SELECT t.id
FROM Table t 
WHERE t.FOREIGNKEY_ID IS NULL AND
      RAND() < 1.0 / 1000
ORDER BY RAND()
LIMIT 1;

WHERE子句的目的是減少可觀的體積,因此ORDER BY不需要花費很多時間。

不幸的是,這將需要掃描表,因此您可能不會在150k的表上獲得100毫秒范圍內的響應。 您可以將其減少為使用t(FOREIGNKEY_ID, ID)上的索引進行索引掃描。

編輯:

如果您想要一個合理的機會來實現均勻分布並且性能不會隨着表的增大而增加,那么這是另一個想法,可惜,這需要觸發器。

向表中添加一個名為random的新列,該列使用rand()初始化. Build an index on . Build an index on隨機. Build an index on 然后運行查詢,例如:

select t.*
from ((select t.*
       from t
       where random >= @random
       order by random
       limit 10 
      ) union all
      (select t.*
       from t
       where random < @random
       order by random desc
       limit 10 
      )
     ) t
order by rand();
limit 1;

這個想法是,子查詢可以使用索引來選擇一組20條非常隨意的行-在所選點的前后10條。 然后對行進行排序(一些開銷,您可以使用limit數進行控制)。 這些被隨機化並返回。

這個想法是,如果您選擇隨機數,將會有任意的間隔,並且這些間隔會使所選的數字不太統一。 但是,通過在該值附近進行更大的采樣,則選擇任何一個值的可能性都應接近均勻分布。 均勻性仍然會產生邊緣效應,但是在大量數據上這些效應應該很小。

如果可以使用准備好的語句,那么這應該起作用:

SELECT @skip := Floor(Rand() * Count(*)) FROM Table WHERE FOREIGNKEY_ID IS NULL;
PREPARE STMT FROM 'SELECT * FROM Table WHERE FOREIGNKEY_ID IS NULL LIMIT ?, 1';
EXECUTE STMT USING @skip;

SELECT語句中的LIMIT可用於跳過行

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM