pg_pinyin 包含两套实现:
- SQL 基线方案(
sql/pinyin.sql) - Rust 扩展方案(
src/lib.rs)
仅提供两类拼音化能力:
pinyin_char_romanize(text)pinyin_char_romanize(text, suffix text)pinyin_word_romanize(text)pinyin_word_romanize(text, suffix text)pinyin_word_romanize(tokenizer_input anyelement)(重载,支持pdbtokenizer 输入,如name::pdb.icu::text[])pinyin_word_romanize(tokenizer_input anyelement, suffix text)(带用户词典后缀的重载)
推荐组合:
- 字级拼音化 +
pg_trgm - 词级拼音化 +
pg_search
CREATE EXTENSION IF NOT EXISTS pg_pinyin;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE TABLE voice (
id bigserial PRIMARY KEY,
description text NOT NULL,
pinyin text GENERATED ALWAYS AS (public.pinyin_char_romanize(description)) STORED
);
CREATE INDEX voice_pinyin_trgm_idx ON voice USING gin (pinyin gin_trgm_ops);
INSERT INTO voice (description) VALUES ('郑爽ABC');
SELECT id, description, pinyin FROM voice;你可以在 pinyin schema 下提供后缀表:
pinyin.pinyin_mapping_suffix1pinyin.pinyin_words_suffix1
当调用 ...(..., '_suffix1') 时,拼音化会使用合并后的词典:
- 基础表(
pinyin_mapping/pinyin_words) - 后缀表(
pinyin_mapping_suffix1/pinyin_words_suffix1),并且后缀表优先级更高
示例:
CREATE TABLE IF NOT EXISTS pinyin.pinyin_mapping_suffix1 (
character text PRIMARY KEY,
pinyin text NOT NULL
);
CREATE TABLE IF NOT EXISTS pinyin.pinyin_words_suffix1 (
word text PRIMARY KEY,
pinyin text NOT NULL
);
INSERT INTO pinyin.pinyin_mapping_suffix1 (character, pinyin)
VALUES ('郑', '|zhengx|')
ON CONFLICT (character) DO UPDATE SET pinyin = EXCLUDED.pinyin;
INSERT INTO pinyin.pinyin_words_suffix1 (word, pinyin)
VALUES ('郑爽', '|zhengx| |shuangx|')
ON CONFLICT (word) DO UPDATE SET pinyin = EXCLUDED.pinyin;
SELECT public.pinyin_char_romanize('郑爽ABC', '_suffix1');
SELECT public.pinyin_word_romanize('郑爽ABC'::pdb.icu::text[], '_suffix1');Rust 扩展在编译时内置以下数据:
sql/data/pinyin_mapping.csvsql/data/pinyin_token.csvsql/data/pinyin_words.csv
在执行 CREATE EXTENSION pg_pinyin 时,会把内嵌 CSV 数据通过 PostgreSQL COPY
写入 pinyin schema 下的字典表(失败时会回退到 INSERT)。
使用扩展时无需额外执行 sql/load_data.sql。
数据脚本已迁入本仓库:
scripts/data/generate_extension_data.pyscripts/generate_data.sh
子模块路径:
third_party/pinyin-data
初始化:
git submodule update --init third_party/pinyin-data一键生成扩展所需数据:
./scripts/generate_data.sh输出文件:
sql/data/pinyin_mapping.csvsql/data/pinyin_token.csvsql/data/pinyin_words.csv
psql "$PGURL" -f sql/pinyin.sql
psql "$PGURL" \
-v mapping_file='/absolute/path/sql/data/pinyin_mapping.csv' \
-v token_file='/absolute/path/sql/data/pinyin_token.csv' \
-v words_file='/absolute/path/sql/data/pinyin_words.csv' \
-f sql/load_data.sqlpgTAP:
./test/pgtap/run.shRust 扩展测试:
cargo pgrx test pg18 --features pg18docker/Dockerfile.test-trixiedocker/Dockerfile.release-trixie
默认使用上游地址(不再改写镜像源):
- 基础镜像:
postgres:18.3-trixie - apt 源:基础镜像默认配置
- rustup/cargo:官方默认地址
构建测试镜像:
docker build -f docker/Dockerfile.test-trixie -t pg_pinyin/test:trixie .构建发布镜像:
docker build -f docker/Dockerfile.release-trixie -t pg_pinyin/release:trixie .Dockerfile 已使用 BuildKit cache mount 缓存 Rust 下载/索引。 如需显式开启 BuildKit:
DOCKER_BUILDKIT=1 docker build -f docker/Dockerfile.test-trixie -t pg_pinyin/test:trixie .仅衡量分词/拼音化性能(不比较检索性能):
scripts/benchmark_pg18.sh
脚本会覆盖以下场景:
- SQL 字级:
characters2romanize(name)(cold+warm) - Rust 字级:
pinyin_char_romanize(name)(cold+warm) - Rust 字级(用户后缀词典叠加):
pinyin_char_romanize(name, '_<suffix>')(cold+warm) - SQL 词级:
icu_romanize(name::pdb.icu::text[])(cold+warm,当存在pg_search) - Rust 词级(tokenizer 输入):
pinyin_word_romanize(name::pdb.icu::text[])(cold+warm) - Rust 词级(后缀词典叠加):
pinyin_word_romanize(name::pdb.icu::text[], '_<suffix>')(cold+warm) - Rust 词级(纯文本输入):
pinyin_word_romanize(name)(cold+warm)
所有基准查询都使用 EXPLAIN (ANALYZE, BUFFERS, MEMORY, SUMMARY),包含内存占用。
运行示例:
ROWS=2000 USER_TABLE_SUFFIX=_bench PGURL=postgres://localhost/postgres ./scripts/benchmark_pg18.sh会话命令:
ROWS=2000 USER_TABLE_SUFFIX=_bench PGURL=postgres://postgres@localhost:5432/postgres ./scripts/benchmark_pg18.sh最新一次结果(PG18,ROWS=2000,2026-03-01):
字级模式:
| 场景 | Cold | Warm | 相对 SQL 提升(cold / warm) |
|---|---|---|---|
SQL 基线(characters2romanize) |
9159.522 |
9253.374 |
1.0x / 1.0x |
Rust(pinyin_char_romanize) |
80.719 |
28.094 |
113.5x / 329.4x |
Rust + 后缀词典(pinyin_char_romanize(name, '_bench')) |
162.319 |
30.233 |
56.4x / 306.1x |
词级模式(pg_search tokenizer 输入):
| 场景 | Cold | Warm | 相对 SQL 提升(cold / warm) |
|---|---|---|---|
SQL 基线(icu_romanize(name::pdb.icu::text[])) |
242.889 |
237.337 |
1.0x / 1.0x |
Rust(pinyin_word_romanize(name::pdb.icu::text[])) |
331.327 |
72.444 |
0.7x / 3.3x |
Rust + 后缀词典(pinyin_word_romanize(name::pdb.icu::text[], '_bench')) |
760.339 |
77.731 |
0.3x / 3.1x |
Rust 纯文本(pinyin_word_romanize(name)) |
336.215 |
35.460 |
0.7x / 6.7x |
以上数值为 EXPLAIN (ANALYZE, BUFFERS, MEMORY, SUMMARY) 的 Execution Time(毫秒)。
Rust 基线路径的 cold 在执行前会先 bump 一次字典版本,用于模拟首次加载缓存。
后缀词典会在首次使用时加载缓存并跨语句复用。若后缀表发生更新,可调用 public.pinyin_clear_suffix_cache('_suffix')(或 public.pinyin_clear_suffix_cache() 清空全部)手动失效缓存。
- 梳理数据生成流水线,并持续扩充词级字典覆盖。
支持用户自定义词典,并可按指定词典执行拼音化。- 提供平滑升级路径(扩展内置词典与用户词典的升级策略)。
- 改进英文处理能力(包括 stemming)。
- 提供更多不依赖
pg_search的示例。 - 持续优化性能与内存平衡(例如评估 frozen hash 相对表查找的收益)。
以下表支持用户直接增删改:
pinyin.pinyin_mappingpinyin.pinyin_wordspinyin.pinyin_token
更新后无需重编译扩展。
若使用 SQL 基线方法(sql/pinyin.sql),请引用:
- CN115905297A: 一种支持拼音检索和排序的方法及系统
@patent{CN115905297A,
author = {梁展钊},
title = {一种支持拼音检索和排序的方法及系统},
number = {CN115905297A},
country = {CN},
year = {2023},
url = {https://patents.google.com/patent/CN115905297A/zh}
}- 汉字词组拼音 TSV 数据来源: tsroten/dragonmapper
hanzi_pinyin_words.tsv