
导读 introduction地图情报核实需判断“某段路到底有没有车走过”。车少的长尾路段乡道、新修路、偏远低频路需把时间拉长到半年才能攒够轨迹而这180天、近10万亿点的历史轨迹存于离线数仓查询一次要耗时数小时。通过一套基于ClickHouse的检索服务实现了在线查询任意区域检索TTFB仅0.35s。本文复盘了如何利用ClickHouse S2地理编码 流式检索在近10万亿轨迹点的底库上实现任意区域秒级可视化。其中包括存储引擎选型、地理索引设计的原理解析第5节以及一线实践中的避坑经验第6节适合关注海量数据检索、时空数据处理、OLAP实战的人士。01 业务背景长尾路段的轨迹从哪来这套服务是地图情报团队“情报核实图灵平台”的底层能力。核实平台每日需挖掘、核实地图上的各类要素如新增道路、路网变化、通行规则等而核实过程中常需回答一个问题“这段路到底有没有车真实走过”轨迹是最直接的证据逻辑简单但难点在于“长尾路段”即乡道、新修路、偏远低频路。主干道车流量大取几天数据就能获得足够轨迹而长尾路段车少近几天甚至几周可能都没有车辆经过难以查询到是否有人走过。解决方案是将时间拉长到180天即便每天只有一辆车经过半年也能积累上百条轨迹使长尾路段具有统计意义。为控制数据规模入库时过滤掉了1 - 4级路高速、国道、主干道等高等级路本身车流量大不缺证据仅保留低等级路。即便如此180天积累下来仍有近10万亿个轨迹点。问题在于这批数据存于“离线数仓”中查询“某区域某时段的轨迹”需要提交任务、排队、扫描全表动辄耗时数小时无法满足地图上点选即看结果的交互式核实需求。核心矛盾在于要在近10万亿行的底库上将查询时间从“几个小时”压缩到“0.35秒”。从成本角度看该方案基于ClickHouse冷热分级SSD热层 BOS冷层覆盖180天、近10万亿行年成本仅约25万大部分存量数据存储在低价的对象存储上比纯内存方案节省成本。实现小时到亚秒的量级差距约5个数量级并非依靠增加机器而是通过存储引擎选型、索引设计和查询策略三者配合。接下来将先介绍数据规模再逐步拆解。02 一组开门见山的数字核心事实为在近10万亿行的底库上对任意一块地图区域、任意180天时间窗进行检索用户0.35秒即可看到第一条轨迹。底库实测count()结果显示trajectory_all_bos为9,868,628,881,572约9.87万亿近10万亿。按天统计的日写入量趋势如下1月中下旬为节前出行高峰日写入量达680 - 1020亿是全周期最活跃的时段2月春节前后回落到300 - 400亿/天符合假期出行下降的规律3月稳定在330 - 360亿/天是全年的基线水位4月中起明显抬升从400亿跃升至800亿档并持续对应有新数据源接入放量5 - 7月常态维持在400 - 900亿之间且呈现明显的隔日节律约450亿与约800亿交替出现说明某个大数据源是按批次、非每日均匀写入的。这对检索服务意味着写入量的季节波动不应影响读侧的检索延迟。后续的技术设计旨在确保无论底库当日灌入多少数据用户的检索TTFB都稳定在0.35秒。03 服务定位与整体架构首先明确边界track - search是情报核实图灵平台的底层能力负责将离线180天的长尾历史轨迹转化为“秒级可查”。轨迹数据的生产链路即由Spark从离线数仓读取进行清洗编码入库时过滤掉1 - 4级路、计算S2、转换为定点整数再批量灌入ClickHouse在离线侧完成不在本文讨论范围内。本文仅聚焦于读侧track - search是一个纯在线检索服务将离线的“要跑几个小时”的数据转化为“秒级可查”。整体架构如图所示。选择ClickHouse而非继续使用离线数仓是因为离线数仓为批处理吞吐设计交互式点查需进行任务调度、扫描全表延迟以小时计。而ClickHouse是列式OLAP引擎结合排序键稀疏索引能够实现“只解压真正命中的数据块”这是将小时级延迟压缩到亚秒级的物理基础。技术栈仅包含GoGDP框架、ClickHouse和S2地理库在线链路直接访问CK不经过Redis缓存或中间存储。该检索引擎通过参数适配可应用于三类典型场景场景A为轨迹挖路全覆盖采用180天全量数据并进行空间抽稀以发现路网上未被采集的新路场景B为实时交通观察使用单天数据并进行速度过滤以查看某片区域的实时通行情况场景C为数据源质量审核指定单一数据源并进行GPS精度过滤以逐源分析数据质量。3.1 平台可视化效果实际的检索可视化界面半径5.5km、卫星影像底图展示了真实的GPS轨迹图上每一条绿色的线都是车辆、终端实际走过的路径。一次框选即可将该区域180天内的所有轨迹铺在卫星影像上。通过观察可以发现轨迹勾勒出了真实路网绿线密集的地方表明确实有人反复走过有绿线但卫星图上看不出明显道路的位置可能是未被采集的新路或地图上标注但实际少车通行的“误挖”路段越是偏远、车少的长尾路段越依赖这张图通过180天的累积才使稀疏的轨迹显现出来。3.2 回到最初的问题这段路到底有没有车走过绕了一圈回到第1节中核实平台反复要回答的问题“这段路到底有没有车真实走过”答案就在上面的图中绿轨迹所到之处即“有人走过”绿轨迹密集却与现有路网不匹配的地方就是需要挖掘的新路或潜在的误挖路段。这套服务的真正意义并非“查得快”本身而是将轨迹检索转变为要素核实与挖路工艺的交互式验证工具。04 检索性能实测4.1 实测数据测试环境为5节点ClickHouse集群。从实测数据来看30天和180天的TTFB均为0.35秒时间窗拉长5倍首字节时间几乎不变。这表明检索延迟主要由空间索引和流式返回决定而非受时间范围影响即首屏速度不受数据总量拖累这也是后续所有技术设计的目标。其中TTFB约为0.3 - 0.5秒前端收到第一条轨迹即可开始渲染无需等待全部计算完成总耗时随数据量线性增长但由于采用流式处理用户的体感仅与TTFB相关。05 技术细节0.35s是怎么抠出来的技术栈仅包含GoGDP框架、ClickHouse和S2地理库。在线检索链路直接访问CK不经过Redis缓存或中间存储依靠CK自身的稀疏索引和本服务的查询策略应对万亿量级的数据。5.1 地基用S2把“区域”变成CK排序键上的整数对万亿行数据进行全表扫描是不可行的。核心思路是将地理区域转换为排序键上的整数区间让CK利用稀疏索引进行跳读仅解压真正命中的数据块。轨迹表trajectory_all_bos分布式表每行包含一个GPS点关键列如图所示。该表按event_day分区、(event_day, s2_id_18, traj_id)排序这个排序键是性能的基础时间在前可进行时间范围裁剪分区空间紧随其后可通过S2范围命中稀疏索引进行跳读这也解释了第4节中时间窗拉长不影响S2定位首字节速度的现象。“空间”通过S2编码塞进排序键。CK只识别s2_id_18上的大小比较不识别“圆”或“矩形”而Google的S2库library/util/s2tool.go841行利用希尔伯特曲线将地表编码成一维cell id实现了空间相邻则id相邻从而将任意区域覆盖为一组连续整数区间。通过“网格 → 希尔伯特编号 → 整数区间SQL”三步完成转换混合层级覆盖是关键需平衡精度与条件数。至此“检索一个圆/多边形”转变为CK擅长的“扫几段排序键整数区间”这是后续过滤、跳读的前提。需要注意的是CK里s2_id_18基于GCJ - 02坐标计算入参通常是WGS - 84查询前必须进行坐标转换trajectory.go:1056否则会出现整体偏移几百米、召回结果错误的情况。5.2 浮点转定点整数经纬度、速度全部存Int表中经纬度lng/lat不使用Float64/Float32存储而是乘以1e6存为Int32速度l_speed乘以10存为UInt16。这种设计在万亿行量级下能带来多方面好处。首先省空间经纬度小数点后6位对轨迹精度已足够Int32只需4字节相比Float64的8字节单是经纬度两列每行就从16字节降至8字节万亿行×2列可节省数十TB磁盘空间速度用UInt162字节比Float324字节同理可再省一半。其次压缩率更高ClickHouse按列压缩整数列的压缩比远高于浮点列能进一步节省空间加快检索速度。再次比较更快、更准整数范围过滤的CPU指令更快且无浮点精度误差。最后对S2索引友好经纬度以整数形态存储配合排序键使整行数据排列紧凑跳读命中率更高。虽然进出口需进行乘除转换但与节省的磁盘空间和IO/压缩收益相比CPU开销可忽略不计。这是海量数据存储中典型的“定点化”取舍用可接受的精度上限换取存储和查询的全面优势。通过线上实测“定点整数化 → 列式排序 delta编码 → LZ4压缩”逐级叠加整表压缩比达6.42:1排序键列压缩效果显著定点整数的坐标列也有较好的压缩表现。这表明定点整数化设计同时实现了省空间、高压缩、快比较、S2友好的四重收益压缩不仅节省成本还能直接提升检索速度。5.3 存储策略SSD热层 对象存储冷层的冷热分级数据存储介质和读取方式直接影响检索下限。但存在现实矛盾180天近10万亿行、单节点数十TB的数据全部存入本地SSD成本过高全部存于远端对象存储又速度过慢。本服务采用冷热分级存储策略策略名脱敏为traj_tiered_policy将存储分为两层数据可按冷热自动流转。读路径方面首先将框选区域通过S2覆盖为若干段s2_id_18 BETWEEN ? AND ?将空间检索转换为排序键上的范围扫描然后利用ClickHouse的稀疏索引定位命中的mark区间接着进行granule跳读只解压命中的granule跳过区域外的数据块最后通过多盘并行扫part从多块SSD上同时拉取命中的part提高IO吞吐。冷热分级方面热层SSD存储近期高频数据多数检索可直接命中热层实现亚秒返回冷层BOS存储历史存量数据容量大、单价低当热层SSD用量达到水位时ClickHouse会自动将最老的part下沉到BOS并配合180天TTL滚动淘汰最旧分区整个冷热迁移对查询透明不影响应用层和SQL。总之稀疏索引和granule跳读决定“读多少”多盘并行决定“读多快”冷热分级决定“数据存储位置和成本”。从“存多少”“存储位置和读取速度”“读多少”三个维度层层递进共同支撑亚秒检索。5.4 两级过滤粗筛跳块 精筛去空隙区域较大时S2区间会覆盖到不属于目标的空隙。查询采用两级过滤兼顾速度和准确性先用PREWHERE进行粗筛命中稀疏索引跳过远处granule再用WHERE进行精筛去掉区间内的空隙误命中。使用PREWHERE可先读最少的列判断是否读取其余列节省IO。5.5 两阶段查询先找轨迹再取点点查接口TrajS2Search分两步进行避免一次拖出海量点。第一步通过两级过滤只SELECT DISTINCT traj_id数据量小、速度快第二步拿traj_id集合回表取完整点按traj_id, point_id排序进行精确点查不再依赖空间索引。5.6 主力接口流式 高并发TTFB 0.35s的直接来源前端拖拽/缩放使用的TrajS2SearchStream接口通过四个优化叠加实现了0.35秒的TTFB。首先采用流式NDJSON结果进入chan缓冲8000主协程边读边写每50条主动flush使用户秒级看到首屏解耦TTFB与总耗时。其次进行空间交错分组并发将大range拆分成若干段交错分配到各组每组并发查询两张表均匀利用集群资源。然后达标即取消查询拉够目标量后其余查询随context取消节省集群算力。最后进行均匀采样和CK深度调优按半径动态调整perCellLimit并设置相关参数。5.7 流式后处理抽稀、降噪、简化都在服务端做从CK查出来的原始GPS点存在抖动、跳变、冗余等问题直接渲染会压垮前端。因此在流式吐出之前需在服务端进行后处理将原始数据加工成成品轨迹。后处理管线分五步空间抽稀在结果≥1万条时触发有四种策略可选保证空间均匀覆盖的同时减少数据量跳点截断在降噪之前进行判定并处理相邻两点距离超过阈值的情况降噪提供五种算法去除GPS抖动毛刺平滑 简化使用EMA指数平滑和道格拉斯 - 普克算法使线条自然并减少冗余点噪声段过滤 路网匹配丢掉过短的碎段对保留轨迹进行路网匹配标注“新路候选”或“误挖”服务于挖路工艺验证。这套后处理不在前端进行的原因有三点前端无法处理卡尔曼、DP等算法服务端加工后前端渲染零负担全部在流式管线里完成不额外增加TTFB用户仍能0.35秒看到首屏。5.8 连接层与兜底连接层通过设置相关参数如dial_timeout、max_execution_time、connection_open_strategy、compress等同时设置最大打开连接数和最大空闲连接数以应对并发请求。Schema降级兜底方面上游写入的表结构可能会演进查询先按全字段查Scan失败后退回精简字段集重查保证表结构变更期间不停服。06 复盘万亿量级读服务的关键决策与避坑将上小时级离线查询压缩到亚秒级在线检索的项目关键在于几个方向性决策、性能优化和避坑经验。以下按这三条线进行复盘。6.1 三个关键决策决策一放弃离线数仓选择ClickHouse。离线数仓不适合交互式点查而ClickHouse的列式和排序键稀疏索引适合“大范围过滤、小范围命中”的点查选型正确是后续优化的基础。决策二使用S2将地理问题降维为整数区间问题。数据库擅长排序键上的范围扫描S2通过希尔伯特曲线将二维坐标编码成一维整数将空间检索难题转化为ClickHouse擅长的任务。决策三采用全链路流式而非查完再返。面对百万级结果集边查边吐可解耦TTFB与总数据量定义了产品的“快”。6.2 性能优化清单性能优化的主线是降扫描量优先于加机器5节点集群能处理近10万亿行数据依靠的是让每个请求只处理真正需要的几千万行数据。6.3 避坑经验以下是一些实际踩过的坑及应对方法坑一坐标系不一致会导致召回结果错误CK里s2_id_18基于GCJ - 02计算入参多为WGS - 84查询前必须进行坐标转换坑二为“聚合加速”建立的摘要表在明细场景中不适用应回归主表排序键本身解决坑三统计每日写入量时需剔除未完成分区避免误判数据下降坑四上游schema演进可能导致查询失败可采用降级兜底策略先按全字段查失败后退回精简字段集重查。总之万亿级读服务的性能依靠“选对存储引擎 让每个请求只处理真正需要的数据 让集群被均匀、可中止地使用”的整套配合。07 写在最后这套方法能迁移到哪本文介绍的轨迹检索方法抽离业务外壳后是一套“海量时空/点数据的在线检索”通用范式可迁移到多个相似场景如任何“空间 时间”的海量点查、“离线数仓扛不动在线交互”的场景以及被结果集大小拖慢体感的接口等。需要强调的是近10万亿行是数据规模而非架构瓶颈。该架构的单请求开销与“命中的数据量”相关几乎不随“底库总量”增长即便底库数据量大幅增加检索延迟也不会线性劣化扩容可通过增加节点、扩展SSD热层、调大并发度等常规手段实现。这套服务最终服务于百度地图路网的持续更新使路网信息查询从耗时数小时变为随点随看当基础设施的检索速度能够支撑交互式探索时将改变整个工艺的工作方式。”