33个稳定版本
1.4.9 | 2024年6月16日 |
---|---|
1.4.8 | 2023年12月14日 |
1.4.3 | 2023年9月4日 |
1.4.0 | 2023年1月28日 |
0.0.1 |
|
#4 在 数据库实现 中
每月211次下载
695KB
29K SLoC
Sonic
Sonic是一款快速、轻量级且无模式的搜索后端。它能够快速处理搜索文本和标识符元组,并在微秒级时间内进行查询。
Sonic可以用作某些用例中替代Elasticsearch等重量级且功能齐全的搜索后端的简单选择。它能够规范化自然语言搜索查询,自动完成搜索查询,并提供与查询最相关的结果。Sonic是一个标识符索引,而不是文档索引;当被查询时,它返回ID,然后可以使用这些ID在外部数据库中引用匹配的文档。
在设计Sonic时,我们非常注重性能和代码整洁性。它旨在无故障运行、超快速且对服务器资源的影响最小(我们的测量数据显示,在负载下,Sonic对搜索查询的响应时间在微秒范围内,消耗约30MB RAM,并具有很小的CPU占用;查看我们的基准测试)。
在Rust版本:rustc 1.74.1 (a28077b28 2023-12-04)
🇫🇷 在法国南特制作。
📰 Sonic项目最初在 我的个人日记上的一篇文章中宣布。
「Sonic」是Sonic项目的吉祥物。我画得它看起来像一只迷幻的嘻哈刺猬。
谁在使用它?
清晰 | Scrumpy |
👋 您使用Sonic并希望被列入其中? 联系我。
演示
Sonic集成在Crisp平台上的所有Crisp搜索产品中。它被用于在每月5美元的1-vCPU SSD云服务器上索引5亿个对象(截至2019年)。Crisp用户使用它来搜索他们的消息、对话、联系人、帮助台文章等。
您可以在以下位置实时测试Sonic: Crisp帮助台,并了解Sonic搜索结果的速度和相关性。您还可以从这里测试搜索建议:键入至少2个字符的单词,获取完整的单词建议(按Tab键展开建议)。 搜索和建议都由Sonic提供支持。
Sonic在帮助台文章中的模糊搜索表现最佳。查找任何单词或术语组,立即获得结果。
功能
- 搜索术语存储在集合中,组织在桶中;您可以使用单个桶,或者如果您需要在单独的索引中进行搜索,则为您的平台上的每个用户使用一个桶。
- 搜索结果返回对象标识符,如果需要丰富搜索结果,可以从外部数据库解析这些标识符。这使得Sonic成为一个简单的单词索引,指向标识符结果。Sonic在其索引中不存储任何直接文本数据,但它仍然保留一个单词图,用于自动完成和拼写纠正。
- 如果搜索查询中的给定单词没有足够的精确匹配结果,Sonic会尝试纠正单词并尝试替换词。您在搜索时可以犯错误。
- 在索引中插入和删除项目;索引更改操作很轻,可以在服务器运行时提交到服务器。后台任务处理程序负责合并索引,以便您推入或弹出的条目可以快速用于搜索。
- 通过建议操作实时自动完成任何单词。这有助于在您的终端用户搜索界面中构建快速的单词建议功能。
- 全Unicode兼容性,支持世界上80多种最常用的语言。Sonic在猜测文本语言后,从任何文本中删除无用的停用词(例如,英语中的“the”),确保任何搜索或摄入的文本在到达索引之前都是干净的; 查看语言。
- 简单的协议(Sonic通道),让您可以搜索索引、管理数据摄入(向索引中推入、从索引中弹出、刷新集合、刷新桶等)并执行管理操作。Sonic通道被设计为资源使用轻量级且易于集成; 阅读协议规范。
- 易于使用的库,让您可以从您的应用程序连接到Sonic; 查看库。
如何使用它?
安装
Sonic是用Rust编写的。要安装它,可以从Sonic发布页面下载版本,使用cargo install
或从master
拉取源代码。
👉 每个发布二进制文件都附带一个.asc
签名文件,可以使用@valeriansaliou GPG公钥进行验证: 🔑valeriansaliou.gpg.pub.asc。
👉 从软件包安装
Soundic为基于Debian的系统(Debian、Ubuntu等)提供了预构建软件包。
重要:目前Soundic只提供针对Debian 12的64位软件包(代号:bookworm
)。您可能仍然能够在其他Debian版本以及Ubuntu上使用它们(尽管它们依赖于特定版本的glibc
,该版本可能在较老或较新的系统上不可用)。
首先,添加Soundic APT仓库(例如,针对Debian bookworm
)
echo "deb [signed-by=/usr/share/keyrings/valeriansaliou_sonic.gpg] https://packagecloud.io/valeriansaliou/sonic/debian/ bookworm main" > /etc/apt/sources.list.d/valeriansaliou_sonic.list
curl -fsSL https://packagecloud.io/valeriansaliou/sonic/gpgkey | gpg --dearmor -o /usr/share/keyrings/valeriansaliou_sonic.gpg
apt-get update
然后,安装Soundic软件包
apt-get install sonic
然后,编辑预填充的Soundic配置文件
nano /etc/sonic.cfg
最后,重启Soundic
service sonic restart
👉 从源代码安装
如果您从Git拉取了源代码,可以使用cargo
构建它
cargo build --release
构建的二进制文件可以在./target/release
目录中找到。
安装build-essential
、clang
、libclang-dev
、libc6-dev
、g++
和llvm-dev
,以便能够编译所需的RocksDB依赖项。
请注意,以下可选功能可以在构建Soundic时启用:allocator-jemalloc
、tokenizer-chinese
和tokenizer-japanese
(其中一些可能默认已启用)。
👉 使用Cargo安装
您可以直接使用cargo install
安装Soundic
cargo install sonic-server
确保您的$PATH
已正确配置以使用Crates的二进制文件,然后使用sonic
命令运行Soundic。
安装build-essential
、clang
、libclang-dev
、libc6-dev
、g++
和llvm-dev
,以便能够编译所需的RocksDB依赖项。
👉 使用Docker Hub安装
您可能发现通过Docker运行Soundic很方便。您可以在Docker Hub上找到预构建的Soundic镜像,地址为valeriansaliou/sonic。
首先,拉取valeriansaliou/sonic
镜像
docker pull valeriansaliou/sonic:v1.4.9
然后,创建一个配置文件并运行它(将/path/to/your/sonic/config.cfg
替换为您配置文件的路径)
docker run -p 1491:1491 -v /path/to/your/sonic/config.cfg:/etc/sonic.cfg -v /path/to/your/sonic/store/:/var/lib/sonic/store/ valeriansaliou/sonic:v1.4.9
在配置文件中,请确保
channel.inet
设置为0.0.0.0:1491
(这允许Soundic从容器外部访问)store.kv.path
设置为/var/lib/sonic/store/kv/
(这允许Soundic访问外部KV存储目录)store.fst.path
设置为/var/lib/sonic/store/fst/
(这允许Soundic访问外部FST存储目录)
Soundic将从tcp://localhost:1491
访问。
👉 从其他来源(非官方)安装
其他安装来源可用
- Homebrew(macOS):
brew install sonic
(查看公式)
请注意,这些来源是非官方的,这意味着它们不属于也不由Soundic项目所有者拥有和维护。这些来源上可用的最新Soundic版本可能与通过Soundic项目可用的最新版本不同步。
配置
使用示例配置文件config.cfg,并根据您的环境进行调整。
如果您想微调配置,您可以阅读我们的详细配置文档。
运行 Sonic
Sonic 可以这样运行
./sonic -c/path/to/config.cfg
执行搜索和管理对象
搜索和对象管理(即数据摄入)仅通过 Sonic Channel 协议处理。由于我们希望 Sonic 的事情简单(类似于 Redis 的做法),Sonic 不提供 HTTP 端点或类似功能;当您需要与 Sonic 搜索数据库交互时,通过 Sonic Channel 连接是唯一的方式。
Sonic 分发官方库,让您轻松将 Sonic 集成到应用程序中。点击下面的库以查看库集成文档和代码。
如果您想了解原始 Sonic Channel TCP 协议的详细信息,您可以阅读我们的详细协议文档。如果您想编写自己的 Sonic Channel 库,这可能很有用。
📦 Sonic Channel Libraries
1️⃣ 官方库
Sonic 为您的编程语言分发官方 Sonic 集成库(官方意味着这些库已经过核心维护者的审查和验证)
- NodeJS:
- PHP:
- Rust:
2️⃣ 社区库
以下列出了社区提供的 Sonic 集成(非常感谢他们!)
- Rust:
- Python:
- Ruby:
- Go:
- go-sonic by @alexisvisco
- go-sonic by @OGKevin
- PHP:
- Java:
- java-sonic by @touhonoob
- jsonic by @alohaking
- Deno:
- Elixir:
- Crystal:
- Nim:
- .NET:
ℹ️ 找不到您编程语言的库?自己构建并在此引用!(联系我)
支持哪些文本语言?
Sonic在其词法分析系统中支持多种语言。如果语言不在列表中,您仍然可以将此语言推送到搜索索引,但停用词将无法排除,这可能导致搜索结果质量较低。
词法分析系统支持的语言有:
- 🇿🇦 阿拉伯语
- 🇸🇦 阿拉伯语
- 🇦🇲 亚美尼亚语
- 🇦🇿 阿塞拜疆语
- 🇧🇩 孟加拉语
- 🇧🇬 保加利亚语
- 🇲🇲 缅甸语
- 🏳 加泰罗尼亚语
- 🇨🇳 中文(简体)
- 🇹🇼 中文(繁体)
- 🇭🇷 克罗地亚语
- 🇨🇿 捷克语
- 🇩🇰 丹麦语
- 🇳🇱 荷兰语
- 🇬🇧 英语
- 🏳 世界语
- 🇪🇪 爱沙尼亚语
- 🇫🇮 芬兰语
- 🇫🇷 法语
- 🇬🇪 格鲁吉亚语
- 🇩🇪 德语
- 🇬🇷 希腊语
- 🇮🇳 古吉拉特语
- 🇮🇱 希伯来语
- 🇮🇳 印地语
- 🇭🇺 匈牙利语
- 🇮🇩 印度尼西亚语
- 🇮🇹 意大利语
- 🇯🇵 日语
- 🇮🇳 卡纳达语
- 🇰🇭 老挝语
- 🇰🇷 韩语
- 🏳 拉丁语
- 🇱🇻 拉脱维亚语
- 🇱🇹 立陶宛语
- 🇮🇳 马拉地语
- 🇳🇵 尼泊尔语
- 🇮🇷 波斯语
- 🇵🇱 波兰语
- 🇵🇹 葡萄牙语
- 🇮🇳 旁遮普语
- 🇷🇺 俄语
- 🇸🇰 斯洛伐克语
- 🇸🇮 斯洛文尼亚语
- 🇪🇸 西班牙语
- 🇸🇪 瑞典语
- 🇵🇭 他加禄语
- 🇮🇳 泰米尔语
- 🇹🇭 泰语
- 🇹🇷 土耳其语
- 🇺🇦 乌克兰语
- 🇵🇰 乌尔都语
- 🇻🇳 越南语
- 🇮🇱 犹太语
- 🇿🇦 祖鲁语
它有多快、多轻量?
Sonic最初是为Crisp构建的。随着Crisp的成长和将越来越多的搜索数据索引到全文搜索SQL数据库中,我们决定是时候切换到适当的搜索后端系统。在审查Elasticsearch(ELS)和其他系统时,我们发现它们是功能齐全但重量级的系统,并不能很好地适应Crisp基于免费增值的成本结构。
最终,我们决定构建自己的搜索后端,设计上资源简单、轻量。
您可以使用以下命令运行函数级别的基准测试:cargo bench --features benchmark
👩🔬 基准测试 #1
➡️ 场景
我们从用于Crisp自身客户支持的所有消息中提取了所有消息。
我们希望将所有这些消息导入一个干净的Sonic实例,然后在构建的索引上执行搜索。我们将测量Sonic执行每个操作(即每个Sonic通道上的PUSH
和QUERY
命令)所花费的时间,并将结果按每1,000个操作分组(这输出了每1,000个操作的平均时间)。
➡️ 上下文
我们的基准测试在以下计算机上运行
- 设备:MacBook Pro(视网膜,15英寸,2014年中期)
- 操作系统:MacOS 10.14.3
- 磁盘:512GB SSD(在AFS文件系统下格式化)
- 处理器:2.5 GHz Intel Core i7
- 内存:16 GB 1600 MHz DDR3
Sonic的编译方式如下
- Sonic版本: 1.0.1
- Rustc版本:
rustc 1.35.0-nightly (719b0d984 2019-03-13)
- 编译器标志:
release
配置文件(带有-03
和lto
)
我们的数据集如下
- 对象数量:约1,000,000条消息
- 总大小:约100MB的原始消息文本(此数据不包括标识符和其他元数据)
➡️ 脚本
我们用于进行基准测试的脚本有
- PUSH脚本:sonic-benchmark_batch-push.js
- QUERY脚本:sonic-benchmark_batch-query.js
⏬ 结果
我们的发现
- 我们导入约1,000,000条动态长度的消息(有些非常长,例如电子邮件);
- 导入后,搜索索引在磁盘上的大小为20MB(KV)+ 1.4MB(FST);
- 导入过程中的CPU使用率平均为单个CPU核心的75%;
- 在基准测试期间,Sonic进程的RAM使用峰值达到28MB;
- 我们使用了单个Sonic通道TCP连接,这限制了导入为单个线程(我们本可以将负载均衡到与CPU数量相同的Sonic通道连接上);
- 我们获得的导入RPS接近每秒4,000次操作(每线程);
- 我们获得的搜索查询RPS接近每秒1,000次操作(每线程);
- 在超线程的4核CPU上,我们可以将操作并行化到8个虚拟核心,从而理论上将导入RPS增加到每秒32,000次操作,而搜索查询RPS将增加到每秒8,000次操作(但我们可能在某些时候受到SSD的限制);
每操作的比较结果(单个对象)
我们从批量操作中抽取了8个结果样本,这些操作产生了总共1,000个结果(1,000,000个物品,每次测量报告中有1,000个物品批量处理)。
这并不十分科学,但它应该能让你清楚地了解Sonic的性能。
每操作耗时
操作 | 平均 | 最佳 | 最差 |
---|---|---|---|
PUSH | 275μs | 190μs | 363μs |
QUERY | 880μs | 852μs | 1ms |
从我们的终端看到的批量PUSH结果(从初始索引:0个对象)
从我们的终端看到的批量QUERY结果(在索引:1,000,000个对象)
限制
- 索引数据限制:Sonic旨在设计用于大型搜索索引,每个集合分割成数千个搜索桶。IID(即内部ID)作为32位数字存储在索引中,理论上允许每个桶中索引约42亿个对象(即OID)。我们观察到存储节省了30%到40%,这在大型数据库中是合理的权衡(与Sonic使用64位IID相比)。此外,Sonic只保留最近推入的N个结果,以滑动窗口方式存储给定单词(滑动窗口宽度可配置)。
- 搜索查询限制:Sonic自然语言处理系统(NLP)不按句子级别工作,出于存储紧凑性的原因(我们保持FST图浅以减少时间和空间复杂度)。它按单词级别工作,因此能够按单词搜索并基于用户输入预测单词,尽管它无法预测句子中的下一个单词。
- 实时限制:每次从桶图中推入或弹出单词时,FST都需要重建。由于这相当复杂,Sonic将批量重建周期。如果你刚刚向索引中推入了一个新单词,但还没有在
SUGGEST
命令中看到它,请等待下一个重建周期开始,或者通过在control
通道中使用TRIGGER consolidate
强制执行。 - 互操作性限制:Sonic通道协议是读取和写入Sonic搜索索引中搜索条目的唯一方式。Sonic不公开任何HTTP API。Sonic通道在设计时考虑了性能和最小的网络占用。如果您需要从不受支持的编程语言访问Sonic,您可以提出一个问题,或者查看参考node-sonic-channel实现,并在您的目标编程语言中构建它。
- 硬件限制:Sonic直接在文件系统上执行搜索;即,它不会将索引放入RAM。搜索查询会导致对磁盘的大量随机访问,这意味着它在老式的HDD上会很慢,而在新的SSD上会非常快。请只将Sonic数据库存储在基于SSD的文件系统中。
🔥 报告一个漏洞
如果您发现Sonic中存在漏洞,您非常欢迎直接通过发送加密邮件至@valeriansaliou([email protected])来报告。请不要在公共GitHub问题中报告漏洞,因为这些漏洞可能被恶意人员利用以针对运行未修补Sonic实例的生产服务器。
⚠️ 您必须使用@valeriansaliou GPG公钥加密您的邮件:[🔑valeriansaliou.gpg.pub.asc](https://valeriansaliou.name/files/keys/valeriansaliou.gpg.pub.asc)。
依赖项
~41MB
~766K SLoC