本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践。前端
【做者介绍】算法
【公司简介】数据库
快手是一家全球领先的内容社区和社交平台,旨在经过短视频的方式帮助人们发现所需、发挥所长,持续提高每一个人独特的幸福感。小程序
传统的关系型数据库,在处理复杂数据关系运算上表现不好,随着数据量和深度的增长,关系型数据库没法在有效的时间内计算出结果。安全
因此,为了更好的体现数据间的链接,企业须要一种将关系信息存储为实体、灵活拓展数据模型的数据库技术,这项技术就是图数据库(Graph Database)。markdown
相比于传统关系型数据库,图数据库具备如下两个优势:网络
第一点,图数据库能很好地体现
数据之间的关联关系session
从上面的图模型能够看出,图数据库的目标就是基于图模型以一种直观的方式来展现这些关系,其基于事物关系的模型表达,使图数据库自然具备可解释性。数据结构
第二点,图数据库能很好地处理
数据之间的关联关系:架构
基于以上两个优势,图数据库在金融反欺诈、公安刑侦、社交网络、知识图谱、数据血缘、IT 资产及运维、威胁情报等领域有巨大需求。
而快手安全情报则是经过整合移动端、PC Web 端、云端、联盟及小程序等全链条的安全数据,最终造成统一的基础安全能力赋能公司业务。
因为安全情报自己具备数据实体多样性、关联关系复杂性、数据标签丰富性等特色,所以采用图数据库来作是最为合适的。
经过收集需求及前期调研,快手安全情报在图数据库上最终选择了Nebula Graph
做为生产环境的图数据库。
对于图数据库的选型来讲,其主要需求是在数据写入与数据查询两个方面:
综上所述,这次选型的适用于大数据架构的图数据库主要须要提供 3 种基本能力:实时和离线数据写入、在线图数据基本查询、基于图数据库的简单 OLAP 分析,其对应定位是:在线、高并发、低时延 OLTP 类图查询服务及简单 OLAP 类图查询能力。
基于以上的肯定性需求,在进行图数据库的选型上,咱们主要考虑了如下几点:
正是基于Nebula Graph
的以上特色以及对咱们使用场景和需求的刚好知足,所以最终选择Nebula Graph
做为咱们生产环境的图数据库来使用。
以下图所示,从情报的角度来看,安全的分层对抗与防守,从下到上,其对抗难度是逐渐增长的:
每个平面上,以前攻击方与防守方都是单独的对抗,如今利用图数据库以后,能够将每个层次的实体ID经过关联关系串联起来,造成一张立体层次的网,经过这张立体层次的网可以使企业快速掌握攻击者的攻击方式、做弊工具、团伙特征等较全貌的信息。
所以基于安全数据的图结构数据建模,能够将原来的平面识别层次变成立体网状识别层次,能帮助企业更清晰准确的识别攻击与风险。
安全情报的图建模主要目的是但愿判断任何一个维度风险的时候,不仅仅局限于该维度自己的状态与属性去看它的风险,而是将维度从个体扩展为网络层面,经过图结构的数据关系,经过上下层次(异构图)及同级层次(同构图)立体去观察该维度的风险。
以设备风险举例:对一个设备而言,总体分为网络层、设备层、帐号层和用户层这四个层面,每一个层面都由其表明性的实体 ID 来表达。经过图数据库,能够作到对一个设备进行立体的三维层次的风险认知,这对于风险的识别会很是有帮助。
如上图所示,这是安全情报的基本图结构建模,以上构成了一个基于安全情报的知识图谱。
在基本图结构之上,还须要考虑的是,每一种关联关系的存在都是有时效性的,A 时间段内关联关系存在,B 时间段内该关联关系则未必存在,所以咱们但愿安全情报能在图数据库上真实反映客观现实的这种不一样时间段内的关联关系。
这意味着须要随着查询时间区间的不一样,而呈现出不一样的图结构模型的数据,咱们称之为动态图结构
。
在动态图结构的设计上,涉及到的一个问题是:在被查询的区间上,什么样的边关系应该被返回?
如上图所示,当查询时间区间为 B、C、D 时,这条边应该要被返回,当查询时间区间为A、E时,这条边不该该被返回。
在面对黑灰产或者真人做恶时,每每会出现这种状况:就是一个设备上面会对应很是多的帐号,有些帐号是不法坏人本身的经常使用帐号,而有些帐号则是他们买来作特定不法直播的帐号。为配合公安或法务的打击,咱们须要从这批帐号里面精准区分出哪些帐号是真实坏人本身的经常使用帐号,而哪些帐号只是他们买来用于做恶的帐号。
所以这里面会涉及到帐号与设备关联关系边的权重问题:若是是该设备经常使用的帐号,那么代表这个帐号与这个设备的关系是较强的关系,则这条边的权重就会高;若是仅仅是做恶/开直播的时候才会使用的帐号,那么帐号与设备的关系则会比较弱,相应权重就会低一些。
所以咱们在边的属性上,除了有时间维度外,还增长了权重维度。
综上所述,最终在安全情报上所创建的图模型是:带权重的动态时区图结构
。
总体安全情报服务架构图以下所示:
安全情报服务总体架构图
其中,基于图数据库的情报综合查询平台,软件架构以下图所示:
情报综合查询平台软件架构图
注:AccessProxy 支持办公网到 IDC 的访问,kngx 支持 IDC 内的直接调用
针对所构建的关联关系数据,天天更新的量在数十亿级别,如何保证这数十亿级别的数据能在小时级内写入、感知数据异常且不丢失数据,这也是一项很是有挑战性的工做。
对这部分的优化主要是:失败重试、脏数据发现及导入失败报警策略。
数据导入过程当中会因为脏数据、服务端抖动、数据库进程挂掉、写入太快等各类因素致使写 batch 数据失败,咱们经过用同步 client API、多层级的重试机制及失败退出策略,解决了因为服务端抖动重启等状况形成的写失败或写 batch 不彻底成功等问题。
在图数据库部分,快手部署了在线与离线两套图数据库集群,两个集群的数据采用同步双写,在线集群承担在线 RPC 类的服务,离线集群承担 CASE 分析及 WEB 查询的服务,这两个集群互不影响。
同时集群的状态监控与动态配置下发模块是打通的,当某一个集群出现慢查询或发生故障时,经过动态配置下发模块来进行自动切换,作到上层业务无感知。
数据架构团队对开源版本的 Nebula Graph 进行了总体的调研、维护与改进。
Nebula 的集群采用计算存储分离的模式,从总体架构看,分为 Meta,Graph,Storage 三个角色,分别负责元数据管理,计算和存储:
Nebula 总体架构图
Nebula 的存储层做为图数据库引擎的底座,支持多种存储类型,咱们使用 Nebula 时选择了经典模式,即用经典的 C++ 实现的 RocksdDB 做为底层 KV 存储,并利用 Raft 算法解决一致性的问题,使整个集群支持水平动态扩容。
存储层架构图
咱们对存储层进行了充分的测试、代码改进与参数优化。其中包括:优化 Raft 心跳逻辑、改进 leader选举和 log offset 的逻辑以及对 Raft 参数进行调优等,来提高单集群的故障恢复时间;再结合客户端重试机制的优化,使得 Nebula 引擎在用户体验上从最初的故障直接掉线改善为故障毫秒级恢复。
在监控报警体系上,咱们构建了对集群多个层面的监控,其总体监控架构以下图所示:
集群监控架构图
包括以下几个方面:
因为现实图网络结构中点的出度每每符合幂律分布特征,图遍历遇到超级点(出度百万/千万)将致使数据库层面明显的慢查询,如何保证在线服务查询耗时的平稳性,避免极端耗时的发生是咱们须要解决的问题。
图遍历超级点问题在工程上的解决思路是:在业务可接受的前提下缩小查询规模。具体方法有:
下面分别描述具体的优化策略:
【前提条件】
业务层面可接受每一跳作 limit 截断,例如以下两个查询:
# 最终作limit截断
go from hash('x.x.x.x') over GID_IP REVERSELY where (GID_IP.update_time >= xxx and GID_IP.create_time <= xxx) yield GID_IP.create_time as create_time, GID_IP.update_time as update_time, $^.IP.ip as ip, $$.GID.gid | limit 100
# 在中间查询结果已经作了截断,而后再进行下一步
go from hash('x.x.x.x') over GID_IP REVERSELY where (GID_IP.update_time >= xxx and GID_IP.create_time <= xxx) yield GID_IP._dst as dst | limit 100 | go from $-.dst ..... | limit 100
复制代码
【优化前】
对第二个查询语句,在优化前,storage会遍历点的全部出度,graph 层在最终返回 client 前才作 limit n 的截断,这种没法避免大量耗时的操做。
另外 Nebula 虽然支持 storage 配置集群(进程)级别参数max_edge_returned_per_vertex
(每一个 vertex 扫描最大出度),但没法知足查询语句级别灵活指定 limit 而且对于多跳多点出度查询也没法作到语句级别精确限制。
【优化思路】
一跳 go 遍历查询分两步:
那么 go 多跳遍历中每跳的执行分两种状况:
而 step2 是耗时大头(查每一个 destVertex 属性即一次 rocksdb iterator,不命中 cache 状况下耗时 500us),对于出度大的点将「limit 截断」提早到 step2 以前是关键,另外 limit 能下推到 step1 storage 扫边出度阶段对于超级点也有较大的收益。
这里咱们总结下什么条件下能执行「limit 截断优化」及其收益:
表注释: N 表示 vertex 出度,n 表示 limit n,scan 表示扫边出度消耗,get 表示获取 vertex 属性的消耗
【测试效果】
对于以上 case1 和 case2 可执行「limit 截断优化」且收益明显,其中安全业务查询属于 case2,如下是在 3 台机器集群,单机单盘 900 GB 数据存储量上针对 case2 limit 100 作的测试结果(不命中 rocksdb cache 的条件):
以上测试结果代表,通过咱们的优化后,在图超级点查询耗时上,取得了很是优异的表现。
针对不能简单作「limit 截断优化」的场景,咱们能够采起「边采样优化」的方式来解决。在 Nebula 社区原生支持的“storage 进程级别可配置每一个 vertex 最大返回出度边和开启边采样功能”基础上,咱们优化后,能够进一步支持以下功能:
max_iter_edge_for_sample
数量的边而非扫全部边(默认)go
每跳出度采样功能enable_reservoir_sampling
”和“每一个 vertex 最大返回出度 max_edge_returned_per_vertex
”都支持 session 级别参数可配经过以上功能的支持,业务能够更灵活地调整查询采样比例,控制遍历查询规模,以达到在线服务的平滑性。
开源的 Nebula Graph 有本身的一套客户端,而如何将这套客户端与快手的工程相结合,这里咱们也作了一些相应的改造与优化。主要解决了下面两个问题:
针对固定关系的查询(写死 nGQL),前端根据返回结果,进行定制化的图形界面展现,以下图所示:
这里前端采用ECharts
的关系图,在前端的图结构数据加载及展现这里也作了一些优化。
问题一:关系图须要能展现每一个节点的详情信息,而 ECharts 提供的图里只能作简单的 value 值的展现。
解决方案:在原代码上进行改造,每一个节点添加点击事件,弹出模态框展现更多的详情信息。
问题二:关系图在点击事件触发后,图会有较长时间的转动,没法辨认点击了哪一个节点。
解决方案:获取初次渲染图形时每一个节点的窗口位置,在点击事件触发后,给每一个节点位置固定下来。
问题三:当图的节点众多时候,关系图展现的比较拥挤。
解决方案:开启鼠标缩放和评议漫游功能。
针对灵活关系的查询(灵活 nGQL),根据部署的Nebula Graph Studio
进行可视化的呈现,以下图所示:
基于以上图数据库的结构与优化,咱们提供了 Web 查询和 RPC 查询两种接入方式,主要支持了快手的以下业务:
例如,群控设备与正常设备在图数据上的表现存在明显区别:
对于群控设备的识别:
感谢开源社区Nebula Graph
对快手的支持。
交流图数据库技术?加入 Nebula 交流群请先填写下你的 Nebulae 名片,Nebula 小助手会拉你进群~~