秒杀类的问题一直都是web领域比较热点的问题,一个超高并发的网站需要考虑从产品、前端优化、站点部署及后端服务等等所有环节进行考虑。mysql所能抗住的写压力是一定的,高并发的web站点,你需要在数据持久化之前控制好压力,而不是把所有的请求都落到数据服务这一层。今天我不在这篇文章里讨论秒杀整体设计的问题(我也没这个资格),我们讨论的是如何在流速已经得到控制的情况下,如何利用mysql更安全、高效的解决这个问题。

从网上可以看到各种各样的实现方案,现在针对这些方案及其优缺点和理解误区进行讨论。

常见写法安全性及效率分析

假设我们的商品表的schema是下面这样的:

1

2

3

4

5

6

7CREATE TABLE `goods` (

`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '自增id',

`name` varchar(256) NOT NULL DEFAULT '' COMMENT '商品名称',

`available` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '库存剩余量',

`stock` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '总库存量',

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品表'

设置为字段无符号解决1

2

3

4

5

6

7

8

9num = select available from goods where id = xx ;

if(num > 0){

affectRows = udpate goods set available = available - 1 where id = xx ;

if(affectRows == 1){

return ok ;

}else{

return fatal ;

}

}

解法释义

这种做法大家的想法是我们将库存字段设置成无符号类型,这样当库存字段在sql执行时候被置为负数的时候mysql就会报错,那么affectRow就会是0或者可以捕获到这个异常,从而实现并发下的数据安全。

实际上这段代码是危险的,因为在不同版本的mysql和配置下,这段代码的表现完全不同。具体的情况会出现3种不同的结果:1.代码正常运行,执行update的时候报错

2.代码最终执行结果出现了 -1

3.最终update操作之后,available变成了一个很大的数目

为什么会出现这三种情况呢?

我想在学习开始学习计算机的时候都讲过计算机的加减法计算方法。

1

2

3

4

5

6

7

8

9

10思考一下,无符号2 减去 无符号3 在计算机中的运算是什么样的?

2 - 3 = 2 + (-3)

假设我们的计算机是4位的,2的补码表示:0010,-3的补码表示为1011

那么加和的结果是

0010

1011 +

------

1111 =

1111解释为有符号数是多少呢? -1

1111解释为无符号数是多少呢? 15

所以呢?

如果mysql不做任何处理的话,你的无符号数减法的结果不会报错,最终你算出来的库存还是一个非常大的值(可怕)。

但是幸运的是mysql 后来的版本帮你做了这件事情(具体哪个版本我也不清楚),所以如果是mysql做了无符号检测的话,如果减出的结果是负值,会报错,这是大多数人期待的结果。

-1这种情况是需要你设置一下sqlmode的,这也是会出现的情况。

解法总结这个办法很多人用的时候没问题,那只能说明可能是机缘巧合,但是对于业务代码而言,不能靠碰运气,需要消除不确定性、缩小迁移成本。

如果你想采用这种办法,辛苦你把你们msyql相应的版本及配置搞清楚,确定无符号在你所在的版本会出现什么结果。

select for update

解法释义

读取时候就开始加排他锁也是网上常见的办法之一,具体实现如下:

1

2

3

4

5

6

7

8

9begin tran ;

num = select avaliable from goods where id = xxx for update;

if (num >= 0){

affectNum = udpate goods set available = available - 1 where id = xx ;

commit ;

return affectNum ;

}else{

rollback ;

}

该解法在用户读取的时候对相应的数据加排他锁,保证自己在更新的时候该行的数据不会被别的进程更改.所有写请求及排他锁加锁都会被阻塞。

想想这样的情况,A进程执行过程中,出现死机的情况导致commit/rollback请求没有被发送到mysqlserver,那么所有请求都会锁等待。

解法总结低流量可以采用这种办法来保证数据的安全性

性能低下,平均需要发送4次mysql请求,同时会造成所有同类请求锁等待。

常见问题

select for udpate 需要在显式的指定在事务代码块执行,不然不会起作用。很多网友都理所当然的人为select for update直接就可以加排他锁

排他锁的释放是在rollback/commit 动作完成才会释放,不是在update操作之后。mysql innodb执行两段锁协议,加锁阶段只加锁,解锁阶段只解锁。

采用事务,先查后写再查,确保没问题

解法释义

这时候的available设置为有符号类型,解决方案一的问题

1

2

3

4

5

6

7

8

9

10

11

12begin tran ;

num = select available from goods where id = xx ;

if(num > 0){

//实际需要关心这里的返回值,这里不考虑

udpate goods set available = available - 1 where id = xx ;

num_afterupdate = select available from goods where id = xx ;

if(num_afterupdate < 0 ){

rollback ;

}else{

commit ;

}

}

这种解法区分于第一种的办法在于,加了事务、available类型更改、采用了更新后确认的形式,尝试解决问题。

我们都知道数据库的事务隔离级别有4种:

RU,RC,RR,Serializable。

我们常见的innodb中RR模式是可以保证可重复读,意思是在同一个事务内部,多次读取的结果是一致的。那么最后一次的读取对于RR隔离级别实际上是无效的。

RC模式下,这个代码是可用的,每次请求可以确保自己的进程不会超发。

解法总结RR、RC模式下结果不一致.RR下不可保证安全、RC可以。

性能不高,一次业务请求到mysql的转化为 1 : 5。

这种解法就像老奶奶锁门,总是不放心自己到底锁了没有,走了几步再回来看看,实际上有些时候是徒劳。

update语句增加available查询条件

解法释义1udpate goods set available = available - 1 where id = xx and available - 1 >= 0 ;

这个语句如果可以的话,那么他的性能必然是上面锁提到的方法中最优的.

问题的关键就集中在怎么证明这句的安全性的。

我们都知道update操作对于id为主键索引的情况下,是会对数据加行锁。

其实update操作在mysql内部也是一个先查后改的过程,这个过程如果是原子的,那么可以保证update语句是串行的,那我们就来看一下update语句在mysql内部的执行过程。

大家有的另一个误区是单条语句不是事务,实际上单条sql也是一个事务。

那么对于上面这个语句,一样遵循两段锁协议。

update执行的过程,会去查询满足条件的行并加锁,这个加锁是innodb做的,那么就可以保证别的事务必须等到该事务执行完了之后才能获得锁,此时拿到最新数据。

解法总结语句安全、效率最优(我的认知里)

采用设置库存而不是扣减库存

这几天我把类似的文章几乎翻了一遍,唯一看到批评我的上一条做法的是我的那个做法是不具备幂等性的。

那么他们的解法是这样的,采用设置而不是扣减,代码如下:

1

2

3num_old = select available from goods where id = xx and available >= 1 ;

num_new = num_old - 1 ;

update goods set num=num_new where id=xx and num=num_old ;

这段代码也是安全的,采用的是乐观所的理念来完成的操作。

总结上面的做法,最后两个是相对安全的,但是你的库存字段还是要设置为无符号,关于是否幂等,要看结合请求看,不是单个扣减块代码。

较真是一个学习的过程,只有较真才能把这些概念搞清楚。如果你需要完全弄懂这些内容,可能你需要对mysql锁、事务、mvcc这些概念都做一下预习。

感谢工作过程中小伙伴们的努力,让我们把问题追查的更清楚。

引用何登成的技术博客mysql udpate流程学习

幂等性做法来源使用设置库存代替库存扣减

mysql商品库存字段_mysql商品库存扣减问题总结相关推荐

  1. mysql 单选字段_mysql字段类型

    学习自 https://www.cnblogs.com/jennyyin/p/7895010.html,感谢原博主的奉献 mysql支持多种类型,大致可以分为三类:数值.字符串.日期/时间. 数值类型 ...

  2. c 读取mysql 时间字段_MySQL中的时间字段的几种数据类型比较

    1.序言 ​ 最近在项目开发时,对于MySQL数据库中的有关时间的字段该选用何种类型,引发了一些争论.所以做了一些简单的研究,看了一些blog,和官方文档.最后做出一个自己的总结. 2.类型比较 IN ...

  3. mysql blob 字段_MySQL中TEXT与BLOB字段类型的区别

    在MySQL中有两个字段类型容易让人感觉混淆,那就是TEXT与BLOB,特别是自己写博客程序的博主不知道改为自己的博客正文字段选择TEXT还是BLOB类型. 下面给出几点区别: 一.主要差别 TEXT ...

  4. mysql所以字段_MySQL|mysql-索引

    原标题:MySQL|mysql-索引 1.索引是什么 1.1索引简介 索引是表的目录,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存 ...

  5. mysql 枚举字段_MySQL字段中的枚举

    MySQL字段相信大家都有一些了解,下面将为您介绍的是MySQL字段中的枚举,希望对您学习MySQL字段方面能够有所帮助. MySQL字段中的枚举: mysql> create table me ...

  6. mysql 备用字段_MySql 命令大全(转载备用)

    http://www.cnblogs.com/zhangzhu/archive/2013/07/04/3172486.html 1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用 ...

  7. mysql 排除字段_mysql中select某表时排除个别字段,shell实现

    在使用mysql的日常生活中,偶尔会有一些奇奇怪怪的需求,例如实现类似:select * EXCEPT password,address from users; 要想从users信息表中查询用户信息, ...

  8. mysql 超长字段_Mysql命令行插入字段超长不报错,而jdbc报错问题分析

    异常信息 exception.ServiceException: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long ...

  9. mysql blob 字段_mysql BLOB字段类型用法介绍

    在mysql中,BLOB类型的字段用于存储二进制数据. MySQL中,BLOB是个类型系列,包括:TinyBlob.Blob.MediumBlob.LongBlob. 这几个类型之间的唯一区别: 存储 ...

  10. mysql set字段_MySQL的SET字段类型

    SET是一个字符串对象,可以有零或多个值,其值来自表创建时规定的允许的一列值.指定包括多个SET成员的SET列值时各成员之间用逗号(',')间隔开.这样SET成员值本身不能包含逗号. 例如,指定为SE ...

最新文章

  1. 2022-2028年中国抗盐粘土行业发展现状调查及前景战略分析报告
  2. Spring-Aop-注解实现
  3. 《VMware 网络技术:原理与实践》—— 3.2 以太网
  4. vue中使用watch监听$route 无效问题
  5. java删除字符串最后一位
  6. IE9真的支持CSS3和HTML5?
  7. 消息队列(2):常见的消息队列协议
  8. 数论 —— 素性测试
  9. (转)Spring+JDBC组合开发
  10. K2 Blackpearl中从数据库直接删除流程实例之K2Server表
  11. shell 踢人办法
  12. opencv python3 找图片不同_使用OpenCV和Python查找图片差异
  13. HFSS天线设计笔记-------主极化与交叉极化
  14. qpsk相点 matlab,qpsk调制解调matlab仿真程序详解
  15. zabbix3.0监控详解
  16. 新浪博客服务器是不是在维护,新浪博客是不是又在升级了?
  17. 计算机怎么查文件打印记录表,打印机历史记录
  18. 如何将小程序内置非promise API转换为promise对象(风袖小程序的学习)
  19. 计算机数据表格方框,excel表格数据变框框了-Excel表格弄着弄着突然变成这样了,内容方框全无,有......
  20. Alien Skin Exposure X7离线安装(PS/LR胶片滤镜模拟插件)

热门文章

  1. 14岁女孩寒夜蹭网上课看哭众人:不吃读书的苦,就要吃生活的苦
  2. Android平台的音乐资源管理与播放
  3. mysql 实例结构体_C语言结构体实例-创建兔子
  4. 违章查询功能如何实现
  5. STATA 学习笔记: outlier(离群值)的处理
  6. 7个实用的免费网站托管站点
  7. 如何评价腾讯云游戏平台 START ?
  8. Java实现QQ邮箱验证码发送
  9. 什么是零信任--用户/应用/设备--识别/认证/权限/信任
  10. pmos低电平驱动_驱动篇 -PMOS管应用