2
0
1
0
专栏/.../

TiDB 的事务和一致性校验工具 BANK

 pepezzzz  发表于  2023-03-09

背景

在分布式数据库的选型和测试过程中,通常需要关注分布式事务在高可用场景下的一致性和 RPO=0 的容灾技术实现。分布式事务需要能影响多张表的多条记录,实现多表事务和跨节点高可用的验证。

BANK 程序

BANK 程序(链接: https://pan.baidu.com/s/1M14Kf0ULGdbkoMZj0iOvDA?pwd=p93k 提取码: p93k)是一个简化转账模型的并发程序。初始化后,会创建流水 record 表和账务 account 表,流水表对应转账过程中的金额变化和事务时间( tso 列,实际函数为 time.Now().UnixNano() ,即执行节点的纳秒时间),account 表记录最终的账户金额和事务时间(tso 列)。

MySQL [test]> show tables;
+----------------+
| Tables_in_test |
+----------------+
| accounts       |
| record         |
+----------------+
2 rows in set (0.00 sec)

MySQL [test]> select * from record order by tso desc limit 1; 
+---------+-------+--------------+--------------------+------------+------------------+--------+---------------------+
| from_id | to_id | from_balance | from_balance_after | to_balance | to_balance_after | amount | tso                 |
+---------+-------+--------------+--------------------+------------+------------------+--------+---------------------+
|     384 |   172 |         1226 |                284 |       1915 |             2857 |    942 | 1661398293659372259 |

MySQL [test]> select * from accounts order by tso desc limit 10; 
+-----+---------+---------------------------------------------------------------------------------------------------+---------------------+
| id  | balance | remark                                                                                            | tso                 |
+-----+---------+---------------------------------------------------------------------------------------------------+---------------------+
| 384 |     284 | abcdefghijklmnopqrs                                                                               | 1661398293659372259 |
| 172 |    2857 | abcdefg                                                                                           | 1661398293659372259 |

使用方法

程序的参数如下:

[root@iZuf6d7xln13sovvijl68rZ ~]# ./bank --help
Usage of ./bank:
-accounts int
the number of the bank accounts (default 1000000)
-create
create new tables for test
-driver string
database driver name, support 'mysql', 'postgres' (default "mysql")
-dsn string
data source name
MySQL: root:@tcp(127.0.0.1:4000)/test
PostgreSQL: postgres://root:@127.0.0.1:5432/postgres?sslmode=disable
(default "root:@tcp(127.0.0.1:4000)/test")
-insert
insert data for test
-interval duration
verify interval (default 2s)
-threads int
threads count (default 10)

初次执行

./bank -dsn 'root:password@tcp(172.16.5.31:34000)/test' -create -insert -accounts 100

配好 DSN 后,第一次执行程序添加 -create -insert 参数会自动初始化数据,初始完成后开始转账,就随机挑选两个账户,select for update 锁定账户,然后 update 账户并记录流水,然后提交事务。

中断后可后续执行

./bank -dsn 'root:password@tcp(172.16.5.31:34000)/test' -accounts 100

bank 程序除转账交易的并发线程(默认10个)外,有一个 goroutine,到达校验 interval (默认 2 秒)时间就会算一下账户总金额是不是保持一致,不是预期的就退出,说明出现事务破裂。

分布式事务和 RAFT 强同步的验证场景

在分布式事务的执行过程中,涉及多个节点参与,在此过程中如果出现节点异常就有可能出现分布式事务的异常处理。

以分库分表中间件中常用的基于 MySQL XA 的分布式事务二阶段提交为例:

  1. 在 xa 事务提交过程中,如果出现 set 节点不可用进行切换,就会有部分节点未提交和事务不一致情况发生。从下图中可以看到,setx 库提交成功时,sety 库如果故障未提交成功,需要有额外的补偿机制 mysqlagent 发现各个 set 的提交情况和全局的 gtid log 不一致,需要补偿处理。

image.png

  1. 在 xa 事务提交过程中,由于在没有事务版本控制,不同节点的提交时间存在先后,有可能会出现事务破裂。由下图中可以看到,如果 XA 事务在 DB-1 和 DB -2 提交, DB-2 晚于 DB-1 提交,如果在提交间隙中查看总金额,就会发现总金额不一致。

image.png

TiDB 的 percolator 事务模型中二阶段的提交就是 primary key 改成 commit 的原子操作,通过 Raft 多数派共识容忍节点故障,可以避免以上的情况。

image.png

ticdc 表间事务的验证场景

ticdc 的早期版本保证复制过程中表内事务一致,但是受大事务延迟影响。v6.1.1 版本起,可以通过配置 sink uri 参数 transaction-atomicity 来控制 TiCDC 是否拆分单表事务,可选值是 none 和 table。当选择 none 时,TiCDC 会拆分单表事务,也就是说可选择不保证表内事务是一致的,当选择 table 时,仍保证单表事务的原子性。v6.1.3 版本起,将 transaction-atomicity 的默认值从 table 修改为 none。

撇弃表内事务一致通过多并发提高大事务投递性能的基础上, TiCDC 通过 syncpoint 功能保证复制过程每隔多长时间定期一致和 redo 持久化功能保证 redo 数据应用后最终一致。BANK 程序中 record 表和 account 表的同一事务(tso 列) 的交易记录和交易账户能对应,通过最新事务的数据可以验证 ticdc 的表间事务验证能力,是否实现过程定期一致和应用最终一致。

不同场景下的校验语句使用方法

  1. 节点高可用情况时验证事务一致性。如果总金额不一致,说明发生事务破裂。

集群高可用后执行如下语句,balance 总金额应该不变,record 表和 accounts 表最新记录应该一致。

select sum(balance) from accounts;
select * from record where tso=(select max(tso) from record);
select * from accounts where tso=(select max(tso) from accounts);

  1. 验证容灾场景的 RPO=0 数据一致性。如果最新的 TSO 不一致,说明不是 RPO=0。

上游集群和下游集群分别执行如下语句,balance 总金额应该不变,最新记录应该一致。

select sum(balance) from accounts;
select * from record where tso=(select max(tso) from record);
select * from accounts where tso=(select max(tso) from accounts);
  1. 验证逻辑复制场景的历史数据一致性。如果历史 TSO 的数据不一致,说明复制过程数据不一致。

上游集群中执行如下语句。

set tidb_snapshot="redo apply ts";
select sum(balance) from accounts;
select * from record where tso=(select max(tso) from record);
select * from accounts where tso=(select max(tso) from accounts);

下游集群中执行如下语句 ,与上游历史状态下的记录应该一致。

select sum(balance) from accounts;
select * from record where tso=(select max(tso) from record);
select * from accounts where tso=(select max(tso) from accounts);

总结

BANK 程序可以在金融行业等需要验证事务一致的情况下使用,在引入新产品新架构评估的过程,使用 bank 程序非常有利于分析所选产品的一致性和可靠性。TiDB 数据库能通过高可用等测试场景向用户展示本身的健壮性。

2
0
1
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论