证书透明度(Certificate Transparency,CT)框架的核心价值在于为所有 TLS 证书建立公开的只追加日志,从而实现对证书签发的审计与监控。然而,随着 Let’s Encrypt 每日签发超过四百万张证书、主流 CT 日志累计已超过七亿条记录,如何在这些海量数据上进行高效查询,已成为工程实践中的关键挑战。本文以 Merklemap 的公开工程实践为案例,剖析其支撑百亿级数据查询的索引架构与参数配置。
证书透明度数据的查询困境
传统的 CT 查询工具(如 crt.sh)在面对大规模数据时面临显著的性能瓶颈。当查询匹配大量日志条目时,系统只能返回截断的结果集,且频繁出现响应超时或服务不可用的情况。这一问题的根源在于 CT 日志的分布式特性与查询需求之间的结构性矛盾:日志本身是跨多个独立运营方的高度分片数据集,而用户查询往往需要跨所有日志进行聚合检索。
构建高效的 CT 查询系统,核心在于解决两个层面的问题:首先是数据的实时采集与聚合存储,其次是针对高频查询模式设计合理的索引结构。前者决定了系统的数据新鲜度与覆盖范围,后者直接决定了查询响应时间与吞吐能力。
单分片 PostgreSQL 的规模边界判断
Merklemap 在架构选型上做出了一个看似激进但实则经过审慎评估的决定:将全部 CT 数据存储在单一 PostgreSQL 实例中,而非采用分布式数据库分片方案。这一决策的核心依据是当代硬件能力的提升使得单机承载百亿级行数据成为可能。当前 Merklemap 运营的数据库规模约为十六至二十 TB,存储超过一千亿行唯一记录,每日以约十五万行每秒的速率持续写入,同时支撑四万次每秒的事务处理和四百万行每秒的读取吞吐量。
选择 PostgreSQL 而非分布式数据库的理由很实际:PostgreSQL 的性能表现稳定可靠,团队具备丰富的运维经验,而分布式数据库在一致性保障和运维复杂度上的开销并未带来明显的性能收益。在单机能够容纳全部数据的判断下,简化架构带来的可靠性提升远大于分布式扩展带来的理论吞吐量优势。
存储层设计:ZFS 与硬件配置
在存储层面,Merklemap 采用 ZFS 文件系统承载 PostgreSQL 数据,这一选择充分利用了 ZFS 在压缩、快照和复制传输方面的能力。ZFS 的默认记录大小设置为 128KB,这一数值经过权衡,既能保证良好的压缩比,又能在顺序扫描场景下维持理想的 IO 效率。CT 日志的证书数据具有较高的重复率,ZFS 的透明压缩能够有效降低存储成本和 IO 压力。
硬件配置方面,每个数据库副本采用 AMD EPYC 9454P 处理器,配合六块 NVMe 固态硬盘组成 RAIDZ-1 阵列,内存配置达到 1TB。这样的硬件配置单副本成本约为两万八千欧元,加上托管机房的持续运营成本。虽然初期投入较高,但在单机架构下避免了分布式系统的心智负担和数据分片带来的查询路由复杂性。
索引策略:查询模式的针对性设计
CT 查询的典型模式包括按域名检索、按证书颁发者筛选、按有效期范围过滤,以及按证书指纹精确匹配。针对这些高频查询,Merklemap 在 PostgreSQL 中建立了多维度的索引结构。域名索引是最核心的检索入口,由于域名存在大量重复(前缀共享),合理的索引类型选择和列压缩策略对查询性能有显著影响。
证书指纹索引支持精确匹配查询,这类查询通常返回唯一结果,对索引的查找效率要求极高。有效期索引则需要支持范围查询,PostgreSQL 的 B-tree 索引在此场景下表现良好,但需要关注索引膨胀问题。值得注意的是,CT 数据的写入模式是持续性的追加,这使得索引的维护成本相对可控,但仍需通过定期 VACUUM 操作防止膨胀导致的性能退化。
PostgreSQL 的一个关键调优点是采用非标准块大小进行编译。默认的 8KB 块大小在面对大规模分析查询时可能产生不必要的 IO 开销,通过调整块大小参数,可以在特定工作负载下获得更好的缓存命中率和查询性能。这一修改需要重新编译 PostgreSQL,属于部署阶段的配置决策。
实时数据采集与索引更新机制
CT 查询系统的数据新鲜度取决于日志采集链路的延迟。Merklemap 通过维护已知 CT 日志列表,建立与各运营方(日志由 Google、Cloudflare、Let’s Encrypt 等机构分别运营)的持久连接,实现对新增证书的实时尾随。当证书被提交至 CT 日志时,系统立即接收通知并拉取完整证书数据,随后进行解析和索引更新。
这一采集架构的关键设计点在于索引更新的异步化处理。实时采集链路负责将数据持久化存储,而索引的构建和维护可以适当解耦,以避免写入阻塞影响查询响应。对于高频写入场景,批量索引构建相比逐条更新能够显著降低索引维护开销,但需要在查询延迟和数据新鲜度之间做出权衡。
运维参数与监控阈值
在生产环境中运行百亿级 PostgreSQL 数据库,以下参数和阈值值得特别关注。VACUUM 的调度策略直接影响索引膨胀程度和查询性能,建议配置 autovacuum_vacuum_scale_factor 和 autovacuum_vacuum_insert_scale_factor 以适应高写入量场景,避免因膨胀导致的性能退化。autovacuum_naptime 的设置需要根据写入速率调整,确保后台 VACUUM 能够跟上数据变更的速度。
复制配置方面,Merklemap 采用主备复制架构,备用副本承担读取负载,实现读写分离。主库的写入压力和备库的读取压力需要分别监控,当复制延迟超过预期阈值时,可能需要检查网络带宽和备库的 IO 能力。
存储空间的监控同样关键。虽然当前单机架构下存储扩展相对简单,但二十 TB 级别的数据量意味着备份窗口和恢复时间的规划需要更加严谨。ZFS 的快照和 send/receive 功能为备份策略提供了便利,但快照频率和数据保留策略需要根据业务需求和存储成本平衡确定。
工程实践的启示
Merklemap 的案例揭示了一个在分布式系统盛行的时代略显反直觉的结论:当数据规模与单机容量边界尚有距离时,简化架构带来的可靠性收益往往超过分布式扩展的理论优势。百亿级数据听起来是典型的分布式场景,但 PostgreSQL 配合合理的硬件配置和参数调优,完全能够在单机上提供稳健的服务能力。
这一实践对于类似规模的数据系统具有参考意义:在考虑引入分布式数据库之前,应当充分评估单机方案的能力边界。分布式系统引入的一致性协议开销、跨分片查询优化难度以及运维复杂度,往往在数据规模未达到明确瓶颈时成为净负担。选择合适的时机进行架构升级,比一开始就设计过度复杂的分布式方案更能交付可靠的工程成果。
参考资料
- Merklemap Documentation: How it Works (https://www.merklemap.com/documentation/how-it-works)
- Merklemap: Scaling Certificate Transparency with 100B+ Rows of Data (https://blog.transparency.dev/merklemap-scaling-certificate-transparency-with-100b-rows-of-data)
- Merklemap runs a 16TB PostgreSQL (Simon Willison’s Weblog)