关于秒杀

随着双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. PL/SQL学习笔记之函数

    一:函数 函数与过程的最大不同就是,函数有返回值.适用于需要返回结果的场景. 二:创建函数 CREATE [OR REPLACE] FUNCTION function_name [(parameter ...

  2. SoapUI Pro Project Solution Collection-change the JDBC Request behavior

    change the jdbc request : 1.change the driver name,connection string,query string or assert. the obj ...

  3. awk学习[参考转载]

    一.基本示例 1.last -n 5 | awk '{print $1}'   $0代表全部 2.last -n 5 | awk -F:  '{print $1}'    -F代表设置分割符, :代表 ...

  4. Linux 下hosts文件详解

    1.主机名: 无论在局域网还是INTERNET上,每台主机都有一个IP地址,是为了区分此台主机和彼台主机,也就是说IP地址就是主机的门牌号. 公网:IP地址不方便记忆,所以又有了域名.域名只是在公网( ...

  5. 【Linux】CentOs中yum与rpm区别

    一.源代码形式 1.      绝大多数开源软件都是直接以原码形式发布的 2.      源代码一般会被打成.tar.gz的归档压缩文件 3.      源代码需要编译成为二进制形式之后才能够运行使用 ...

  6. Atitit 快速开发体系建设路线图

    Atitit 快速开发体系建设路线图 1.1. 项目类型划分 哑铃型 橄榄型  直板型(可以立即实行)1 1.2. 解决方案知识库 最佳实践库 最佳流程优化(已成,需要一些整理)2 1.3. 功能模板 ...

  7. 苹果App Store审核指南中文翻译(更新至140227)

    前言 感谢您付出宝贵的才华与时间来开发iOS应用程程序.从职业与报酬的角度而言,这对于成千上万的开发员来说一直都是一项值得投入的事业,我们希望帮助您加入这个成功的组织.我们发布了<App Sto ...

  8. Python--Redis实战:第四章:数据安全与性能保障:第7节:非事务型流水线

    之前章节首次介绍multi和exec的时候讨论过它们的”事务“性质:被multi和exec包裹的命令在执行时不会被其他客户端打扰.而使用事务的其中一个好处就是底层的客户端会通过使用流水线来提高事务执行 ...

  9. Socket网络编程--小小网盘程序(4)

    在这一小节中实现了文件的下载,具体的思路是根据用户的uid和用户提供的文件名filename联合两张表,取得md5唯一标识符,然后操作这个标识符对应的文件发送给客户端. 实现下载的小小网盘程序 cli ...

  10. 【spark 深入学习 05】RDD编程之旅基础篇-01

    ---------------- 本节内容 1.RDD的工作流程 2.WordCount解说  · shell版本WordCount  · java版本WordCount -------------- ...