我最喜欢的一部电影《危情谍战》中有这样一句台词,“Things Happen for A Reason”,不知道为何,直到今天我对这句台词的印象都很深入,也许是当时看到这句话的时候脑海里灵光一现,或是引发共鸣才会烙下这么深入的印象吧。几乎从那之后,不管是作事仍是进行思考,我都会天然而然的养成从底层逻辑逐步的来挖掘和分析事物的核心原理。html
好比说最近在作产品的技术架构设计,其中几乎有一大半时间都是在梳理业务流程和需求,将业务进行分类处理,再作相关的技术调研。其中 Neo4j 算是技术方案中的核心角色之一,这篇 post 也算是作这个架构设计中关于 Neo4j 这个 NoSQL 数据库的一些感想。java
如何快速的了解 Neo4j 呢,网上有不少很长的文字,不过我以为都没有简单明确的讲清楚。我想了想,如何让你们最快最高效的理解 Neo4j,最好的办法就是下面总结的几句话:node
图数据库有不少,在这个 wiki 上有一个比较完整的列表:List of graph databases,其中有的彷佛已经没有维护了,有的呢是传统的 NoSQL 数据库,可是提供图数据结构存储,有的也像 Neo4j 同样是专用的图数据库,可是即便是专门的图数据库,其底层实现,应用场景和生态也是有天差地别的。mysql
今天咱们着重看看 Neo4j,我们主要从下面几个方面来学习和深刻理解 Neo4j。redis
前面咱们讲过,如今其实已经有不少图数据库了,其中有一些 document 类 NoSQL 数据库(假设叫 A DB),也支持图数据结构的存储和检索,那这类数据库和 Neo4j 最大的区别,就是接下来咱们要说的 Native 和 non-Native 图数据库了。简单来讲,Neo4j 是 Native 的图数据库,A DB 属于 non-Native 图数据库了。算法
所谓 Native 图数据库,中文通常翻译为 “原生图数据库”,指从一开始即是为了解决图类数据结构而设计的数据库。对了解数据结构的同窗来看,这里的 “原生” 的体现,其实主要就在两个方面:sql
咱们也从这两个点来聊聊 Neo4j。数据库
图数据结构的存储编程
无论是什么数据库,mysql,sql server,mongo,neo4j 等等,其存储的数据,最终都要落地到文件系统上的,那 neo4j 落地的文件大概都是什么样的呢?这里我以本地的一个应用的数据库来介绍一下。缓存
上面是个人本地一个图数据库的数据库文件,能够看出 Neo4j 把其数据库文件分为四大类来分类存储:
其实按照咱们图论中的通常说法,其实对于一个图数据结构来讲,只须要存储节点和关系就能够了,以下面这个图数据同样:
可是现实生活中,咱们的图结构是很是复杂的,并且数据量很大,常常须要作各类各样的分析,甚至还须要作聚类分析(这里的聚类分析不是指机器学习里面的聚类模型,而是指把同一类数据提取出来单独作分析,相似于 groupby 的操做)。因此 Neo4j 里面在文件系统上单独存储了标签和属性,就是为了在作检索和分析的时候保证性能。下面我们以一个形象的例子来介绍什么是标签,什么是属性。
这里是咱们应用中一个实际案例,你们看看咱们对标签和属性的划分:
-- 这里我们先看看这个语句表达的什么意思,这是 Neo4j 专用的查询语言 Cypher
-- 你们不要听到又要学一门新语言就打退堂鼓哈,哈哈哈,后面咱们会简单讲讲这个语言的,两个字:简单
-- 下面这个查询语句含义以下:
-- step 1: 首先查询一个具备标签为 “沪深股市”,且有一个属性对 “{name: "sha_600610"}” 的节点,
-- step 2: 而后查找这个节点的全部双向关系
-- step 3: 在这些关系的中,要求另一个节点具备 “人物_公司股东” 这个标签
-- step 4: 返回这个节点,以及知足要求的关系和相关节点
MATCH (node1:沪深股市 {name: "sha_600610"})-[relation]-(node2:人物_公司股东)
RETURN node1, node2, relation
复制代码
有人可能会问,其实也能够不用单独存储标签和属性的呀,咱们彻底能够把标签和属性做为节点的元信息存储在节点里面。这个说法首先在理论和工程实现上来讲彻底没有问题。可是试想一下,一个节点和关系很是很是大的图,要作上面这个检索的时候,会不会面临一些问题?而 Neo4j 的这种存储设计,是如何来解决这些问题的呢?这两个问题留给你们思考,能够在评论里交流你们的想法。
图数据的处理和查询
图数据的处理,同传统数据库同样的四字法则:CURD。而由于图数据的特殊性,在大多数状况下,数据库中每个节点都有与之相连的关系,每一条关系,都必须有这个关系对接的两个节点。这就要求图数据库的建立,更新,读取,删除都必须知足一致性(或者事务完整)的原则。Neo4j 如何实现 ACID 的底层算法我尚未看,感兴趣的能够先看看这个 talk: Evolution of Neo4j with ACID transactions, HA cluster, and CRUD transactions。
数据库的查询,这个无论是什么数据库,都是最基础的功能,对于 sql 和 nosql 来讲,简单的查询其实并无多大差异,只是一些复杂查询或者针对特定场景的查询条件下才会特意的选择某一种数据库。好比最多见的就是如今的大多数 WEB 应用,在存储上基本上都会涉及下面几类数据库:
可是在实际应用中,会常常性出现一些很复杂的查询语句,好比下面几种例子:
在 APP 应用中,咱们想要知道昨天新增的用户中,有多少今天是继续登录 APP 的,这种查询扩展开来,就是作 APP 用户增加中耳熟能详的 cohort analysis,下面有截图展现这种分析的图表
人际关系网络中,给定两个节点,查询这两个节点的最短路径,而且这些路径知足必定的条件?这样说是否是有点书面化,那咱们换一种说法,在社交网络中,若是你想认识特朗普的女儿伊万卡,查询一条最短路径让你结识到伊万卡,而且这个路径中每一个人都是单身的。相信我,这是完彻底全可能的,并且无论你是谁,极可能经过6~7我的就能认识伊万卡了,amazing。不信的同窗能够去了解了解六度分隔理论这个东西。
在上面这些复杂查询中,特别是第二种状况,用传统的关系型数据库实现起来实在是不切实际。不光是性能上的缘由,还有开发人员可用性的考虑,试想若是这样子一个查询,用 sql 来写的话,这个 sql 语句应该怎样写?下面是一个案例,展现一个查询用 neo4j 和 sql 下的对比。
上面咱们讲了 Neo4j 的数据存储大概分为:节点,关系,属性,标签这四个类别,这是 Neo4j 存储模块的工程实现,而如今咱们要讲的 Property Graph Model 就是这个工程实现的理论来源。正若有的朋友会说,其实简单的存储节点和关系,而后其余标签,属性什么的均可以算作节点和关系的元信息来存储就好了。工程上实现的确也能够,可是理论上去研究,会发现这样的效率会很低。
Property Graph Model,简单的说,其实就是 Neo4j 的底层数据模型,这个数据模型推进了工程上按照四个方面来实现存储模型。那什么叫 Property Graph Model 呢,其实从核心上来说,Property Graph Model 有两个核心要素:
下面是一个 Property Graph Model 的展现:
每种数据库都有本身的一套查询语言或者标准,就算是 SQL 中的王者 Sql Server 和 MySQL,其在一些语法细节上也有差别,更别说 Mongo, Redis 相似的非关系型数据库了。Neo4j 也同样,有本身专属的查询语言 Cypher。有的朋友听到若是用 Neo4j 又要学习一门新的编程语言就很头疼,其实这里能够分享下我本身的经历,之前我刚接触 Neo4j 的时候,确实以为又要学习一门新语言有些蛋疼,但是后来接触下来,我以为可能普通人估计只用花一两天就能掌握 Cypher 了,由于 Cypher 的语意确实简洁,直观。
好比下面咱们直接经过代码来对比下 MySQL 和 Neo4j 里面的查询,我相信即便不写任何注释,即便第一次接触 Neo4j 的人也能轻轻松松的看懂这些查询语句。
<!-- 1. 全表扫描 -->
<!-- mysql -->
SELECT p.*
FROM products as p;
<!-- neo4j -->
MATCH (p:Product)
RETURN p;
<!-- 2. 查询价格最贵的10个商品,只返回商品名字和单价 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products as p
ORDER BY p.UnitPrice DESC
LIMIT 10;
<!-- neo4j -->
MATCH (p:Product)
RETURN p.productName, p.unitPrice
ORDER BY p.unitPrice DESC
LIMIT 10;
<!-- 3. 按照商品名字筛选 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName = 'Chocolade';
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName = "Chocolade"
RETURN p.productName, p.unitPrice;
<!-- 其余的写法 -->
MATCH (p:Product {productName:"Chocolade"})
RETURN p.productName, p.unitPrice;
<!-- 4. 按照商品名字筛选2 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products as p
WHERE p.ProductName IN ('Chocolade','Chai');
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName IN ['Chocolade','Chai']
RETURN p.productName, p.unitPrice;
<!-- 5. 模糊查询和数值过滤 -->
<!-- mysql -->
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName LIKE 'C%' AND p.UnitPrice > 100;
<!-- neo4j -->
MATCH (p:Product)
WHERE p.productName STARTS WITH "C" AND p.unitPrice > 100
RETURN p.productName, p.unitPrice;
<!-- 6. 多表联合查询-->
<!-- mysql -->
SELECT DISTINCT c.CompanyName
FROM customers AS c
JOIN orders AS o ON (c.CustomerID = o.CustomerID)
JOIN order_details AS od ON (o.OrderID = od.OrderID)
JOIN products AS p ON (od.ProductID = p.ProductID)
WHERE p.ProductName = 'Chocolade';
<!-- neo4j -->
MATCH (p:Product {productName:"Chocolade"})<-[:PRODUCT]-(:Order)<-[:PURCHASED]-(c:Customer)
RETURN distinct c.companyName;
<!-- 7. -->
<!-- mysql -->
SELECT e.EmployeeID, count(*) AS Count
FROM Employee AS e
JOIN Order AS o ON (o.EmployeeID = e.EmployeeID)
GROUP BY e.EmployeeID
ORDER BY Count DESC LIMIT 10;
<!-- neo4j -->
MATCH (:Order)<-[:SOLD]-(e:Employee)
RETURN e.name, count(*) AS cnt
ORDER BY cnt DESC LIMIT 10
复制代码
随着应用的复杂化,技术的专业化,如今愈来愈难全面准确的评论一个框架,平台甚至产品的好坏了。不过在我自身的经验里,我以为一个好的框架,确定是有一个完善的周边生态的。这也是我常常说,在比较常见的开源框架里面,neo4j 和 spark 是我以为作得比较出色的,同时也是我最看好的两个开源团队。其一是他们作的不少事情都是从用户角度来考虑的,而不只仅是单纯从工程角度来思考,特别是 spark 团队,还成立了他们本身的平台 databricks 提供给大众使用;其二是这两个开源产品的周边生态都很丰富,体如今两个方面,和流行框架产品的融合度,以及本身周边开发工具的丰富程度。
好比说 Neo4j,官网有专门介绍其生态系统的地方,感兴趣的盆友们请移步这里:Neo4j Ecosystem。在这么多开发工具里面,最让我惊叹的是 Neo4j browser 和今年上半年发布的 neo4j bloom。
neo4j browser
neo4j browser 有点相似于 mysql 里面的 workbench 或者 mongo 里面的 RoboMongo,说白了就是一个数据库的客户端,可是 neo4j browser 作得很是友好,甚至看上去,用起来就像是一个成熟的产品同样。记得好久好久之前我在跟团队分享 neo4j 的时候,团队里面对 neo4j 不熟悉的人还问我这个哪一个产品,交互作得很不错。amazing!
neo4j bloom
提及 neo4j bloom,到如今我还忍不住想称赞 neo4j 团队,虽然 bloom 不是实现了什么特别 NB 的算法,也没有什么伟大的创新,可是这个产品倒是实打实的站在用户层面去考虑,一会儿就把 neo4j 的可用性,宜用性,实用性提升了好几个档次了。
谈起 bloom,还得说咱们准备作的知识图谱产品开展开了,咱们作的每个图谱,都有两个亮点的地方:
其中第一个亮点,咱们以前作了不少调研,发现不少传统的图谱产品在内容更新和修改上都作得很复杂很很差用,好比说有的图谱更新是后台提供一个 excel 表格给相关专业人员去填写这个表格;有的产品呢是有专门作的一个内容管理后台,学会用那个管理后台还得话很多时间。最重要的是,若是以这种模式提供给专家去修改内容,首先咱们还得让专家理解从 excel模版或者内容管理后台 到图谱内容结构这样一个过程的思惟转变,要否则极有可能出现内容修改错误的状况。
因此,咱们一直想作一个容许特定帐号直接在图谱上进行修改的产品,好比说给专家的帐号开通修改权限,专家能够和普通用户打开一样的产品页面,看到一样的产品内容,只是专家帐号在点击图谱节点时,能够有一个修改节点及其关系的权限,作到实时修改,这样子极大的提升了专家操做的效率,并且还节省了很多前台,后台开发的工程量。
正当咱们定下这个方案不久,我就看到了 neo4j 发布的关于 bloom 的文章和视频,真的大为惊叹,当时我就直接把文章和视频转发到团队的微信群里,neo4j 开发的 bloom 和咱们设想的产品模式几乎一致,它直接想把咱们作的事完成了,并且还作得漂亮,光说 bloom 的那个交互,就已经把国内很多作图谱的产品甩开几条街了。
感兴趣的盆友能够先看看这个视频体验体验:Neo4j Bloom: Investigating Patterns in Financial Transactions
伴随 2012 年 google 正式发布知识图谱搜索引擎和 2013 年 facebook 开放知识图谱搜索入口以来,知识图谱迎来了一波发展浪潮,neo4j 做为原生的图数据库,也迎来了它的春天。可是就业内朋友交流来看,知识图谱类创业公司和产品数量上并很少,质量上也缺少重磅产品,除了传统的社交网络方面(这方面有 facebook 的关系搜索和 linkedin 的人脉搜索)。究其缘由,其实有两个比较大的缘由,一个是熟悉图数据库,了解知识图谱的人才还比较少;另一个缘由是业务的抽象化自己门槛就比较高。
葛优在《天下无贼》中有一句话特别出名 “21世纪什么最贵?人才“。咱们在业内交流下来,其实也发现一个比较值得思考的问题,就是虽然知识图谱发展也有六七年的样子,但其实真正在图数据库方面去研究的人才不多。一样是 NoSQL 数据库,为何常常能够听到关于 mongo,redis 等数据库技术的讨论,却不多见到 neo4j 的技术问答呢?咱们以为,其中的缘由多是由于 mongo,redis 这类数据库相对于 neo4j 来讲,是比较 general 的技术,就是说 mongo,redis 在各类应用中的大多数场景中均可以用到,它们并不依赖某一种特定类型的应用。而 neo4j 则有所不一样,neo4j 比较专一在图相关的数据结构的存储和查询方面,并且 neo4j 自己还须要使用特定的查询语言 (cypher,咱们前面也有讲过),一些企业和工程师就缺少动力去使用这门新的技术。因此从这两个点来讲,neo4j 的辨识度,流行度远远不及 mongo,redis 类的 nosql。下面是我查询了 google trend 自 2008 年来这三个关键词的表现,可见一斑。
不过若是只看 neo4j 的 google trend 数据的话,它仍是慢慢在走向人们的视野。
而说到知识图谱,其实国内外在这方面的成熟产品能够说百里挑一,出去上面咱们说的技术方面的人才储备问题外,其实最大的仍是自己业务的抽象化门槛过高。好比说,在金融领域有很多创业公司尝试过用知识图谱来描述和分析一个公司,我们来看看下面这个案例截图:
上面这个金融知识图谱,眨眼一看,彷佛信息还挺齐全,但是若是站在一个金融专业投资者角度来看,问题就暴露不少了:
还有其余不少的点就不一一列出了,列这么多其实只是想说,在知识图谱这个领域,无论作什么产品都要于业务自己紧密相连,如今是一个论专业化,比专业性的时代,不能简简单单的把一个技术套到一个产品上就叫创新了,而是要经过技术把以前难以实现的具备价值的事情作成作好。
若是要用几句话来总结这篇 post 里我想表达的意思,我想应该是下面几句: