比较 PostgreSQL 和 ClickHouse
Postgres 与 ClickHouse:等效和不同的概念
来自 OLTP 系统的用户习惯于 ACID 事务应该注意,ClickHouse 为了性能而故意在不完全提供这些事务的情况下做出妥协。如果理解透彻,ClickHouse 语义可以提供高耐用性保证和高写入吞吐量。我们在下面重点介绍了一些关键概念,用户在从 Postgres 使用 ClickHouse 之前应该熟悉这些概念。
分片与副本
分片和复制是两种用于在单个 Postgres 实例存储和/或计算成为性能瓶颈时进行扩展的策略。在 Postgres 中,分片涉及将大型数据库拆分为更小、更易于管理的部分,分布在多个节点上。但是,Postgres 本身不支持分片。相反,分片可以通过使用诸如 Citus 之类的扩展来实现,其中 Postgres 成为能够水平扩展的分布式数据库。这种方法允许 Postgres 通过将负载分散到多台机器上来处理更高的交易率和更大的数据集。分片可以是基于行的,也可以是基于架构的,以便为事务型或分析型等工作负载类型提供灵活性。分片会在数据管理和查询执行方面引入重大复杂性,因为它需要跨多台机器进行协调并保证一致性。
与分片不同,副本是包含来自主节点的所有或部分数据的其他 Postgres 实例。副本用于各种原因,包括增强读取性能和 HA(高可用性)场景。物理复制是 Postgres 的一项原生功能,它涉及将整个数据库或重要部分复制到另一台服务器,包括所有数据库、表和索引。这涉及通过 TCP/IP 将 WAL 段从主节点流式传输到副本。相比之下,逻辑复制是更高级别的抽象,它基于 INSERT
、UPDATE
和 DELETE
操作流式传输更改。虽然相同的结果可能适用于物理复制,但针对特定表和操作以及数据转换和支持不同的 Postgres 版本,可以实现更大的灵活性。
相比之下,ClickHouse 分片和副本是与数据分布和冗余相关的两个关键概念。ClickHouse 副本可以认为类似于 Postgres 副本,尽管复制最终是一致的,没有主节点的概念。分片与 Postgres 不同,是原生支持的。
分片是表数据的某个部分。您始终至少有一个分片。将数据跨多个服务器进行分片可用于在超过单个服务器的容量时(使用所有分片并行运行查询)来分担负载。用户可以在不同的服务器上为表手动创建分片并将数据直接插入其中。或者,可以使用带有定义数据路由到的分片的 sharding key 的分布式表。sharding key 可以是随机的,也可以是哈希函数的输出。重要的是,一个分片可以包含多个副本。
副本是数据的副本。ClickHouse 始终至少有一份数据的副本,因此副本的最小数量为 1。添加数据的第二个副本提供容错能力,并可能为处理更多查询提供额外的计算能力 (并行副本 也可用于分配单个查询的计算能力,从而降低延迟)。副本是使用 复制的 MergeTree 表引擎 实现的,该引擎使 ClickHouse 能够在不同的服务器上同步地保留多个数据副本。复制是物理的:仅压缩部分在节点之间传输,而不是查询。
总之,副本是提供冗余和可靠性(以及潜在的分布式处理)的数据副本,而分片是允许分布式处理和负载均衡的数据子集。
ClickHouse Cloud 使用存储在 S3 中的单个数据副本,并有多个计算副本。数据对每个副本节点都可用,每个节点都有一个本地 SSD 缓存。这仅依赖于通过 ClickHouse Keeper 的元数据复制。
最终一致性
ClickHouse 使用 ClickHouse Keeper(C++ ZooKeeper 实现,ZooKeeper 也可以使用)来管理其内部复制机制,主要侧重于元数据存储并确保最终一致性。Keeper 用于在分布式环境中为每个插入分配唯一的连续编号。这对于维护操作的顺序和一致性至关重要。此框架还处理合并和变异等后台操作,确保这些操作的执行工作分布在所有副本之间,同时保证它们以相同的顺序执行。除了元数据之外,Keeper 还充当复制的综合控制中心,包括跟踪存储的数据部分的校验和,并充当副本之间的分布式通知系统。
ClickHouse 中的复制过程 (1) 从数据插入到任何副本时开始。这些数据以其原始插入形式 (2) 写入磁盘,以及其校验和。写入后,副本 (3) 尝试通过分配唯一的块号并在复制日志中记录新部分的详细信息,在 Keeper 中注册这个新数据部分。其他副本在 (4) 检测到复制日志中的新条目后,(5) 通过内部 HTTP 协议下载相应的数据部分,并根据 ZooKeeper 中列出的校验和进行验证。此方法确保所有副本最终都拥有一致且最新的数据,尽管处理速度不同或可能存在延迟。此外,该系统能够同时处理多个操作,优化数据管理流程,并允许系统可扩展性和对硬件差异的健壮性。
请注意,ClickHouse Cloud 使用了 云优化复制机制,该机制适用于其存储和计算架构的分离。通过将数据存储在共享的对象存储中,数据自动对所有计算节点可用,而无需在节点之间物理复制数据。相反,Keeper 用于仅在计算节点之间共享元数据(哪些数据存在于对象存储中的哪个位置)。
PostgreSQL 采用与 ClickHouse 不同的复制策略,主要使用流式复制,它涉及主副本模型,其中数据从主节点连续流式传输到一个或多个副本节点。这种类型的复制确保了近乎实时的同步,并且是同步或异步的,允许管理员控制可用性和一致性之间的平衡。与 ClickHouse 不同,PostgreSQL 依赖于具有逻辑复制和解码的 WAL(预写日志),以在节点之间流式传输数据对象和更改。PostgreSQL 中的这种方法更直接,但在 ClickHouse 通过使用 Keeper 来实现分布式操作协调和最终一致性,在高度分布式环境中可能无法提供相同级别的可扩展性和容错能力。
用户影响
在 ClickHouse 中,脏读的可能性——用户可以将数据写入一个副本,然后从另一个副本读取可能未复制的数据——源于其通过 Keeper 管理的最终一致复制模型。这种模型强调分布式系统中的性能和可扩展性,允许副本独立运行并异步同步。因此,新插入的数据可能不会立即在所有副本中可见,具体取决于复制延迟以及更改在系统中传播所需的时间。
相反,PostgreSQL 的流式复制模型通常可以通过使用同步复制选项来防止脏读,在同步复制选项中,主节点会在提交事务之前等待至少一个副本确认收到数据。这确保一旦事务提交,就会保证数据在另一个副本中可用。如果主节点发生故障,副本将确保查询看到已提交的数据,从而保持更高程度的一致性。
建议
ClickHouse 的新手用户应该意识到这些差异,这些差异将在复制的环境中体现出来。通常,最终一致性在对数十亿甚至数万亿个数据点的分析中已经足够了——在这些分析中,指标要么更稳定,要么估计就足够了,因为新的数据正在以高速度不断插入。
如果需要,可以采用几种方法来提高读取的一致性。这两种方法都需要增加复杂度或开销,从而降低查询性能,并使 ClickHouse 的扩展变得更加困难。**我们建议只有在绝对必要的情况下才使用这些方法。**
一致路由
为了克服最终一致性的一些限制,用户可以确保客户端路由到相同的副本。这在多个用户查询 ClickHouse 且结果应在请求之间确定性的情况下很有用。虽然结果可能会有所不同,因为新数据插入,但应查询相同的副本以确保一致的视图。
这可以通过多种方法实现,具体取决于您的架构以及您使用的是 ClickHouse OSS 还是 ClickHouse Cloud。
ClickHouse Cloud
ClickHouse Cloud 使用 S3 中的单个数据副本,并具有多个计算副本。数据可供每个副本节点使用,这些节点具有本地 SSD 缓存。因此,为了确保一致的结果,用户只需要确保一致地路由到同一节点。
与 ClickHouse Cloud 服务的节点通信通过代理进行。HTTP 和本机协议连接将在保持打开状态的期间路由到同一节点。对于大多数客户端的 HTTP 1.1 连接,这取决于 Keep-Alive 窗口。这可以在大多数客户端(例如 Node Js)上配置。这也需要服务器端配置,这将高于客户端,并且在 ClickHouse Cloud 中设置为 10 秒。
为了确保跨连接的一致路由(例如,如果使用连接池或连接过期),用户可以确保使用相同的连接(对于本机更容易),或者请求公开粘性端点。这为集群中的每个节点提供一组端点,从而允许客户端确保查询以确定性方式路由。
请联系支持以获取粘性端点的访问权限。
ClickHouse OSS
在 OSS 中实现这种行为取决于您的分片和副本拓扑,以及您是否使用 分布式表 进行查询。
当您只有一个分片和副本(自 ClickHouse 垂直扩展以来很常见)时,用户在客户端层选择节点并直接查询副本,确保以确定性方式选择此副本。
虽然在没有分布式表的情况下可以使用多个分片和副本的拓扑,但这些高级部署通常具有自己的路由基础设施。因此,我们假设使用多个分片的部署都使用分布式表(分布式表可以与单分片部署一起使用,但通常是不必要的)。
在这种情况下,用户应确保基于属性(例如 session_id
或 user_id
)执行一致的节点路由。设置 prefer_localhost_replica=0
、load_balancing=in_order
应 在查询中设置。这将确保首选分片的任何本地副本,并首选配置中列出的副本,前提是它们具有相同的错误数量 - 如果错误更高,则将发生随机选择的故障转移。 load_balancing=nearest_hostname
也可以用作这种确定性分片选择的替代方案。
创建分布式表时,用户将指定一个集群。此集群定义(在 config.xml 中指定)将列出分片(及其副本) - 从而允许用户控制从每个节点使用它们的顺序。使用此方法,用户可以确保选择是确定性的。
顺序一致性
在特殊情况下,用户可能需要顺序一致性。
数据库中的顺序一致性是指数据库上的操作似乎按某种顺序执行,并且此顺序在与数据库交互的所有进程中都是一致的。这意味着每个操作似乎在其调用和完成之间立即生效,并且所有进程观察到的所有操作都存在一个单一的、一致的顺序。
从用户的角度来看,这通常表现为需要将数据写入 ClickHouse 并在读取数据时,保证返回最新插入的行。这可以通过多种方法实现(按优先级顺序):
- **读/写到同一节点** - 如果您使用的是本机协议,或者使用 会话通过 HTTP 进行读写,那么您应该连接到同一个副本:在这种情况下,您直接从您写入的节点读取,然后您的读取将始终保持一致。
- **手动同步副本** - 如果您写入一个副本并从另一个副本读取,您可以在读取之前使用问题
SYSTEM SYNC REPLICA LIGHTWEIGHT
。 - **启用顺序一致性** - 通过查询设置
select_sequential_consistency = 1
。在 OSS 中,还必须指定设置insert_quorum = 'auto'
。
请参阅 此处,以获取有关启用这些设置的更多详细信息。
使用顺序一致性将对 ClickHouse Keeper 造成更大的负载。结果可能意味着插入和读取速度更慢。SharedMergeTree(在 ClickHouse Cloud 中用作主要表引擎)的顺序一致性 开销更小,并且扩展性更好。OSS 用户应谨慎使用这种方法并测量 Keeper 负载。
事务性(ACID)支持
从 PostgreSQL 迁移的用户可能习惯于其对 ACID(原子性、一致性、隔离性、持久性)属性的强大支持,使其成为事务性数据库的可靠选择。PostgreSQL 中的原子性确保每个事务都被视为一个单元,要么完全成功,要么完全回滚,防止部分更新。一致性通过执行约束、触发器和规则来维护,这些约束、触发器和规则保证所有数据库事务都导致有效状态。PostgreSQL 支持从 Read Committed 到 Serializable 的隔离级别,允许对并发事务的更改可见性进行微调控制。最后,持久性通过预写日志 (WAL) 实现,确保一旦事务提交,即使在系统故障的情况下,它也会保持提交状态。
这些属性对于充当真相来源的 OLTP 数据库很常见。
虽然功能强大,但这会带来固有的局限性,并使 PB 规模的挑战。ClickHouse 为了以 PB 规模提供快速分析查询并保持高写入吞吐量,放弃了这些属性。
ClickHouse 在 有限的配置 下提供 ACID 属性 - 最简单的情况是在使用具有一个分区的非复制的 MergeTree 表引擎实例时。用户不应该期望在这些情况之外具有这些属性,并确保这些属性不是必需的。
使用 PeerDB 复制或迁移 Postgres 数据
ClickHouse, Inc. 收购了一家名为 PeerDB 的 Postgres 复制公司。PeerDB 允许您无缝地将数据从 Postgres 复制到 ClickHouse。您可以使用此工具进行以下操作:a) 使用 CDC 进行持续复制,允许 Postgres 和 ClickHouse 共存 - Postgres 用于 OLTP,ClickHouse 用于 OLAP;以及 b) 从 Postgres 迁移到 ClickHouse。