Java 这个SQL会导致任何问题吗?

Java 这个SQL会导致任何问题吗?,java,php,c++,python,mysql,Java,Php,C++,Python,Mysql,我相信每个人都知道线程并发的乐趣 想象一下,在noobily设置的MySQL数据库上的每个页面加载上都会出现以下场景: UPDATE stats SET visits = (visits+1) 如果一千个用户同时加载该页面,计数是否会导致任何问题?这是表锁定/行锁定机制吗?mysql使用哪一个。不,这不会搞砸的。这在任何符合ACID的DB中都是完全可以接受的。I代表孤立。每个查询都将锁定visit表中的所有行。ACID中的A代表原子性,表示事务必须完全运行或根本不运行。没关系。 所有那些表锁/

我相信每个人都知道线程并发的乐趣

想象一下,在noobily设置的MySQL数据库上的每个页面加载上都会出现以下场景:

UPDATE stats SET visits = (visits+1)

如果一千个用户同时加载该页面,计数是否会导致任何问题?这是表锁定/行锁定机制吗?mysql使用哪一个。

不,这不会搞砸的。这在任何符合ACID的DB中都是完全可以接受的。I代表孤立。每个查询都将锁定visit表中的所有行。ACID中的A代表原子性,表示事务必须完全运行或根本不运行。

没关系。 所有那些表锁/行锁都是发明数据库来处理的垃圾

当数千名用户同时加载页面时,可能会出现其他问题,如索引更新。但这是另一回事,noobily MySQL安装程序无论如何也不是这样。

请确保已设置自动提交,以便将其视为事务处理,并且计数将是正确的。唯一的问题是性能,例如MySQL有一个表热点,它说:

[Repeatable read]是InnoDB的默认隔离级别。[…]对于[…]UPDATE和DELETE语句,锁定取决于该语句是使用具有唯一搜索条件的唯一索引,还是使用范围类型搜索条件。对于具有唯一搜索条件的唯一索引,InnoDB只锁定找到的索引记录,而不锁定之前的间隙。对于其他搜索条件,InnoDB使用间隙锁或下一个键间隙加索引记录锁锁定扫描的索引范围,以阻止其他会话插入该范围覆盖的间隙

所以我想说是的,您很好,尽管这个特定的查询可能会锁定整个表。这可能会更好:

UPDATE stats SET value = value + 1 WHERE key = 'visits'

索引位于键上。

您有两个潜在问题:

你会得到正确的答案吗? 你会得到不合理的锁定,你的整个应用程序会变得非常缓慢甚至死锁。 正确的答案取决于两个用户是否可以在相同的访问值上计算visit+1。我们可以想象数据库需要执行以下操作:

  Read visit count
  Add one to visit count
  Write visit count
那么,如果两个用户同时工作,他们是否都能读取相同的旧值?这就是事务的隔离级别发挥作用的地方。根据Artefactor的观察,默认隔离级别为可重复读取,因此我们得到:

 Grab a lock
 Read, increment, Write
 Release lock
而不是

 Read  (two users same old value)
 Increment
 First user Grab Lock, second waits
 Write  
 Release, second user grabs lock
 Write (same value!)
 Release
然而,争用级别可能相当高,并且在很大程度上取决于事务的范围。假设你有:

  Begin transaction

  Do the visit increment stuff

  Do some serious business work

  End transaction   <==== visit lock is held until here
然后你会有很多人在等你的访问锁。我们不知道你的应用程序的整体结构,也不知道你是否使用像这样的大事务范围。很可能每个SQL语句都会有一个默认的事务行为,在这种情况下,争用只在SQL语句的持续时间内进行,这与您所希望的差不多

其他人可能没有这么幸运:有一些环境,例如JavaEEservlet,在这些环境中,基础设施可以创建隐式事务作用域,然后我在上面展示的较长时间的事务默认发生。更糟糕的是,您的代码编写可能与访问增量不一致—始终在第一位,或始终在最后—您可以得到:

  Begin transaction
  Do the visit increment stuff
  Do some serious business work
  End transaction   <==== visit lock and business locks held until here

宾果游戏:僵局


对于大容量站点,您可以考虑将访问事件写入队列,并拥有守护程序监听这些事件并保持计数。更复杂,但争用问题可能更少。

到目前为止,所有答案似乎都假设使用InnoDB表,它确实支持事务;使用MyISAM表,您可以得到原子事务,这对于您的特定用例来说应该是不错的,尽管在一般情况下,原子事务远远低于完整的ACID

在MySQL交易文档中,例如,它将您的更新表单作为一个良好实践的典型案例,具体来说,我引用…:

这给了我们一些 类似于列锁定,但是 实际上更好,因为我们 使用更新某些列 相对于其属性的值 当前值。这意味着 典型的更新语句看起来 类似这样的:

…这是非常有效的,即使另一个客户更改了pay_back[[列]中的值,也能正常工作

如果您:

正在事务中运行 行锁定设置正确 尤其要注意第二点。这不是一个简单的问题,因为MySQL允许您将锁定约束放松到一个确实会出错的程度


另一方面,当锁定设置正确时,如果遇到一些非常繁忙的流量,这可能会成为您的瓶颈,因为它只能在单个线程中执行。如果您将交易打开的时间长于仅仅更新号码,则更有可能发生这种情况,如果您不小心,甚至可能导致死锁,正如djna详细解释的那样。

正如所有人所说,这将导致死锁 如果正在使用InnoDB,请锁定该行。现在,如果只使用一行,并且该行存储有关所有访问的统计信息,那么在查询等待获取锁时,锁定该行可能会降低速度。在您的负载下,这种减速可能是无法察觉的。如果它很重要,您可以通过写入多个不同的行来绕过它,比如0-255。这仍然会锁定每一行,但锁争用的几率现在是原来的1/256。当您需要总计时,您可以将所有行相加

UPDATE stats SET value=value+1 WHERE id=X 
其中X是一个随机id 0-255

然后


会给你真正的总数。

应该可以。我写这篇文章不是为了回答问题,因为我不完全确定。如果他没有明确启动一个事务怎么办?我不知道MySQL中的任何事务是automagic for noobs吗?InnoDB中的Sayem Ahmed,所有用户活动都发生在一个事务中。如果启用了自动提交模式,则每个SQL语句各自形成一个事务。那么,他应该没事了:这些查询中的每一个都会锁定访问表中的所有行:不,MyISAM的tablelock,bdb的pagelock,InnoDB的rowlock,在性能上有很大的不同:
UPDATE tablename SET pay_back=pay_back+125;
UPDATE stats SET value=value+1 WHERE id=X 
SELECT SUM(value) FROM stats