【转】记录PHP、MySQL在高并发场景下产生的一次事故
看了一篇网友日志,感觉工作中值得借鉴,原文如下:
事故描述
在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且直觉告诉我们,这是不可能的,就一直没有在意,直到后来真的发现了一个用户确实是42亿,当时我们整个公司都震惊了,如果有大量用户是这样的情况,公司要亏损几十万,我们的老大告诉我们,肯定是什么地方数据溢出的,最后我们一帮人,疯了似的查代码,发现……
如果按照正常的程序逻辑走下去,代码是完全没问题,但是我发现了一个地方,如果在高并发的情况下,是会走出错误的程序逻辑的,为什么我看到了,很简单,我在上一家公司,因为这个问题困扰了我一个多月,真是刻骨铭心呀!不多说了,说多了都是泪呀!上代码,重现场景(以下都是一些简单的代码,用来重现场景),道具名就定义为props。
<?php
mysql_connect("localhost", "mysql_user", "mysql_password");
mysql_select_db("user");
$consume_props_count = 10; // 要消耗的道具数量
$select = 'SELECT `count` from `user` where `userid`=123456';
$query = mysql_query($select);
$row = mysql_fetch_assoc($query); // 对比当前道具数量
if ($row['count'] < $consume_props_count) {
return false;
} // 扣除道具数量
$update = "update `user` set `count`=`count`-{$consume_props_count}";
$query = mysql_query($update);
return mysql_num_rows($query);
?>
大家可以看到,如果按照正常的扣除道具的流程来走,这个是没有问题的,但是在高并发场景下,两次扣道具的查询极有可能获取的是同一个结果,然后他们都能通过对比当前道具数量这一步逻辑,但是加入第一次的扣道具的操作把道具扣光了,或者扣的不够第二次继续扣了,想想会发生什么样的情况!
坑爹了!第二次会把道具数量扣为负数。
但是这依然不能解释这42亿的溢出那里来的!哎,祸不单行的古语再次应验了,我们的count字段的数据类型是Unsigned int,坑爹的MySQL,假如字段是Unsigned int,然后输入了一个负数,它就会让这个数字变为42亿这个巨大的数值。
看明白了吧!这42亿就是这么来的,坑啊!
解决方案的话,可以在update的sql改成这样
<?php
$update = "UPDATE `user` SET `count`=(CASE WHEN `count`<={$consume_props_count} THEN 0 ELSE `count`-{$consume_props_count} END) WHERE `userid`=123456"
?>
这样,就不会在扣成负数了,另外以防万一,还要将Unsigned int的字段类型,改为int。
摘自:http://h5b.net/php-mysql-high-concurrency-accident
【转】记录PHP、MySQL在高并发场景下产生的一次事故的更多相关文章
- HttpClient在高并发场景下的优化实战
在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器
package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...
- 高并发场景下System.currentTimeMillis()的性能问题的优化
高并发场景下System.currentTimeMillis()的性能问题的优化 package cn.ucaner.alpaca.common.util.key; import java.sql.T ...
- C++高并发场景下读多写少的解决方案
C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...
- C++高并发场景下读多写少的优化方案
概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...
- MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"
本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”.“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视.以下实践为个人在实际开发工作中,针对相对“大数据” ...
- Mysql在高并发情况下,防止库存超卖而小于0的解决方案
背景: 本人上次做申领campaign的PHP后台时,因为项目上线后某些时段同时申领的人过多,导致一些专柜的存货为负数(<0),还好并发量不是特别大,只存在于小部分专柜而且一般都是-1的状况,没 ...
- 高并发场景下System.currentTimeMillis()的性能优化
一.前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法, 有时不得不使用, ...
随机推荐
- python基础===【爬虫】爬虫糗事百科首页图片代码
import requests import re import urllib.request def getHtml(url): page = requests.get(url) html = pa ...
- Percona XtraDB Cluster(PXC)原理
Percona XtraDB Cluster(PXC)原理 介绍: PXC曾经属于一套近乎最完美的mysql高可用集群解决方案(现mgr总体上要优于pxc),相比传统的基于主从复制模式的集群架构MHA ...
- selenium 点击浏览器按钮
利用以下的方法,selenium 也可以模拟点击各种浏览器按钮:browser.back()点击“返回”按钮.browser.forward()点击“前进”按钮.browser.refresh()点击 ...
- sicily 数据结构 1014. Translation
Description You have just moved from Waterloo to a big city. The people here speak an incomprehensib ...
- 4.Python3标准库--算法
(一)functools:管理函数的工具 import functools ''' functools模块提供了一些工具来管理或扩展和其他callable对象,从而不必完全重写 ''' 1.修饰符 f ...
- UVALive 6426
UVALive 6426 /** 题意:给一个n*m的矩阵,求某一个区间的数的数量 做法:刚开始想用树状数组,但是RE,题目中说数据是从二进制流中读入, 用scanf会挂掉 所以用fread 读入 s ...
- jenkins上展示html报告【转载】
转至博客:上海-悠悠 前言 在jenkins上展示html的报告,需要添加一个HTML Publisher plugin插件,把生成的html报告放到指定文件夹,这样就能用jenkins去读出指定文件 ...
- oracle 11g安装教程
oracle 11g安装教程 第1步 第2步 第3步 第4步 第5步 第6步 第7步 第8步 第9步 第10步 第11步 第12步 第13步 第14步 第15步 第16步 第17步 第18步 第19步 ...
- cocos2djs ctor init onEnter的区别
cocos2d-html5 onEnter init ctor构造函数 ---js特有特性(和c++有点不一样 ctor 构造函数, new 一个对象的时候调用-----coco2d-js , 默认c ...
- HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)
Kth number Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...