昆仑分布式数据库使用MySQL做存储节点,称为kunlun-storage。Kunlun-storage目前最新版本基于percona-mysql-8.0.26开发,在此社区版本基础上,我们补充了MySQL的XA事务和binlog 复制方面的容错和数据一致性漏洞,增加了kunlun数据库集群需要的若干功能,并且增强了其性能。我们也一直在合并上游版本,持续汇集MySQL社区的最新成果到kunlun-storage中。
在Kunlun-storage Fullsync机制开发完成之前,我们一直在使用MySQL Group Replication(MGR)实现存储集群高可用。为了实现更好的数据写入性能包括更高的吞吐量和更短的延时,以及降低对存储系统和网络带宽的消耗,并且在高可用方面实现更加灵活的策略,我们在kunlun-storage中设计并开发了Fullsync高可用机制。
Kunlun-storage Fullsync功能简介
Kunlun-storage Fullsync概况和原理
昆仑数据库的Fullsync机制是一种存储集群的高可用机制,它用于确保一个存储集群在发生节点故障、网络分区等问题时,该集群中存在可用的备机含有所有向用户确认提交成功的事务的binlog,以便可以按需选举出新的主节点,确保集群持续可以写入,实现高可用。
昆仑数据库的Fullsync机制基于MySQL久经考验的Row Based Replication(RBR)binlog复制机制,实现了主备复制的强同步,也就是确保主节点上提交的每一笔事务 --- 包括显式普通事务(即begin 。。。Commit),autocommit的update/delete/insert语句,以及XA事务 --- 在完成内部的事务提交流程(即engine log和binlog flush&sync和engine commit 这三个阶段)之后,持续等待直到收到了足够数量的备机的确认(ACK)之后,才向客户端(在昆仑数据库中就是计算节点)确认这个事务成功提交。
一个ACK是一个binlog文件标识和偏移值的组合,它代表的是这个位置之前的binlog都已经被这个备机收到并且持久存储(刷盘)。这样,所有那些在主节点的binlog文件中其binlog存储在此位置之前的事务都得到了确认,它们的提交操作就都可以返回成功状态给客户端了。
一个备机发送该ACK是向其主节点确认收到并持久化(刷盘)了一组事务的binlog到其relay log文件中。只有主节点收到这个确认,才向客户端返回事务提交成功的确认状态,客户端收到此结果后才能发送下一条SQL语句给昆仑数据库。只有向客户端返回事务成功提交(或prepare),昆仑分布式数据库才有义务保障这样的事务的改动不丢失(即ACID的D,Durability)。
Kunlun-storage Fullsync的前提条件
Kunlun-storage Fullsync机制需要一组特定的参数组合才能正常工作。Kunlun-storage自带的参数配置模版文件中含有经过我们开发团队调优之后的参数设置,其中包含了fullsync功能需要的参数设置。
Kunlun数据库集群的所有存储节点实例都是使用其自带的参数模版创建的。具体来说包括一下参数:
- gtid_mode=on, enforce_gtid_consistency=1,log_slave_updates=ON
- binlog_format=row, i.e. 使用Row based replication
- 所有主备节点都使用binlog
- 会话都使用binlog,即 sql_log_bin=true。如果把一个会话的 sql_log_bin设置为false则此会话中fullsync机制不工作但是其他会话中fullsync仍然工作。
- enable_fullsync = ON 打开fullsync全局开关
- thread_handling=2或者0, 即kunlun-storage的fullsync机制适用于线程池(thread_handling=2)以及每个线程处理一个事务(thread_handling=0)的情况。
Kunlun-storage Fullsync的功能设计与实现
1. 主节点
kunlun-storage的fullsync机制是一种after-commit的同步模式。在处理用户会话thd的工作线程thr 完成事务T提交或者prepare(XA prepare)并且还未向客户端确认成功(即发送OK包)之前,主节点检查事务T的binlog是否已经收到了足够数量的备机的ACK(备机的ACK 确认收到若干个事务的binlog) --- 此条件称为释放条件。
Fullsync_consistency_level定义了主节点需要让每个事务等待多少个备机的ack,如果是0就不等待任何ack;如果大于0则等待这么多个备机的ack。
在昆仑数据库集群中,clustermgr会根据集群节点数量为每个master节点设置合理的Fullsync_consistency_level,通常的设定方法是对于一个2*n+1个节点的storage shard,那么设置Fullsync_consistency_level=n,这样就达到了简单多数,所以同时有n个节点消失的情况下,集群仍然可以正常写入。clustermgr也可以支持其他策略,比如要求所有备机全部确认等。
如果释放条件满足那么thr直接返回成功状态给客户端并且完成本次请求处理,否则工作线程thr就把会话对象thd放到fullsync ack等待队列,然后去处理其他连接中收到的请求。
主节点收到ACK后会对等待队列中的会话做释放条件检查,满足释放条件条件的会话会被释放,也就是返回成功状态给客户端。在等待备机ACK的过程中,用户回话并不占用工作线程。
如果超时(fullsync_timeout)未收到足够的ack来释放一个等待的会话时,kunlun-storage有两种策略,由全局变量disable_fullsync_on_slave_ack_timeout来控制:
A. 如果disable_fullsync_on_slave_ack_timeout=1,那么fullsync会自动退化为异步。这样后续等待的事务将不再做fullsync等待。当主节点再次收到备机ack后,会自动启用fullsync机制。
B. 如果disable_fullsync_on_slave_ack_timeout=0,那么fullsync等待超时的会话,会返回错误(错误号9000) 给客户端,对于昆仑数据库集群来说,就是计算节点收到了这个错误,会触发主备切换。
如果主节点宕机时有某个事务T没有收到任何备机的ack,导致切换后新的主节点缺少原来已经在旧主机M0上 提交的T事务的binlog,并且这个老主节点M0随后重新加入集群, kunlun-storage会对M0做flashback(闪回),把T的改动及其binlog从实例的存储引擎(innodb)和binlog文件中去掉。
由于T并没有返回给客户端所以昆仑数据库并没有向客户端承诺T提交成功了,因此我们完全可以把T闪回,并没有影响事务的durability。
2. 备节点
备机收到事件组(event group,即binlog事务,包括普通显式事务,XA事务第一阶段,XA事务第二阶段,DDL语句,autocommit语句这几种类型。下文简称EG)的终止binlog事件(XID_EVENT, XA_PREPARE_LOG_EVENT或者DDL事务)后,它会决定是否需要把收到的若干个EG写到relay log文件并且刷到持久存储系统中(即flush&fsync relay log),然后发送ACK给主节点来确认持久化了这些收到的EG。
这个决定基于最小化资源消耗和最优化性能来做出 --- 如果备机收到了足够多的EG(配置参数:fullsync_fsync_ack_least_txns)或者足够量的binlog(配置参数:fullsync_fsync_ack_least_event_bytes) ,或者太久没有发送ACK了( fullsync_fsync_ack_wait_max_milli_secs), 他就会flush&fsync relay log然后发送ACK。
一个ACK包含这些信息:备机的server_id, 落盘的最后一个EG在主节点binlog中的终止位置(文件编号和偏移值)。主节点收到一个备机的ACK后就可以确信这个备机收到并持久存储了ACK位置之前的所有EG。
使用fullsync_relaylog_fsync_ack_level 全局变量来控制一个备机节点的Fullsync机制在flush&fsync relay log和发送ACK的行为,其含义如下:
fullsync_relaylog_fsync_ack_level | 备机行为 |
---|---|
0 | 不flush或者fsync relay log也不发送ACK |
1 | 不flush或者fsync relay log也不发送ACK |
2 | Flush&fsync relay log后发送ACK |
在主节点上打开 log_fullsync_replica_acks 可以在mysqld运行日志中记录每一个收到的ACK,这个功能仅仅用于调试备机ACK机制,在生产系统中千万不要打开否则会严重影响性能。
备机有两种方法发送ACK给主节点,这两种方法都要求备机使用mysql客户端库连接主节点,这样每个备节点有两个连接连到主节点,一个是备机的IO线程的连接,另一个是备机发送ACK的连接。在此连接中备机发送kunlun-storage特有的COM_BINLOG_ACK命令或者发送kunlun-storage可以理解的SQL语句,前者性能更好,但是后者可以让各种第三方binlog存储组件向主节点发送ACK。
- COM_BINLOG_ACK
使用kunlun-storage的客户端库文件及其mysql.h 头文件编译程序,然后调用 mysql_send_binlog_ack() 函数发送ACK。kunlun-storage fullsync功能使用此方法发送ACK给其主节点。 - SLAVE server_id CONSISTENT TO file_index offset SQL 语句
这种方法可以使用任何社区版mysql客户端库,kunlun-storage的主节点可以正确处理该语句,把它当作确认ACK。此方法特别适合各种binlog存储组件。 - Kunlun-storage Fullsync的状态变量
这些状态变量可以帮助DBA观察fullsync的运行情况和性能,并且作为辅助调节fullsync配置参数的依据。其意义在表格中,都是简单的英语,相信大家都能看懂,因此不再翻译了。
status variable name | |
---|---|
fullsync_received_replica_acks | NO. of received replica acks |
fullsync_old_acks_received | NO. of received ACKs that are obsolete,i.e. an obsolete ACK ACKs a position already ACK'ed by previously received ACKs. |
fullsync_txns_acked | NO. of txns the replica ACK'ed |
fullsync_txns_fully_acked_before_wait | NO. of txns pre-ACK'ed before it starts to wait --- when the txn tries to wait for ack, its receives all needed ACKs from slaves |
fullsync_txns_acked_before_wait | NO. of txns partly ACK'ed before it starts to wait, when the txn tries to wait for ack, its receives part of all needed ACKs from slaves |
fullsync_txns_long_wait_warnings_for_acks | NO. of txns long wait warnings for ACKs. although the wait doesn't timeout, it's still long enough to trigger a fullsync warning. |
fullsync_txns_timed_out_waiting_for_acks | NO. of txns timed out waiting for ACKs |
fullsync_txns_received_by_replica | NO. of txns received by the replica |
fullsync_relay_log_syncs | NO. of relay log syncs. |
fullsync_acks_sent_to_master | NO. of ACKs sent to master |
fullsync_num_txns_in_acked_group | set by a replica, NO. of txns flushed and fsync'ed corresponding to latest ACK |
fullsync_replica_skipped_old_trx_acks | NO. of times the replica skipped sending ACKs because received txns are too old |
fullsync_replica_ack_upto_file and fullsync_replica_ack_upto_offset; | fullsync replicas have ACKed upto this position(file and offset within master's binlog file). |
fullsync_replica_fully_acked_upto_file; and fullsync_replica_fully_acked_upto_offset; | fullsync replicas have fully ACKed upto this position(file and offset within master's binlog file). |
fullsync_latest_recvd_trx_ts timestamp on master node of latest received transaction,it's the timestamp when the transaction on master node is flushed to its binlog file. it can be used to measure IO thread latency. | fullsync_replica_ack_timedout whether the master node timed out waiting for replica acks |
fullsync_effective whether fullsync is effective on this master or slave node | fullsync_num_waiting_txns NO. of transactions currently waiting for ACKs on master node |
- Fullsync配置参数
Kunlun-storage Fullsync支持丰富的配置参数让用户在性能、资源消耗和一致性方面取得适当的平衡。这些变量都是MySQL的全局变量,其意义和用法说明见下面的表格,都是简单的英语,相信大家都能看懂,因此不再翻译了。
Fullsync Variables | meanings |
---|---|
fullsync_consistency_level | At end of transaction commit, whether and how to wait for fullsync replica ACKs before replying the client that a transaction has committed. 0: no wait; 99: wait for simple majority replicas; 100: wait for all replicas; [1, 98]: wait for this number of ACKs. |
fullsync_relaylog_fsync_ack_level | When fullsync is enabled, how should the replica fsync relay log and/or reply an ACK to primary after it write its received event group(s) to relay log file.\t" "0: don't fsync or send ACK; 1: don't fsync but send ACK; 2: fsync and send ACK. |
fullsync_fsync_ack_least_event_bytes | Accumulate at least this many relay log bytes before fsync'ing the relay log and sending an ACK. |
fullsync_fsync_ack_least_txns | Accumulate at least this many event groups before fsync'ing the relay log and sending an ACK. |
fullsync_fsync_ack_wait_max_milli_secs | Replica nodes wait for more event groups to arrive at most this many milli-seconds before fsync'ing the relay log and sending an ACK. |
skip_fullsync_replica_acks_older_than | If a replica is this many milli-seconds later than the primary node, skip fsync'ing the relay log or sending ACKs. |
fullsync_warning_timeout | If a replica ACK arrives this many milli-seconds since the transaction started to wait for it, write a warning in error log. |
fullsync_timeout | If a replica ACK doesn't arrive after this many milli-seconds since the transaction started to wait for it, return error to client and write an error in error log. |
log_fullsync_replica_acks | Whether log replica ACKs to mysqld error log. Note that when fullsync is enabled there can be a huge amount of such logs which are seldom used. |
enable_fullsync | Whether enable fullsync mechanism. |
disable_fullsync_on_slave_ack_timeout | Whether disable fullsync when replicas do not ACK in time and timeout happened, if this is false, then the primary node can't be written when it has no running replicas. |
Kunlun-storage Fullsync的优势
与MySQL的半同步(semisync)插件相比,kunlun-storage 的fullsync机制有如下优势。
等待备机确认期间,客户端会话及其事务不占用工作线程。这样就可以避免大量工作线程阻塞等待备机的ACK,导致线程池为了处理来自其他客户端会话的持续大量到来的请求不得不启动更多的工作线程,消耗过多系统资源。
备机会聚集若干个事务的binlog后(可配置),才对relay log做fsync,确保binlog落盘。这样不仅可以避免备机断电或者其OS crash或者重启导致备机丢失了最近的relay log的严重问题,还不会对存储设备造成巨大的写入负载。达到了性能,延时和存储资源消耗之间的完美平衡。
Fullsync有灵活的配置能力,让用户在高可用性、数据强一致性和性能之间做出灵活的控制和取舍。
Fullsync提供丰富的运行状态信息,方便DBA监控fullsync的运行时状态,为有效的fullsync相关配置提供完整的信息
DBA可以配置主节点去等待若干个(可配置)备机返回ACK,从而达到更高的容错级别。比如对于某些高价值业务,DBA可以配置一主四备,让主节点为每个事务等待2个备机ACK。任何一个事务提交操作完成后在返回给客户端之前,必须等待收到两个备机的ACK,这个事务提交状态才能返回给客户端。
同时,DBA还可以配置一个备机的特定channel是否发送ACK,即使这个备机的fullsync_relaylog_fsync_ack_level为1或者2,达到灵活配置集群高可用架构的目标。
END
昆仑数据库是一个HTAP NewSQL分布式数据库管理系统,可以满足用户对海量关系数据的存储管理和利用的全方位需求。
应用开发者和DBA的使用昆仑数据库的体验与单机MySQL和单机PostgreSQL几乎完全相同,因为首先昆仑数据库支持PostgreSQL和MySQL双协议,支持标准SQL:2011的 DML 语法和功能以及PostgreSQL和MySQL对标准 SQL的扩展。同时,昆仑数据库集群支持水平弹性扩容,数据自动拆分,分布式事务处理和分布式查询处理,健壮的容错容灾能力,完善直观的监测分析告警能力,集群数据备份和恢复等 常用的DBA 数据管理和操作。所有这些功能无需任何应用系统侧的编码工作,也无需DBA人工介入,不停服不影响业务正常运行。
昆仑数据库具备全面的OLAP 数据分析能力,通过了TPC-H和TPC-DS标准测试集,可以实时分析最新的业务数据,帮助用户发掘出数据的价值。昆仑数据库支持公有云和私有云环境的部署,可以与docker,k8s等云基础设施无缝协作,可以轻松搭建云数据库服务。
请访问 http://www.zettadb.com/ 获取更多信息并且下载昆仑数据库软件、文档和资料。
KunlunDB项目已开源
【GitHub:】
https://github.com/zettadb
【Gitee:】
https://gitee.com/zettadb