看了一篇网友日志,感觉工作中值得借鉴,原文如下:

事故描述

在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达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在高并发场景下产生的一次事故的更多相关文章

  1. HttpClient在高并发场景下的优化实战

    在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...

  2. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

  3. 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

    package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...

  4. 高并发场景下System.currentTimeMillis()的性能问题的优化

    高并发场景下System.currentTimeMillis()的性能问题的优化 package cn.ucaner.alpaca.common.util.key; import java.sql.T ...

  5. C++高并发场景下读多写少的解决方案

    C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...

  6. C++高并发场景下读多写少的优化方案

    概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...

  7. MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"

    本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”.“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视.以下实践为个人在实际开发工作中,针对相对“大数据” ...

  8. Mysql在高并发情况下,防止库存超卖而小于0的解决方案

    背景: 本人上次做申领campaign的PHP后台时,因为项目上线后某些时段同时申领的人过多,导致一些专柜的存货为负数(<0),还好并发量不是特别大,只存在于小部分专柜而且一般都是-1的状况,没 ...

  9. 高并发场景下System.currentTimeMillis()的性能优化

    一.前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法, 有时不得不使用, ...

随机推荐

  1. devm_xxx机制

    前言 devm是内核提供的基础机制,用于方便驱动开发者所分配资源的自动回收.参考内核文档devres.txt.总的来说,就是驱动开发者只需要调用这类接口分配期望的资源,不用关心释放问题.这些资源的释放 ...

  2. MACHINE_START与MACHINE_END【转】

    转自:http://blog.csdn.net/cxw3506/article/details/8475965 版权声明:本文为博主原创文章,未经博主允许不得转载. 在移植Linux时,有个结构体需要 ...

  3. C核心 那些个关键字

    概述 - C语言老了 目前而言(2017年5月12日) C语言中有 32 + 5 + 7 = 44 个关键字. 具体如下 O(∩_∩)O哈哈~ -> C89关键字 char short int ...

  4. 3.Python3标准库--数据结构

    (一)enum:枚举类型 import enum ''' enum模块定义了一个提供迭代和比较功能的枚举类型.可以用这个为值创建明确定义的符号,而不是使用字面量整数或字符串 ''' 1.创建枚举 im ...

  5. JS对象转化为JSON字符串

    js方法: JSON.stringify 把一个对象转换成json字符串 JSON.parse 把一个json字符串解析成对象. 实例: var jsObj = {}; jsObj.testArray ...

  6. linux命令(14):ifup/ifdown/ip addr命令

    开启网卡:ifup eth0 关闭网卡:ifdown eth0 查看网卡接入状态:ip addr[可查看哪块网卡up/down状态]

  7. 【转】Debug 运行正常,Release版本不能正常运行

    http://blog.csdn.net/ruifangcui7758/archive/2010/10/18/5948611.aspx引言 如果在您的开发过程中遇到了常见的错误,或许您的Release ...

  8. Linux下cp命令的使用说明

    [root@www ~]# cp [-adfilprsu] 来源档(source) 目标档(destination)[root@www ~]# cp [options] source1 source2 ...

  9. 前端读者 | CSS三角形和饼图

    @羯瑞 三角形 .triangle{width:0;height:0;border-width:50px;border-style:solid;border-color:red blue green ...

  10. 35.Spark系统运行内幕机制循环流程

    一:TaskScheduler原理解密 1,  DAGScheduler在提交TaskSet给底层调度器的时候是面向接口TaskScheduler的,这符合面向对象中依赖抽象而不依赖的原则,带来底层资 ...