数据库并发、事务问题,find、insert操作会重复插入数据吗?

发布于 2022-09-04 02:39:37 字数 487 浏览 19 评论 0

好像出现了重复插入现象。

$db->begin();

$obj = table::findFirst("code=521");
if( $obj) 
    return $db->rollback(),false;
    
$obj = new table();
$obj->code=521;
$obj->create();

$db->commit();


结果好像表里面code为521的数据有十多条。

当时数据库很卡,估计用户点了好几十次按钮。。然后就出现了重复数据。

猜想: 这些并发过来的请求会分不在不同内核中,然后同时运行到find表发现没有code为521的数据,想写入却发现数据库很卡写不进去,然后等数据库正常了,十来个线程都判断没有521这个code然后开始各自执行各自的插入code为521的数据。

这种猜想对吗?
事务不就是要保持这两个操作 find 和insert是要原子执行吗?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

亢潮 2022-09-11 02:39:37

MySQL数据库事务默认的隔离级别是REPEATABLE-READ(可重复读).

[mysqld]
transaction-isolation = REPEATABLE-READ

是可以避免脏读和不可重复读的,PHP不需要关心.
脏读:一个事务读到另外一个事务还没有提交的数据.
不可重复读:两次执行同样的查询,可能得到不一样的结果.

楼主要做的应该是想先select查找有没有code是521的记录,没有就insert一条.
既然如此,我觉得楼主为什么不给code这个字段加一个UNIQUE KEY (code)唯一约束呢?
这样做根本不需要开启事务,直接insert,然后根据返回的受影响的行affected_rows来判断插入操作是否成功.

最后再说下事务的原子性.
没有提交的事务,数据库会自动回滚.
但是业务逻辑里是需要开发者根据条件自行进行回滚操作的.
比如:用户1支付50元购买了商品2

<?php
$db = new mysqli('127.0.0.1','user','pass','dbname',3306);
$db->query('SET AUTOCOMMIT=0');  //可以写成 $db->autocommit(false);
$db->query('START TRANSACTION'); //可以写成 $db->begin_transaction();
$db->query('UPDATE user SET coin=coin-50 WHERE id=1 AND coin>=50');
$ar1 = $db->affected_rows;
$db->query('UPDATE goods SET num=num-1 WHERE id=2 AND num>1');
$ar2 = $db->affected_rows;
//两条UPDATE语句都操作成功时(受影响的行都为1)提交事务,否则回滚
if($ar1 === 1 && $ar2 === 1) {
    $db->query('COMMIT');       //可以写成 $db->commit();
} else {
    $db->query('ROLLBACK');     //可以写成 $db->rollback();
}
$db->query('SET AUTOCOMMIT=1'); //可以写成 $db->autocommit(true);

注意,InnoDB引擎会把写操作(insert/update/delete/select for update)当做事务来处理,所以最后记得SET AUTOCOMMIT=1把事务设置回自动提交,否则需要手动COMMIT普通的写操作.

无名指的心愿 2022-09-11 02:39:37

数据库的原子性保证了在一个事务里面的操作要么全部执行成功,要么全部执行失败.事务(隔离级别不为Serialization)不能保证事务执行的串行性

失眠症患者 2022-09-11 02:39:37

你用的是Mysql吧,Serialization可以解决你的问题,但是代价太大。还是修改代码或者把这个521设置为唯一索引吧。

丶情人眼里出诗心の 2022-09-11 02:39:37

简单做就是加唯一索引,前提是数据有唯一要求。

对于不支持唯一要求的,那只能把提交数据请求到redis,由服务器异步处理!或者请求的时间写入redis,短时间内不能重复提交!


另前端方面也可以避免,如点击提交按钮后禁用(但避免的也仅是用户的操作行为,真正的并发问题还是需要后端解决!)

对岸观火 2022-09-11 02:39:37

重复插入,我认为这属于脏读的问题。
数据库事务是需要相应的隔离级别来避免脏读的问题的,这个可以考虑使用来应对这种并发情况。
数据库事务只能保证你的业务逻辑是ok的

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文