我有一個小表(< 100行),它包含一個有序的項目列表。我想隨機排列這些項目。我想這樣做的方式是選擇最近使用的5個項目,並隨機選擇其中一個。但是,我只想偶爾做一次。防止一個動作被不同的服務器多次調用
我正在考慮使用存儲過程來完成此操作,然後查詢變得像SELECT TOP 1 * FROM myTable ORDER BY LastUsedDate DESC
一樣。
不幸的是,這個解決方案並不好。如果每個排列之間的時間間隔(每次運行存儲過程)都是可變的,則每隔X分鐘運行的SQL-Server作業將不起作用。如果我讓我的服務器執行排列,多個服務器可能最終會進行排列。
這是我的想法是做服務器上的邏輯:
- 從數據庫獲取最近使用的項目
- 如果它的時間來進行置換,儘量放在桌上 得到一個鎖
- 如果它已經被鎖定,這意味着另一臺服務器已經進行置換(轉到1)
- 進行置換
- 返回最近使用的項目。
但是,我可以想象,鎖定表並不是一個很好的解決方案。所以我在尋找建議:)。
我在Hibernate服務器上使用Java。
謝謝!
更新:
最後我想使用Hibernate而不是有一個存儲過程(更容易調試,易於推動)鎖定行。但是,我不認爲hibernate正確鎖定了necassary行。這裏是我的代碼:
Session s = sessionFactory.openSession();
Transaction tx = null;
try {
tx = s.beginTransaction();
//Check whether the most recent tournament is expired or not. If it's not, abort (another server already updated it)
TournamentTemplateRecord lastActiveTournament = (TournamentTemplateRecord) s.createCriteria(TournamentTemplateRecord.class)
.addOrder(Order.desc("lastUse"))
.setMaxResults(1)
.uniqueResult();
long startTime = lastActiveTournament.getLastUse().getTime();
long tournamentDurationMillis = lastActiveTournament.getDurationInSec() * 1000;
if ((startTime + tournamentDurationMillis) < System.currentTimeMillis()){
//Tournament is still active and valid. Abort.
System.out.println("Tournament is still active");
tx.rollback();
return;
}
// Fetch the 5 least recently used tournaments
List<TournamentTemplateRecord> leastRecentlyUsedTournaments = s.createCriteria(TournamentTemplateRecord.class)
.addOrder(Order.asc("lastUse"))
.setMaxResults(5)
.setLockMode(LockMode.PESSIMISTIC_WRITE)
.setTimeout(0) //If rows are locked, another server is probably already doing this.
.list();
Random rand = new Random();
// Pick one at random
TournamentTemplateRecord randomTournament = leastRecentlyUsedTournaments.get(rand.nextInt(leastRecentlyUsedTournaments.size()));
randomTournament.setLastUse(new Date());
s.update(randomTournament);
tx.commit();
} catch (Exception e) {
if(tx != null) {
tx.rollback();
}
} finally {
s.close();
}
但是,休眠不會產生SELECT ... FOR UPDATE NOWAIT
。有任何想法嗎?
這裏的生成HQL:
Hibernate:
WITH query AS (select
ROW_NUMBER() OVER (
order by
this_.lastuse desc) as __hibernate_row_nr__,
this_.combattemplateid as id89_0_,
this_1_.combattypeid as combatty2_89_0_,
this_1_.combattargetid as combatta3_89_0_,
this_1_.resourcenameid as resource4_89_0_,
this_1_.resourcedescriptionid as resource5_89_0_,
this_1_.rewardloottemplateid as rewardlo6_89_0_,
this_1_.combatcontainertypeid as combatco7_89_0_,
this_.requirementtemplateid as requirem2_90_0_,
this_.assetid as assetid90_0_,
this_.durationinsec as duration4_90_0_,
this_.lastuse as lastuse90_0_
from
tournament_tournamenttemplate this_
inner join
readyforcombat_combattemplate this_1_
on this_.combattemplateid=this_1_.id) SELECT
*
FROM
query
WHERE
__hibernate_row_nr__ BETWEEN ? AND ?
Hibernate:
WITH query AS (select
ROW_NUMBER() OVER (
order by
this_.lastuse asc) as __hibernate_row_nr__,
this_.combattemplateid as id89_0_,
this_1_.combattypeid as combatty2_89_0_,
this_1_.combattargetid as combatta3_89_0_,
this_1_.resourcenameid as resource4_89_0_,
this_1_.resourcedescriptionid as resource5_89_0_,
this_1_.rewardloottemplateid as rewardlo6_89_0_,
this_1_.combatcontainertypeid as combatco7_89_0_,
this_.requirementtemplateid as requirem2_90_0_,
this_.assetid as assetid90_0_,
this_.durationinsec as duration4_90_0_,
this_.lastuse as lastuse90_0_
from
tournament_tournamenttemplate this_
inner join
readyforcombat_combattemplate this_1_ with (updlock, rowlock)
on this_.combattemplateid=this_1_.id) SELECT
*
FROM
query
WHERE
__hibernate_row_nr__ BETWEEN ? AND ?
Hibernate:
update
Tournament_TournamentTemplate
set
RequirementTemplateId=?,
AssetId=?,
DurationInSec=?,
LastUse=?
where
combatTemplateId=?
是的,我覺得需要一個企業搜索解決方案(這是一個結果相關性問題)。我會用[Solr](https://lucene.apache.org/solr/)和一個自定義的[QueryElevationComponent](https://wiki.apache.org/solr/QueryElevationComponent)來解決這個問題。 –
「只需對隨機化負責,在服務器上運行它,在需要時隨機化。」我想這樣做,但是服務器是相同的,所以這意味着他們都會嘗試在某個時間點隨機化,而其中只有一個應該。 – Nepoxx
他們是你的服務器,有一個負責這個過程(總是可以在某個數據庫表中使用一個條目)。只要知道如果該服務器停機但處理故障轉移情況。 –