赞
踩
看看这本书,里面还是有不少常识和启发的。
本篇会摘取一些里面比较好的内容,然后做一个简单但比较有用的应用。
Mysql这样的数据库,在需要多个join(递归)时表现会很差。在例如「商品推荐」这样的问题时就会碰到比较大的问题。
例如在库中寻找朋友关系
层数 | Mysql执行时间 | Neo4j | 返回记录数 |
---|---|---|---|
2 | 0.016 | 0.01 | ~2500 |
3 | 30.267 | 0.168 | ~11000 |
4 | 1543.505 | 1.359 | ~600000 |
5 | 未完成 | 2.132 | ~800000 |
可以看出来两种库的差别。
模型是为了让不规则的领域的一些具体方面变成结构化的,可操纵的空间。对于事物实际存在的方式,并没有一种天然的表达方式,我们只能有目的地选择、抽象和简化,一些方法能更好地满足特定的目标。
这个定义是我见过比较好的一种(现在的图像和语音等都是转为矩阵计算的)。和另一个异曲同工:
所有的模型都是错的,但有一些模型有用(可以用)。
关于图数据库的启发性:
图数据库减少了分析和实现之间的阻抗不匹配问题,这个问题已经困扰关系型数据库很多年了。特别有意思的一点是,图模型不仅描绘除了我们是如何看待事物时间的关系,还清晰地表达出了我们可能在这个领域出提出的问题。
关于动态系统的描述和安排,例如一个数据中心的部署,用图来表示是非常自然的(回头我们会继续说)
关系型数据库因为死板的模式以及复杂的建模特性,使得它对于快速变化的需求束手无策。我们想要的模型是一个可以和领域保持高度一致,不用牺牲性能,同时还能支撑业务的发展,也可以在快速的变化和增长之中保持数据的完整性的模型。
上面这段话的意义更大。如果有过系统的实践和应对甲方爸爸的需求,感受会更深刻。
如何验证一个图数据库是否构建好了?
1 最简单的方式是观察图是不是通俗易懂。找一个起始节点,沿着练习找他相邻的节点,同时读出每个节点的标签和每个联系的名字,看是否能连成一个有意义的句子。
2 我们还需要考虑什么样的查询会在这个图上运行。
平时怎么去应用图呢?
使用复杂事件处理(CEP)来应对底层需求,当重要领域更新时才更新我们的图。
图就像是大脑中枢,最终的处理一定是汇聚成一个有向、无环的,关系上统计归一的主图。(类似于主表、宽表)之前也讨论过py2neo和cypher处理关系的差别。py2neo默认会把同种关系的属性合并在一起,就是主图的概念;而cypher允许在两个节点间创建同种关系的多条边,类似日志图。
都有用,但我们在决策时肯定是根据主图(一个或多个)进行建模和决策的。
商业洞察力往往依赖于我们队复杂价值链背后的网络效应的理解。为了到达一定程度的理解,我们需要联合多个领域,同时又不能让每个领域的细节失真或者牺牲掉。属性图为这种情况提供了解决方案。使用属性图,我们可以给一个价值链建模,使其成为一个图的集合,每张图里都由具体的联系关联其子领域,又能将其区别开来。
不仅仅是商业,真正的知识图谱也有赖与如上所述的特点。
关系是图数据库里的一等公民,标签是属性里的一等公民。关系反映了变化,标签表示了集合(分类)
图的查询(先明确要查的是什么,节点,还是关系):
信心交流模式Fenix是一个经典的图问题,它涉及用图去发现领域专家、关键影响力以及信息传播的通信通道。
反过来,也可以用于找坏人。
建立图模型最关键的一步是区分哪些被抽象为实体(对应的关系也就顺理成章了)。我认为,凡是可以“持久化”的就是实体,而影响“持久化”的则是关系。例如文中提到发现垃圾邮件问题,邮件(虽然不是实物),但是从数据上是一种可持久化的虚拟实体,而不是被归为“发邮件”这样的动作中。当然,视具体的问题而定,图的抽象可能也会不同,不过先建立一个小的原则更有助于思考。
文中的建议是:
常用的名字可以作为标签。(点)
带有宾语的动词作为联系名称。(边)
这两条建议很对,顺带的想起了我的另一套东西( 修饰+名字-> 泛名词;修饰+动词 -> 泛动词),以后有机会再写。
用节点表示事物,用联系表示结构
- 1 使用节点表示实体,即我们感兴趣的事物,他们可以被标签和分组。
- 2 使用联系表示节点之间的关联,并为每个实体建立语义上下文,从而构建领域。
- 3 使用有向联系进一步阐明语义联系。属性图中常常存在有向联系,其原因是练习的不对称性。对于双向连接,我们通过在查询中忽略方向,而非使用两个练习。
- 4 使用节点属性以及任何有必要的实体元数据,如时间戳、版本号等,来表示实体的属性
- 5 使用联系属性以及必要的关系元数据,如果时间戳、版本号,来表达联系的强度、权重或质量
这些概念对应了图的一些创建概念,可以参考我的另一篇文章。
当两个或多个领域实体在一段时间内交互,事实(Fact)就出现了。依据结果,即行为(Action)产生的事物用来建模行为。
关系除了有起始时间,可能还可以设置终止时间。
实体之间发生关系,可能生成新的实体。(道生一,一生二,二生三,三生万物)例如A和B在一起洽谈,生成了一个新的实体(合同)。
两种常用的图结构:
- 1 时间轴树
- 2 链表
1 时间轴树可以反映事件随时间的变化。顺着时间轴“翻书”是一件很正常的事情,可以做例如日程安排和用户每日上传文件的分类存放。
2 链表反映事物的时序变化。 这个结构对于很多工艺流程比较长的工作来说非常有用,例如建模过程。
从数据到形成模型的过程非常复杂,最大的难度是“深”。例如之前提到的FuncChain(函数链)就很适合用Neo4j来实现。
虽然可以不基于任何数据库来完成FuncChain, 但是实际使用可能会有障碍。
1 使用者很难看到全貌。如果不基于Neo4j,那么整个流程链条(可能有很多)很难看清楚。当然我们也可以自己构建web来展示,但总之不适合在终端中使用文字查看。Neo4j自己提供了一个web端,所以初期我觉得没必要自己折腾(目前我看到自定义的图显示做的都不如neo4j美观)
2 持续性工作很难。因为不方便看到内容,要继续之前的工作要自己重新load所有的数据要花不少时间,这导致了往前继续工作很难。
3 汇总分析很难。假设整个环境是一个多用户多任务的系统,那么分析哪些功能/数据用的多,哪些函数用的多,哪里容易出问题是很难被统计的。
如果程序结合Neo4j,这个过程可能可以变的非常有趣。(如果把关系理解为变化的话,那么变化就是图数据库的一等公民,而建模过程恰恰是要处理多种变化)
一个简单的应用过程:
节点名称 | 节点类型 | 作用 |
---|---|---|
F1 | 函数节点 | 根据路径地址,读入Excel,变为DataFrame,然后存为Pickle |
D1 | 数据节点 | 里面只有原始字符串,文件路径 |
最终的D2也是一个数据节点,可以增加标签 Pkl。通过大类的标签和小类的标签,可以让节点的性质一目了然。
如果函数本身也是通过串行生成的,那么有可能是这样的
按照一个标准的python函数包的组织方式,通过from … import xx 很容易建立上述的函数依赖关系。
通过这样的网络,我们可以实现数据节点和和函数节点的初始化,并可以不断进行变化。需要做的是把python程序和neo4j的数据库融合起来:
[ (function1 ,variable1) ,((function1 ,variable1), ...]
可以想象,在实际使用的时候,很多操作会与数据库交互。观察操作的结果既会观察数据结果,也会观察结构变化。
目前图数据库已经在六个领域的应用中取得了成功(有待验证,应该可行):
- 1 社交
- 2 推荐
- 3 地理空间
- 4 主数据管理
- 5 网络和数据中心管理
- 6 授权和访问控制
首先至少这几个领域的确是传统结构化库比较难搞的,从图的角度来看也比较make sense, 而且实际上已经有不少公司已经利用图做到了一些事情。特别的:
下面就以表达和构建自有算网的结构来尝试一下图的方式。
目标有三点:
实现的是我可以通过portal看到所有设备的物理载荷和逻辑载荷,并可以直接在web上方便的调节。最终整个的部署和调节会通过算法进行运筹优化,自动配置合适的冗余,提高服务效率。
我梳理了一下关系(关系是一等公民):
在关系中,不宜存节点的属性,最主要的元素应该是有4个。
还有节点的状态在实验里暂时没有更新,例如prj1部署在了m2上,但是可以有一个状态(节点属性)表示prj1此时是启动的还是停止的。
通过cypher查询就可以很快知道当前网络的资源配置及使用情况。(这里的图本质上是主图,即两个节点的同类关系只会有一条边,边的属性可以变动)
真正在生产使用时可以使用mongo记录日志,同步地使用最新的记录更新neo4j的主图。
注意:节点的ID和标签,关系的ID和标签不允许有任何空格,最好按abc123的方式存储。
以下是实现的代码:
unwind_rel_base.j2
with [ {{for_start}} {{loop_if}} { {{if_attr_list}} } {{for_end}} ] as data UNWIND data as row merge (n:{{from_node_label}}{ {{node_id_var}}:row.{{from_node_id_var}}}) merge (n1:{{to_node_label}}{ {{node_id_var}}:row.{{to_node_id_var}}}) create unique (n)-[r:{{reltype}}]->(n1) set {{set_attr_list}} return r.{{rel_id_var}}
test3.py(操作的函数没有进行进一步封装,所以代码比较冗长)
# https: // www.dazhuanlan.com/2019/12/09/5dee0b4e3cf23/ import copy import DataManipulation as dm import json import pandas as pd import numpy as np import time from py2neo import Graph, Node, Relationship, Subgraph, NodeMatcher, RelationshipMatcher # 使用文本字符串直接生成结果 from jinja2 import Template env = 'prod' if env.lower() == 'local': url = "http://127.0.0.1:7474" pwd = '111' else: # 腾讯云 url = "http://111.111.111.111:7474" pwd = '111' graph = Graph(url, username="neo4j", password=pwd) # j2_str_dict j2_str_dict = {} # 字符型变量if模板 j2_str_dict['str_if_template_obj'] = '''{%% if obj['%s'] %%} %s%s:'{{obj['%s']}}' {%% endif %%}''' # 数值型变量if模板 j2_str_dict['num_if_template_obj'] = '''{%% if obj['%s'] %%} %s%s:{{obj['%s']}} {%% endif %%}''' # 是用关系模板 unwind_rel_base.j2 j2_str_dict['loop_if'] = '''{%if not loop.first%} , {%endif%}''' j2_str_dict['for_start'] = '''{% for obj in data_list %} ''' j2_str_dict['for_end'] = '{% endfor %}' def j2_module_if_str_attr_list(str_attr_template, str_attr_list): _if_attr_list = '' for i, v in enumerate(str_attr_list): if i == 0: tems = str_attr_template % (v, '', v, v) else: tems = str_attr_template % (v, ',', v, v) _if_attr_list += tems return _if_attr_list def j2_module_if_num_attr_list(num_attr_template, num_attr_list): _if_attr_list = '' for i, v in enumerate(num_attr_list): if i == 0: tems = num_attr_template % (v, '', v, v) else: tems = num_attr_template % (v, ',', v, v) _if_attr_list += tems return _if_attr_list def j2_module_set_attr_list(attr_list): set_attr_list = '' for i, attr in enumerate(attr_list): if i == 0: tems = 'r.{0}=row.{0}'.format(attr) else: tems = ',r.{0}=row.{0}'.format(attr) set_attr_list += tems return set_attr_list # 如果声明了属性列表,第一个不得为空(数值型的还不允许为0) rel_template = { 'searchpath': './', 'template_name': 'unwind_rel_base.j2', 'if_attr_list': None, 'set_attr_list': None, 'loop_if': j2_str_dict['loop_if'], 'for_start': j2_str_dict['for_start'], 'for_end': j2_str_dict['for_end'], 'from_node_label': 'Machine', 'to_node_label': 'Machine', 'reltype': 'Frp_to', 'node_id_var': 'eid', 'from_node_id_var': 'from', 'to_node_id_var': 'to', 'rel_id_var': 'rid' } def j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list=None, rel_template=rel_template): if num_attr_list is None: if_attr_list = j2_module_if_str_attr_list( j2_str_dict['str_if_template_obj'], str_attr_list) else: if_attr_list = j2_module_if_str_attr_list( j2_str_dict['str_if_template_obj'], str_attr_list) + ',' + j2_module_if_num_attr_list(j2_str_dict['num_if_template_obj'], num_attr_list) set_attr_list = j2_module_set_attr_list(set_rel_attrs_list) temp_dict = copy.deepcopy(rel_template) temp_dict['if_attr_list'] = if_attr_list temp_dict['set_attr_list'] = set_attr_list template_s = dm.gen_by_j2(**temp_dict) template = Template(template_s) the_cypher = template.render(data_list=data_list) # print(the_cypher) return graph.run(the_cypher).data() rel1_df = pd.read_excel('主机间连接关系.xlsx', skiprows=1) rel1_df = rel1_df.dropna(subset=['rid']) rel1_df['from'] = rel1_df['from'].apply(lambda x:x.strip()) rel1_df['to'] = rel1_df['to'].apply(lambda x: x.strip()) rel1_df['rid'] = rel1_df['rid'].apply(lambda x: x.strip()) # 1 第一种关系 rel_frp_df = rel1_df[rel1_df.reltype == 'FrpTo'] rel_frp_dict = json.loads( rel_frp_df[['from', 'to', 'rid', 'create_time']].fillna(1).to_json(orient='index')) data_list = list(rel_frp_dict.values()) str_attr_list = ['from', 'to', 'rid', 'reltype'] num_attr_list = ['create_time'] set_rel_attrs_list = ['rid', 'create_time'] j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list) # 2 第二种关系 rel_frp_df = rel1_df[rel1_df.reltype == 'SSHTo'] rel_frp_dict = json.loads( rel_frp_df[['from', 'to', 'rid', 'create_time']].fillna(1).to_json(orient='index')) data_list = list(rel_frp_dict.values()) str_attr_list = ['from', 'to', 'rid', 'reltype'] num_attr_list = ['create_time'] set_rel_attrs_list = ['rid', 'create_time'] rel_template['reltype'] = 'SSHTo' j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list, rel_template=rel_template) # 3 第三种关系 rel_frp_df = rel1_df[rel1_df.reltype == 'ImplementedTo'] rel_frp_dict = json.loads(rel_frp_df[['from', 'to', 'rid', 'create_time']].fillna(1).to_json(orient='index')) data_list = list(rel_frp_dict.values()) str_attr_list = ['from', 'to', 'rid', 'reltype'] num_attr_list = ['create_time'] set_rel_attrs_list = ['rid', 'create_time'] rel_template['reltype'] = 'ImplementedTo' rel_template['from_node_label'] = 'DCApp' j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list, rel_template=rel_template) # 4 第四种关系 rel_frp_df = rel1_df[rel1_df.reltype == 'BelongsTo'] rel_frp_dict = json.loads(rel_frp_df[['from', 'to', 'rid', 'create_time']].fillna(1).to_json(orient='index')) data_list = list(rel_frp_dict.values()) str_attr_list = ['from', 'to', 'rid', 'reltype'] num_attr_list = ['create_time'] set_rel_attrs_list = ['rid', 'create_time'] rel_template['reltype'] = 'BelongsTo' rel_template['from_node_label'] = 'Port' rel_template['to_node_label'] = 'Machine' j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list, rel_template=rel_template) # 5 第五种关系 rel_frp_df = rel1_df[rel1_df.reltype == 'VisitTo'] rel_frp_dict = json.loads(rel_frp_df[['from', 'to', 'rid', 'create_time']].fillna(1).to_json(orient='index')) data_list = list(rel_frp_dict.values()) str_attr_list = ['from', 'to', 'rid', 'reltype'] num_attr_list = ['create_time'] set_rel_attrs_list = ['rid', 'create_time'] rel_template['reltype'] = 'VisitTo' rel_template['from_node_label'] = 'User' rel_template['to_node_label'] = 'Port' j2_module_save_rel_list(data_list, str_attr_list, set_rel_attrs_list, num_attr_list, rel_template=rel_template)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。