2012-02-06 33 views
0

这种事情已经完成了一百万次,但我确信,但我的搜索foo今天看起来很虚弱,我想获得关于通常被认为是最佳实现这一目标的方法。锁定线程间并发数据库记录

我的应用程序会跟踪系统中在线用户的会话。每个会话对应于数据库中的单个记录。会话可以通过以下两种方式之一结束。收到“停止”消息,或会话可能超时。前一种情况很简单,它在消息处理线程中处理,一切都很好。后一种情况是关注点来自哪里。

为了处理超时,每条记录都有一个结束时间列,每次收到该会话的消息时都会更新一次。为了使超时工作,我有一个线程返回NOW()(结束时间为过去)的数据库中的所有记录,并通过处理关闭这些会话。这里的问题是,当超时线程正在处理同一会话时,可能会收到会话消息。我最终在超时线程和消息处理线程之间进行竞争。

我可以使用一个信号灯或类似的东西,只是防止消息线程在超时发生时处理,因为它只需要每30秒或一分钟运行一次。但是,随着用户表变大,这将会遇到一些严重的性能问题。我想我想知道的方法是在消息线程中知道此记录目前正在由超时线程处理。如果我能够实现这一点,我可以放弃该消息或等待超时线程结束,但仅在冲突现在而不是总是冲突的情况下。

目前我的应用程序直接使用JDBC。如果我使用Hibernate这样的框架,会不会有更简单/标准的方法来解决这个问题?

+0

您可以通过会话ID而不是线程级别来包装信号量,因此您已在消息处理程序和超时线程的关闭部分中同步(sessionID)块。我认为它不像你想要的那么干净,但是它比将整个线程锁定在单个信号量上的性能影响更小,因为只有该会话的消息才会被阻塞。 – Thomas 2012-02-06 16:24:18

+0

这将如何工作?我的理解是,这将锁定* object * sessionID,而不是sessionID的*值*。由于在数据库读取之后在两个线程中创建的对象不是同一个对象,所以它看起来好像永远不会阻塞。如果您有一个锁对象的字段并在同步块之前从两个线程更新它,则您拥有与之前的并发性相同的问题,只需将焦点切换到锁对象即可。除非有一种方法可以同步我不知道的*值*上的块。 – tdimmig 2012-02-06 21:51:13

+0

答案根据你的结构而改变。如果你有mysession.handleMessage(x)和mysession.closeAndCleanup(),你应该可以锁定mysession。然后竞争条件是,如果在会话被标记为关闭之后但在到达同步块之前接收到消息,则将处理消息并关闭会话或关闭会话,并且消息将返回关于超时的错误。如果结构是handlemessage(ID,msg)和close(ID),其中ID只是一个int,那么您需要一个Map或其他东西来同步。 – Thomas 2012-02-07 15:53:12

回答

1

这是各种疯狂错误发生的绝佳机会,某些治疗可能会导致性能问题。

经典的解决方案是使用事务(http://dev.mysql.com/doc/refman/5.0/en/commit.html)。这使您可以保证数据的一致性 - 但数据库上长时间运行的事务将其变成一个巨大的瓶颈;如果您的“查找超时会话”代码运行一分钟,则该事务可能会在整个时间段内运行,从而有效锁定对受影响的表的写访问权限。大多数系统不能很好地处理这个问题。

我对这种情况的偏爱解决方案是有一个“状态机”状态;我喜欢将它作为历史表来实现,但这确实会导致数据库快速增长。

您将会话的状态定义为“启动”,“运行”,“超时 - 关闭”,“超时 - 关闭”和“由用户停止”(例如)。

您可以实现代码,在您拥有的任何数据访问逻辑中尊重状态转换逻辑。然后你的“清理”脚本的伪代码可能是:

  • 更新的所有记录,其结束时间< NOW(),其状态为“运行中,设置状态=‘超时 - 关闭’
  • 对于状态的每个记录是“超时 - 关闭”
    • 做你需要做的
    • 更新该记录集状态什么其他的东西“超时 - 关闭”,其中状态=“超时 - 关闭“
  • 下一条记录

修改会话记录当前状态的所有其他尝试都必须检查当前状态对尝试的更改是否有效。

例如,“手动”停止代码应该是这样的:

update sessions 
set status = "stopped by user" 
where session_id = xxxxx 
and status = 'running' 

如果自动关闭例程的时候拉开帷幕显示用户界面和数据库的代码中,其中间子句将不匹配任何记录,因此其余代码不会运行。

为此,所有修改会话状态的代码都必须检查其前提条件;最可维护的方法是编码状态并允许转换到单独的数据库表中。

你也可以编写触发器来强制执行这个逻辑,尽管我通常不是触发器的粉丝 - 只有在必须时才这样做。

我不认为这增加了显着的性能问题 - 但测试和优化。数据库的大部分额外工作是为更新语句添加额外的“where”子句;假设你有一个关于状态的指数,它不太可能产生可衡量的影响。

+0

这会向工作负载添加一些数据库调用,这显然会引起性能问题。另外,除非我错过了某些东西,否则您仍然有可能发生并发问题?如果线程2在线程1持续“超时 - 关闭”之前读取记录,由于没有任何东西可以阻止它,它会认为一切正常,并继续进行任何处理。也许这可以通过在处理开始时标记问题窗口来缩小问题窗口,而不是等待结束,但窗口仍然存在。 – tdimmig 2012-02-06 21:54:53

+0

我已更新答案以反映您的疑虑。 – 2012-02-07 09:23:54