ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

一个周末10美元:打造50万域名的垂直搜索引擎

2026/8/30 12:43:53 拓冰建站 浏览量
一个周末10美元:打造50万域名的垂直搜索引擎 如果你常做独立开发、工具站或者经常需要给新项目起名字大概率经历过这样的流程打开注册商搜索框输入一个关键词看看域名还在不在不在就换个后缀再换个组合反复试十几次之后耐心耗尽最后草草选一个备选名字。上个月我在做一个面向开发者的导航站时发现自己需要批量比较几组关键词的可注册性和已有项目现有的域名查询工具要么只能单条查询要么查询结果导不出来要么干脆藏在付费功能后面。于是我用一个周末做了一个只给自己用的域名搜索服务50 万条域名元数据一个带全文检索的查询接口总成本控制在 10 美元左右。项目标题翻译成中文大致是“我花一个周末用 10 美元做了一个 50 万域名的搜索引擎”。做完之后我最想记录的其实不是搜索引擎本身而是这件事为什么能成立。1. 500k 域名搜索引擎难点不在“搜索”而在“垂直化”1.1 垂直搜索和通用搜索不是一回事搜索引擎这个词很容易让人觉得需要爬虫、需要大规模索引、需要分布式架构。但如果把数据规模锁定为 50 万条域名情况完全不一样。垂直搜索面对的并不是全网内容而是一个已经确定边界的数据集域名、标题、描述、标签、时间戳。用户查询意图也很明确想找包含某关键词的域名想确认某个关键词组合有没有人用过想按标签筛选一批备选项目名。这带来了一个连锁反应技术栈可以极简因为不需要照顾海量并发和复杂分词。用数据库自带的全文本检索或者内嵌式索引库基本就能覆盖需求。以前做工具站时我会不自觉地想上 Elasticsearch但这次因为时间限制我强迫自己在选型上做减法最后发现 500k 行数据根本不需要那么重的组件。1.2 500k 的规模决定了架构选型把“500k”这个数字摆出来心里会更踏实。假设每条域名记录包含 6 个字段域名、标题、描述、标签、来源、更新时间平均 300 字节。500k 条记录大约 150MB加上全文索引和数据库内部开销约在 1GB 上下。这个量级放在现代云主机上几乎是“连风扇都转不起来”的程度查询走到索引上而不是全表扫描时单次响应通常在几十毫秒以内。这个规模也意味着你可以直接使用 SQLite、PostgreSQL 或轻量级全文检索组件。用一句话说500k 是什么概念它是“个人开发者用一两天时间就能把数据灌进去、把服务跑起来”的量级。真正要担心的不是查询性能而是数据从哪里来、如何判断数据是否干净、以及后续怎么更新。核心原则先定义你的数据规模再决定架构。规模本身就是最好的架构师。2. 一个周末的限制帮我删掉了大量不该做的事2.1 四步链路数据获取、清洗入库、索引构建、服务化我把整个项目压缩成四段链路数据获取 - 清洗入库 - 索引构建 - 服务化。这是很多垂直搜索项目的统一骨架数据量大只是把每一段变成分布式工程数据量小则可以用脚本和单机库完成。时间分配上我的经验是数据获取最多半天因为优先找现成快照而不是去爬全网清洗入库2 到 3 小时重点处理重复、格式、字段缺失索引构建1 到 2 小时确定查询模式后建 FTS 表服务化一个下午做查询接口、限流和日志判断标准只有一个先跑通再优化。不要在第一版做实时更新、多机部署、语义搜索、自动过期等等。你的目标是“在周末内完成一个可用版本”不是“做出一个生产级系统”。2.2 数据源先选快照不追实时域名类数据最常见的渠道有三类我选择先看哪个渠道能最快拿到快照而不是哪个渠道最完整公开数据集一些网络测量和公开研究项目会定期发布 DNS 快照里面通常包含大量域名记录适合先下载解析。证书透明日志CT 日志以公开方式记录为域名签发的证书每天都有增量按天拉取后能得到较新的域名覆盖。已有目录数据一些站长工具或网络目录会发布批量域名列表适合做小规模验证。这里需要补充我没有用实时爬虫和字典扫描一方面是为了控制时间另一方面是批量主动探测很容易带来合规和误用风险完全没有必要。对个人项目来说先用手边的快照数据把链路跑通比追求数据新鲜度重要得多。2.3 技术选型能少装一个组件就少装一个一个周末的限制本质上是一个过滤器。它逼你回答一个问题如果只能做最有用的 20%你要做哪 20%我当时的技术选型是存储SQLite / PostgreSQL根据有没有现成实例决定索引内置 FTS 全文检索而不是单独部署搜索引擎服务一个轻量 HTTP 接口不做前端页面后台只有一个定时更新脚本不做实时同步这套组合的好处是组件少、心智负担低。缺点是它不适合非常复杂的查询解析、多语言语义搜索、超高并发。但针对“50 万条域名、低频查询、个人使用”它刚好在甜点上。3. 可落地实现从 50 万条原始数据到一个可查询 API3.1 表结构设计如果你也是从零开始第一件事不是写代码而是先把表结构定好。一个常见的结构是这样CREATE TABLE domains ( id INTEGER PRIMARY KEY, domain TEXT NOT NULL UNIQUE, title TEXT DEFAULT , description TEXT DEFAULT , tags TEXT DEFAULT , source TEXT DEFAULT , first_seen TEXT, last_updated TEXT );有几个设计细节要注意domain 必须有唯一约束因为同一域名可能来自不同数据源去重时要优先保留信息更完整的记录。tags 可以用逗号分隔的字符串也可以用单独关联表500k 规模下直接字符串在大多数查询里也能接受但如果你要按标签精确筛选单独建标签表会更清晰。source 字段用于记录数据来源后续排查数据质量时很重要。3.2 全文索引与查询模式表结构建好之后下一步是全文索引。以 SQLite FTS5 为例可以建立一张虚拟表CREATE VIRTUAL TABLE domains_fts USING fts5( domain, title, description, tags );同步数据时把主表字段复制进 FTS 表INSERT INTO domains_fts(rowid, domain, title, description, tags) SELECT id, domain, title, description, tags FROM domains;查询时用 MATCHSELECT id, domain, title FROM domains_fts WHERE domains_fts MATCH ? ORDER BY rank LIMIT 50;注意两点FTS5 不是所有 SQLite 编译版本默认都启用使用前要先确认。FTS5 的 MATCH 语法有自己的分词规则特殊字符可能导致查询报错如果只需要子串包含类查询直接用LIKE反而更简单只是性能上限会低一些。对 500k 行数据来说FTS 查询通常在毫秒到几十毫秒之间。如果有一天数据涨到千万级再考虑迁移到独立的全文检索引擎也不迟。3.3 一个最小的查询服务有了表和索引接下来只需要一个最薄的服务层。很多开发者会选择 Flask、FastAPI 这类轻量框架也可以直接使用 Serverless 函数。接口可以非常简洁下面是一个示例结构app.route(/search) def search(): q request.args.get(q, ).strip() limit min(int(request.args.get(limit, 20)), 100) if not q: return jsonify({error: missing q}), 400 results search_domains(q, limit) return jsonify({results: results, total: len(results)})这里的重点是返回结果字段不要塞太多东西否则接口响应会越来越大。先返回 id、domain、title、tags 四五个字段足够覆盖大多数场景。3.4 性能预期与真实体验实际体验中我原本想给搜索做自动补全和前端页面但因为周末限制我把前端砍掉了只保留 API。后来发现这件事反而帮我快速验证了“垂直搜索是否真的被需要”如果 API 调用频率很高说明有价值如果没人调用说明问题不在于界面不好看而是数据或场景不够匹配。性能层面在个人电脑级配置上跑这 500k 条数据单机 SQLite 配 FTS5 已经足够流畅。真正的瓶颈往往不是查询而是数据写入时的清洗和去重多个数据源之间的格式不一致、域名大小写、重复记录都会消耗大量时间。4. 成本控制在 10 美元内靠的是一组“不为清单”4.1 成本拆解成本可以从四个维度看项目常见方案量级算力轻量云主机 / Serverless 函数几美元到十几美元/月按量计费更低存储SQLite 文件 / 云盘 / 对象存储几百 MB通常在免费或低额范围内数据获取公开数据集 / CT 日志 / 目录快照0 到几美元网络带宽低频 API 查询通常可以忽略10 美元不是一个精确造价而是一个“成本边界”。它真正的好处是让人愿意做实验就算推倒重来损失也很小。反过来如果一开始就租一个高配数据库实例心理压力反而会让你不敢改结构。4.2 哪些决定会让成本迅速失控有几个决定会瞬间把成本拉高做之前要想清楚实时更新每分钟去刷新数据背后的计算和存储成本会持续上升。多节点一上集群网络、运维、监控都变成负担。商业查询 API每次查询按条计费如果 500k 条域名全部跑一遍账单会非常难看。采集扩展把数据源从“公开快照”换成“全网协议扫描”后成本完全不同。我并不是说不要做这些而是说在第一版不要做。要记住这个项目的定位是验证一个垂直数据服务不是做一个高吞吐平台。4.3 超预算时的降级路径如果实测下来成本超了降级路径也很清晰。第一减少数据范围把 500k 缩到 50k 或 100k查询体验在大多数情况下没有明显变化。第二把 API 改成离线导出先出一份 CSV 或 Excel验证用户是否愿意用再回来考虑在线化。第三把部署从公网挪到本地运行或内网访问直接省掉带宽和防护成本。这其实是一个很朴素的原则先给最小能力再根据真实需求和预算逐步加码。永远先做“能跑通的最小闭环”而不是“看起来完整的系统”。前者解决的是有没有的问题后者解决的是好不好的问题。5. 放公网之前先处理四个问题5.1 数据时效性是域名数据最大的坑域名元数据比我们想象的更容易过时。一个域名去年还在解析今年可能已经过期一个项目的标签今天还有效下个月可能改版。如果服务跑在公网上用户查到一条已经失效的域名信息体验会很差。所以我建议在数据表里永远保留 first_seen 和 last_updated 两个时间字段并且在结果里展示“数据快照时间”。更新策略可以很简单每天或每周定时任务重新拉一次增量数据做 upsert 更新。不要为了效果把更新时间伪装成实时状态。5.2 安全与限流不鉴权就不要接公网如果你把服务放到公网至少要处理三件事鉴权加一个 API Key哪怕只是硬编码在配置里也比完全裸奔好。限流对单 IP 或单 Key 设置 QPS 上限避免被人写脚本循环调用。日志记录查询时间、查询参数、响应码方便排查异常。很多临时项目把这些省掉结果就是某天被扫描工具发现数据被拖走甚至变成别人的免费代理。对个人开发者来说一旦决定接公网安全不是可选项而是成本的一部分。5.3 排查链路从请求到数据逐层定位如果搜索服务慢了或查不出结果不建议一上来就怀疑数据库性能。我一般按这个链路排查看请求参数q 是否为空、limit 是否被调成很大的值、查询词是否包含 FTS 语法中的特殊字符。看服务端日志有没有报错、有没有慢查询记录。看查询语句是走了索引还是全表扫描MATCH 语法是否合法。看索引FTS 是否重建过、后台同步任务是否执行成功。看数据有没有缺失字段、重复数据、脏数据被包含在结果里。看环境磁盘是否写满、内存是否充足、数据库是否被锁。大多数“突然变慢”的问题最后都出在数据同步中断或者查询参数异常而不是服务器性能不够。5.4 适用边界明确不硬撑这个方案有明确的应用边界。适合的场景是个人或小团队使用、数据规模在百万级以内、查询是低频且字段有限、对实时性要求不高。不适合的场景是对外提供高可用 API 服务、需要实时域名状态、需要语义搜索或多语言处理、需要支撑大量并发请求。如果需求进入了“不适合”列表最好重新评估是继续自建基础设施还是采购商业搜索服务或者直接利用数据库的LIKE查询反而更务实。6. 这件事真正沉淀下来的是一条可复用的垂直数据流水线6.1 搜索器只是外壳流水线才是内核一个周末做完这个项目之后我发现自己无意间掌握了一套做垂直搜索的模板。下次无论是想做“开源项目搜索”“工具站搜索”“电子书/文档搜索”还是“本地笔记库搜索”都能复用同一条链路拿到一个确定规模的数据集清洗成固定字段建一张表建全文索引包一个 API然后上线观察。真正沉淀下来的不是“我写了一个能搜域名的服务”而是“我知道怎么把一份数据变成一种可以查询的基础设施”。6.2 如果现在让我重做一遍我会这样做重来一次的话我会更早想清楚三件事第一先确认数据源有没有更新机制再决定要不要建在线服务。第二先做 1000 条数据的最小原型验证字段设计和查询方式再灌全部数据。第三提前把日志接好从第一版就统计查询词因为查询词才是后续迭代最宝贵的输入。这些经验不是项目完成后才有的而是踩了几次坑之后才变成习惯。6.3 如果你也想做类似的工具我建议从这步开始如果你也正在考虑做类似的工具我建议不要被“搜索引擎”四个字吓住。先把数据集规模写出来把字段写出来把用户查询意图写出来。只要这三样是清楚的剩下的就是一个普通数据库项目。用最熟悉的语言、最熟悉的数据库、最少的组件在一天内跑通一个最小版本。你会发现“一个周末、10 美元”并不是什么了不起的成就但它会让你更愿意开始下一件事。再回到那个英文项目标题“I built a 500k-domain search engine for makers in a weekend for $10”。它真正想说的也许不是“我做了个搜索系统”而是“50 万条数据、一个周末、10 美元”这三个数字放在一起后个人开发者完全有能力把一个小而垂直的数据需求变成可运行的服务。愿意为一个小需求动手并且用约束帮助自己做减法这套能力在未来任何数据产品里都会反复用到。