Postgres正则表达式性能优化:pg_re2扩展的引入与应用

本文字数:4947;估计阅读时间:13 分钟

作者:David Wheeler and Philip Dubé

编者按: 本文译自 ClickHouse 原博客。 原文围绕「基于RE2的Postgres高速正则表达式扩展」展开。对于Postgres用户而言,pg_re2扩展的引入为正则表达式操作带来了显著的性能提升。其基于RE2引擎,有望解决传统正则表达式的性能瓶颈。

速度是 ClickHouse 产品设计与工程的核心驱动力。我们对性能的重视,在开发 ClickBench 和 PostgresBench 等基准测试中可见一斑,其中一些已成为行业标准。这种理念自然也贯穿于我们产品的方方面面,甚至延伸至 SQL 函数和运算符。

秉持这一理念,我们推出了 pg_re2,这是一个 Postgres 扩展,旨在为 Postgres 提供基于 RE2 的高速正则表达式功能。你可以通过 PGXN 或 GitHub 安装它;ClickHouse Managed Postgres 已预装此扩展。

安装方式如下:

复制代码
CREATE EXTENSION re2;

安装后即可开始使用。

复制代码
SELECT 'HouseQuake' @~ 'Quake\b';

?column? 
----------
 t
(1 row)

有关完整功能列表,请参阅文档(https://pgxn.org/dist/re2/doc/re2.html)。

Postgres 本身已提供丰富的正则表达式运算符和函数。既然如此,为何还要提供替代方案?主要原因有二:速度与兼容性。

速度需求

究其根本,Postgres 和 RE2 的正则表达式引擎基于截然不同的搜索算法。Postgres 的 POSIX 正则表达式采用回溯(backtracking)算法,返回最长匹配结果;而 RE2 则依赖于有限自动机(finite automata)。2010 年,Russ Cox 在其宣布 RE2 的文章中总结称:

RE2 证明,利用自动机理论几乎可以实现像 PCRE 这样的现代回溯正则表达式库的所有功能。由于其深植于自动机理论基础,RE2 在执行时间上提供了比临时实现更强的保障,并支持那些临时实现难以或无法完成的高级分析。最后,RE2 是开源的。祝您使用愉快!

鉴于这些发现,ClickHouse 的字符串搜索、字符串替换、字符串提取 和字符串分割功能均以 RE2 为基础。我们也希望为 Postgres 用户带来同样优势。

基准测试

pg_re2 项目 中包含一个基准测试,旨在比较其基于 RE2 的正则表达式函数与 Postgres 原生函数的实际性能。下图总结了在运行 PostgreSQL 18 的 AWS m6g.2xlarge 实例上的横向加速倍数:

测试结果如下:

• RE2 在所有测试中均表现出色 ,性能达到 Postgres 的 1.8 倍至 8.6 倍。

• 最大的性能提升来自 extract_all (7.2-8.6 倍)。该函数直接返回数组,与 Postgres regexp_matches(..., 'g') 函数因返回集合而产生的 坦承不公平的 额外开销相比,优势显著。

索引

pg_re2 v0.4.0 新增了匹配运算符 @~,作为 Postgres ~ 运算符的补充。此外,它还增加了 btree 和 GIN 索引支持,以减少评估 RE2 正则表达式时所需的表扫描。第二个基准测试比较了在同一服务器上 RE2 与 Postgres 正则表达式索引的性能表现:

研究发现如下:

• RE2 在所有测试中再次领先 ,相比等效的 Postgres 操作,性能提升了 1.1 到 1.8 倍。

• 对 btree 索引进行 前缀 匹配时,其整体加速效果与全表扫描相似,因为两者都能以原始执行速度处理索引条目。

• gin_re2_ops GIN 操作类受益于收集 RE2 所需的字节等效 三元组 ,甚至包括跨越标点符号的情况。因此,像 error_code=420-9 这类模式可以使用索引,而当 Postgres 的正则表达式使用 pg_trgm 操作类时,则会回退到全表扫描。

• 对于 普通词模式 ,RE2 的 gin_re2_ops 和 Postgres 的 pg_trgm 操作类提取的键相似,且 pg_trgm 较低开销的一致性检查可能更具优势(参见上文的 error_code=123 )。

总体而言,Russ Cox 提出的性能改进在 Postgres 的正则表达式中同样适用。

高度兼容

创建 pg_re2 的第二个原因是为了与 ClickHouse 正则表达式函数兼容。事实证明,RE2 不仅速度有别,其语法也与 Postgres POSIX 表达式存在差异。这些差异至关重要,因为 pg_clickhouse v0.2.0 为多个 PostgreSQL 运算符和函数添加了下推(pushdown)支持。这对于简单的正则表达式效果良好:

复制代码
SELECT id, by FROM log.entries WHERE by ~ 'Click' ORDER BY id;

id |     by     
----+------------
  1 | ClickHouse
  5 | MouseClick
 (2 rows)

如果你想尝试这些示例,下面的例子使用了这个 ClickHouse 表:

复制代码
CREATE TABLE entries (
    id UInt32 NOT NULL,
    by String NOT NULL
) ENGINE = MergeTree ORDER BY (id);

INSERT INTO entries VALUES
    ( 1, 'ClickHouse' ),
    ( 2, 'HouseQuake' ),
    ( 3, 'HouseQuake' ),
    ( 4, 'QuakerOats' ),
    ( 5, 'MouseClick' );

以及 Postgres 中的这个 pg\_clickhouse 外部表配置:

复制代码
CREATE EXTENSION pg_clickhouse;
CREATE EXTENSION re2;
CREATE SERVER ch FOREIGN DATA WRAPPER clickhouse_fdw OPTIONS(driver 'binary');
CREATE USER MAPPING FOR CURRENT_USER SERVER ch;
CREATE SCHEMA log;
IMPORT FOREIGN SCHEMA "default" FROM SERVER ch INTO log;

它也适用于简单的锚定表达式,例如 `^` 和 `$`:

复制代码
SELECT id, by FROM log.entries WHERE by ~ '^House' ORDER BY id;

id |     by     
----+------------
  2 | HouseQuake
  3 | HouseQuake
(2 rows)

但有时 pg\_clickhouse 会返回意想不到的结果:

复制代码
SELECT id, by ~ 'Quake\b' FROM log.entries WHERE by ~ 'Quake\b';

id | ?column? 
----+----------
  2 | f
  3 | f
(2 rows)

在此例中,`by ~ 'Quake\b'` 在 `WHERE` 子句中会被下推到 ClickHouse 执行,并对两行数据返回 true;但在 SELECT 列表中执行的相同表达式(它在 Postgres 中执行),结果却出人意料地返回 false。这究竟是为何?

Postgres POSIX 与 ClickHouse RE2

这个令人费解的结果源于两个引擎之间的差异。在 ClickHouse 中,\b 代表词边界,而在 Postgres 中它代表退格字符。因此,下推到 ClickHouse 的 `WHERE` 表达式返回真,但在 Postgres 中执行的 SELECT 表达式则不返回真。

这两个引擎之间存在诸多此类差异。许多反斜杠(\)特性在两者中的工作方式不同,或仅在其中一个引擎中存在:

两者都支持通过 (?xyz) 或作为 Postgres 函数的额外参数来传递标志。然而,这些标志的差异也十分显著:

两个引擎都支持的标志,其表述虽有不同,但*似乎*会产生等效行为。例如,PostgreSQL 的术语"非换行符匹配"意味着模式对换行符不敏感,因此*确实*匹配换行符。这些文档差异使得在没有实际测试的情况下,难以验证其行为。

此外,还有一些例子:

  • ClickHouse 不支持任意的先行断言或后行断言 ((?=re), (?!re), (?<=re), and (?<!re))

  • ClickHouse 不支持反向引用 (\1, \2, etc.),例如用于匹配引号的 ("')(.*?)\1;但在替换表达式中,两者都支持反向引用。

  • 排序规则和区域设置决定了 Postgres 字符类中包含哪些非 ASCII 字符,而 ClickHouse RE2 字符类只包含 ASCII 字符。

影响

这些差异只是两个正则表达式引擎之间诸多区别中的一小部分。显然,在查询 pg\_clickhouse 表时,必须警惕这些差异。务必彻底测试你的正则表达式,尤其是在使用标志、字符类和反斜杠特性时。

为了简化问题(或者可能制造 两个问题(https://regex.info/blog/2006-09-15/247 "Source of the famous "Now you have two problems" quote")),我们在 pg\_clickhouse v0.3.0 中增加了 pg\_re2 函数的下推功能。与下推 Postgres 正则表达式函数相比,在 pg\_clickhouse 表的列中使用 pg\_re2 函数具有多项优势:

  • 无需判断是否发生下推,因为两个数据库上的行为一致。

  • RE2 提供渐近更优的性能,这一点我们已在 前文 提及。

  • 对于 ClickHouse 用例,只需处理一种正则表达式语法。

当我们回顾本节开头提到的那些意想不到的结果时,这些优势便会显而易见:

复制代码
SELECT id, by ~ 'Quake\b' FROM log.entries WHERE by ~ 'Quake\b';

id | ?column? 
----+----------
  2 | f
  3 | f
(2 rows)

再次强调,~ 运算符下推到 ClickHouse 时返回 true,但在 Postgres 中执行时返回 false。将 Postgres 的 ~ 运算符替换为 re2 扩展的 re2match() 函数后,它仍会下推至 ClickHouse 的 match function:

复制代码
SELECT id, re2match(by, 'Quake\b')
FROM log.entries where re2match(by, 'Quake\b');

id | re2match 
----+----------
  2 | t
  3 | t
(2 rows)

但由于 re2 扩展与 ClickHouse 使用相同的 RE2 库,查询现在能够返回一致的结果:re2match() 在 Postgres 中执行时返回 true,下推到 ClickHouse 执行时也返回 true。

开始使用

re2 扩展 的开发宗旨,是将 Postgres 与 ClickHouse 整合为一个协同工作的高性能数据平台。为此,它复刻了 ClickHouse 完整的正则表达式函数集,这些函数在 字符串搜索、字符串替换、字符串提取 和 字符串分割 文档中均有描述。此外,其测试套件也借鉴了 ClickHouse 现有的正则表达式测试。

因此,我们建议在条件允许时,尽可能使用 pg_re2 来提升 Postgres 上正则表达式的执行速度。对于分析查询尤其如此,这有助于确保数据迁移到 ClickHouse 后,性能和下推(pushdown)保持一致。

关于我们

ClickHouse 是面向 AI 时代打造的高性能实时分析数据库,能够以极致性能处理海量数据分析任务。凭借高并发、低延迟和云原生架构,ClickHouse 广泛应用于可观测性、数据仓库、实时分析及 AI 数据基础设施等场景。我们致力于帮助企业在公有云平台上构建安全、弹性且高性价比的实时分析与 AI 数据平台,加速释放数据价值,推动智能化创新与数字化转型。目前,Trip.com、DiDi、Meta、Sony、Netflix、Deutsche Bank、Sierra、Cloudflare 等全球领先企业均在使用 ClickHouse 支撑其关键业务和数据分析平台。

相关推荐
艾莉丝努力练剑1 小时前
【MYSQL】MYSQL学习的一大重点:视图
android·服务器·数据库·学习·mysql·面试·视图
三江番长 陀舍古帝1 小时前
细说SQL Server中的加密
运维·服务器·数据库
艾莉丝努力练剑1 小时前
【MYSQL】MYSQL学习的一大重点:事务(下)- InnoDB 事务隔离性原理(MVCC 视角)
android·数据库·b树·sql·学习·mysql·面试
小园子的小菜2 小时前
Redis 核心原理深度解析:从数据结构、IO 模型到持久化机制
数据库·redis·缓存
YUS云生2 小时前
大模型学习·第44天:Chroma向量数据库与RAG链式组装
数据库·学习
毛驴赶鹿9 小时前
【金仓数据库征文】KES V9性能调优实战记录:从慢SQL排查到全链路优化
数据库·sql
码农阿豪10 小时前
【金仓数据库征文】Mac开发环境|SpringBoot3 + MyBatisPlus对接KES V9 Docker实战全指南
数据库·macos·docker
杨云龙UP10 小时前
MySQL 数据库常用备份工具对比:mysqldump、mydumper 与 XtraBackup
linux·运维·服务器·数据库·mysql·dba·备份恢复
ZENERGY-众壹10 小时前
500个工商业电站压垮数据库?海量光伏时序数据存储的架构取舍
数据库·架构·influxdb·光伏·光伏运维