【PGCCC】在 Postgres 中构建复制安全的 LSM 树

在这里插入图片描述

在原生 Postgres 实现中,全文搜索由B 树或GIN(广义倒排索引)结构支持。这些索引针对相对快速的查找进行了优化,但受限于 B 树的写入吞吐量。

当我们构建pg_searchPostgres 搜索和分析扩展时,我们的优先级有所不同。为了成为 Elasticsearch 的有效替代方案,我们需要支持高效的实时扫描。我们选择了一种更适合密集发布列表位图和高数据采集工作负载的数据结构:日志结构化合并 ( LSM ) 树。

然而,当我们在物理复制(允许 Postgres 将数据从主节点复制到一个或多个只读副本的两种机制之一)下测试 LSM 树时,我们遇到了一些波折。最令人惊讶的是,我们发现 Postgres 基于预写日志 (WAL) 传输机制构建的开箱即用的物理复制支持不足以让LSM 树这样的高级数据结构实现复制安全。在本文中,我们将深入探讨:

  1. LSM 树的复制安全意味着什么
  2. Postgres 的 WAL 传输如何保证物理一致性
  3. 为什么原子日志对于逻辑一致性是必要的
  4. 我们如何利用鲜为人知但功能强大的 Postgres 设置hot_standby_feedback

什么是 LSM 树?

图片

日志结构合并树 (LSM 树) 是一种写优化数据结构,常用于 RocksDB 和 Cassandra 等系统。

LSM 树的核心思想是将随机写入转换为顺序写入。传入的写入首先存储在内存缓冲区(称为 memtable)中,该缓冲区更新速度很快。一旦 memtable 写满,它将被刷新到磁盘,形成一个已排序的、不可变的段文件(通常称为 SSTable)。

这些段文件按大小组织成层或级别。较新的数据写入最顶层。随着时间的推移,数据通过称为“压缩”的过程逐渐下推到较低的级别。在此过程中,较小段中的数据会被合并、去重,然后重写到较大的段中。

复制安全是什么意思?

可靠的分布式数据存储(保证“复制安全”)必须证明跨数据库副本的物理和逻辑一致性。

  1. 物理一致性意味着副本包含结构有效的数据——磁盘上的每个页面或块都是格式良好的,并且对应于主节点上某个时刻存在的状态。
  2. 逻辑一致性确保副本上的数据反映数据库的一致且稳定的视图,这是主数据库上的事务可以看到的。

物理一致的状态并不总是逻辑一致的状态。具体来说,如果在复制正在进行的事务时拍摄物理一致的副本的快照,则它可能在逻辑上不一致。一个很好的比喻是想象复制一本书。物理一致性就像精确地复制每一页,即使正在复制某一章的中间部分——保证有真实的页面,但最终可能会缺少半句话或脚注。逻辑一致性就像等到章节完成后再复制,确保结果对读者有意义。

WAL 传输:Postgres 如何保证物理一致性


在主备物理复制设置中,主服务器与备用服务器配对,备用服务器充当其主服务器的只读副本。服务器通过使用预写日志 (WAL) 来保持同步,在发生任何二进制更改之前,这些更改都会记录在主服务器上的存储块上。然后,对此仅追加的 WAL 文件的更改将流式传输到备用服务器(此过程称为“日志传送”),并按接收的顺序应用。此过程使两台服务器之间能够实现近乎实时的数据同步,因此有“热备”的说法。

为什么原子性是物理一致性的必要条件

原子性是物理一致性的必要条件,因为 Postgres 的锁不会在副本服务器上重放。这是因为重放主服务器上获取的每个锁需要严格的时间同步,这会严重影响性能,并妨碍备用服务器提供读取服务的能力。相反,WAL 使用每个缓冲区的锁来按特定顺序增量重放编辑:它获取缓冲区(块在内存中的表示形式)的独占锁,进行更改,然后释放它。

当修改跨越多个 Postgres 缓冲区的数据结构时,就会出现问题。由于无法保证操作在整个结构上是原子的,这些修改可能会导致结构损坏。

例如:pg_search使用Postgres 缓冲区的展开链表,其中每个节点保存 LSM 树中一批段的读取有效性。为了确保主服务器永远不会发现损坏的链表,我们使用了手动锁定(也称为锁耦合)来保证该列表在主服务器上保持物理一致性。列表中的每个缓冲区被修改后,其 WAL 条目将在副本上以原子方式可见。

但是,当我们想要“一次性”(原子地)编辑列表中的多个条目时,例如当一个新压缩的段替换多个旧段时,会发生什么情况?如果只有主服务器重要,那么我们可以通过在列表本身上应用全局锁来保护多个列表节点的逻辑完整性,确保列表内容仅在有效状态下可见。但是副本服务器无法访问全局锁,因此无法同时协调跨多个节点(和多个缓冲区)的编辑。

相反,对于多节点操作,pg_search使用列表的写时复制 (CoW) 克隆,并在头部进行原子交换。更一般地说,原子操作通过消除对粗粒度锁的依赖,使免受危险。

问题:真空破坏逻辑一致性

在这里插入图片描述

调整算法以在块级别原子地工作是物理复制的赌注:如果不这样做,您的数据结构就会被破坏,并且您将无法一致地使用它们。

但即使单个 WAL 操作和数据结构在原子上兼容,VACUUM也会干扰跨多个 WAL 条目的并发事务的执行,并损害逻辑一致性。

为了说明这个问题,假设主数据库有一个包含一定数量行的表。为了确保并发写入操作能够安全地进行,而不会相互阻塞,Postgres 使用了一种称为多版本并发控制 ( MVCC ) 的机制,该机制会为修改后的行(或元组)创建多个版本,而不是就地更新元组。当更新或删除一行时,先前的元组不会被立即删除,而是被标记为“已死”。

这些“死”的元组会一直保留在磁盘上,直到运行名为 VACUUM 的定期维护操作。与其他操作一样,VACUUM 操作会记录在 WAL 中,然后发送到备用数据库,并在那里重放。

由于元组的“失效”发生在服务器本地,而 VACUUM 操作则会在全局范围内重放,因此如果过早地从备库中对某个元组执行 VACUUM 操作,则可能会出现错误。备库可能正在读取某个元组,并对其进行迭代(遍历多个 WAL 条目),而主库可能并发地决定执行 VACUUM 操作,将该元组从数据库中移除。由于备库缺乏任何锁协调或对并发操作的感知,会在前一个事务仍在进行时重放 VACUUM 操作。如果一个长时间运行的查询尝试访问已执行 VACUUM 操作的元组,则可能导致查询失败。

为什么 LSM 树特别容易受到这个问题的影响

如果您的 Postgres 配置了只读副本,并且写入量很大,那么即使使用 B 树索引,您可能也已经遇到过这个问题。如果在查询命中只读副本的同时,主服务器上正在运行 VACUUM,Postgres 可能会中止读取操作。但是,在典型的 Postgres 设置中,这些错误可能很少发生,并且可以容忍,因为 VACUUM 每隔几个小时运行一次。

但 LSM 树的情况则不同,因为压缩是系统的核心,并且是持续执行的部分。在高写入吞吐量的系统中,压缩每分钟甚至每秒都可能发生多次。这增加了发生冲突的可能性。

与 VACUUM 类似,压缩会重写主服务器上的数据,并且需要知道正在进行的查询何时不再需要该数据,以便能够安全地删除旧段。

合理的解决方案:热备反馈

此时,Postgres 中一个可选的设置hot_standby_feedback就派上用场了。启用后,hot_standby_feedback备用服务器可以告知主服务器从副本服务器的角度来看哪些数据是可以安全清理的。此信息显著降低了元组被过早 VACUUM 的可能性,并允许pg_search确定何时可以安全删除段。

要理解实际传递的信息hot_standby_feedback,我们必须首先了解 Postgres 中元组版本控制的工作原理。Postgres 中的每个元组都有两个关键的元数据属性: xmin 和 xmax。存储创建 或 插入 该特定元组版本的xmin事务的事务 ID (XID) ,而 存储更新 或 删除该元组版本的事务的 XID ,从而有效地将其标记为已过时。当元组被删除时,其 值将使用删除事务的 XID 进行更新。由于 XID 是按顺序分配的,因此后续事务的 XID 会被分配更大的编号,因此另一种思考方式是将其作为元组的“创建时间”和“上次更新或删除时间”的代理。
在这里插入图片描述
启用后hot_standby_feedback,副本将定期传达xmin其任何活动查询当前固定的最小(最早的“创建时间”),这xmin标识了备用服务器上仍在使用的最旧元组。

有了这些信息,主服务器就可以更明智地决定何时允许执行清理操作(例如 VACUUM)。如果它发现备用查询仍在对原本会被视为“死亡”的元组进行操作,则可以将清理操作推迟到该查询完成。

最后的想法

即使借助hot_standby_feedback,备用服务器也基本上要依赖 WAL 来提供按接收顺序和时间安全执行的指令。本地激励与全局需求之间的矛盾,只是在分布式 Postgres 系统中实现完全复制安全性的挑战性维度之一。

为了实现物理和逻辑一致性,pg_search我们实现了原子记录的 LSM 树,而为了实现逻辑一致性,我们依赖于hot_standby_feedback。

这项挑战值得挑战,因为它能够在不牺牲一致性的情况下实现最快的搜索性能。要查看实际效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90500.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

架构如钟摆:在变与不变之间优雅平衡

在当今数字转型浪潮中,企业在“快速创新”与“长期稳定”之间反复拉扯。是否应该重建所有架构以适应AI?又是否该死守传统系统确保安全与合规?在The Open Group阿姆斯特丹峰会上,凯捷全球 CTO Ron Tolido 借用了一个极具画面感的比…

LLM中的位置嵌入矩阵(Position Embedding Matrix)是什么

LLM中的位置嵌入矩阵(Position Embedding Matrix)是什么 在大语言模型(LLM)中,位置嵌入矩阵(Position Embedding Matrix) 是用来表示输入序列中每个词的位置信息的矩阵。它的核心作用是:让模型能够区分“相同词在不同位置的语义差异”(比如“猫喜欢鱼”中的“猫”和“…

国产DevOps平台Gitee:如何重塑中国企业研发效能新格局

国产DevOps平台Gitee:如何重塑中国企业研发效能新格局 在全球数字化转型浪潮中,软件研发效率已成为企业竞争力的核心指标。作为中国最大的代码托管平台,Gitee正通过其全栈式DevOps解决方案,助力中国企业突破研发效能瓶颈&#xff…

告别混乱!【Java Web】项目分层架构全指南:核心三层 + 关键辅助包详解

目录 1.前言 2.正文 2.1为什么要分层 2.2核心三层详解 2.2.1Controller层(表现层/API层) 2.2.2Service层(业务逻辑层) 2.2.3DAO层(持久层) 2.3. 核心关系与数据流转:分层架构的交互逻辑…

解决Docker Compose报错

解决Docker Compose报错:exec ./entrypoint.sh: no such file or directory在使用Docker Compose部署应用时,你是否遇到过exec ./entrypoint.sh: no such file or directory这个令人头疼的错误?本文将深入分析错误原因并提供多种解决方案&…

【element plus】el-select,allow-create不需要点回车键

<el-selectv-model"row.expertName"filterableremoteallow-createdefault-first-optionreserve-keywordplaceholder"请输入姓名":remote-method"remoteMethod":loading"loadingName"change"(val) > handleNameChange(row, …

RK3588 HDMI-RX 驱动、RGA 加速与 OpenCV GStreamer 支持完整指南

一、环境检测与前置依赖 确认内核与 HDMI-RX 节点&#xff1a; uname -a # 输出&#xff1a;6.1.0-1025-rockchip ...dmesg | grep -i hdmirx # 应能看到 hdmirx-controller 节点&#xff1a; # fdee0000.hdmirx-controller driver probe ok!如果仅出现&#xff1a; rockchi…

AS32A601芯片QSPI 调试技术解析与与实战经验分享

一、概述&#xff08;一&#xff09;QSPI 简介QSPI&#xff08;Quad Serial Peripheral Interface&#xff09;是一种高速串行通信接口&#xff0c;在标准 SPI&#xff08;Serial Peripheral Interface&#xff09;的基础上扩展至 4 条数据线&#xff08;Quad Mode&#xff09;…

TDengine 转化函数 TO_TIMESTAMP 用户手册

TDengine TO_TIMESTAMP 函数用户使用手册 函数概述 TO_TIMESTAMP 是 TDengine 中的标量函数&#xff0c;用于将字符串按照指定格式转换为时间戳。该函数在数据导入、时间格式转换、以及处理各种时间字符串格式时非常有用。 语法 TO_TIMESTAMP(ts_str_literal, format_str_liter…

关于我司即将对商业间谍行为进行法律诉讼的通知

最后警告我司所属社交媒体中所有友商间谍&#xff1a;请于2025年7月26日上午十点前&#xff0c;自行删除我方好友&#xff0c;并停止通过欺诈行为&#xff08;包括但不限于冒充客户等&#xff09;盗取我司商业秘密的行为。十点后&#xff0c;我司将开始进行逐一排查&#xff0c…

【打怪升级 - 03】YOLO11/YOLO12/YOLOv10/YOLOv8 完全指南:从理论到代码实战,新手入门必看教程

引言&#xff1a;为什么选择 YOLO&#xff1f; 在目标检测领域&#xff0c;YOLO&#xff08;You Only Look Once&#xff09;系列模型一直以其高效性和准确性备受关注。作为新版本&#xff0c;YOLO系列的新版本总能在前辈的基础上进行了多项改进&#xff0c;包括更高的检测精度…

JMeter每次压测前清除全部以确保异常率准确(以黑马点评为例、详细图解)

目录 一、前言 二、未清除全部会出现的情况(以乐观锁解决超卖问题为例) 三、清除全部就能得到准确的结果 一、前言 在学习黑马点评之前我并没有接触过JMeter这个压测软件&#xff0c;然后在黑马点评视频中老师也是直接拿起JMeter就开始使用&#xff0c;所以我一直在不断搜索…

关于新学C++编程Visual Studio 2022开始,使用Cmake工具构建Opencv和SDK在VS里编译项目开发简介笔记

1. C 项目build文件夹 2. VS解决方案管理器Solution——.sln文件 3. CMake 自动化构建工具 4. SDK软件开发工具包作为初学者&#xff0c;从工程项目开始接触完整一套流程工具和编译&#xff0c;有助于快速上手。 一、C 项目build文件夹在 VS2022 中打开 C 项目后&#xff0c;在…

测试ppyoloe的小样本few-shot能力,10张图片精度达到69.8%

近期公司有个项目&#xff0c;需要解决长尾样本的问题&#xff0c;所以测试了一下paddlepaddle小样本的能力。 环境&#xff1a;&#xff1a;T4 、ubuntu 、cuda-11.6 、py3.9、 paddlepaddle-gpu2.6.0、pip install opencv-python4.5.5.64 -i https://pypi.tuna.tsinghua.…

结构化布线系统详解

1. 结构化布线系统概述 结构化布线系统(Structured Cabling System, SCS)是一种标准化、模块化的建筑物或建筑群内信息传输基础设施&#xff0c;它为语音、数据、图像等多媒体业务提供了统一的物理传输介质。与传统的点对点布线方式不同&#xff0c;结构化布线采用层次化、标准…

【Java学习】匿名内部类的向外访问机制

目录 一、方法局部变量的访问 1.生命周期 1.1方法生命周期 1.2匿名实例生命周期 1.3生命超时性 2.变量捕获 2.1按值捕获 2.1.1值捕获优势 2.1.1.1生命及时访问 2.1.1.2线程安全 2.1.2常量值捕获优势 2.2按引用捕获 引用捕获风险 (1)生命超时访问 (2)线程不安全 …

LinkedList的模拟实现+LinkedList和ArrayList的区别

目录 LinkedList的模拟实现 什么是双向链表 增加数据 头插法&#xff1a; 尾插法&#xff1a; 指定的下标插入&#xff1a; 删除数据 删除双向链表中出现的第一个key 置空所有数据 LinkedList和ArrayList的区别 顺序表对应的集合类是ArrayList&#xff1b;链表对应的集…

Vue + WebSocket 实时数据可视化实战:多源融合与模拟数据双模式设计

在现代交通大屏项目中&#xff0c;实时数据的采集和可视化尤为重要。本文结合 Vue3 和 ECharts&#xff0c;分享一个支持多 WebSocket 数据源实时合并、模拟数据调试、自动重连的完整设计方案&#xff0c;帮助你快速搭建健壮的数据可视化组件。一、项目背景与核心需求实时接收多…

C#索引器、接口、泛型

以下是对提供的 C# 代码中涉及的核心知识点的梳理和总结&#xff0c;涵盖索引器、接口、泛型三大核心内容&#xff0c;以及相关实践要点&#xff1a;一、索引器&#xff08;Indexer&#xff09;索引器是一种允许类或结构体像数组一样通过[]语法访问成员的特殊成员&#xff0c;本…

界面组件DevExpress WPF中文教程:Grid - 如何过滤节点?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…