分享
Scan me 分享到微信

地图开发者如何存储和检索海量数据

如何对海量数据进行存储和检索?

  本文作者:wisecrazycat。更多内容请关注:Map_technology。

  简介

  LBS云存储云检索系统是百度地图针对LBS开发者,结合已有的地图API和SDK服务推出的平台级服务,通过开放服务端存储,计算能力,提供海量位置数据实时存储、检索、展示一体化的解决方案。

  开发者无需考虑软硬件部署和配置、升级和扩展,数据安全,数据一致性,检索性能,索引结构设计,实时更新等复杂的技术问题,仅通过云存储API或LBS云数据管理平台就可以将数据托管到LBS云,完成海量数据存储,获得高性能大数据检索的能力,从而能够把更多精力集中在自身的业务逻辑上。

  一、系统特点

  1. 开放免费大量存储空间:支持千万级数据(单个数据表百G)存储。

  2. 支持高效的地理空间检索:采用GeoHash算法,QPS每秒上万。

  3. 高实时性:数据更新到存储推送到检索端耗时在秒级别,可以支撑实时性要求比较高的业务。

  4. 高可用性:存储可用性4个9,检索可用性5个9以上。

  5. 很强的灵活性:允许自定义数据列,列属性,字段是否参与检索等

  6. 数据安全:包括safety和security,所有数据都有3份副本复制,不会丢失数据。数据隔离,用户A不能访问用户B的数据,用户凭唯一的AK获取自己的数据。

  二、初期架构以及演化史

  主要模块介绍:

  控制服务:负责鉴权转发,流量控制,配额控制。所有LBS云对外提供的服务都通过控制服务接入。

  存储接入层:解析转发存储请求,可根据用户的设定,控制更新的数据是否需要发布到云检索。

  检索接入层:解析检索请求,从存储集群读取数据的列属性,转化成AS可解析的请求,向后端检索集群发起检索,根据获取的索引内容,从存储集群中读取数据的详细摘要返回。

  AS:高级检索单元,将接受到请求经过DA分析后进一步发送到基础检索集群,并把结果归并过滤后返回。

  DA:query解析(包括不同粒度分词,where + what解析等)

  AC:路由,负责将检索请求/增量库更新消息路由到指定的基础检索单元。

  建库集群:负责定期将全量库与增量库合并推送到基础检索集群以及增量更新数据的转发。

  云分析:为用户提供用户检索行为分析报告

  云展示:对检索、分析数据可视化

  问题1:云存储保证用户数据互相隔离,A用户不能访问B用户的数据,但云检索中索引全量库的设计是:将所有用户数据的索引混建在一起,在取得倒排拉链后,再根据用户的唯一标识user_id做过滤,这种设计使得某用户的检索会被其他用户的数据增长影响性能变差,更严重的是会导致一些用户的数据由于拉链过长截断而一直没有机会返回,随着接入LBS云的用户越来越多,这种索引没有分离的设计对用户体验造成了极大的伤害。

  目标:

  1. 全面隔离用户,用户之间互不干扰,有各自独立的索引区间。

  2. 重构后性能要比现状好,解决系统性能不合理地变差问题。

  方案:

  1. 重新设计了全量库索引结构,提供了支持区间有序,允许全局有重复key的基础索引文件结构,提供了区间二分检索的查询。

  2. 采用新的keyfile结构的同时,设计了Table.meta二级索引结构,通过记录(起始位置,长度) 来维护每个用户对应的索引区间。

  上图中table A B C ....的索引内容完全分离,互不影响。基础检索集群的平响从12.7毫秒降到了7毫秒,检索平响超过100ms的长尾比例从原来的2.82%,12%减少到1.58%,5.89%,彻底解决了用户检索效果的问题。

  问题2:初期的检索架构性能非常差,一次检索需要几百毫秒,而主要瓶劲在接入层。其实接入层本身的逻辑非常简单,但由于需要多次访问存储集群而且是PHP模块,db-php-driver耗时几乎是db-c-driver的10倍,于是首先对检索做了C++化的重构改造,与存储集群的连接由短连接改成长连接,平响降到了几十毫秒。

  问题3:之前的检索架构,检索接入层从倒排拉链获取到数据id后,会查存储集群获取摘要详情,而由于存储性能上的局限性,一次摘要检索的耗时在20+毫秒。分析过LBS云检索的用户流量后,我们发现其实大量的query都集中在对某些数据热表的访问上,于是我们引入了cache。

  目标:

  摘要检索平响从20+毫秒优化到10毫秒。

  方案:

  使用高性能的redis集群做cache。

  为保证检索接入层的性能,未命中的数据同步到cache采用异步的方式,将 message发送到消息系统即返回,之后由di-sync di-manager配合完成cache的更新。

  为了减少访问cache的次数,我们将存储集群中存储用户数据相关的三张表合并成两张表存到cache中,减少了一次请求。

  如何保证时序性。设计di-manager时,更新同一条数据id的message只会被一个线程顺序执行,更新不同数据的操作可以并发执行。由于数据都是全字段带版本信息更新,保证了数据不会因为消息到达顺序的不同而出现不合理的更新。同时,我们也设计了转发层,根据请求中数据的唯一标识id,做一致性hash,将请求发送到后端的实例,构建了一个非常方便水平扩展的集群。

  重构优化后的架构参考下图:

  问题4:在v2版本后,云存储引入了批量操作,用户可方便灵活地对一个位置数据表进行各种批量操作,包括:上传多条数据,删除多条数据,删除多个自定义扩展列,更新数据(如字段、字段属性、字段对应的值等)等,“批量操作”接口采用异步方式推送到检索,但在引入强大功能的同时也引入了不稳定因子。我们发现:

  用户的一些批量操作会使得原本控制层的配额保护机制失效。

  系统并没有恰当的流量控制,导致用户触发大量的异步任务时,对建库集群造成了非常大更新的压力,消息系统中出现了严重的数据堆积。

  于是,我们结合用户的默认配额,对批量操作做了配额的管理限制,超过配额的批量上传任务会被直接丢弃。根据后端的处理能力对批量任务做了合理的流量控制,避免了批量任务对实时更新数据流的影响,后续我们还会进一步的优化。

  三、总结

  以上内容是在对之前的架构进行系统分析后,做过的一些优化迭代演进,未来我们还会对系统的实时更新能力做进一步的扩展提升,也会结合业务扩展存储内容(支持线面等的存储)以及相关的检索需求,为开发者提供存储内容更丰富、更可靠、检索更高效的系统。

     如果您对您所在行业有很深认知,也想在我们的平台上说点什么,我们欢迎您来投稿!

喜欢您正在阅读的内容吗?欢迎免费订阅泰伯每周精选电邮。 立即订阅

参与评论

【登录后才能评论哦!点击

  • {{item.username}}

    {{item.content}}

    {{item.created_at}}
    {{item.support}}
    回复{{item.replynum}}
    {{child.username}} 回复 {{child.to_username}}:{{child.content}}

更多精选文章推荐

下一篇

未来几年北斗“看什么”?