【阿里在线技术峰会精彩回顾】何登成:AliSQL性能优化与功能突破的演进之路

2022-02-25 00:00:00 数据库 线程 请求 图片 热点

本文根据阿里数据库专家何登成在首届阿里巴巴在线技术峰会上的分享整理而成。他主要介绍了AliSQL相对于MySQL进行的性能优化。通过大连接、高并发下的数据库稳定性保障和库存热点更新两个问题的解决方案介绍了高低水位限流和线程池的使用方法以及库存热点优化的三步改进,后提出了AliSQL的完整生态体系。


以下为整理内容。




背景:当使用官方的MySQL时,发现在我们的体量下会面临比较大的挑战,所以,2011年我们在MySQL的基础上做了一些功能增强和bug修复。在之后的版本中增加了并行复制,增强了线程池、热点补丁,加入了SQL防火墙。


AliSQL改进一览——性能优化



AliSQL改进一览——功能、稳定性增强



主要讲解一个很小的功能:在线执行计划优化。在MySQL内部,每一个SQL执行都会解析,在解析的过程中可能会解析出一个错误的执行计划,致使执行效率大大降低,甚至会把整个系统打爆。针对这个问题,其实Oracle是有解法的,即所谓的执行计划绑定。在在线执行计划优化技术出现之前,我们能做的是告诉应用我们现在有SQL走错了执行计划,让应用加hint把执行计划固定下来,这种做法耗时非常长,并且需要应用介入,对可持续服务是一个很大的挑战。所以,我们可以做这么一件事,在线把一个应用的执行计划绑定好。具体做法是:我们识别在线的SQL,对SQL进行模式识别,对于错误的SQL,我们在SQL发到MySQL之后再对它识别之后做一个hint来把执行计划固定下来。这样一个功能能够很快把整个系统的响应时间降下来,使业务马上恢复服务。


两个完整的故事


1、大连接、高并发下的数据库稳定性保障


大连接、高并发下的数据库会出现什么问题呢?



这是一个商品库的监控图。上边是threads running,用于在MySQL内部监控活跃线程数,下面是response time(响应时间),这两个是MySQL里面两个重要的指标。一般情况下,这两个数值都是非常低的,但是偶尔会出现图中的高峰,高峰的持续时间也很短,但是峰值特别高。



在压力很低的情况下,数据库为什么会时不时的停止服务?如上图所示,下面是数据库,上面是APP Servers,每一个应用服务器到达数据库的时间是不固定的。如果很多用户恰巧在同一时间访问相同商品的话,从系统看来就是几万个连接同时活跃,就会出现之前所说的问题。这个问题并不是阿里特有的问题,它其实是一个排队论的问题。


解决方案1:高低水位限流


高水位限流方法:当发现数据库的threads running超过一个阈值时,直接将超过阈值的连接数抛弃掉。这种方式虽然保证了部分请求能够顺利的执行并且有很好的响应时间,但是却抛弃了很大一部分的并发用户请求。



考虑到抛弃的不可行性,想到了排队,即低水位限流:在数据库内部做了一个队列,所有的用户请求到达的时候,先hash到队列上。当数据库出现并发的请求数激增时,可以先服务一批,服务完成之后再服务下一批,这样就可以在不抛弃的基础上保证数据库的稳定。低水位限流有了一些线程池的思想在里面,是一个比较粗的线程池。


解决方案2:线程池



处理并发请求,线程池是一个很好的解决思路,上图就是线程池的简单架构图。在MySQL中,一个用户请求对应到MySQL的一个工作线程。线程池是把用户请求线程和工作线程进行解耦,用户请求线程可以非常多,比如用户请求线程可以有成千上万,而工作线程只有固定的几十个上百个。把这些工作线程进行聚合,每一个Group里面有多个工作线程。用户线程通过哈希算法映射到Group中。如果Group中所有的工作线程都正在服务,那么就进行排队等待。为什么所有工作线程不放到一起,而是分开成一个个的Group?因为Group是可以与CPU的核进行绑定的,可以减少很多CPU的调度开销,这样又进一步提高了整个线程池的效率。


使用线程池时应该注意哪些问题?


  • 慢SQL是线程池的大敌!如果慢SQL比较多的话,所有的工作线程就会被占满,这样导致非常快的SQL也不能去抢占工作单元,相当于慢SQL把整个系统完全堵住了。所以,使用线程池时要对整个数据库系统进行一个很大的优化,把数据库的SQL性能进行比较好的调优,频繁执行的SQL不能是慢SQL。慢SQL的来源包括:正常业务SQL、定时任务、系统后台操作(例如:binlog dump)。

  • 无论任何原因导致线程池堵塞,确保管理命令不受影响!管理命令不应该走正常的业务线程池,而应该走一个单独的接口。当线程池堵塞时,管理命令可以通过Extra port将慢SQL处理掉。

  • 线程池和用户的登录请求也是有关的。登录到数据库的那些请求其实走的也是线程池。使用线程池之后,处理连接风暴的能力也会变弱。


2、库存热点更新



“双十一”时,有很多商品是大家都想去抢购的,库存在数据库内部只是一行标识商品剩余件数的记录,买商品的行为其实是大家在并发的扣减商品记录。当我们并发的去扣减记录的时候,为了保证正确性,一定要对这条记录加锁,由于锁的存在,就把商品扣减变成了一个串行的过程。这个问题与之前问题的不同之处是,这个问题是很多用户去抢一个热点商品所带来的问题。


先把它做成一个简化的模型,先开始一个事务,对它做一个插入,更新热点行,读出热点行的后项,后进行提交。其中,更新热点行需要加锁,提交的时候放锁。MySQL会出现排队等锁的过程,并且每次排队都会进行死锁检测,这样逐渐使得死锁检测时间越来越长。



版做了一个很简单的优化,由于很多线程进入到数据库,但是又无事可做,所以控制进入数据库的线程数,保证InnoDB中有固定数目的线程,这样会使死锁检测、线程切换时间等都急剧下降。



继续分析该模型,持有锁的时间是从第三步开始,第五步结束,这个过程中有两个网络交互时间。考虑到降低网络交互时间,把第三步和第四步合并,先对热点行进行更新,更新完之后顺便把热点行返回给应用服务器。在此基础上,再考虑继续减少网络交互时间,把三四五步都合并。更新库存时,如果更新到了一行,并且更新后项也满足,就直接把这个事务提交;如果说这里面的逻辑不满足,则自动把事务回滚掉。这样一来,所有持有锁的SQL都在数据库内部,没有任何网络交互。



既然串行已经优化到了一定的,能不能把所有穿行的用户请求做一个batch合并?怎么演进?用户发SQL过来后,对热点记录做哈希,其中每一个桶是一个热点行,对每个桶做收集,然后由桶里面的个事务一批次的把当前桶里收集的所有扣减请求全部一次性提交掉,这样虽然仍旧是串行化,但是用批处理去做了。为了提升整体性能,当批提交的时候第二批开始收集,这样就将单线程的串行变成了批处理的线程。


完整生态体系



数据库追求的是一个体系,而不仅仅是一个数据库。想要用好数据库,需要为数据库构建完整的生态体系。其中,第二层是运维组件层,第三层是运维服务层,上层是用户服务层。运维组件层直接和数据库内核打交道,包括一些高可用、数据恢复、测试、调度等。运维服务层提供自动化运维平台DBFree,把整个数据库的申请、交付、扩容、缩容全部做成一个自动化平台。面向用户服务中面向应用开发的可以通过数据服务平台自助完成。


AliSQL未来发展规划


紧跟开源,引领变革,扩展数据库服务边界,为阿里业务发展赋能。



本文为云栖社区文章,如需转载,请注明出处,并附上云栖社区微信公众号:yunqiinsight。


相关文章