当前位置: 首页 > 科技观察

就这么简单:秒杀应用MySQL数据库优化

时间:2023-03-18 12:11:29 科技观察

关于秒杀随着双11活动的不断开展和小米饥饿营销模式的兴起,“秒杀”成为热词。在某些活动中,热销商品会以惊人的速度售罄。比如笔者最近抢购的美图M4手机,中午12:00开售,一分钟内就被抢购一空。秒杀的实现对于关注数据库的笔者来说,更关心的是如何高效的实现秒杀应用。此前,淘宝在2013数据库大会上分享了他们的秒杀计划,修改MySQL数据库源代码,实现高效秒杀应用。但是那个分享太高大上了,没有给出具体的实现过程。另外我从其他渠道听说这个方案还没有上线生产环境。不知道有没有其他知道内情的小伙伴,具体有没有上线淘宝的解决方案。当然秒杀应用的优化方法有很多,比如使用memcached的CAS功能,但是这些方法都不能实现事务的特性。对于被JimGray的事务处理教育出来的一代,笔者觉得一切都应该是事务性的。如果不支持交易,那只能是暂时的胜利。整个世界的哲学应该是交易性的,即要么全部做,要么根本不做,不在中间状态。作者的理念是,要么不设定目标,要么就达到目标。比如作者决定读博,他一定会完成这个研究。笔者感觉淘宝虽然没有给出具体的实现方法,但是已经抛出了秒杀应用对数据库的压力问题,即大并发下更新同一行数据的压力。例如并发执行如下SQL语句模拟秒杀场景:BEGIN;INSERTINTOstock_logVALUESSELECTcountFROMstockWHEREid=1ANDcount>0FORUPDATE;UPDATEstockSETcount=count-1WHEREid=1ANDcount>0;COMMIT;在秒杀的时候,最重要的就是对库存表进行操作,可能还需要Insert一些其他的操作,比如日志等,然后更新库存表。下图是并发量增加时事务处理的性能:很明显,随着并发量的增加,事务处理的性能越来越差。这与之前淘宝分享的数据基本一致。原因是秒杀更新同一个商品,需要锁定同一行记录。因此秒杀操作虽然是并行的,但是在数据库层面却是串行的。随着并发量的不断增加,事务的锁等待和唤醒操作不断发生,导致性能急剧下降。如果你通过perf工具观察,你应该能够观察到类似以下内容:#59.06%mysqldmysqld[.]lock_deadlock_recursive16.63%mysqldlibc-2.13.so[.]0x1151713.09%mysqldmysqld[.]lock_rec_get_prev2.96%mysqldmysqld[.]my_strnncollsp_utf8......可以发现锁的死锁检测占用了大部分CPU时间。原因是锁在等待。有innodb_thread_concurrency的朋友可能知道innodb_thread_concurrency这个参数可以用来控制InnoDB存储引擎层的并发。确实,通过这个参数,可以限制进入InnoDB引擎层的事务数量。在对比测试中,性能确实会有一定程度的提升:可以发现,将innodb_thread_concurrency设置为16,确实会对性能有一定程度的提升。当并发线程数为128时,TPS从原来的4300提升到7200,性能提升近65%。但是256个线程之后,性能还是堪忧。上面的原因是虽然InnoDB存储引擎层有“限流”,但是MySQL数据库上层的线程仍然需要等待唤醒。#p#线程池技术业界提供了很多秒杀mysql的解决方案,但是定制性很强,需要修改和信任程序,比如通过在sql语句中写hints来排队,而这种排队机制就在里面在我看来,低并发下性能会变差。因此,一种常见的解决方案是使用线程池技术。线程池可以限制MySQL上层同时运行的MySQL事务数,从而解决尖峰导致的资源竞争问题。比如通过前面的测试已经知道,当并发线程数为16时,秒杀可以有最好的性能,那么用户将线程池的大小设置为16,这样就可以达到用户所期望的性能得到:可以发现即便是在4096个并发线程下,秒杀依然可以有接近10000的TPS。借助线程池技术,秒杀就是这么简单,应用端无需任何修改。但是线程池里面有个参数thread_pool_oversubscribe。这个参数其实有点类似于云计算中“oversubscribe”的概念,即MySQL线程池允许额外的线程运行。该参数默认值为3,在thread_pool_size设置为16之前,一共允许16*(1+3)=64个线程同时运行。这个参数的默认值是没有问题的,但是对于秒杀应用来说是没有必要的,因为前面讲过,秒杀应用是串行的。因此,如果将参数thread_pool_oversubscribe设置为1,秒杀应用可以进一步提升:可以发现在大并发线程下,性能可以提升10%到30%。综上所述,秒杀应用的数据库层优化很简单,做好各级排队即可,如:应用层做好单次抢购数量限制产品。MySQL数据库层使用线程池技术保证大并发下的性能。调整参数thread_pool_oversubscribe为进一步提高性能,MySQL企业版提供了线程池插件,但需要额外付费。小伙伴们可以使用开源的MySQL版本InnoSQL,它免费提供了一个线程池,可以保证应用在大并发量下的稳定性,尤其是秒杀的应用。点击下方阅读原文下载InnoSQL5.5.30-v6/InnoSQL5.6.19-v1版本,提供免费线程池、双机高可用套件、TopSQL等插件、并行复制等功能。使用InnoSQL可以获得作者完整的免费技术支持,还等什么呢?