V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
? MySQL 5.5 Community Server
? MySQL 5.6 Community Server
? Percona Configuration Wizard
? XtraBackup 搭建主从复制
Great Sites on MySQL
? Percona
? MySQL Performance Blog
? Severalnines
推荐管理工具
? Sequel Pro
? phpMyAdmin
推荐书目
? MySQL Cookbook
MySQL 相关项目
? MariaDB
? Drizzle
参考文档
? http://mysql-python.sourceforge.net/MySQLdb.html
sheldondai
V2EX  ?  MySQL

mysql update 库存问题

  •  2
     
  •   sheldondai · 2016-02-01 22:33:51 +08:00 · 6841 次点击
    这是一个创建于 3012 天前的主题,其中的信息可能已经有所发展或是发生改变。

    腾讯云最新优惠活动来了:云产品限时1折,云服务器低至88元/年 ,点击这里立即抢购:9i0i.cn/qcloud,更有2860元代金券免费领取,付款直接抵现金用,点击这里立即领取:9i0i.cn/qcloudquan

    (福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

    Mysql 5.6
    最近在做一些类似秒杀的业务场景,遇到了库存问题,按我之前的做法我们都没有直接使用 mysql 的update -1操作,因为担心超卖,但是一位同事说只要把需要 update 的字段设为无符号型就可以解决超卖的问题,我试了下,确实如此。但这里的疑惑是网上所以的文章都是 mysql 在高并发 update -1 的情况下会超卖。
    比如这篇 http://www.cnblogs.com/billyxp/p/3701124.html 里面写到

    II : 其次,超卖的根结在于减库存操作是一个事务操作,需要先 select ,然后 insert ,最后 update -1 。最后这个-1 操作是不能出现负数的,但是当多用户在有库存的情况下并发操作,出现负数这是无法避.免的。

    25 条回复  ?  2016-10-22 17:22:54 +08:00
    Sunyanzi
        1
    Sunyanzi  
       2016-02-01 22:40:11 +08:00
    一个提示 ... Redis 的所有命令都是原子性的 ... 其中包括 INCRBY ...

    今天净碰到商城相关的问题是为什么 ...
    yangqi
        2
    yangqi  
       2016-02-01 22:45:12 +08:00
    你给的文章也说明白了, mysql 就是一个数据库, 高并发的问题解决不了,也不应该让 mysql 来解决,只能把逻辑提出来处理。
    sheldondai
        3
    sheldondai  
    OP
       2016-02-01 22:49:17 +08:00
    @yangqi 现在的问题是同事问我说他只要把字段设为 unsigned int 就不会出现库存为负情况,我测试了下确实如此,没有出现超卖
    yangqi
        4
    yangqi  
       2016-02-01 22:56:48 +08:00
    @sheldondai 你这个只能保证库存不为负。但是如果库存为 1 的时候,多个人同时下单成功的情况你怎么处理?
    sheldondai
        5
    sheldondai  
    OP
       2016-02-01 23:01:38 +08:00
    @yangqi 测试结果是只有一个可以 update 成功
    realpg
        6
    realpg  
       2016-02-01 23:03:08 +08:00   ?? 1
    如果纯在 mysql 的场景下操作,不用内存 key-value 系统,我更倾向于用另外一种模型处理这种竞争抢购的逻辑。

    “需要先 select ,然后 insert ,最后 update -1 。最后这个-1 操作是不能出现负数的”

    我可以分享一下我的思路,以及我的做法。

    首先,楼主以及这个文章都提到超售的问题,如果你的系统设计上能出现超售,那说明你的逻辑太差。就这种 select insert update-1 的模型,在抢购的瞬间是怎么操作的呢?我们分析一下:

    首先 用户 submit_order, 先去 select 库存,判断有,然后 insert 一个 order 表数据,然后 update 库存减 1

    在 MYSQL 并发非常高的瞬间,这三部操作,都可能出现大坑,只要三步中一个操作被锁坑了,整体就坑了。

    然后我们再抽象:假设抢购 50000 件商品

    抢购前,库存表一条记录 值 50000 ,订单表 0 条记录
    抢购后,订单表 50000 条记录,库存表 1 条记录,值 0

    在抢购过程中(假设非常火, 5 秒钟抢完)
    中间 select 次数远大于 50000 (抢购失败的也得查询库存), update50000 次库存表-1 , insert50000 个订单表 这么多查询吧。这三步是一个顺序逻辑,任何一步出问题整个操作都失败。

    我的抢购系统,绝对不会设计的!

    我有 50000 件商品,我在抢购开始前一个小时到抢购前 5 分钟, 55 分钟的时间里,写入订单表 50000 条记录。其中 orders 表的 uid 做 unique 约束。 UID 使用-1~-50000 预先填写好
    我有 55 分钟时间插入 50000 条记录,不用 5 秒钟, MYSQL 无负载无压力

    开始抢购,所有人就开放一个接口,拼命地往数据库里提交一个查询
    update ignore `orders` set uid = '你的 UID' where uid<0 and item=3 limit 1
    不用等执行结果 完全非阻塞的只接受一个指令 就行了

    整个抢购过程,就是一群人发一个查询往里去,等管理端发现 50000 个记录确认都有主了,抢购结束标志设置好,关闭抢购接口,完成抢购。大家可以访问我的订单看抢购结果了。
    sheldondai
        7
    sheldondai  
    OP
       2016-02-01 23:10:14 +08:00
    @realpg 兄弟这个做法和我们之前的做法类似,确实可以保证不会超发。不过今天跑到 V2EX 上主要是想解决我同学问我的问题,在 mysql 性能够用的情况下,对一个无符号的 int 进行 updte-1 也可以保证不超发
    moro
        8
    moro  
       2016-02-01 23:15:21 +08:00
    begin
    select .... for update;
    update .... where ....;
    commit;
    yangqi
        9
    yangqi  
       2016-02-01 23:15:50 +08:00
    @sheldondai 你库存和订单存在一个表里?你得先说清楚你的操作流程,不然不好判断
    realpg
        10
    realpg  
       2016-02-01 23:16:52 +08:00
    @sheldondai
    不只是超售的问题,超售属于算法 0 分不及格。
    性能差,抗压能力差两个量级。
    skydiver
        11
    skydiver  
       2016-02-01 23:30:15 +08:00
    不能小于 0 ,所以改成无符号整数,然后库存变成了 4294967295 ……
    sheldondai
        12
    sheldondai  
    OP
       2016-02-01 23:30:42 +08:00
    @yangqi 是这样的,我们不是专门做电商公司。是现在有个小活动让用户抽奖,有个同事的方法是这样,有个表保存库存量
    ```
    1. 有用户请求,检查库存是否大于 0
    2.大于 0 的话,则 update-1 ,如果 update 成功就算抽奖成功
    ```
    当然中间计算概率的在这省略了。


    @realpg 感谢,我们现在并发更高的项目里用的是另外两种方法,一种和你们的类似,另一种是使用了 redis 。效果也都不错。今天主要是想问下 update-1 的问题
    sheldondai
        13
    sheldondai  
    OP
       2016-02-01 23:31:13 +08:00
    @skydiver 我们测试不会出现这种情况, mysql5.6
    wy315700
        14
    wy315700  
       2016-02-01 23:34:44 +08:00
    @sheldondai
    提前把每个货物分开
    使用事务,有请求的时候,先把库存表里锁住一个货物,然后处理完改状态,
    yangqi
        15
    yangqi  
       2016-02-01 23:44:11 +08:00
    @sheldondai 这样自然是可行的,不过有个前提就是 mysql 的 SQL_MODE 不能为'NO_UNSIGNED_SUBTRACTION', 否则 update 不会出错,只会返回 warning.

    你这个和秒杀类的不一样,要简单多了
    sheldondai
        16
    sheldondai  
    OP
       2016-02-01 23:58:37 +08:00
    @yangqi 我们用的阿里云的 RDS ,刚查了下是空。但执行 sql 确实报错了
    BIGINT UNSIGNED value is out of range in '(`yao_admin`.`article`.`status` - 1)'。

    这个设置之前不知道,学习了。那网上很多文章里说的库存减为负的是怎么出现的呢
    yangqi
        17
    yangqi  
       2016-02-02 00:10:59 +08:00
    @sheldondai 那些自然是字段没有设为 unsigned ,因为这样没用,而且也不是最优方案。因为 update 失败后返回错误,你还要检查错误是否是 out of range 。如果只看是否成功,出了其他问题没有 update 成功都算作库存为 0 的话问题就来了。
    realpg
        18
    realpg  
       2016-02-02 02:07:58 +08:00
    @sheldondai
    现在都是依托内存 kv 引擎做这种东西了。性能极强,抗压能力提升三个量级以上。

    我很早就搞过这种东西的算法,各种避免 select 再 update 的模型,最开始的理由极其朴素
    你有没有发现我提到的这种算法可以不依赖支持事务的引擎?

    最开始就是在高并发的前提下, MyISAM 引擎上跑类似这种逻辑,才提出的这种方案……
    neoblackcap
        19
    neoblackcap  
       2016-02-02 02:45:19 +08:00
    @realpg 你这用法感觉跟游戏公司一个用户一个表有异曲同工之妙啊。
    br00k
        20
    br00k  
       2016-02-02 09:04:48 +08:00 via Android
    @realpg ???求解决淘宝超售问题。
    ugg
        21
    ugg  
       2016-02-02 11:27:50 +08:00
    redis INCRBY + mysql update - 1 where > 0
    skydiver
        22
    skydiver  
       2016-02-02 13:03:12 +08:00
    @sheldondai 重点不是有没有这种情况,重点是解决问题的思路太奇葩……
    brucefeng
        23
    brucefeng  
       2016-02-02 13:21:03 +08:00
    秒杀系统的高峰 QPS 是多少,期望解决问题的成本有多高(比如开发量多大能接受)。

    如果没有这两个限制条件,讨论下去没有最优解,因为很多 NB 的技术太复杂,实现成本比较高。
    zacard
        24
    zacard  
       2016-02-02 16:37:38 +08:00
    先经过 redis 原子操作下
    xiaoyong
        25
    xiaoyong  
       2016-10-22 17:22:54 +08:00
    这个问题与我正要解决的问题相同,正准备试试 @realpg 的方法。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2262 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 06:11 · PVG 14:11 · LAX 23:11 · JFK 02:11
    Developed with CodeLauncher
    ? Do have faith in what you're doing.


    http://www.vxiaotou.com