关于秒杀

随着双11活动的不断发展,小米饥饿营销模式的兴起,“秒杀”已经成为一个热点词汇。在一些活动中,热销商品会以惊人的速度售罄,比如最近本人在抢购美图M4手机,12点开卖,1分钟之内就被售罄。

秒杀的实现

对于关注数据库的本人来说,更关心的是如何高效的实现秒杀应用。之前淘宝在2013年的数据库大会上分享过他们的秒杀方案,修改MySQL数据库源码来实现高效的秒杀应用。但是,那篇分享过于高大上,没有给出具体的实现过程。另外,从其他渠道打听到的是这个方案并没有在生产环境上线,不知道有没有其他知道内幕的小伙伴,具体来说说淘宝的方案是否有上线。

当然,有多种方法来优化秒杀应用,比如使用memcached的CAS功能,但是这些方法都不能实现事务的特性。对于深受Jim Gray事务处理教育长大的一代,本人觉得任何事情都应该事务的,不支持事务只不过能取得暂时的胜利,整个世界的哲学应该就是事务,即要么全做,要么全不做,不要处于一个中间状态。本人的为人哲学就是,要么不去设定一个目标,否则这个目标一定会去实现。比如,本人决定去读博,那么一定会完成这个学业。

本人感觉虽然淘宝没有给出具体的实现方式,但是抛出了秒杀应用对于数据库压力的问题所在,即大并发量下更新同一行数据的压力。例如并发执行如下的SQL语句模拟秒杀场景:

BEGIN;
INSERT INTO stock_log VALUES
SELECT count FROM stock WHERE id=1 AND count>0 FOR UPDATE;
UPDATE stock SET count = count -1 WHERE id=1 AND count > 0;
COMMIT;

在做秒杀时,最主要是对库存表进行操作,在操作前可能需要插入一些其他操作,比如日志等,然后就是对库存表进行更新。下图显示增大并发量的情况下,事务处理的性能:

显而易见的是随着并发量的增大,事务处理的性能越差。这和淘宝之前分享的数据基本一致。导致其中的原因就是秒杀是对同一件商品进行更新,需要对同一行记录加锁,因此秒杀操作虽然是并行的,但是在数据库层面是串行的。

随着并发的不断增大,不断发生事务的锁等待与唤醒操作,导致性能的急剧下降。如果通过perf工具来观察的话,应该可以观察到类似如下的内容:

#
59.06%  mysqld  mysqld         [.] lock_deadlock_recursive
16.63%  mysqld  libc-2.13.so   [.] 0x115171
3.09%   mysqld  mysqld         [.] lock_rec_get_prev
2.96%   mysqld  mysqld         [.] my_strnncollsp_utf8
...... 可以发现锁的死锁检测占据了大部分的CPU时间,究其原因,就是因为锁等待。

innodb_thread_concurrency

有小伙伴或许会知道可以通过innodb_thread_concurrency参数来控制InnoDB存储引擎层的并发量。的确,通过这个参数可以限制进入InnoDB引擎层的事务数量,对比测试的话,性能上的确会有一定的提升:

可以发现,将innodb_thread_concurrency设置为16,性能的确会有一定的提升。并发线程数在128的时候,TPS从原有的4300提升为了7200,将近有65%的性能提升。但是在256线程之后,性能依旧堪忧。

导致上述的原因是虽然在InnoDB存储引擎层做了“限流”,但是MySQL数据库上层的线程依然需要等待唤醒。

线程池技术

业界提供了很多关于秒杀MySQL的解决方案,然而非常的定制化,并且需要应用修改相信的程序,比如通过在SQL语句中写hint来进行排队,而这种的排队机制在我看来在低并发量下性能反而又会变差。因此,一个通用的解决方案是采用线程池技术。

线程池可以在MySQL上层限制住同时运行的MySQL的事务数,这样就解决了由秒杀而导致的资源竞争问题。例如,通过前面的测试,已经得知并发16线程时,秒杀可以有最好的性能,那么这时用户将线程池的大小设置为16,这样就能获得用户预期想要的性能:

可以发现即使在4096个并发线程下,秒杀依然可以有近10000的TPS。通过线程池技术,秒杀就是这么简单,无需任何应用端的修改。

但是线程池这里有个参数thread_pool_oversubscribe,这个参数其实有点类似云计算中“超售”概念,即MySQL的线程池允许有额外的线程运行。该参数默认是3,之前thread_pool_size设置为16,那么总共允许16*(1+3)=64个线程同时运行。这个参数的默认值本身没有问题,但是对于秒杀应用来说确是不需要的,因为之前已经讨论过,秒杀应用是串行的。所以将参数thread_pool_oversubscribe设置为1,秒杀应用还能有进一步的提升:

可以发现在大并发的线程下,性能还能有10%~30%的提升。

总结

其实秒杀应用的数据库层优化非常简单,各个层面做好排队即可,如:

  • 应用层做好对于单个商品抢购的数量限制

  • MySQL数据库层使用线程池技术来保证大并发量下的性能

  • 调整参数thread_pool_oversubscribe用来进一步提升性能

MySQL企业版提供了线程池插件,但是需要额外的费用。小伙伴们可以使用开源的MySQL版本InnoSQL,其免费提供了线程池,可以保证应用在大并发量下依旧保证应用的稳定性,特别是对于秒杀类的应用。

秒杀应用的MySQL数据库优化的更多相关文章

  1. 关于MySQL数据库优化的部分整理

    在之前我写过一篇关于这个方面的文章 <[原创]为什么使用数据索引能提高效率?(本文针对mysql进行概述)(更新)> 这次,主要侧重点讲下两种常用存储引擎. 我们一般从两个方面进行MySQ ...

  2. 【MySQL】花10分钟阅读下MySQL数据库优化总结

    1.花10分钟阅读下MySQL数据库优化总结http://www.kuqin.com2.扩展阅读:数据库三范式http://www.cnblogs.com3.my.ini--->C:\Progr ...

  3. 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决(转载)

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  4. 50多条mysql数据库优化建议

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 缺省情况下建立的索引是非群集索引,但有时它并不是最佳的.在非群集索引下,数据在物理上随机存 ...

  5. 解开发者之痛:中国移动MySQL数据库优化最佳实践(转)

    开源数据库MySQL比较容易碰到性能瓶颈,为此经常需要对MySQL数据库进行优化,而MySQL数据库优化需要运维DBA与相关开发共同参与,其中MySQL参数及服务器配置优化主要由运维DBA完成,开发则 ...

  6. 30多条mysql数据库优化方法【转】

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  7. 百万行mysql数据库优化和10G大文件上传方案

    百万行mysql数据库优化和10G大文件上传方案 最近这几天正在忙这个优化的方案,一直没时间耍,忙碌了一段时间终于还是拿下了这个项目?项目中不要每次都把程序上的问题,让mysql数据库来承担,它只是个 ...

  8. 从运维角度来分析mysql数据库优化的一些关键点【转】

    概述 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善. 1.数据库表设计 项目立项后,开发部根据产品部需求开发项目,开发工程师工作其中一部分 ...

  9. 关于mysql数据库优化

    关于mysql数据库优化 以我之愚见,数据库的优化在于优化存储和查询速度 目前主要的优化我认为是优化查询速度,查询速度快了,提高了用户的体验 我认为优化主要从两方面进行考虑, 优化数据库对象, 优化s ...

随机推荐

  1. art.template 循环里面分组。

    后台提供给我们一个数组,我们要用模版实现上面的格式输出怎么版呢? 下面就是解决方案: <h2>循环4个一组</h2> <script type="text/ht ...

  2. 解决 Docker pull 出现的net/http: TLS handshake timeout 的一个办法

    echo "DOCKER_OPTS=\"\$DOCKER_OPTS --registry-mirror=http://f2d6cb40.m.daocloud.io\"&q ...

  3. pycharm调整代码长度分割线

    1.File ->  Settings  ->  Code Style -> Right margin (columns) 的值为80,大功告成. 2.具体设置的数值可以根据个人电脑 ...

  4. vmlinux,zImage,bzImage,vmlinuz,uImage,关系

    zImage和uImage的区别 一.vmlinuz vmlinuz是可引导的.压缩的内核.“vm”代表“Virtual Memory”.Linux 支持虚拟内存,不像老的操作系统比如DOS有640K ...

  5. 学习下知然网友写的taskqueue

    博主在他的博客里对taskqueue的各种使用情况和使用方法都介绍的很清楚:http://www.cnblogs.com/zhiranok/archive/2013/01/14/task_queue. ...

  6. OpenCV 学习笔记 05 人脸检测和识别 AttributeError: module 'cv2' has no attribute 'face'

    1 环境设置: win10 python 3.6.8 opencv 4.0.1 2 尝试的方法 在学习人脸识别中,遇到了没有 cv2 中没有 face 属性.在网上找了几个方法,均没有成功解决掉该问题 ...

  7. GuavaCache学习笔记一:自定义LRU算法的缓存实现

    前言 今天在看GuavaCache缓存相关的源码,这里想到先自己手动实现一个LRU算法.于是乎便想到LinkedHashMap和LinkedList+HashMap, 这里仅仅是作为简单的复习一下. ...

  8. Socket网络编程--聊天程序(7)

    接上一小节,本来是计划这一节用来讲数据库的增删改查,但是在实现的过程中,出现了一点小问题,也不是技术的问题,就是在字符界面上比较不好操作.比如要注册一个帐号,就需要弄个字符界面提示,然后输入数字表示选 ...

  9. Google file system

    读完了Google file system论文的中文版,记录一下总结,懒得打字,直接上草图:

  10. html 用一个frame刷新另一个frame(同一个Frameset中)

    假设主页面index.html,其中frameset结构如下所示: <html> <head> <meta http-equiv="Content-Type&q ...