开发者学堂课程【MySQL 实战进阶:MySQL 高并发场景实战】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/852/detail/14060
MySQL 高并发场景实战
内容介绍:
一、问题和挑战
二、系统调优
三、流程管理和生态工具
一、问题和挑战
阿里巴巴 CEO 逍遥子说“双11是商业界的奥林匹克”。
双11从2009年开始举办,从图中可以看到每一年的交易额、支付笔数和订单创建几乎都是翻倍增长的,不但给商业界提供了大炼金的机会,相应的给后端的技术、架构,以及各个模块等等都是比较好的技术沉淀。
在双11面临哪些问题和挑战呢?今天的主题是 MySQL 高并发场景的问题和挑战,这个并发不仅是高并发,而且是洪峰般地并发。
1、洪峰般地并发
根据市场部部门的推广和引流、历年双11的经验,大促的起始时刻呈现接近90度上升趋势。在这么大访问流量下,所有的核心链路的增删改查都是在数据库上操作,对数据库有比较大的冲击,在大量线程并发工作时线程调度工作过多、大量缓存失效、资源竞争加剧、锁冲突严重,如果有复杂 SQL 或大事务的话还可能导致系统资源耗尽,整个数据库服务不可用,进而导致大促受到影响,甚至失败,比如:下单失败、网页无法打开、无法支付等。此外此类场景也会发生在在线教育、直播电商、在线协同办公等。
2、热点行更新
库存扣减场景是一-个典型的热点问题,当多个用户去争抢扣减同一个商品的库存(对数据库来说,一个商品的库存就是数据库内的一行记录),数据库内对同一行的更新由行锁来控制并发。当单线程(排队)去更新一行记录时,性能非常高,但是当非常多的线程去并发更新一行记录时,整个数据库的性能会跌到趋近于零。
3、突发 SQL 访问
当缓存穿透或异常调用、有数据倾斜 SQL、未创建索引 SQL 等情况发生时,在高并发场景下很容易导致数据库压力过大,响应过慢,导致应用链接释放慢,导致整个系统不可用。
4、智能化运维
双十一期间这些实例的水位管控,机器水位管控,高风险实例识别,高风险实例优化,在流量高峰期从收到报警、识别问题、解决问题至少需要十多分钟,如果处理不及时峰值已经过去,导致大促失败。
此外还有商品超卖、资源的挑战等等。
二、系统调优
1、容量评估
(1)经验评估
预估压力/单机性能=服务器数量
根据服务器的数量和应用机器的数量、deb 机器的数量,可以得出整个数据库的连接池的设置、扩充的实例、扩充的应用机器等等,这些配置也会相应出来。
(2)单元压测
需要针对对单个实例、单个单元、单个应用模块进行压测,单元压测的主要功能是完成单元内的验证,不光是验证整个双11的流量,还要验证在单元内这个容量是否充足,以及单个系统的容量,比如是压测某一个模块(交易模块、优惠模块等等)。
(3)全链路压测
基于场景化的仿真测试,这是最接近业务的系统值的,针对全链路压测可以借助压测来验证整个分布式系统容量是否充足。
2、性能评测
(1)压测目的
发现基础设施瓶颈、中间件瓶颈、系统容量瓶颈是否充足
发现分布式系统短板
(2)压测用途
保障大促容量充足,保障业务正常运转
保障核心功能、保证用户体验
评估大促成本
(3)难点
真实业务场景压测
真实 SQL 模拟
(4)基准测试
基准测试可以用 sysbench、mysqlslap 等等,官网上有基于通用场景的测试,每一个实例所达到的容量在不同的业务场景下是不一样的,通过基准测试可以预估实例所能达到的最大值、极限值,通常情况下真实的业务场景不大可能超过基准业务的值的,所以针对每一个实例所能达到的瓶颈做到心中有数。
(5)全链路压测——大促备战核武器
真实业务场景的压测往往比基准测试要复杂,阿里在双11的场景下是如何压测的呢?整个过程大概可以分为4个步骤。
首先,针对基础的涉及到的业务模块进行梳理,相应也会梳理技术架构模块以及容量预估是否充足。梳理完成之后需要造数据,准备服务器、压测流量、业务请求。
第三步进入正式压测阶段,通过压测发现短板,验证容量是否充足,验证预案。验证压测执行完成后需要针对发现的短板问题、容量问题、架构问题进行针对性的优化。
这个过程不是一蹴而就的,而是要经过很多轮的压测,阿里巴巴集团及各 BU 每年压测4000+次,每一年通过全链路发现几百个线上在以前的测试过程中没有发现的问题,所以这个真实的业务场景压测已经成为双11的必备阶段,是大促准备的备战核武器。
(6)工具
PTS(Performance Testing Service)是面向所有技术背景人员的云化测试工具。有别于传统工具的繁复,PTS 以互联网化的交互,提供性能测试、API 调试和监测等多种能力。自研和适配开源的功能都可以轻松模拟任意体量的用户访问业务的场景,任务随时发起,免去繁琐的搭建和维护成本。
更是紧密结合监控、流控等兄弟产品提供一站式高可用能力,高效检验和管理业务性能。
DAS(Database Autonomy Service)智能压测主要应用在以下两种场景:
●为应对即将到来的短期业务高峰,验证当前的 RDS MySQL 规格是否需要扩容。
●数据库迁移上云前,验证目标 RDS MySQL 的规格是否满足业务需求。
(7)注意
●参数的对齐
如果想对比两个参数,在打开和关闭之后,以及设置不同的值对性能的影响,可以单独做测试。一旦测试出来最优值,在压缩之前最好把参数调到最优值,不要等到压缩出来有问题了再来调参数。
●网络
一般情况下,购买实例的时候 ECS 和 RDS 是在同一个 VBC 内的,但是不排除用户拿线下的应用的机器链接本地的数据库,然后拿本地的应用机器链接 RDS 来做性能对比,这种就是不合适的,一个主要的原因就是压测机到 RDS 的网络延迟和到本地数据库的延迟差异很大。
●规格
不同 RDS 的规格,它的性能差别是比较大的,如果想测试 CPU 的性能,物理 IO要少,数据在内存里,但是大多数业务场景都是涉及到物理 IO 的,所以到测试数据的时候,数据量要大于内存的大小。
●ECS 的网络带宽
阿里云的 ECS 限制网络带宽,以往有用户做测试的时候,RDS 资源都没有用满,但是压力也上不去,后来经过定位使 ECS 的网络带宽打满了,所以在准备整个压测的环境时,要把这些内容调好。
3、架构调优
针对数据库来说,如果所有业务的访问都用数据库来支撑,这个成本太高了。
首先是缓存,它可以代替一部分关系型数据库在读方面的请求,此外,基于原理的设计以及成本方面来考虑缓存的独特性能是要比关系型数据库要好的,在成本方面也是性价比,是比较高的。
到数据库层面,如果要是读多写少,针对单个实例很难支撑,还可以借助于只读实例,只读实例可以实现在线弹性的扩展读能力,读的业务请求可以实现隔离,比如说可以把轻分析型、拖数据型的在只读实例内完成。
此外,每一个只读实例都有一个单独的链接地址,如果想把某一类的业务和其他的业务区分开,严格的隔离,比如说让拖数据类的场景,或者是某一类的只读的场景,它只到某一个实例上面来访问,就可以单独的链接那个只读实例的链接串,但是如果想从整个层面来控制主实例和只读实例的访问,可以借助于负载均衡独享代理来完成。
独享代理可以缓解大量的短链接的场景,使用代理后不用反复的变更应用内的链接地址,减少维护成本,可以对线上的资源实现扩展,承受更高的流量。如果 RDS 的实例规格以及只读实例都已经升到最大,但仍然不能支撑业务的发展,可以考虑把 RDS 升级到 Polar MySQL,或者是 PolarX 2.0这种方式来完成读写容量上面的扩展。
刚才提到只读实例,其实大家最关心的一个问题就是主实例和只读实例的数据一致性,比如说有延迟以及中断的这一类的场景的延迟是最多的,针对线上的延迟的问题,我们做了一个分析,主要原因包括5个方面:
●主实例的 DDL
占40%,如 alter、drop 等。需要 kill DDL 语句或用 DMS 的无锁变更等。
●主实例的大事务
占20%,如大批量导入、删除、更新数据。需要将大事务拆分成为小事务进行批量提交,这样只读节点就可以迅速的完成事务的执行,不会造成数据的延迟。
●主实例写入压力过大
占20%,如主实例规格较小压力过大。这种情况需要升级主实例和只读实例的规格。
●只读节点规格过小
占10%,这种情况升级只读实例规格。
●其他(无主键)
占10%,RDS目前已经支持对表添加隐式主键,但是对于以前历史创建的表需要进行重建才能支持隐式主键。
刚才提到缓存,在高并发场景下,写入压力过大,怎样提升缓存命中率?根据以往的经验,有4种更新方式:Cache aside,Read through,Write through,Write behind caching。缓存的更新、应用可以从 Cache 里读取数据,取到后返回,或者没有取,从数据库里取,成功后再返回缓存中。Read through 是没有读到后更新缓存。Write through 是在数据更新时,发现缓存里没有,就更新缓存。Write back 类似于底层的操作系统的机制,它可以合并同一个数据的多次操作。以上几种方式都不能保证版权命中率,那如何保证版权命中率呢?做核心系统的时候,有一个小巧思,如图
就是应用程序把写请求到 RDS,读请求到 Redis,RDS 的变更数据底层是通过数据变更的这种方式,拿到增量数据后,更新 Redis,这是一个小巧思。它的好处是:
●更新路径短,延迟低
缓存失效为异步流程,业务更新 DB 完成后直接返回,不需要关心缓存失效流程,整个更新路径短,更新延迟低。
●应用简单可靠
应用无需实现复杂双写逻辑,只需启动异步线程监听增量数据,更新缓存数据即
可。
●应用更新无额外性能消耗
因为数据订阅是通过解析 DB 的增量日志来获取增量数据,获取数据的过程对业务、DB 性能无损。
4、实例调优
(1)弹性扩容
(2)参数调优
影响性能的几个主要参数:
●和连接相关的几个参数
back_ log 标记 MySQL 的并发链接数
table_ open_ cache 所有线程打开表的数量
thread_ cache_ size
loose_ thread_ pool_ enabled
●和 IO 相关的几个参数
sync_ binlog
innodb_ fush_ logs_ at _trx _ commit
innodb_ io_ capacity
innodb_ io_ capacity_ max
●和内存相关的几个参数
innodb_ buffer _pool_ instances
join_ buffer_ size
tmp_ table_ size
我们把我们认为是对性能的影响比较大的参数调整到高性能模板里面,应用时就可以找到对应的参数,应用到对应的实例。下图是根据普通模板以及高性能模板做的一个对比,我们可以看到普通模板相比高性能模板要低25%左右的性能。
5、内核调优
(1)版本升级
从版本上来讲,现在官方维护的是5.6、5.7和8.0,每一个大版本的升级会带来一些新的特性,同时,它的性能也会上升,从表里可以看到8.0的性能是最强的,但是升级时也会踩一些坑,比如执行计划有些和原来的版本不太兼容。
(2)AliSQL 特性
最开始讲了高并发场景下面临的挑战,也会针对这些挑战提供解决方案,针对高并发的场景以及热点库存的场景,AliSQL 提供了四个补丁,其中三个补丁针对热点库存更新,其中一个补丁是库存注释。
库存注释:在秒杀的业务场景中,减库存场景包含两个,从业务逻辑上来讲,包括拍下减库存和付款减库存。拍下减库存就是在客户拍下的时候把库存减掉,业务逻辑比较简单。付款减库存的业务逻辑比较复杂,在用户付完款后才减库存,因为复杂所以一条语句不能完成,是在事务里完成的,行锁冲突本来就比较严重,如何才能提升事务里的性能,通常情况下,开启和结束事务都是由应用来完成,如果应用处理的过慢或者网络交互的时间过慢、网络拥堵,它会增加它所持有的时间。
库存注释把持有行锁的时间、行锁释放交给数据库自己来做,AliSQL 使用排队和事务性 hint 成功就提交,失败就回滚,把控制权交给 MySQL 内核,这样可以减少行锁持有的时间,快速结束事务,进而提高减库存的吞吐能力。
在减库存场景里,MySQL 分为引擎层和 server 层,在高并发场景下,同一行的行锁会在 server 层、引擎层来回切换,InnoDB 中事务锁的最细粒度是行级锁,如果语句针对相同行进行并发操作,会导致冲突比较严重,所以 AliSQL 把这个冲突放在 server 层进行排队,对于相同行的冲突在一个桶内,可以减少冲突检测的开销,进而减少引擎层、server 层来回切换带来的消耗。
第三个是语句返回,这个特性在 PG 里有,更新完后直接将结果返回,但是 MySQL 是没有的,AliSQL 把这个特性吸纳进来后也带有这个特性,如果在一个事务里更新完一行记录后,应用再发起 select,这条结果再返回给应用,可以看到它会多一次网络交互。
如果直接把 abdata 的结果返回就可以减少这次网络交互,有多次的时候性能提升可以节省多次的网络交互和应用判断的时间,也可以带来事务的性能提升以及应用性能的提升。
对于高并发场景下,AliSQL 是用线程池解决的,线程池在有大量线程来并发访问的时候,线程池会自动调节并发的线程数量在合理的范围内,避免线程池过多造成的大量缓存失效。
还有在高并发场景下,线程池会语句和事务分为不同的优先级,分别控制语句和事务的并发数量,减少资源竞争,线程池也会根据不同语句的复杂性来控制它的优先级,所以使用线程池可以将不同的 SQL 控制在合理的连接数的范围,使数据库在大并发的场景下保持高性能。
下图是用库存注释、语句队列做的性能对比:
和原生的相比,已经翻了40倍。
6、监控报警
系统调优必不可少监控,有 RDS 自带的监控、资源层的监控、引擎层的监控,还有慢日志的监控。
对于收费来说,包括审计日志以及自治服务会基于上面的 RDS 自带的以及审计日志会做一些分析,在这个基础上做的一些报警,此外,还有分布式的监控可以监控在不同的组件里面,它的瓶颈在哪里。
三、流程管理和生态工具
基于生态工具的保障可以从变更的流程、稳定性和数据恢复三方面来管理、介绍。
1、变更流程
在大促期间,可以有很好的流程来提升稳定性,这个是增加在审批环节来增加审批的流程,稳定性相关的不要因为人为的因素给系统造成压力,可以设置通过 DMS 来执行的 SQL,不要让它超过一定的时间,也可以针对某一类的 DDL 或者是 DML 不允许执行,可以设置安全规则。
内核的特性,有的用户他会在业务高峰的时候会进行大文件的删除,会造成文件系统的 hint,进而影响数据库的稳定性,可能会造成数据库的抖动。
针对这个特性特点,阿里 SQL 提供这样的一个补丁,可以提供小批量的异步删除,慢慢的删,来减少、降低,甚至没有,取决于每一次设置的清理大小。
还有并发的 SQL 访问,当有缓存失效或者是有数据倾斜,或者是未创建索引 SQL 的时候,会造成数据库压力过大。
阿里 SQL 针对这一类的 SQL 做了并发控制的补丁,根据关键词识别出来之后可以控制这一类 SQL 的并发度、并发执行,最多允许执行多少,防止某一类的 SQL 把系统打卡的风险。
2、数据恢复
从数据恢复上来说,有的用户一般在大促的时候甚至在高峰的时候人肉来做一些数据的变更,万一数据变更条件写错,也可以通过数据追踪来恢复。
数据恢复还可以通过备份来恢复,备份可以分为克隆实例、整个实例级别的恢复以及库/表级别的恢复,还可以通过 DBS 数据库备份来恢复。