Blog

事务标识符(Transaction ID,zxid)

Content #

事务标识符(Transaction ID,也就是 zxid),就像下图的样子。

从图中你可以看到,X、Y 对应的事务标识符分别为 <1, 1> 和 <1, 2>,这两个标识符是什么含义呢?

你可以这么理解,事务标识符是 64 位的 long 型变量,有任期编号 epoch 和计数器 counter 两部分组成(为了形象和方便理解,我把 epoch 翻译成任期编号),格式为 <epoch, counter>,高 32 位为任期编号,低 32 位为计数器:

  1. 任期编号,就是创建提案时领导者的任期编号,需要你注意的是,当新领导者当选时,任期编号递增,计数器被设置为零。比如,前领导者的任期编号为 1,那么新领导者对应的任期编号将为 2。

  2. 计数器,就是具体标识提案的整数,需要你注意的是,每次领导者创建新的提案时,计数器将递增。比如,前一个提案对应的计数器值为 1,那么新的提案对应的计数器值将为 2。

为什么要设计的这么复杂呢?因为事务标识符必须按照顺序、唯一标识一个提案,也就是说,事务标识符必须是唯一的、递增的。

From #

ZAB操作的顺序性

Content #

能保证操作顺序性的,基于主备模式的原子广播协议。

我还是以 X、Y 指令为例具体演示一下,帮助你更好理解为什么 ZAB 能实现操作的顺序性(为了演示方便,我们假设节点 A 为主节点,节点 B、C 为备份节点)。

首先,需要你注意的是,在 ZAB 中,写操作必须在主节点(比如节点 A)上执行。如果客户端访问的节点是备份节点(比如节点 B),它会将写请求转发给主节点。如图所示:

接着,当主节点接收到写请求后,它会基于写请求中的指令(也就是 X,Y),来创建一个提案(Proposal),并使用一个唯一的 ID 来标识这个提案。这里我说的唯一的 ID 就是指 事务标识符(Transaction ID,zxid)

在创建完提案之后,主节点会基于 TCP 协议,并按照顺序将提案广播到其他节点。这样就能保证先发送的消息,会先被收到,保证了消息接收的顺序性。

你看这张图,X 一定在 Y 之前到达节点 B、C。

然后,当主节点接收到指定提案的“大多数”的确认响应后,该提案将处于提交状态(Committed),主节点会通知备份节点提交该提案。

在这里,需要你注意的是,主节点提交提案是有顺序性的。主节点根据事务标识符大小,按照顺序提交提案,如果前一个提案未提交,此时主节点是不会提交后一个提案的。也就是说,指令 X 一定会在指令 Y 之前提交。

最后,主节点返回执行成功的响应给节点 B,节点 B 再转发给客户端。你看,这样我们就实现了操作的顺序性,保证了指令 X 一定在指令 Y 之前执行。

Viewpoints #

From #

15 | ZAB协议:如何实现操作的顺序性?

Multi-Paxos无法保证操作顺序性

Content #

我们假设当前所有节点上的被选定指令,最大序号都为 100,那么新提议的指令对应的序号就会是 101。

首先节点 A 是领导者,提案编号为 1,提议了指令 X、Y,对应的序号分别为 101 和 102,但是因为网络故障,指令只成功复制到了节点 A。

假设这时节点 A 故障了,新当选的领导者为节点 B。节点 B 当选领导者后,需要先作为学习者了解目前已被选定的指令。节点 B 学习之后,发现当前被选定指令的最大序号为 100(因为节点 A 故障了,它被选定指令的最大序号 102,无法被节点 B 发现),那么它可以从序号 101 开始提议新的指令。这时它接收到客户端请求,并提议了指令 Z,指令 Z 被成功复制到节点 B、C。

假设这时节点 B 故障了,节点 A 恢复了,选举出领导者 C 后,节点 B 故障也恢复了。节点 C 当选领导者后,需要先作为学习者了解目前已被选定的指令,这时它执行 Basic Paxos 的准备阶段,就会发现之前选定的值(比如 Z、Y),然后发送接受请求,最终在序号 101、102 处达成共识的指令是 Z、Y。就像下图的样子。

在这里,你可以看到,原本预期的指令是 X、Y,最后变成了 Z、Y。

Viewpoints #

From #

15 | ZAB协议:如何实现操作的顺序性?

TiFlash存储分离

Content #

如果底层存储是一份数据,那么天然就可以保证 OLTP 和 OLAP 的数据一致性,这是 PAX 的最大优势,但是由于访问模式不同,性能的相互影响似乎也是无法避免,只能尽力选择一个平衡点。TiDB 展现了一种不同的思路,介于 PAX 和传统 OLAP 体系之间,那就是 OLTP 和 OLAP 采用不同的存储方式,物理上是分离的,然后通过创新性的复制策略,保证两者的数据一致性。

TiDB 是在较早的版本中就提出了 HTAP 这个目标,并增加了 TiSpark 作为 OLAP 的计算引擎,但仍然共享 OLTP 的数据存储 TiKV,所以两种任务之间的资源竞争依旧不可避免。直到近期的 4.0 版本中,TiDB 正式推出了 TiFlash 作为 OLAP 的专用存储。

我们的关注点集中在 TiFlash 与 TiKV 之间的同步机制上。其实,这个同步机制仍然是基于 Raft 协议的。TiDB 在 Raft 协议原有的 Leader 和 Follower 上增加了一个角色 Learner。这个 Learner 和 Paxos 协议中的同名角色,有类似的职责,就是负责学习已经达成一致的状态,但不参与投票。这就是说,Raft Group 在写入过程中统计多数节点时,并没有包含 Learner,这样的好处是 Learner 不会拖慢写操作,但带来的问题是 Learner 的数据更新必然会落后于 Leader。

看到这里,你可能会问,这不就是一个异步复制吗,换了个马甲而已,有啥创新的。这也保证不了 AP 与 TP 之间的数据一致性吧?

...

融合性存储PAX(Partition Attributes Across)

Content #

PAX 增加了 minipage 这个概念,是原有的数据页下的二级单位,这样一行数据记录在数据页上的基本分布不会被破坏,而相同列的数据又被集中地存储在一起。 PAX 本质上还是更接近于行式存储,但它也在努力平衡记录内局部性和记录间局部性,提升了 OLAP 的性能。

理论上,PAX 提供了一种兼容性更好的存储方式,可让人有些信心不足的是其早在 2002 年提出,但在 Spanner 之前却少有落地实现。

与这个思路类似的设计还有 HyPer 的DataBlock(SIGMOD2016),DataBlock 构造了一种独有的数据结构,同时面向 OLTP 和 OLAP 场景。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?

DSM(列式存储)

Content #

DSM(Decomposition Storage Model)就是列式存储,它的出现要晚于行式存储。

列式存储就是将所有列集中存储,不仅更加适应 OLAP 的访问特点,对 CACHE 也更友好。这种特点称为记录间的局部性(Inter-Record Spatial Locality)。列式存储能够大幅提升查询性能,以速度快著称的 ClickHouse 就采用了列式存储。

列式存储的问题是写入开销更大,这是因为根据关系模型,在逻辑上数据的组织单元仍然是行,改为列式存储后,同样的数据量会被写入到更多的数据页(page)中,而数据页直接对应着物理扇区,那么磁盘 I/O 的开销自然增大了。

列式存储的第二个问题,就是很难将不同列高效地关联起来。毕竟在多数应用场景中,不只是使用单列或单表数据,数据分散后,关联的成本会更高。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?

NSM(行式存储)

Content #

NSM(N-ary Storage Model)就是行式存储,也是 OLTP 数据库默认的存储方式,始终伴随着关系型数据库的发展。我们常用的 OLTP 数据库,比如 MySQL(InnoDB)、PostgreSQL、Oracle 和 SQL Server 等等都使用了行式存储。

顾名思义,行式存储的特点是将一条数据记录集中存在一起,这种方式更加贴近于关系模型。写入的效率较高,在读取时也可以快速获得一个完整数据记录,这种特点称为记录内的局部性(Intra-Record Spatial Locality)。

但是,行式存储对于 OLAP 分析查询并不友好。OLAP 系统的数据往往是从多个 OLTP 系统中汇合而来,单表可能就有上百个字段。而用户一次查询通常只访问其中的少量字段,如果以行为单位读取数据,查询出的多数字段其实是无用的,也就是说大量 I/O 操作都是无效的。同时,大量无效数据的读取,又会造成 CPU 缓存的失效,进一步降低了系统的性能。

图中显示 CPU 缓存的处理情况,我们可以看到很多无效数据被填充到缓存中,挤掉了那些原本有机会复用的数据。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?

解决OLAP系统数据时效性问题的两种思路

Content #

OLAP和OLTP通过ETL来衔接 体系的主要问题就是 OLAP 系统的数据时效性, T+1 太慢了。进入大数据时代后,商业决策更加注重数据的支撑,而且数据分析也不断向一线操作渗透,这都要求 OLAP 系统更快速地反映业务的变化。

两种解决思路 #

  1. 用准实时数据计算替代原有批量 ETL 过程,重建 OLAP 体系; Kappa架构

  2. 弱化甚至是干脆拿掉 OLAP,直接在 OLTP 系统内扩展,也就是 HTAP。

HTAP(Hybrid Transaction/Analytical Processing)就是混合事务分析处理。

它打破了 OLTP 和 OLAP 之间的隔阂,在一个数据库系统中同时支持事务型数据库场景和分析型数据库场景。这个构想非常美妙,HTAP 可以省去繁琐的 ETL 操作,避免批量处理造成的滞后,更快地对最新数据进行分析。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?

Kappa架构

Content #

OLAP和OLTP通过ETL来衔接 体系的主要问题就是 OLAP 系统的数据时效性,T+1 太慢了。进入大数据时代后,商业决策更加注重数据的支撑,而且数据分析也不断向一线操作渗透,这都要求 OLAP 系统更快速地反映业务的变化。

重建 OLAP 体系,重视数据加工的时效性,正是近年来大数据技术的主要发展方向。Kappa 架构就是新体系的代表,它最早由 LinkedIn 的 Jay Kreps 在 2014 年的一篇文章中提出。

在 Kappa 架构中,原来的批量文件传输方式完全被 Kafka 替代,通过流计算系统完成数据的快速加工,数据最终落地到 Serving DB 中提供查询服务。这里的 Serving DB 泛指各种类型的存储,可以是 HBase、Redis 或者 MySQL。

要注意的是,Kappa 架构还没有完全实现,因为在实践中流计算仍然无法替代批量计算,Serving DB 也无法满足各种类型的分析查询需求。未来,Kappa 架构需要在两方面继续完善:

  1. 流计算能力的增强,这需要用到 Kafka 和 Flink 等软件;
  2. Serving DB 即时计算能力的增强,这就寄希望于 OLAP 数据库的突破,就像 ClickHouse 已经做的那样。

总的来说,新的 OLAP 体系试图提升即时运算能力,去除批量 ETL,降低数据延迟。这个新体系是流计算的机遇,也是 OLAP 数据库的自我救赎。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?

OLAP和OLTP通过ETL来衔接

Content #

OLTP 是面向交易的处理过程,单笔交易的数据量很小,但是要在很短的时间内给出结果;而 OLAP 场景通常是基于大数据集的运算。

OLAP 和 OLTP 通过 ETL 进行衔接。为了提升 OLAP 的性能,需要在 ETL 过程中进行大量的预计算,包括数据结构的调整和业务逻辑处理。

这样的好处是可以控制 OLAP 的访问延迟,提升用户体验。

但是,因为要避免抽取数据对 OLTP 系统造成影响,所以必须在日终的交易低谷期才能启动 ETL 过程。这样一来, OLAP 与 OLTP 的数据延迟通常就在一天左右,习惯上大家把这种时效性表述为 T+1。其中,T 日就是指 OLTP 系统产生数据的日期,T+1 日是 OLAP 中数据可用的日期,两者间隔为 1 天。

Viewpoints #

From #

18 | HTAP是不是赢者通吃的游戏?