【发布时间】:2013-04-13 16:43:30
【问题描述】:
如果有两个人同时向数据库发送相同的查询,而一个人让另一个查询返回不同的东西,会发生什么?
我有一家商店,那里只剩下一件商品。两个或更多人购买该商品,查询同时到达 MySQL 服务器。我的猜测是它只会排队,但如果是这样,MySQL 是如何选择第一个执行的,我可以对此产生影响吗?
【问题讨论】:
-
“我可以对此施加影响吗?”:- 是的。使用适当的事务隔离级别,例如可序列化
如果有两个人同时向数据库发送相同的查询,而一个人让另一个查询返回不同的东西,会发生什么?
我有一家商店,那里只剩下一件商品。两个或更多人购买该商品,查询同时到达 MySQL 服务器。我的猜测是它只会排队,但如果是这样,MySQL 是如何选择第一个执行的,我可以对此产生影响吗?
【问题讨论】:
它们在用户请求时立即执行,因此如果有 10 个用户同时请求查询,那么将有 10 个查询同时执行。
任何事情都不会同时发生,CPU 也不会一次完成所有事情。它一次做一件事情(每个核心和/或线程)。如果有 10 个用户正在访问运行查询的页面,他们将以特定顺序“访问”服务器并按该顺序进行处理(尽管该顺序可能以毫秒为单位)。但是,如果一个页面上有多个查询,您不能确定一个用户页面上的所有查询都会在另一个用户页面上的查询开始之前完成。这可能会导致并发问题。
编辑:
运行SHOW PROCESSLIST 找到你想杀死的连接的ID
.
SHOW PROCESSLIST 将为您提供所有当前正在运行的查询的列表。
来自here。
MySQL 将在快速 CPU 上表现良好,因为每个查询都在单个线程中运行,并且不能跨 CPU 并行化。
【讨论】:
考虑类似以下的查询:
UPDATE items SET quantity = quantity - 1 WHERE id = 100
无论 MySQL 服务器并行运行多少查询,如果运行 2 个这样的查询并且 id 100 的行有 quantity 1,那么默认情况下会发生这样的事情:
items 中id 为100 的行quantity 从 1 更改为 0 并解锁该行items 中id 为100 的行quantity 从 0 更改为 -1 并解锁该行【讨论】:
MySQL 中的查询是并行处理的。你可以read more about the implementation here。
【讨论】:
这本质上是一个并发问题。有一些方法可以通过使用事务来确保 MySQL 中的并发性。这意味着在您的 eshop 中,您可以确保像您描述的那样的竞争条件不会成为问题。有关 MySQL 中的事务,请参见下面的链接。
http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html
http://zetcode.com/databases/mysqltutorial/transactions/
根据您的隔离级别,两个并发查询会返回不同的结果。
【讨论】:
同时发送相同的查询
查询并不总是并行运行
这取决于数据库引擎。使用 MyISAM,几乎每个查询都获得表级锁,这意味着查询作为队列顺序运行。它们可以与大多数其他引擎并行运行。
echo_me 说nothing happens at the exact same time and a CPU does not do everything at once
这并不完全正确。 DBMS 有可能在具有多个 cpu 和多个网络接口的机器上运行。 非常不可能同时到达 2 个查询 - 但并非不可能,因此有一个互斥锁来确保配对/执行转换仅作为单个线程运行(执行 - 不一定是相同的轻量级工艺)。
解决并发 DML 有 2 种方法 - 使用事务(每个用户有效地获得数据库的克隆)和当查询完成时 DBMS 尝试协调任何更改 - 如果协调失败,则 DBMS 滚动返回其中一个查询并将其报告为失败。另一种方法是使用行级锁定 - DBMS 识别将由查询更新的行并将它们标记为保留更新(其他用户可以读取每行的原始版本,但任何更新数据的尝试都将是被阻塞,直到该行再次可用)。
您的问题是您有两个 mysql 客户端,每个客户端都检索到剩余一件库存的事实。由于(因为您提到 PHP)库存水平可能是在与随后的库存调整不同的 DBMS 会话中检索到的,这使情况更加复杂 - 您的事务不能跨越 HTTP 请求。因此,您需要在单个事务中重新验证在 DBMS 之外维护的任何事实。
乐观锁定可以创建一个伪事务控制机制——你用时间戳和用户标识符标记你将要修改的记录(对于 PHP,PHP 会话 ID 是一个不错的选择)——如果你来修改它, 其他东西改变了它,那么你的代码就知道它之前检索到的数据是无效的。但是,这可能会导致其他并发症。
【讨论】: