目次
回复内容:
ホームページ バックエンド開発 PHPチュートリアル 【大神在哪里】mysql并发的问题,能不能有办法避免sql出错的情况,数据的完整性怎才能做到……?

【大神在哪里】mysql并发的问题,能不能有办法避免sql出错的情况,数据的完整性怎才能做到……?

Jun 06, 2016 pm 08:15 PM
laravel mysql php sql thinkphp

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

<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 までご連絡ください。

ホット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ヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP:データベースとサーバー側のロジックの処理 PHP:データベースとサーバー側のロジックの処理 Apr 15, 2025 am 12:15 AM

PHPはMySQLIおよびPDO拡張機能を使用して、データベース操作とサーバー側のロジック処理で対話し、セッション管理などの関数を介してサーバー側のロジックを処理します。 1)MySQLIまたはPDOを使用してデータベースに接続し、SQLクエリを実行します。 2)セッション管理およびその他の機能を通じて、HTTPリクエストとユーザーステータスを処理します。 3)トランザクションを使用して、データベース操作の原子性を確保します。 4)SQLインジェクションを防ぎ、例外処理とデバッグの閉鎖接続を使用します。 5)インデックスとキャッシュを通じてパフォーマンスを最適化し、読みやすいコードを書き、エラー処理を実行します。

PHPの目的:動的なWebサイトの構築 PHPの目的:動的なWebサイトの構築 Apr 15, 2025 am 12:18 AM

PHPは動的なWebサイトを構築するために使用され、そのコア関数には次のものが含まれます。1。データベースに接続することにより、動的コンテンツを生成し、リアルタイムでWebページを生成します。 2。ユーザーのインタラクションを処理し、提出をフォームし、入力を確認し、操作に応答します。 3.セッションとユーザー認証を管理して、パーソナライズされたエクスペリエンスを提供します。 4.パフォーマンスを最適化し、ベストプラクティスに従って、ウェブサイトの効率とセキュリティを改善します。

PHP:サーバー側のスクリプト言語の紹介 PHP:サーバー側のスクリプト言語の紹介 Apr 16, 2025 am 12:18 AM

PHPは、動的なWeb開発およびサーバー側のアプリケーションに使用されるサーバー側のスクリプト言語です。 1.PHPは、編集を必要とせず、迅速な発展に適した解釈言語です。 2。PHPコードはHTMLに組み込まれているため、Webページの開発が簡単になりました。 3。PHPプロセスサーバー側のロジック、HTML出力を生成し、ユーザーの相互作用とデータ処理をサポートします。 4。PHPは、データベースと対話し、プロセスフォームの送信、サーバー側のタスクを実行できます。

PHPとWeb:その長期的な影響を調査します PHPとWeb:その長期的な影響を調査します Apr 16, 2025 am 12:17 AM

PHPは過去数十年にわたってネットワークを形成しており、Web開発において重要な役割を果たし続けます。 1)PHPは1994年に発信され、MySQLとのシームレスな統合により、開発者にとって最初の選択肢となっています。 2)コア関数には、動的なコンテンツの生成とデータベースとの統合が含まれ、ウェブサイトをリアルタイムで更新し、パーソナライズされた方法で表示できるようにします。 3)PHPの幅広いアプリケーションとエコシステムは、長期的な影響を促進していますが、バージョンの更新とセキュリティの課題にも直面しています。 4)PHP7のリリースなど、近年のパフォーマンスの改善により、現代の言語と競合できるようになりました。 5)将来的には、PHPはコンテナ化やマイクロサービスなどの新しい課題に対処する必要がありますが、その柔軟性とアクティブなコミュニティにより適応性があります。

なぜPHPを使用するのですか?利点と利点が説明されました なぜPHPを使用するのですか?利点と利点が説明されました Apr 16, 2025 am 12:16 AM

PHPの中心的な利点には、学習の容易さ、強力なWeb開発サポート、豊富なライブラリとフレームワーク、高性能とスケーラビリティ、クロスプラットフォームの互換性、費用対効果が含まれます。 1)初心者に適した学習と使用が簡単。 2)Webサーバーとの適切な統合および複数のデータベースをサポートします。 3)Laravelなどの強力なフレームワークを持っています。 4)最適化を通じて高性能を達成できます。 5)複数のオペレーティングシステムをサポートします。 6)開発コストを削減するためのオープンソース。

DockerによるMySQLを開始する方法 DockerによるMySQLを開始する方法 Apr 15, 2025 pm 12:09 PM

DockerでMySQLを起動するプロセスは、次の手順で構成されています。MySQLイメージをプルしてコンテナを作成および起動し、ルートユーザーパスワードを設定し、ポート検証接続をマップしてデータベースを作成し、ユーザーはすべての権限をデータベースに付与します。

PHP対Python:ユースケースとアプリケーション PHP対Python:ユースケースとアプリケーション Apr 17, 2025 am 12:23 AM

PHPはWeb開発およびコンテンツ管理システムに適しており、Pythonはデータサイエンス、機械学習、自動化スクリプトに適しています。 1.PHPは、高速でスケーラブルなWebサイトとアプリケーションの構築においてうまく機能し、WordPressなどのCMSで一般的に使用されます。 2。Pythonは、NumpyやTensorflowなどの豊富なライブラリを使用して、データサイエンスと機械学習の分野で驚くほどパフォーマンスを発揮しています。

docker lnmpの呼び方 docker lnmpの呼び方 Apr 15, 2025 am 11:15 AM

Docker LNMPコンテナコールステップ:コンテナの実行:Docker Run -D - Name LNMP -Container -P 80:80 -P 443:443 LNMPスタックコンテナIPを取得する:Docker Inspect LNMP -Container | GREP iPaddress Access Webサイト:http://&lt; container ip&gt;/index.phpsshアクセス:docker exec -it lnmp -container bash access mysql:mysql -u roo

See all articles