我有一个小表(< 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
他们是你的服务器,有一个负责这个过程(总是可以在某个数据库表中使用一个条目)。只要知道如果该服务器停机但处理故障转移情况。 –