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 支撑其关键业务和数据分析平台。

相关推荐
bksczm几秒前
MySQL基础篇之事务
linux·数据库·sql·mysql
李白客2 分钟前
数据库管理工具怎么选?Navicat、DataGrip、DBeaver 六维横向对比与选型建议
运维·数据库
马立杰2 分钟前
mysql中Illegal mix of collations for operation “UNION”错误的解决方法
数据库·sql·网络安全
蓝速科技5 分钟前
蓝速 K10 三防平板在工业恶劣工况下的选型与应用指南
大数据·运维·数据库·人工智能·科技
bksczm6 分钟前
MySQL基础篇之视图与用户管理
linux·数据库·sql·mysql
香菜TTT13 分钟前
怎么实现MySQL的主从同步机制
数据库·mysql
2501_933670799 小时前
2026秋招数据分析岗备考路线:SQL、BI、项目与面试题拆解
数据库
我要见SA姐111 小时前
告别 Copilot?Codex 本地化部署指南
运维·数据库·机器学习·oracle·回归
xcLeigh12 小时前
聊聊国产化替换:好用数据迁移工具KDMS怎么帮咱们搞定评估难
数据库·sql·数据迁移·kes·kdms
Elastic 中国社区官方博客12 小时前
列式存储并不等同于列式数据库。Columnar 模式为 Elasticsearch 带来了什么
大数据·运维·数据库·elasticsearch·搜索引擎