찾다
백엔드 개발PHP 튜토리얼【大神在哪里】mysql并发的问题,能不能有办法避免sql出错的情况,数据的完整性怎才能做到……?

先看一个常见的情况,用户注册:

<code>if (select id from tb_user where name = '$name') {
    // 可以注册
    INSERT INTO tbl_name (name, password) VALUES ('$name', '$password'); 
    
} else {
    // 用户名已存在不能注册
}

</code>

上面这是我写的注册代码,我看到大多数程序也都是这样的,但是现在我发现了一个问题,当很多人同时注册时,可能导致一个问题,这是我此前从未注意想到的:

mysql select 默认会使用共享读锁,当两个进程同时执行到第一条sql select判断用户是否注册(明显此时还未执行到第二条新增的sql语句,用户还没有注册),会导致两个进程谁也不知道谁,傻傻的以为该用户名可以被注册,于是执行插入,这就导致了name用户名重复了,而用户名应该是唯一的。

有人可能说了唯一的用户名必须是唯一索引,好吧,的确我建了唯一索引,虽然这样避免了表中出现相同name值,但是两次INSERT INTO插入相同的name导致sql执行错误了,这就是我要说的问题1:

【1】能否有办法避免sql出错

当多个人同时使用相同的用户名进行注册,只会有一个人得到提示“注册成功”,其他人会得到提示“注册失败”,那些注册失败不幸的人,我们该怎对他解释你是如何会注册失败的?
很伤心,sql执行出错了,我该用什么心情去对待它,怎么去感受,怎么去理解它,如果在调试的时候一般会直接终止程序报错的,如果在部署时不终止程序,记录出错日志信息,我应该这样做吗?
这就是我要说的第二个问题和第三个问题:

【2】不幸的事还是发生了,SQL执行出错了,我们该怎么跟用户解释,怎么向他解释他为何注册失败了
【3】当在系统执行时SQL出错了,我们是终止程序报错,还是继续执行,只是记录错误日志呢

我们要保证数据的完整性,不能出现任何的意外,如果你支付宝的余额字段一会少一会多你愿不愿意,一会突然没有了你愿意吗,如果银行突然跟你说你存的钱不见了,不知道哪儿去了,你的余额为零,你什么感想?
这就是我要说的第三个问题:(实际是对第二个问题的深入探讨)

【3】为确保安全,使用innodb引擎,每次执行sql前开启事物,只要程序一有失败(包括sql执行失败,程序逻辑控制的等等不是全部成功的)就立即回滚,中断退出程序执行流程

第三个问题说到方法看起来可以完美的解决所有所有的问题,并且其实实现也非常容易(在使用的框架时代通过getDbError()就能知道是否有sql错误了),但是这也引发一些思考,不管什么情况都开启事物值不值得,有没有必要,明显有的情况可能没有必要,这就是我要说的问题四:

【4】什么时候需要使用事物,以及如何使用,事物是必须的吗,为什么DZ,WordPress不使用事物也照样安全

探讨这个问题时我们先来思考一下事物本身,以及问题本身:

1:我们遇到什么问题,迫于没有好的办法解决
2:事物能帮我们解决什么问题

我们先用事物解决第一个注册时的并发问题,开启事物后使用查询时可以使用for update行锁来避免并发的问题,在本次事物提交前会一直锁着,所以那些不幸的用户会得到“该用户名被占用,不能注册”的提示,OK这个问题解决了。

以上就是我的一些困惑,和一些浅薄的理解,希望大神指点迷境,我郁闷好久了,这些问题困扰着我,让我每天过的提心吊胆的,整个人都不好了,看着之前那些代码,没有事物,没有回滚,没有提交,复杂的逻辑,一排的sql增删改,如新增一个商品就会设计多张表的sql,逻辑等等,我只在调试时没有问题就部署上线了,万一那一条sql失败,后果不敢想象,我只能假设调试好了,永远不可能有意外的,可以我只是假想这样,心里没有底。

比如面对我项目中这样的情况,我心里没有底了:

<code>$sql = update tb_goods `collection` = `collection` + 1 where id = 1;
#sql2 = update tb_user `collection_num` = `collection_num` + 1 where uid = 1;
if ($sql) {
    if($sql2) {
        return  '收藏成功!';
    } else {
        return  '收藏失败!';
    }
} else {
    return '收藏失败';
}</code>

现在所有的代码都是这个样子的,假如第一条SQL成功,第二条失败怎么办?

还有跟多这样的情况,有的甚至根本就没有判断

<code>// 在一个商品添加逻辑中
$sql :  insert 一行数据 添加一个商品
$sql2 :  update 更新分类表对应的某一行的商品数量
$sql3 :  update 更新商家表对应的某一行的商品数量
$sql4 :  insert 针对当前商品ID新增一条统计记录
$sql5 :  insert 针对当前商品ID新增一条操作记录
……
$sql_n 根据系统逻辑有n条这样sql
……

// 很多sql我们不可能这样吧
if(!$sql)
    exit('商品添加失败!');
if(!$sql2)
    exit('商品添加失败!');
if(!$sql_n)
    exit('商品添加失败!');
   
exit('商品添加成功!');    // 最后商品添加成功

// 或者
if ($sql) {
    if ($sql2) {
        if ($sql_n) {
            exit('商品添加成功!');
        } else exit('商品添加失败!');
    } else exit('商品添加失败!');
} else exit('商品添加失败!');</code>

现在像这样的sql ~ sql_n 这样的情况我只判断关键的几条sql就敢返回 商品添加成功,或者 商品添加失败 其它的一些sql就假设它会成功,不用判断,我当时小不懂事,一直就是这么来的,看别人的吗好像也是这样的,只判断关键的sql if ($sql) return 1; else return 0;其它的都假设会成功,我现在觉得这样不靠谱,求大神解答啊

希望大神结合实例说明,解除我心中的困惑,感激不敬 ^_^

回复内容:

先看一个常见的情况,用户注册:

<code>if (select id from tb_user where name = '$name') {
    // 可以注册
    INSERT INTO tbl_name (name, password) VALUES ('$name', '$password'); 
    
} else {
    // 用户名已存在不能注册
}

</code>

上面这是我写的注册代码,我看到大多数程序也都是这样的,但是现在我发现了一个问题,当很多人同时注册时,可能导致一个问题,这是我此前从未注意想到的:

mysql select 默认会使用共享读锁,当两个进程同时执行到第一条sql select判断用户是否注册(明显此时还未执行到第二条新增的sql语句,用户还没有注册),会导致两个进程谁也不知道谁,傻傻的以为该用户名可以被注册,于是执行插入,这就导致了name用户名重复了,而用户名应该是唯一的。

有人可能说了唯一的用户名必须是唯一索引,好吧,的确我建了唯一索引,虽然这样避免了表中出现相同name值,但是两次INSERT INTO插入相同的name导致sql执行错误了,这就是我要说的问题1:

【1】能否有办法避免sql出错

当多个人同时使用相同的用户名进行注册,只会有一个人得到提示“注册成功”,其他人会得到提示“注册失败”,那些注册失败不幸的人,我们该怎对他解释你是如何会注册失败的?
很伤心,sql执行出错了,我该用什么心情去对待它,怎么去感受,怎么去理解它,如果在调试的时候一般会直接终止程序报错的,如果在部署时不终止程序,记录出错日志信息,我应该这样做吗?
这就是我要说的第二个问题和第三个问题:

【2】不幸的事还是发生了,SQL执行出错了,我们该怎么跟用户解释,怎么向他解释他为何注册失败了
【3】当在系统执行时SQL出错了,我们是终止程序报错,还是继续执行,只是记录错误日志呢

我们要保证数据的完整性,不能出现任何的意外,如果你支付宝的余额字段一会少一会多你愿不愿意,一会突然没有了你愿意吗,如果银行突然跟你说你存的钱不见了,不知道哪儿去了,你的余额为零,你什么感想?
这就是我要说的第三个问题:(实际是对第二个问题的深入探讨)

【3】为确保安全,使用innodb引擎,每次执行sql前开启事物,只要程序一有失败(包括sql执行失败,程序逻辑控制的等等不是全部成功的)就立即回滚,中断退出程序执行流程

第三个问题说到方法看起来可以完美的解决所有所有的问题,并且其实实现也非常容易(在使用的框架时代通过getDbError()就能知道是否有sql错误了),但是这也引发一些思考,不管什么情况都开启事物值不值得,有没有必要,明显有的情况可能没有必要,这就是我要说的问题四:

【4】什么时候需要使用事物,以及如何使用,事物是必须的吗,为什么DZ,WordPress不使用事物也照样安全

探讨这个问题时我们先来思考一下事物本身,以及问题本身:

1:我们遇到什么问题,迫于没有好的办法解决
2:事物能帮我们解决什么问题

我们先用事物解决第一个注册时的并发问题,开启事物后使用查询时可以使用for update行锁来避免并发的问题,在本次事物提交前会一直锁着,所以那些不幸的用户会得到“该用户名被占用,不能注册”的提示,OK这个问题解决了。

以上就是我的一些困惑,和一些浅薄的理解,希望大神指点迷境,我郁闷好久了,这些问题困扰着我,让我每天过的提心吊胆的,整个人都不好了,看着之前那些代码,没有事物,没有回滚,没有提交,复杂的逻辑,一排的sql增删改,如新增一个商品就会设计多张表的sql,逻辑等等,我只在调试时没有问题就部署上线了,万一那一条sql失败,后果不敢想象,我只能假设调试好了,永远不可能有意外的,可以我只是假想这样,心里没有底。

比如面对我项目中这样的情况,我心里没有底了:

<code>$sql = update tb_goods `collection` = `collection` + 1 where id = 1;
#sql2 = update tb_user `collection_num` = `collection_num` + 1 where uid = 1;
if ($sql) {
    if($sql2) {
        return  '收藏成功!';
    } else {
        return  '收藏失败!';
    }
} else {
    return '收藏失败';
}</code>

现在所有的代码都是这个样子的,假如第一条SQL成功,第二条失败怎么办?

还有跟多这样的情况,有的甚至根本就没有判断

<code>// 在一个商品添加逻辑中
$sql :  insert 一行数据 添加一个商品
$sql2 :  update 更新分类表对应的某一行的商品数量
$sql3 :  update 更新商家表对应的某一行的商品数量
$sql4 :  insert 针对当前商品ID新增一条统计记录
$sql5 :  insert 针对当前商品ID新增一条操作记录
……
$sql_n 根据系统逻辑有n条这样sql
……

// 很多sql我们不可能这样吧
if(!$sql)
    exit('商品添加失败!');
if(!$sql2)
    exit('商品添加失败!');
if(!$sql_n)
    exit('商品添加失败!');
   
exit('商品添加成功!');    // 最后商品添加成功

// 或者
if ($sql) {
    if ($sql2) {
        if ($sql_n) {
            exit('商品添加成功!');
        } else exit('商品添加失败!');
    } else exit('商品添加失败!');
} else exit('商品添加失败!');</code>

现在像这样的sql ~ sql_n 这样的情况我只判断关键的几条sql就敢返回 商品添加成功,或者 商品添加失败 其它的一些sql就假设它会成功,不用判断,我当时小不懂事,一直就是这么来的,看别人的吗好像也是这样的,只判断关键的sql if ($sql) return 1; else return 0;其它的都假设会成功,我现在觉得这样不靠谱,求大神解答啊

希望大神结合实例说明,解除我心中的困惑,感激不敬 ^_^

好长的问题……但感觉你想问你你已经知道答案了呀。
事务就是解决这个问题的好方案。

至于你说的DZ或者WP没有使用事务也照样安全,这句话并不是正确的,只是大部分时候他们所承载的并发并不大,所以显得很安全而已。

不是大神,简单讲一下自己的理解

首先第一个问题,需要考虑的并不是sql出错了,要担心的应该是数据 写/读 冲突,sql没有报错你却不知道的这个情况。
比如注册的并发,使用PRIMARY/UNIQUE就好了,然后直接INSERT,也犯不着先去查一遍username是否存在,让数据库自己解决这个问题。

第二个问题,返回错误了就告诉用户用户名已经占用了就好,各个业务逻辑部分所执行的sql指令自然有它的意义,既然错误了,那就告诉用户错误了,为何纠结于此?

第三个问题,如果这个数据操作的错误会是致命的,你在上线之前就应该以更完善的逻辑来处理,不应该出现程序异常终止的情况,因为对于数据的操作,在未知的位置停止程序,数据的结果也是未知的。保证数据的完整性和可知性应该为第一目的。

第四个问题,对于高并发读写来说,选择InnoDB应该是更合理的并且性能也是更好的,毕竟有行锁;并发冲突不会很严重的使用MyISAM引擎会有更好的性能和更小的开销;至于是否使用事务,只能看具体的业务,再衡量了。如果像是支付宝这种,需要高安全性,却又不想让用户等太久,同时还有超高的并发,可能会采用分布式+堆积队列+事务的形式了。但是银行这种需要超高可靠性的系统,我听说都是拿大型机抗下来的。

给你找了一些资料,参考一下:
http://jm-blog.aliapp.com/?p=3483 //淘宝中间件-消息队列系统
http://tech.meituan.com/innodb-lock.html //美团点评团队-事务级别和锁
http://mysql.taobao.org/monthly/2015/12/01/ //阿里云RDS-InnoDB事务

开启事务可以较为简单而且直接地解决你遇到的问题,而且对现有代码的改变量也是最小的。至于你说的值不值得,因为所有事务只有在写入时才开启,而且innoDB的锁又是基于行的,所以在并发写入不是特别大的时候对系统的影响是非常小的。

1:开启事务处理
2:如果发现sql出错就是那个unique索引,直接提示用户,网络繁忙,或者请更换一个用户名试试,另外注册的时候加验证码有效避免同一时间很多并发注册请求

好长,来晚了,但是我要指出一个很重要的事情:

错误处理是程序的正常、并且是非常重要的一部分。你不需要保证每个调用都不出错,而要尽量保证妥善处理这个错误。

事务对我来说是必须的。我每接手一个涉及 MySQL 的项目,它就会多出好多事务处理。因为我没心情去处理用户点快了导致数据库出现不该出现的数据之类的情况。

开启事物

sql1
...
sqln

有异常就回滚,没异常就提交。

基本逻辑就是上面的样子。sql只要有一条没成功,就会回滚所有。所有sql都成功,才会真正提交。

可以考虑把name 字段在数据表中设置为unique 在验证是否有人已经注册时直接插入,如果成功则没有人应用。

不是大神,帮你解答一下,
1,你考虑的SQL失败的问题,这个不算是问题,SQL失败了就给一个友好的提示即可,但是同时要触发报警系统,搞这个系统的很快就会修复的。
2,一般情况下只要是涉及到很重要的数据必须要使用事务,事务的基本原则就不讲了,学计算机的都应该熟练掌握,特别是后端
3,一般大公司会有独立的用户系统,这个系统一般都是很通畅的,一般采用CAS模式单点登录
4,关于安全,看你怎么理解了,没有绝对的安全,但是要做到尽量不要让别人钻空子,SQL注入什么的还是要防的,看你写php的,尽量使用一些框架吧,可以先读读这些框架的原理

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
PHP의 지속적인 사용 : 지구력의 이유PHP의 지속적인 사용 : 지구력의 이유Apr 19, 2025 am 12:23 AM

여전히 인기있는 것은 사용 편의성, 유연성 및 강력한 생태계입니다. 1) 사용 편의성과 간단한 구문은 초보자에게 첫 번째 선택입니다. 2) 웹 개발, HTTP 요청 및 데이터베이스와의 우수한 상호 작용과 밀접하게 통합되었습니다. 3) 거대한 생태계는 풍부한 도구와 라이브러리를 제공합니다. 4) 활성 커뮤니티와 오픈 소스 자연은 새로운 요구와 기술 동향에 맞게 조정됩니다.

PHP 및 Python : 유사점과 차이점을 탐구합니다PHP 및 Python : 유사점과 차이점을 탐구합니다Apr 19, 2025 am 12:21 AM

PHP와 Python은 웹 개발, 데이터 처리 및 자동화 작업에 널리 사용되는 고급 프로그래밍 언어입니다. 1.PHP는 종종 동적 웹 사이트 및 컨텐츠 관리 시스템을 구축하는 데 사용되며 Python은 종종 웹 프레임 워크 및 데이터 과학을 구축하는 데 사용됩니다. 2.PHP는 Echo를 사용하여 콘텐츠를 출력하고 Python은 인쇄를 사용합니다. 3. 객체 지향 프로그래밍을 지원하지만 구문과 키워드는 다릅니다. 4. PHP는 약한 유형 변환을 지원하는 반면, 파이썬은 더 엄격합니다. 5. PHP 성능 최적화에는 Opcache 및 비동기 프로그래밍 사용이 포함되며 Python은 Cprofile 및 비동기 프로그래밍을 사용합니다.

PHP와 Python : 다른 패러다임이 설명되었습니다PHP와 Python : 다른 패러다임이 설명되었습니다Apr 18, 2025 am 12:26 AM

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP와 Python : 그들의 역사에 깊은 다이빙PHP와 Python : 그들의 역사에 깊은 다이빙Apr 18, 2025 am 12:25 AM

PHP는 1994 년에 시작되었으며 Rasmuslerdorf에 의해 개발되었습니다. 원래 웹 사이트 방문자를 추적하는 데 사용되었으며 점차 서버 측 스크립팅 언어로 진화했으며 웹 개발에 널리 사용되었습니다. Python은 1980 년대 후반 Guidovan Rossum에 의해 개발되었으며 1991 년에 처음 출시되었습니다. 코드 가독성과 단순성을 강조하며 과학 컴퓨팅, 데이터 분석 및 기타 분야에 적합합니다.

PHP와 Python 중에서 선택 : 가이드PHP와 Python 중에서 선택 : 가이드Apr 18, 2025 am 12:24 AM

PHP는 웹 개발 및 빠른 프로토 타이핑에 적합하며 Python은 데이터 과학 및 기계 학습에 적합합니다. 1.PHP는 간단한 구문과 함께 동적 웹 개발에 사용되며 빠른 개발에 적합합니다. 2. Python은 간결한 구문을 가지고 있으며 여러 분야에 적합하며 강력한 라이브러리 생태계가 있습니다.

PHP 및 프레임 워크 : 언어 현대화PHP 및 프레임 워크 : 언어 현대화Apr 18, 2025 am 12:14 AM

PHP는 현대화 프로세스에서 많은 웹 사이트 및 응용 프로그램을 지원하고 프레임 워크를 통해 개발 요구에 적응하기 때문에 여전히 중요합니다. 1.PHP7은 성능을 향상시키고 새로운 기능을 소개합니다. 2. Laravel, Symfony 및 Codeigniter와 같은 현대 프레임 워크는 개발을 단순화하고 코드 품질을 향상시킵니다. 3. 성능 최적화 및 모범 사례는 응용 프로그램 효율성을 더욱 향상시킵니다.

PHP의 영향 : 웹 개발 및 그 이상PHP의 영향 : 웹 개발 및 그 이상Apr 18, 2025 am 12:10 AM

phphassignificallyimpactedwebdevelopmentandextendsbeyondit

스칼라 유형, 반환 유형, 노조 유형 및 무효 유형을 포함한 PHP 유형의 힌트 작업은 어떻게 작동합니까?스칼라 유형, 반환 유형, 노조 유형 및 무효 유형을 포함한 PHP 유형의 힌트 작업은 어떻게 작동합니까?Apr 17, 2025 am 12:25 AM

PHP 유형은 코드 품질과 가독성을 향상시키기위한 프롬프트입니다. 1) 스칼라 유형 팁 : PHP7.0이므로 int, float 등과 같은 기능 매개 변수에 기본 데이터 유형을 지정할 수 있습니다. 2) 반환 유형 프롬프트 : 기능 반환 값 유형의 일관성을 확인하십시오. 3) Union 유형 프롬프트 : PHP8.0이므로 기능 매개 변수 또는 반환 값에 여러 유형을 지정할 수 있습니다. 4) Nullable 유형 프롬프트 : NULL 값을 포함하고 널 값을 반환 할 수있는 기능을 포함 할 수 있습니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SecList

SecList

SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경