本文将介绍易仓跨境SaaS云原生数据库架构演进过程,并分享PolarDB MySQL引擎在易仓跨境SaaS实践经验。
作者介绍:
程涵,高级DBA,主要负责易仓SaaS数据库架构设计,监控设计优化,性能调优以及新技术预研工作。
SaaS(Software as a Service,软件即服务)是指软件厂商将应用统一部署在其服务器上,客户根据实际需要向厂商订购及支付费用,并通过互联网最终获得服务的模式。相比于传统软件,SaaS最大的特点在于其多租户架构,包括容器、数据库等服务,对多租户的资源复用能够有效降低开发和运维成本,实现持续快速的升级迭代。早期SaaS软件服务商一般会使用Oracle或者SQLServer比较多,但因为生态、人才、成本的原因,国内厂商趋势慢慢转向MySQL及MySQL生态兼容的数据库。除了数据库选型外,还有一个必须要谨慎考虑的问题是,怎么隔离租户数据?早期一般采用按字段区分租户,所有的租户共享一套库表结构,数据隔离性会比较差,租户数据拆分困难,所以头部SaaS厂商慢慢转向按库分租户,每个租户一套表结构,并期望单实例可以支持更多租户,降低租户资源成本,同时实现更好的租户资源隔离,所以单实例往往会有几百上千租户,单实例要支持数百万表。
01 SaaS场景海量表
对于小租户,为了给租户提供更好的数据隔离性,易仓采用的是按库分租户的模式,数百租户共享一个MySQL数据库实例,单实例会有数百万张表,这对数据库是一个巨大的挑战。在MySQL实例海量表场景下,数据库实例在版本升级,参数变更,或者故障时,需要快速重启数据库恢复服务,但由于表结构太多,MySQL实例启动过程中,需要加载所有表文件,读取文件头页获取文件的meta数据,当表数百万时,表文件也有数百万,要读取的文件太多,每个头文件一次IO,重启时长甚至会长达数十分钟,对业务影响会非常大。另外,在海量表场景,热点表数量也会相应陡增,不仅系统要支持更大句柄数,而且需要更多内存资源缓存热点表的元数据和表文件句柄信息,DBA需要结合业务场景,不断优化参数来提升数据库性能。
以我们重点优化的两个场景为例:
首先,针对易仓跨境SaaS单实例数百万表单场景,易仓运维团队联合PolarDB MySQL团队做了专项快速重启优化,将表的文件meta数据下沉到⽂件系统superblock,一次IO可读大批量文件的meta,bypass所有的⽂件操作,单实例100万表的重启时间提速到30 ~ 60s,200万张可以控制在120秒内。重启时间大幅缩减到原来的1/10,大幅降低实例重启时的服务中断时间,保障易仓跨境SaaS服务可以提供高水平可用性。
其次,针对易仓跨境SaaS海量表场景,热点表徒增的问题,使用PolarDB默认参数配置,往往不能同时满足性能和稳定的要求。我们对部分核心参数进行了调优:
- 调大参数open_definition_cache和open_table_cache,缓存更多热点表的元数据,提高打开表的效率,同时适当调低参数innodb_buffer_pool_size,让出一些内存给到open_definition_cache和open_table_cache,使得数据库的性能达到一个性能比较均衡状态。
- 调大参数table_open_cache_instances,那么实例会创建更多open_table_cache对象,相当于增加了锁的分桶数目,降低了锁的粒度,减少高并发时锁冲突的等待时间,提升PolarDB数据库性能。
下表是我们跨境ERP在实践过程中,形成的一个PolarDB参数配置最佳实践。
02 快速DDL+并发控制
在SaaS场景多租户架构背景下,我们数百小租户共享一个数据实例,按库分租户,每个库一套相同的表。由于业务快速发展,版本迭代变更频繁,经常要给表加索引,给表新增、修改字段,删除表,truncate表数据等操作,这种一般都是由系统自动批量下发数据库,一次数百个DDL操作。
官方MySQL的实现里,很多DDL操作都是有锁变更,DDL会对表加MDL锁,会长时间堵塞数据库影响业务运行,导致业务变更效率太低,耗时不可控。其次针对很多涉及到数据拷贝的DDL操作,如建索引,修改主键等,MySQL的原生实现都是单线程穿行,对一些大表往往耗费数小时甚至数天的时间,进而导致失败概率高对业务的影响时间也长。
PolarDB MySQL支持Parallel DDL,利用并行 scan/build Index,和深度优化的并行merge sort,可以帮助我们大幅度提升加索引效率,将DDL的速度提升了10倍,极大的减小了发版变更对业务影响的时间窗口,进而减轻了运维团度的工作量。
另外,还有一个一直困扰我们的问题,怎样可以快速对表新增、修改和删除字段?在多租户架构下,虽然可能只能对一张表新增一个字段,但乘以租户数,DDL的数量动辄达到数百个,所以我们十分需要数据库支持快速DDL。上线PolarDB MySQL引擎后,现在可以秒级新增表字段,而且PolarDB即将推出秒级修改和删除字段的能力,在变更操作时只需修改表定义信息,无需修改底层数据,非常适合我们这种按库分租户的SaaS场景。
--使用秒级加字段功能,ddl操作加上algorithm=instant即可
alter table test.t add column test_column int, algorithm=instant;
下面对一张47亿行数据的表操作增加列,耗时仅0.02秒:
我们使用了PolarDB秒级加字段后,由于涉及到表元数据变更,还是会有短暂的MDL锁。这在PolarDB实例负载过高的情况下,对数据库实例中所有库一起加字段或者做其它DDL操作,还是可能会堵塞实例,所以我们希望可以分批次操作。基于PolarDB提供的语句并发控制CCL(Concurrency Control)功能,可以控制语句的并发数,避免堵塞数据库。比如,系统一次下发300个alter表语句,可以通过CCL限制一次并发执行30个,分10次执行,这块可以保证不会阻塞数据库实例。
- 增加ccl规则
-- 限制表名为ec_product的alter语句,每次只能并发执行10个
dbms_ccl.add_ccl_rule('alter','','ec_product', '10', '');
• 查看ccl规则
dbms_ccl.show_ccl_rule(); -- 显示当前正在生效的限流规则
• 删除ccl规则
dbms_ccl.del_ccl_rule(75792); -- 删除编号为75792的限流
03 大量短连接优化
易仓应用开发语言主要是PHP,应用层面没有使用连接池,在数据库中会产生大量短连接。对每一个短连接,传统上MySQL都会创建一个独立的线程来响应请求。当MySQL数据库有大量的连接存在时,会出现频繁线程切换消耗CPU导致性能下降。同时大量的系统线程调度和缓存失效,也会导致数据库性能急剧下降。所以,我们期望数据库内核层面可以提升处理效率,在大量短链接场景保证性能不下降。
经过调研和测试,在应用端存在PHP大量短连接的场景,我们选择使用了PolarDB数据库代理层面自带事务级连接池功能,其大幅减少了短连接场景下频繁创建短连接给数据库带来的负载。PolarDB开启事务级连接池后,当客户端发送请求时,会先与PolarDB代理建连,代理不会马上将其与后端数据库建连,而是先从事务级连接池里查找是否存在可用的连接(库名、用户名、客户端IP三要素可以链接复用)。若不存在,代理会与数据库创建一个新连接;若存在,则从连接池里直接拿出并使用,并在当前事务结束后将该连接放回事务级连接池,方便下个请求继续使用。数据库层面支持连接复用,可以无缝地解决我们PHP大量短连接场景遇到的问题,业务无需做任何代码调整,极大的降低了业务开发成本。
04 计算快弹 + 存储按量付费
易仓主要做跨境电商业务,租户(卖家)会经常搞大促,特别在黑5、双11等节日,业务流量会徒增,MySQL数据库计算资源存储需要支持快速扩容,应对业务洪峰。租户业务也有生命周期,当租户停用下线时,如果数据库存储资源不能自动回收,会导致存储资源浪费,增加资源成本。传统上面对这种问题,一般通过迁移新实例或合并实例方式来回收存储资源,但耗时太长,运维成本会比较高。
PolarDB MySQL采用存储和计算分离的架构,所有计算节点共享一份数据,扩容时不需要迁移底层数据,增减节点可以在分钟级完成,可以满足我们跨境电商大促时,快速升配的需求。PolarDB存储采用分布式块存储设计和文件系统,使得存储容量不限制于单节点的规格,扩展比较容易,可以支持100TB级别的数据规模,我们不用担心会有存储容量瓶颈。而且存储空间无需手动配置,根据数据量自动伸缩,按实际使用量计费。租户停用下线清理后,存储会自动回收,节约资源成本。
基于共享存储架构的数据库完美解决了传统本地盘部署时,DBA需要面对的扩缩容及容量管理的挑战。极大降低了运维负担。
结语
一直以来,我们也有尝试各种类型的数据库,期望可以解决易仓在跨境SaaS遇到的独特问题和挑战。从早期自建MySQL,然后到RDS,再到TiDB…,中间我们也踩过很多坑,但一直没有找到一个完美的解决方案。
直到最后使用了PolarDB, 因为PolarDB在海量表支持、计算存储资源快速弹性伸缩、快速稳定DDL、海量短链接支持等方面的能力,完美的解决了我们SaaS场景遇到的各种极端问题。最终我们的业务在PolarDB的支持下持续稳定的进行迭代,更好的服务了我们的客户!