检查用户是否已投票

时间:2017-05-21 03:41:28

标签: c# database-design

我正在为类似博客的网站上的帖子创建一个投票系统。

每个帖子都有一个评级,存储在数据库中。用户可以单击upvote或downvote,数据库中的数字将增加或减少。简单的东西。

但我如何检查当前用户是否已经投票?

我最初的想法是在数据库中为每个帖子创建两个额外的列,“UsersUp”和“UsersDown”,存储已经投票并为每个帖子投票的用户名。我会检查用户名是否存在,如果存在,则会删除投票而不是添加其他投票。 我甚至可以完全删除“评级”列,并通过计算UsersUp中的用户名数量并减去UsersDown中的用户名数来实现相同的目的。

我是以错误的方式接近这个吗?

我不是要求任何代码,只是我如何实现这一目标的理论。

我需要用户不能上下两次投票,他们需要再次点击即可收回投票。

1 个答案:

答案 0 :(得分:2)

您应该使用normalized database design

您可能有一个User表,根据问题,您有一个Post表。

您可以使用第三个UserVote表来表示用户,帖子和投票之间的关系。这允许精细跟踪投票的人,投票的时间和方式(VoteTypeId可以包含upvote或downvote的ID)。

+--------+--------+------------+----------+
| UserId | PostId | VoteTypeId | VoteDate |
+--------+--------+------------+----------+
|    1   |    1   |      1     | 1/1/2017 |
+--------+--------+------------+----------+
|    2   |    1   |      1     | 2/1/2017 |
+--------+--------+------------+----------+
|    3   |    1   |      2     | 2/2/2017 |
+--------+--------+------------+----------+

这为数据建模。你应该如何从c#中调用它?

if( !HasVoted( userId, postId ) ){
    SaveVote( userId, postId, voteTypeId );
    UpdateVoteCount( postId, voteTypeId );
}

然而,这非常容易出错。如果UpdateVoteCount()失败怎么办?

有多种方法可以解决这个问题,其中包括:

  • 在您需要时对总计投票进行计数(不要存储它们)。
  • 在交易中包装操作。
using( var scope = new TransactionScope() ){    
    if( !HasVoted( userId, postId ) ){
        SaveVote( userId, postId, voteTypeId );
        UpdateVoteCount( postId, voteTypeId );
    }

    scope.Complete();
}

阅读更多:TransactionScope

这有助于数据完整性,但代码仍然有race condition,允许系统被滥用。

我们需要锁定整个操作。实现此目的的一种方法是使用存储过程sp_getapplock,它允许您阻止命名资源,直到锁的持有者释放它。

您可以使用单个命名锁定,或者您可以锁定每个项目(如果对某个项目进行投票可能会产生更广泛的后果,例如声誉计算,则会很棘手。)

阅读更多:sp_getapplock

我们的序列变为:

  1. 采取命名锁定。
  2. 开始交易。
  3. 检查用户是否已经投票。
  4. 注册投票。
  5. 更新其他值(例如总投票数,用户信誉增益)。
  6. 提交交易。
  7. 解锁。
  8. 虽然这听起来很复杂,并且锁定听起来像是瓶颈,但它实际上表现良好。几年前,我对这种设计进行了压力测试,很容易实现每秒2500次投票操作的吞吐量。

    调用代码应确保:

    • 所采取的锁具有短暂的超时
    • 即使抛出异常,也会提交/回滚事务。
    • 即使抛出异常,锁也会被处理掉。