暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

通过转账测试验证 PolarDB-X 的分布式事务

小希 2023-06-28
526

完整的事务支持是对于分布式数据库最大的挑战之一,在 PolarDB-X 2.0 中,我们基于对 InnoDB 的 CTS 扩展,在 XA 事务的基础上实现了 TSO 事务,并提供了兼具高效和一致性的 SI 隔离级别。我们提供了转账测试来模拟银行业务,测试分布式事务的一致性。

什么是转账测试?

转账是金融业务最核心的场景之一,也是对事务要求最高的能力。转账测试是 PolarDB-X 实现的一套测试框架,通过转账业务的模拟,验证我们分布式事务实现的正确性。转账的核心逻辑就是高并发地模拟两个账户之间的交易,并通过观察者查询数据是否一致。

转账测试的插件模式

虽然仅有两个场景,但事实上,由于数据库代码的复杂性,我们的测试应该尽可能覆盖所有的事务路径,事实上转账测试以插件的形式支持了超过十种场景:

  • 交易场景
    1. transfer_transaction: 使用 BEGIN/COMMIT 显式开启事务进行转账。
    2. transfer_pre_query: 转账前使用 FOR UPDATE 锁定数据,确保余额充足,这个场景可能会产生死锁,可以测试我们的分布式死锁检测功能。
    3. transfer_single_partition:仅发生在一个分片内的转账,测试我们单分片优化策略(一阶段提交)的正确性。
    4. transfer_big:在同一个事务内运行多笔转账。
  • 查询场景
    1. read_transaction:使用 BEGIN/COMMIT 显式开启事务,并查询所有账户中 balance 的数据,检查总和。
    2. read_transaction_readonly:使用 START TRANSACTION READ ONLY 显式开启只读事务,这种情况下会有针对只读事务的一定优化,需要测试。
    3. read_autocommit:同1,使用 autocommit=1 策略进行查询。
    4. read_sum:直接使用 SELECT SUM(balance) FROM accounts 查询总和。
    5. read_secondary_index:强制使用(FORCE INDEX)二级索引查询数据。
    6. read_global_secondary_index:强制使用(FORCE INDEX)全局二级索引查询数据。
    7. read_long:每查询一批账户 sleep 一段时间,测试长事务。
    8. read_long_transaction:显式开启事务,并多次查询数据,检查是否能保证 REPEATABLE READ。
    9. read_follower:PolarDB-X 支持备库一致性读的 feature,这个插件通过 Follower 查询数据并验证结果。

以上场景有独立开关,并且以混合负载的形式跑在同一个表上。

运行转账测试

话不多说,我们先尝试运行一下转账测试:

首先我们创建一个逻辑库:

CREATE DATABASE tt;

使用转账测试的 binary 连接到数据库上,准备数据:

transfer prepare -config=config_example_polarx.toml -dsn='root@tcp(127.0.0.1:3306)/tt'

这个语句会创建一个拆分表,并插入 10000 条数据(可配置):

CREATE TABLE `accounts` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `balance` INT DEFAULT 1000 COMMENT '账户余额',
  `sid` INT,
  `gsid` INT,
  `version` INT DEFAULT 0 COMMENT '检查 follower_read 是否足够新'
) dbpartition by hash(`id`);
CREATE INDEX `idx_sid` ON `accounts` (`sid`);
CREATE GLOBAL INDEX `idx_gsid` ON `acounts` (`gsid`) dbpartition by hash(`gsid`);

转账测试通过 toml 来配置,每个plugin 有一个单独的 section,一个简单的配置如下:

row_counts = 100000 initial_balance = 1000 [transfer_transaction] enabled = true threads = 50 [read_transaction] enabled = true threads = 10

然后可以直接运行转账测试,就会按照配置自动运行各个 workload。

transfer run -config=config_example_polarx.toml -dsn='root@tcp(127.0.0.1:3306)/tt'

失败情况测试

分布式事务最重要的是测试异常情况下是否能正常工作,为了模拟事务的失败,我们在我们分布式事务的实现中各个关键点都埋下了 failpoint,可以通过 hint 触发,例如:

/*+TDDL(TRANSACTION_FAIL(AFTER_PREPARE))*/UPDATE `accounts` SET balance = balance + 1 WHERE id = 0;
/*+TDDL(TRANSACTION_FAIL(BEFORE_COMMIT))*/COMMIT;

我们可以在 toml 每个插件的配置中添加如 fail_ratio = 0.05 的参数,这可以在场景中注入一定量的符合预期的失败情况,验证在这种情况下我们是否依然能保证正确的隔离级别和一致性。

failpoint 只能测试一些预期之中的失败,我们还基于 chaos 系统对运行中的 PolarDB-X 任何节点模拟了网络失败、部分节点不可用、部分存储节点数据丢失等场景,转账测试会对各种异常情况的 PolarDB-X 实例进行测试,验证我们的实现是否正确。

总结

转账测试是 PolarDB-X 事务实现中最重要的测试之一,只有通过最完整可靠的测试,我们针对事务的优化才会上线。在实现上,我们对转账测试做了高度插件化的设计,保证我们可以很轻松地添加新的 case 到我们的测试中,我们会进一步完善转账测试,为 PolarDB-X 用户数据的正确性保驾护航。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论