我在下面提供了原始 MySQL 查询以及我以编程方式执行此操作的代码。如果同时执行两个请求会导致以下错误模式:
SQLSTATE[40001]:序列化失败:1213 尝试获取锁时发现死锁;请尝试重新启动交易(SQL:
update user_chats set updated_at = 2018-06-29 10:07:13 where id = 1
)
如果我执行相同的查询但没有事务块,它将在许多并发调用中正常工作而不会出错。为什么 ?(交易获得锁,对吗?)
有没有办法在不锁定整个表的情况下解决这个问题?(想尽量避免表级锁)
我知道使用 InnoDB 在 MySql 中插入/更新/删除表需要锁定,但仍然不明白为什么会在这里发生死锁以及如何以最有效的方式解决它。
START TRANSACTION;
insert into `user_chat_messages` (`user_chat_id`, `from_user_id`, `content`)
values (1, 2, 'dfasfdfk);
update `user_chats`
set `updated_at` = '2018-06-28 08:33:14' where `id` = 1;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
以上是原始查询,但我在 PHP Laravel Query Builder 中执行如下:
/**
* @param UserChatMessageEntity $message
* @return int
* @throws \Exception
*/
public function insertChatMessage(UserChatMessageEntity $message) : int
{
$this->db->beginTransaction();
try
{
$id = $this->db->table('user_chat_messages')->insertGetId([ …
Run Code Online (Sandbox Code Playgroud) 在我工作的项目中,用于选择对象的 sql 总是选择更新,无论上下文是否为事务。
我的问题是它是否有任何风险、性能问题、或导致死锁或其他顽皮的可能性?
我知道 select for update 只能在事务内锁定一行,这是常见的用例。
我提供了以下两个具体用例,无论是否在事务内部,都在选择更新后使用。
book = booksRepository->getBook(7);
return book;
Run Code Online (Sandbox Code Playgroud)
bookRepository->startTransaction();
book = booksRepository->getBook(7);
if (//some condition with the book properties) {
bookRepository->updateBookAuthor(7, 'bbb');
}
bookRepository->commitTransaction();
Run Code Online (Sandbox Code Playgroud)
在上面的两个例子中,存储库中的函数 getBook 确实选择更新
SELECT * FROM BOOKS
WHERE id = :id
FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
我问了其他开发人员,他们为什么这样做的答案是“因为否则我们将不得不在存储库中创建两个函数,一个 getBook 和一个 getBookForUpdate,所以我们只选择始终进行更新”...
该答案没有为我可以学习的地方提供任何见解或解释,所以我在这里问。