亚马逊AWS官方博客

Amazon Aurora 新功能 — 本地写入转发测评

2023 年 7 月,本地写入转发在兼容 Amazon Aurora MySQL 的第 3 版(3.04 版本,与 MySQL 8.0 兼容)中全面推出。这项新功能可以轻松扩展需要写后读一致性的读取工作负载。客户现在可以发出包含对 Aurora 读取副本的读取和写入的交易,写入将自动转发到单一写入器实例以供执行。需要读取扩展的应用程序可以利用多达 15 个 Aurora 副本来扩展读取,而无需维护将读取与写入分开的复杂应用程序逻辑。

本文主要关注在开启了本地写入转发后,对于不同场景下的实测情况,以帮助客户在选择本地写入转发功能时提供参考。

本地写入转发是什么

参考这篇博客(https://aws.amazon.com/cn/blogs/china/local-write-forwarding-with-amazon-aurora/),我们可以了解,根据应用程序的需求,本地写入转发即使不能消除,也可以大大减少对代理或对应用程序代码进行任何特殊修改的需求。应用程序现在可以连接到集群中的任何读取器节点并发出读取和写入操作。读取将直接由读取器提供,写入操作将自动转发给写入器运行,如下图所示。

如何开启本地写入转发

  • 通过控制台,在创建或修改 aurora 时,选择 3.04 版本,会有该选项出现
  • 命令行有 -local-write-forwarding--no-enable-local-write-forwarding 可进行参数调整

在开启本地写入转发后,我们通过下面的例子,可以看到连接到读节点的应用也能够进行写请求的处理:

  • 集群未开启写入转发
  • 集群开启写入转发,modify 集群,勾选 Turn on local write forwarding,等待 modify 完成,并进行参数配置
set aurora_replica_read_consistency = 'eventual';
  • 在开启本地写入转发的集群上执行 failover 时,经过测试当应用连接的是集群的 reader endpoint 时,能够始终保持拥有本地写入转发的能力

和本地写入转发相关的参数和说明

在 Amazon Aurora 集群开启本地写入转发后,可以创建对应的集群和实例的参数组,配置合适的参数,并且在集群增加 reader 节点时,可以选择对应的实例参数组,来保证新增的 reader 节点均符合本地写转发的配置。下面我们具体的来看下各个参数所代表的含义:(各个参数的详细说明和可定义的值范围可以参考文档:https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-mysql-write-forwarding.html#aurora-mysql-write-forwarding-params

  • 使用 Amazon Aurora 可以在控制台便捷地创建数据库参数组,并且可修改对应的参数。通过 modify 数据库实例的参数组,能够将修改后的参数应用到该数据库实例上去。
  • aurora_fwd_writer_idle_timeout

写入器数据库实例在关闭从读取器实例转发的连接之前等待此连接上的活动的秒数。如果会话在此期间之后仍处于空闲状态,则 Aurora 取消会话。默认值为 60s。

  • aurora_fwd_writer_max_connections_pct

此参数指定写入器上可用于写入转发的连接百分比。例如,如果写入器最多可以接受 1000 个连接,并且 aurora_fwd_writer_max_connections_pct 参数设置为 10,则写入器上最多允许 100 个连接用于从读取器转发的写入。以下是一个简单的测试,来验证 aurora_fwd_writer_max_connections_pct 参数的有效性和观察超过参数配置连接数后应用的表现。

    • 配置 max_connection  = 100
    • 配置 aurora_fwd_writer_max_connections_pct = 1
    • 使用 2 个 session 登录读节点,在一个 session 中执行插入操作,同时在第二个 session 中执行插入,会因为触发连接数上限,第二个 session 操作报错,提示 forwarding limit exhausted on writer

在 idle 时间超过 aurora_fwd_writer_idle_timeout(默认 60s)后,session2 能够正常执行 write 操作,或者 session1 关闭后,session2 能够执行 write forwarding 操作。

  • aurora_replica_read_consistency

aurora_replica_read_consistency 参数最初是在 Aurora 全局数据库的只读副本写入转发中引入的。此参数通过本地写入转发继续传递,使用户能够指定最终一致性、会话一致性或全局一致性,从而控制读取一致性的级别。在本文的后面章节会对此参数进行进一步探讨和测试。

使用本地写入转发后的数据库表现

在本测试中,我们希望通过实测结果来观察数据库开启了本地写入转发后的表现,特别是在不同的写负载下读节点所呈现的性能指标,以下是测试配置:

  • 准备数据 — testdb 8 tables 1000W rows
  • sysbench 执行 EC2 机型:large,在运行过程中并不会成为瓶颈
  • Amazon Aurora 采用 1 写 1 读的模式,实例规格采用 r5.xlarge(4C32G)
  • 本地写入转发的参数中,
    • aurora_replica_read_consistency = EVENTUAL,
    • aurora_fwd_writer_max_connections_pct = 90,
    • aurora_fwd_writer_idle_timeout = 60s(默认)

通过 sysbench 进行测试,分别进行了 read_write 和 write_only 的测试,测试结果如下所示:

基于本次测试的情况,可以看到:

  • 在 read-write 混合的 workload 下,读节点在 32 thread 及以下场景下整体 TPS/QPS 略优于主节点,可能的原因是读负载在读节点本地执行,写负载通过本地写入转发执行,相比只有一个写节点执行会有更多资源支持;但在负载上升之后,本地写入转发写负载的效率与写节点写负载的效率差距变的更大,超过了读节点读负载在本地获得的更多资源所带来的优势,因此整体性能不如写节点。
  • 在 write-only 的 workload 下,读节点始终无法达到写节点的 TPS/QPS,且在 64 thread 及更高负载下,差距越来越大,也侧面验证了 read-write 下超过 64 thread 情况下读节点 TPS/QPS 低于主节点。
  • 在更高的 thread 并发下,由于机型本身的限制,TPS/QPS 没有继续向上,但 read-write 下,差距较为稳定,在高并发情况下,建议写负载还是去主节点。
  • write-only 模式下,写节点在高负载下,差距也相对稳定,且测试中读节点和写节点 latency 差距显著,建议写负载为主的 workload 还是继续保持在主节点。

不同读写比下启用本地写入转发后读节点的表现

经过了上面的测试,我们继续进行了 read_write 模式下不同读写比例的测试,希望观察在不同读写比例下,开启了本地写入转发的读节点的表现。

测试配置如下:

  • 准备数据 — testdb 8 tables 1000W rows
  • sysbench 执行 EC2 机型:large,在运行过程中并不会成为瓶颈
  • Amazon Aurora 采用 1 写 1 读的模式,实例规格采用 r5.xlarge(4C32G)
  • 本地写入转发的参数中,
    • aurora_replica_read_consistency = EVENTUAL,
    • aurora_fwd_writer_max_connections_pct = 90,
    • aurora_fwd_writer_idle_timeout = 60s(默认)

本次实验使用 sysbench 的 oltp_read_write.lua 脚本进行测试,可以通过命令 sysbench oltp_read_write.lua help 查看可配置的参数和其默认值,通过修改不同的参数值来调整读写比。通过下图左半部分可以看到在测试脚本中有对应的 select,delete,update 等操作,具体每个语句执行多少次可通过修改命令参数进行调整,可以参考下图有半部分,在本次实验中,主要修改了 point_selects 数值进行读写比调整。

在 sysbench 的测试脚本中我们可以看到默认有 point_selects 10 个,加上 range_selects 4 个,共 14 个读;另有 index 和 non index 的 update 以及 delete,insert 操作,共 4 个写。因此我们在测试中将 range_select 置成 off,只调整 point_selects 数值,来进行读写比的调整。在读写比 50:50 下,配置 point_selects= 4,形成 4 读 4 写;在读写比 60:40 下,配置 point_selects= 6,形成 6 读 4 写;在读写比 70:30 下,由于在 4 写情况下需要 9.33 读,因此配置 point_selects= 10 来匹配;在读写比 80:20 下,配置 point_selects = 16,形成 10 读 4 写;在读写比 90:10 下,配置 point_selects = 36,形成 36 读 4 写。

在本次实验中,采用了以下命令分别在不同读写比下进行了测试,通过对读节点和写节点分别提交,并通过 32 thread,64 thread, 128 thread 等不同并发下的表现进行查看:

sysbench oltp_read_write.lua --db-driver=mysql --mysql-host=aurora-write-forwarding-test.cluster-cczbecyi7nlv.ap-northeast-1.rds.amazonaws.com --mysql-port=3306 --mysql-user=admin --mysql-password=passw0rd --mysql-db=testdb --tables=8 --table-size=10000000 --threads=64 --report-interval=30 --range_selects=off --point_selects=4 --time=600 run  
  
sysbench oltp_read_write.lua --db-driver=mysql --mysql-host=aurora-write-forwarding-test.cluster-cczbecyi7nlv.ap-northeast-1.rds.amazonaws.com --mysql-port=3306 --mysql-user=admin --mysql-password=passw0rd --mysql-db=testdb --tables=8 --table-size=10000000 --threads=64 --report-interval=30 --range_selects=off --point_selects=6 --time=600 run  
  
sysbench oltp_read_write.lua --db-driver=mysql --mysql-host=aurora-write-forwarding-test.cluster-cczbecyi7nlv.ap-northeast-1.rds.amazonaws.com --mysql-port=3306 --mysql-user=admin --mysql-password=passw0rd --mysql-db=testdb --tables=8 --table-size=10000000 --threads=64 --report-interval=30 --range_selects=off --point_selects=10 --time=600 run  
  
sysbench oltp_read_write.lua --db-driver=mysql --mysql-host=aurora-write-forwarding-test.cluster-cczbecyi7nlv.ap-northeast-1.rds.amazonaws.com --mysql-port=3306 --mysql-user=admin --mysql-password=passw0rd --mysql-db=testdb --tables=8 --table-size=10000000 --threads=64 --report-interval=30 --range_selects=off --point_selects=16 --time=600 run  
  
sysbench oltp_read_write.lua --db-driver=mysql --mysql-host=aurora-write-forwarding-test.cluster-cczbecyi7nlv.ap-northeast-1.rds.amazonaws.com --mysql-port=3306 --mysql-user=admin --mysql-password=passw0rd --mysql-db=testdb --tables=8 --table-size=10000000 --threads=64 --report-interval=30 --range_selects=off --point_selects=36 --time=600 run

结果如下所示:

基于本次测试的情况,可以看到:

  • 不同读写比下,写比例越高,写节点的整体效率更高,在读比例逐步上升过程中,读节点的效率会逐渐接近写节点
  • 在不同的 thread 下,由于上面的 write only 测试结果显示写节点在高 thread 下相比读节点的 TPS/QPS 更好,因此在此处测试中,64 和 128 thread 的读节点效率始终无法明显优于写节点,但在 32thread 下,读比例在超过 7:3 之后,读节点的效率会相比写节点更高
  • 在测试实例规格下(r5.xlarge(4C32G)),通过 sysbench 测试发现,在 sysbench 测试脚本的 32 thread 下时,读写比超过 7:3,使用本地写入转发是有效的,在高并发情况下,需要读写比至少达到 8:2 以上,才能看到本地写转发能够起到一定作用,最佳的方式还是以读为主的负载,偶尔有部份写操作,可以减少开发适配难度
  • 在 64thread 下,我们额外测试了采用了 2 台 reader 来进行了读节点的横向扩展,同时将 sysbench 的任务拆分为 2 个 32thread 的任务分别提交给两个读节点,在结果上我们对两个读节点的 TPS/QPS 进行了累加,发现在高读写比(超过 7:3)下,使用本地写入转发也可以通过横向扩展读节点来提高集群整体性能表现,缓解写节点的压力

本地写入转发一致性定义和实测表现

上面文章提到的 aurora_replica_read_consistency 参数,对于开启了本地写入转发的数据库,需要根据不同的使用场景,来配置合适的读一致性参数。aurora_replica_read_consistency 参数包含以下三个选项:

  • EVENTUAL – 将读取一致性设置为 EVENTUAL 后,使用写入转发的数据库实例的查询可能会看到由于复制滞后而稍微过时的数据。在对主节点执行写入操作并将其同步到读节点之前,看不到同一会话中写入操作的结果。查询不会等待更新的结果可用。因此,它可能会检索较旧的数据或更新的数据,具体取决于语句的时间和复制滞后量。
  • SESSION – 所有使用写入转发的查询都会看到在该会话中进行的所有更改的结果。无论事务是否已提交,这些更改都是可见的。如有必要,查询将等待要复制的转发写入操作的结果。
  • GLOBAL – 会话看到数据库集群中所有会话和实例上的所有已提交更改。每个查询可能会等待一段时间,该时间取决于会话滞后量。从查询开始时,如果数据库集群处于最新状态(具有来自写入器的所有已提交数据),查询将继续进行。

具体说明可参考文档:https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-mysql-write-forwarding.html#aurora-mysql-write-forwarding-consistency

以下测试验证了不同一致性配置下的写后读的表现,建议客户根据实际应用场景进行选择:

  • EVENTUAL
set aurora_replica_read_consistency = 'eventual';
select count(*) from Test;
insert into Test(ID,c1) values (7,7); select count(*) from Test;
select count(*) from Test;
  • SESSION
set aurora_replica_read_consistency = 'session';
select count(*) from Test;
insert into Test(ID,c1) values (8,8); select count(*) from Test;
select count(*) from Test;

可以发现在 session 内能够读到更新后的表数据。

  • global
set aurora_replica_read_consistency = 'global';
select count(*) from Test;
insert into Test(ID,c1) values (9,9); select count(*) from Test;
select count(*) from Test;

设置成 global 后,查询请求会根据当前 replication lag 情况进行同步,因此会造成每次查询的耗时不同,相比如果是配置成 eventual,则每次查询都会直接查询本地数据,耗时稳定。

另外,可以通过对不同实例和不同的 session 进行配置,来达到更细粒度的读一致性的配置。

  • 可以通过在参数组里配置 aurora_replica_read_consistency 的值来对不同的 instance 实现不同的 consistency
  • 在每次请求前进行配置 aurora_replica_read_consistency,可执行不同级别的 consistency
set aurora_replica_read_consistency = 'eventual';

小结

通过上述的测试,我们初步了解了在开启了本地写入转发后,读节点的一些表现。从整体上来看,本地写入转发能够简化应用程序开发,通过开启本地写入转发,开发者能够较为容易的将原先的数据库负载应用到 Amazon Aurora 数据库上。同时,根据测试表现来看:

  • 在 Amazon Aurora 上能够非常便捷地启用本地写入转发,并通过参数组来对本地写入转发的行为进行配置。同时,使用 Amazon Aurora 能够支持多达15个副本的扩展读取,来应对业务弹性伸缩。
  • 在读多写少的工作负载下,建议可以尝试本地写入转发。使用本地写入转发避免了拆分这些事务或将它们专门发送给写节点的必要性,从而简化了应用程序开发。对于需要读取事务中最新写入内容且对写入延迟不敏感的工作负载,通过这项新功能可以轻松实现读取扩展。比如电商业务中大量的浏览商品是读负载,相比下单的写负载比例更高,利用本地写入转发,能够帮助客户在业务高峰期间便捷实现横向扩展;在金融行业,金融机构的应用伴随大量的行情和资讯的浏览,也能够利用本地写入转发来应对每日用户使用的高峰时段(如有重大新闻的交易日,或重要经济数据发布的时段)。
  • 在开启了本地写入转发后,可以通过配置 aurora_fwd_writer_max_connections_pct 参数,来调整写节点接受本地写入转发请求的连接数上限。通过测试我们发现,如果在数据库的参数组中配置了 aurora_fwd_writer_max_connections_pct = 0 后,即使整个 cluster 启用了本地写入转发,读节点也无法执行本地写入转发,通过调整这个参数,可以有效保护写节点在业务高峰期间能够优先满足对主节点的写入请求。
  • 对于读一致性的配置,可以在配置了参数组后,对每个 session 中配置不同的隔离级别,用户可根据自己的实际场景来配置合适的一致性参数,对于一些需要立即读到最新更新数据的请求,可以配置成 ‘SESSION’ 级别的一致性来确保读到最新写入的数据。

本篇作者

郑明明

亚马逊云科技解决方案架构师,服务于国内及全球多家头部金融机构,在 IT 与云计算领域有丰富的项目经验,对量化金融机构使用云计算有较为深入的探索和实践。