Uber利用Golang构建高性能地理查询服务实践( 四 )

对于每次查找 , 首先通过对所有城市地理围栏进行线性扫描找到所需的城市 , 然后通过第二次线性扫描在该城市内查找包含的地理围栏 。 虽然解决方案的运行时复杂度保持为O(N) , 但这种简单的技术将所需的N从10000s减少到了100s 。

架构

服务的架构总体设计是无状态的 , 因此每个请求都可以调度到该服务的任何一个实例 , 并能期望得到相同的结果 。 这样每个服务实例都可以服务整个领域 , 无需使用分区 。 架构上还是使用了确定性的轮询计划 , 因此来自不同服务实例的地理围栏数据保持同步 。 因此 , 该服务具有最简单的体系结构 。 后台作业定期轮询来自各种数据存储的地理围栏数据 。 这些数据保存在主存储器中以服务查询 , 并序列化到实例本地文件系统 , 可以在服务重启时快速启动 , 总体机构图如下:

处理Go Memory模型

服务的体系结构要求对内存中的地理索引同时进行读/写访问 。 特别是后台轮询作业要写到索引 , 而前台查询引擎则从索引中读取 。 Golang的内存模型可能会有一些问题 。 Golang中常用的方法是对并发读写用协程和通道同步 , 但是又会影响性能 。 团队尝试使用sync/atomic包中的StorePointer和LoadPointer方法自己编写了管理内存代理 , 这样导致了代码脆弱且难以维护 。

推荐阅读