多个用户同时更新MySQL中的同一行,如何实现计数器功能?

7
我正在尝试为用户的帖子增加点赞计数器。 MySQL表中的帖子有一个名为likes的字段,显示此特定帖子拥有多少个赞。如果多个用户同时点赞同一篇文章,会发生什么?我认为这将导致冲突或无法正确递增。我该如何避免这种情况?我需要锁定行还是有其他选项?我的查询可能类似于以下内容:
UPDATE posts
    SET likes = likes + 1,
    WHERE id = some_value

当用户取消赞一个帖子时,这应该减少点赞数——>点赞数-1。
谢谢您的任何帮助!

2
“更新”查询是原子性的。如果5个人同时点击同一篇文章,您将执行5次更新,每次更新都不会影响其他更新。除非您通过客户端往返数据,否则这是不好的。例如,“update ... set views=views+1”是原子性的。“select views; fetch views. views=views+1; update views”不是原子性的,并且可能导致计数出错。 - Marc B
你能发布一下你的PHP代码吗?也许那里有什么问题? - ProgrammerWannabe
@AnthonyRutledge:这也意味着,如果同时有五个单独的“update foo set field=field+1”命中数据库,最终结果是field增加了5。它不是一个“获取值、更新值、写入值”的序列,其中一个查询可能会覆盖另一个查询的更新。 - Marc B
1
原子操作 = 无法分解为更小的操作。你真的没有理解这个。更新操作在“用户”级别上不能被细分。在内部,它会获取当前值,将该值递增,然后将该值写回表中。但由于更新本身是原子性的,这些内部细节不会被任何其他查询中断。其中一个“同时”查询将赢得比赛并执行其更新。因此,计数器变为(例如)5->6。然后另外4个中的另一个将获胜并执行6->7等等...在任何时候都不可能变为5->6->5->等等...这就是原子操作的含义。 - Marc B
@MarcB。引用你的参考资料。除法不是问题。在你看来,一切都是同时发生的,这意味着所有的“UPDATE”查询都以相同的起始值开始。必须有顺序,否则它们最终放入表中的值将是相同的。这被称为竞争条件。 - Anthony Rutledge
显示剩余7条评论
3个回答

5

这是一个很简单的查询:

UPDATE mytable SET mycolumn = mycolumn + 1;

这样即使有多人同时查看,查询也不会在完全相同的时间运行,因此最终可以得到正确的数字。

类似这样的查询只需要几分之一秒的时间,因此您不必担心多个用户在同时点击它们,除非您拥有数百万用户,那么您将面临许多与查询相关的问题。


好的,这就是我想听到的。几乎太简单了。但我想它会完成任务的。如果它有一百万用户,我相信我肯定会遇到很多其他问题 ;) 谢谢!! - user3216026
事务?它们也可能是一个答案吗? - Anthony Rutledge
@Styphon 可能吧,但并不总是。 - Anthony Rutledge
@styphon,你能否在你的回答中添加where子句,以帮助新手? - David Addoteye
@Styphon,你能列出问题清单吗? - Kabir Hossain
显示剩余5条评论

3

如果有人在同一时间点喜欢相同的帖子,这将只会导致长而复杂的查询出现问题。

否则,整个SQL语言都将变得毫无用处。因此,这应该已经足够实现增量了。

UPDATE posts
SET likes = likes + 1
WHERE id = some_value

您可以通过编程将此查询运行两次并查看结果。我可以保证它会从0变为2。

someTableAdapter.LikePost(postID);
someTableAdapter.LikePost(postID);

1
好在有人知道在更新时添加条件/约束! - Anthony Rutledge
如果楼主真的使用了“最佳答案”,他/她很快就会发现逻辑错误。希望如此。 - dev_JORD

2
您描述的是一种竞态条件(即,争先完成的比赛。有点像两个人玩音乐椅,但只有一个椅子)。我相信如果您研究事务,您可以实现您的代码并具有伪并发的点赞功能。这里是MySQL手册的链接。 MySQL 5.1手册:事务和锁定语句 YouTube:MySQL事务
MySQL使用行级锁定来支持InnoDB表的同时写入访问,使它们适用于多用户、高并发和OLTP应用程序。对于MyISAM、MEMORY和MERGE表,MySQL使用表级锁定,只允许一个会话一次更新这些表,使它们更适合于只读、读多或单用户应用程序。[1](MySQL手册8.7.1:内部锁定方法)
但是,如果交易过多,请确保您至少使用InnoDB存储引擎,并且如果您正在使用符合ACID标准的数据库以及适当级别的隔离,那么一切都应该没问题。有很多好的参考资料,但希望我已经指出了正确的方向。安东尼

好的,谢谢您的回复。您认为@Styphon的答案怎么样? - user3216026
@user3216026 我认为 @Styphon 的答案取决于 mycolumn 有一个稳定的值,否则我可能会错过对 likes 的计数。:-) 然而,这很聪明。只是别在银行使用它。 - Anthony Rutledge
@user3216026 ,特别是因为 @Styphon 的答案将清除所有同样的数量,并将它们变为mycolmn + 1。不要忘记在SQL查询中添加WHERE约束条件。 - Anthony Rutledge
是的,谢谢你提到。 - user3216026

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接