我有一个 MDB(消息驱动的 bean),它接收带有表示单词的字符串的消息。我在数据库中也有一个表。 MDB应该在表中存储单词和每个单词被接收的次数(计数器)。
问题在于,为了获得更好的性能,MDB 在许多实例中启动,并且当不同实例接收到相同的新词时,它们都会创建计数为 1 的相同行。
要解决这个问题,我应该使 word 字段唯一,然后第二个实例将在提交时失败,重新传输消息,这会起作用,但可能会有问题。这是一个好的做法吗?
另一种解决方案是在对计数器求和后合并这些行。但是,如果另一个实例将在更新过程中增加计数器怎么办。
如果两个实例试图增加计数器怎么办? @Version
应该够了吧?
我不确定这里的正确解决方案是什么。您将如何处理此类情况?
您还可以推荐一些关于并发实践的书籍吗(不是关于 synchronized
的使用,因为我需要支持 Java EE 并且可能运行一个应用程序服务器集群)?
更新: 在阅读了有关 EJB 和 JPA 的更多信息后,我想我想要类似锁定实体的东西。例如,我可以创建一个只有 id 和 key 列以及数据的新表,如下所示:
ID | KEY
1 | WORDS_CREATE_LOCK
所以当我需要处理一个新词时,我会做这样的事情(不是确切的代码,不确定它是否会编译):
// MAIN FUNCTION
public void handleWord(String wordStr) {
Word w = getWord(wordStr);
if (w == null)
w = getNewOrSychronizedWord(wordStr);
em.lock(w);
w.setCounter(w.getCounter() + 1);
em.unlock(w);
}
// Returns Word instance or null if not found
private Word getWord(String wordStr) {
Word w = null;
Query query = em.createQuery("select w from words as w where w.string = :wordStr order by w.id asc");
query.setParameter("wordStr", wordStr);
List<Word> words = query.getResultList();
if (words.getSize() > 0)
w = words.get(0);
return w;
}
// Handles locking to prevent duplicate word creation
private Word getNewOrSynchronizedWord(String wordStr) {
Word w = null;
Locks l = em.find(WORDS_CREATE_LOCK_ID, Locks.class);
em.lock(l);
Word w = getWord(wordStr);
if (w == null) {
w = new Word(wordStr);
em.persist(w);
}
em.unlock(l);
return w;
}
所以问题是它会那样工作吗?我可以在不维护带锁定行的数据库表的情况下做同样的事情吗?可能是某种 Java EE 容器锁定机制?
如果有帮助,我正在使用 JBoss 4.2。
我对此有了新的想法。我可以创建两个 MDB:
第一个 MDB 允许许多实例,它将处理所有消息,如果未找到该词,则将该词发送到第二个 MDB
第二个 MDB 只允许一个实例,将连续处理消息并允许创建新词
最好的部分:没有整个表/方法/进程锁定,只有计数器更新时的行锁定
这有多好?
谢谢。
最佳答案
如果您正在寻找性能、无锁定等。我建议有另一个表:(word, timestamp)。您的 MDB 将只插入单词和时间戳。另一个进程将计数并用总数更新表格。
关于Java EE 并发和锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/834843/