并发实体使用Hibernate和Postgres读取/更新

时间:2015-04-01 20:55:21

标签: java hibernate postgresql concurrency transactions

我有一个用Java,Spring,Hibernate,Postgres编写的应用程序。

它跟踪用户的登录尝试。如果用户在不到1小时内从同一IP地址尝试的次数超过5次,则该IP地址将被阻止。

我有以下实体类来存储有关登录尝试的信息:

@Entity
public class BlockedIp {

private String ipAddress;
private Date blockDate;
private Date unblockDate;
private Date lastAttemptDate;
private Integer wrongAttemptsCount;
...}

首先,当app获取登录请求时 - 它会检查IP地址是否已被阻止(blockDate!= null)。然后返回给用户的特殊响应代码。

如果应用获取登录请求且凭据错误:

  • 如果最后一次尝试不到1小时前 - 它会增加wrongAttemptsCount和if(wrongAttemptsCount == 5) - 设置blockDate。
  • 如果最后一次尝试超过1小时 - 它将wrongAttemptsCount设置为1。

如果应用获取登录请求且凭据正确 - 它会将wrongAttemptsCount重置为零,因此用户最多可以再犯5次错误:)

问题是当某些用户尝试从同一IP同时登录时。 例如,wrongAttemptsCount = 4,因此用户只能进行最后一次尝试。我们有3个传入请求,并且它们都有错误的凭据。理论上,只有第一个请求会通过,但其他两个请求将被阻止。当然,在实践中,所有得到的错误都是从数据库中获得的AttemptsCount等于4,因此它们都将被处理为非阻塞。

那么,如果可能的话,我必须解决这个问题并尽量减少性能损失? 我为我的查询考虑了SELECT FOR UPDATE语句,但我的同事说这会严重影响性能。 他还提出要看@Version注释。是不是真的值得吗?它快得多吗? 也许有人可以提供其他选择?

1 个答案:

答案 0 :(得分:1)

乐观锁定产生最佳性能,同时prevents lost updates in multi-request conversations,它将阻止多个并发事务更新同一行而不会收到有关新行版本的通知。

只有一个事务会通过,其他事务会获得陈旧状态异常。你必须明白locks are acquired even if you don't explicitly request it so。每个修改过的行都会锁定,只是transactional write-behind cache后实体状态转换为当前事务的结束。

SELECT FOR UPDATE,与任何pessimistic locking mechanism一样,采用显式锁定。您还可以使用PESSIMISTIC_READ获取共享锁,因为PostgreSQL也支持此功能。

PESSIMISTIC_READ将阻止其他事务获取User实体上的共享锁,但如果没有乐观锁定,您仍可能有超过5次失败尝试。在当前事务释放锁之后,另一个竞争事务将获取新释放的锁并保存新的失败逻辑尝试。 READ_COMMITTED会发生这种情况,REPEATABLE_READ or SERIALIZABLE阻止了这种情况,但提高事务隔离级别确实会降低应用程序的可伸缩性。

总而言之,使用乐观锁定并相应地处理陈旧状态异常。