DDIA 第二章:数据模型和查询语言(20231017~20231030 )

DDIA 学习会青藤木鸟2023-10-16 16:1

材料

中文参考:https://ddia.qtmuniao.com/#/ch02

b 站之前分享视频:

Youtube:

答疑

评论区打卡答疑:

  1. 第二章进度打卡。形式:YYYYMMDD-章节标题

  2. 第二章问题提问:形式:章节标题-问题。

在线答疑两次,时间定在:

  1. 20231023 周一,北京时间晚九点到十点:音频回放戳这里

  2. 20231030 周一,北京时间晚九点到十点:音频回放戳这里

答疑链接我到时候会提前放出来,可以关注本文或者群里通知。

微信答疑。其他零散问题随时在《DDIA 读书会》群里问,我每天会集中回答,我不在的时候大家自由讨论。

答疑问题整理

只整理了一个大概,更多可以去听音频原文。

https://www.xiaoyuzhoufm.com/episodes/6537737e75608281a9e8ae93

https://www.xiaoyuzhoufm.com/episodes/653fbe169cb67569a2f0e40a

1. 在使用图建模的时候,如何高效地找到入边和出边?

在分布式图数据库中,可以把点与其出边存在一个 Partition 中。但为了高效的找到每个点的入边,通常也会在一个 Partition 中将入边再存一次。但当然,这样一来,每条边就冗余的存了两次。

在单机基于外存(磁盘,SSD)的图数据库中,通常会点和边分成两个文件存。以 Neo4j 为例,会通过双向指针将一个点的所有入边和出边都串起来。

逻辑上的一个无向图:

物理上会存成点文件、边文件和属性文件。下图是上面逻辑图边的物理存储,其核心在于将所有共点的边使用双向链表串起来,然后在点表里存储指向其所有邻接边的组成的双向链表的头尾。

在基于内存的图数据库中,就更好说了,比如常见的邻接表和邻接矩阵。以及基于邻接表的压缩表示方法:

这里解释下上图中第二种方法 CSR,这是一种针对不可变图的邻接表(上图中第三中方法)的压缩表示方法。基本原理是使用一个数组将所有出边代表的点拍平到一个数组中,然后用另外一个数组通过存下标的方式存储每个点出边列表的界限。

小结一下,就是:

  1. 使用某种方式将点和其出入边的入口建立联系

  2. 使用某种方式将一个点的所有出边和入边串起来

2. 我们的数据有很多冗余之后,有什么好的一致性保证的办法?

多副本间的一致性,工业界常使用共识协议(比如 Raft 或者 Paxos)来解决。

如果想让延迟更低一些、实现更简单一些,就需要适当放松一致性,这个后面章节也会讲。比如使用 Quorum,甚至更简单的主从复制。

  1. 现在比较火的向量数据库属于第二章的哪个模型?

第二章应该是没有讲向量模型。这个主要用在机器学习中应用,突出一个基于向量内积的“近似性”搜索。

基本思想是,首先会把所有的数据转化为 embeding 存到向量数据库中,然后查询到来时,会也先讲查询转化为 embeding,然后在向量池中进行搜索。

向量数据库所做的事情,就是合理的组织这些 embeding ,以支持快速的最相似的 TopN 搜索。并且,将 embeding 关联上其原本的一些属性,可以在搜索匹配上之后,把这些原来的属性返回。

由于在组织 embeding 的时候(hnsw算法),有时候会用图的方式进行组织,所以和图数据库有一些关联。

  1. 分布式数据库中的 CAP 怎么理解?

可以先看看这篇文章:https://www.qtmuniao.com/2020/02/16/not-cp-or-ap/

  1. 前面提到了一个分区间的一致性是什么意思?

主要是指分布式事务所需要保证的跨分区间数据的一致性,比如银行转账,一个分区的用户转出去了之后,另一个分区的用户一定要收到。不能因为实现问题出现钱凭空消失的情况。

另一个角度来理解,副本间的一致性是数据冗余带来的,分区间的一致性是跨对象操作带来的。前者是分布式实现时引入的,不能为用户可见。而后者是数据库一般都会提供的——事务——将一组对象的一组操作进行打包的能力。

  1. 分布式环境中,服务端会给客户端提供一些接口,如果某些请求出现异常或者耗时较长,阻塞其他请求怎么解决?

单机的话,多线程是必不可少的。可以用一个线程来分发请求到线程池中,前者只负责分发,后者负责处理,有点 IO 多路复用的感觉。这样一个请求阻塞暂时不会影响其他线程,多了几个并发度。

另一个方向是,可以通过增加超时判断异常检测手段来把某些异常请求 kill 掉。也可以引入优先级机制,如果这些异常请求优先级不太高,可以把他们干掉,让他们重试。

  1. 面向对象和关系模型的不匹配是指?

是内存中的对象天然是嵌套的——一个对象可以作为另外一个对象的字段,这种嵌套方式和 Json 很像,但和关系模型不像。因为在 SQL 中,通常是每个表排平的,然后通过额外关系表+外键的方式来表达这种包含关系(一对多)——但表达的并不好。

换句话说,对于这种一对多的包含关系,关系型数据库通常表达的不是自然。

  1. 网络模型是一个什么样的模型,为什么被抛弃?

与现在的图模型相比,网络模型更加底层,不提供想 OpenCypher 这种声明式的查询语言。你需要记住你存的数据的结构,你才能在网络模型中进行遍历,有点像过程式。

  1. 第二章有没有讲可以用于 metric 监控场景的数据模型?

第二章主要讲了关系模型、文档模型和图模型。对于其他诸多数据模型都没有怎么涉及。包括 KV 模型、对象模型、时序模型、文件模型。其中指标监控场景就是时序模型或者流模型的典型应用场景,第十一章流处理有一些相关。

微服务时代、物联网时代,确实对监控需求比较大,因此流和时序其实也是提的比较多的。

另外,在大模型时代,专门针对 AI System 使用什么样的方式对数据进行建模呢?现在大家都也还在探索。相关的有向量数据库。

10. 图模型的一些应用场景?

NLP:知识图谱

银行:风控,反欺诈

互联网:推荐(协同过滤)

11. 文档存储如果只改其中一个字段,需要修改整个文档吗?

这取决于实现,类似关系数据库中的行存和列存。最基本的实现应该按 Document 粒度编码存在一块的,因此即使改一个字段,也需要读取整个文档。

  1. 书中提到有一些需求驱使着 NoSQL 的诞生,包括对高写入吞吐的需求;相对关系型数据库,NoSQL 为什么能满足高吞吐的写入需求?

英文书中原文是:

There are several driving forces behind the adoption of NoSQL databases, including:

  • A need for greater scalability than relational databases can easily achieve, including very large datasets or very high write throughput

这里的主要原因应该是关系型数据库是 schema-on-write,写时会做很多校验(比如各种字段类型是否满足、事务、一致性检查等等);NoSQL 通常是 schema-on-read,写的时候盲写,因此效率会高。

  1. 文中的一段内容“文档型数据库很擅长处理一对多的树形关系,却不擅长处理多对多的图形关系。如果其不支持 Join,则处理多对多关系的复杂度就从数据库侧移动到了应用侧。如,多个用户可能在同一个组织工作过。如果我们想找出在同一个学校和组织工作过的人,如果数据库不支持 Join,则需要在应用侧进行循环遍历来 Join”,意思是文档型数据库不擅长处理多对多的关系,举的例子没太明白?

比如说我们有:

  1. 按人组织的集合(Collection),也就是一个文档( doc )是一个人的简历信息;

  2. 按公司组织的集合,也就是一个文档是一个公司的基本信息;

在这种情境下,我们想找到属于某个公司的所有在职员工,以及这个公司一些基本信息。如果文档数据库不支持 join,就需要我们在应用层手动的把所有用户捞上来,然后逐个遍历,过滤在这个公司的人,然后再去公司集合中,捞出这个公司;再逐一和之前的相关用户结合。

如果没有 join,公司和员工间的这种多对多关系就只能在应用层手动处理。当然 MongoDB 是支持 join 的。只不过效率肯定不如只查询一对多关系(比如在上述情境中查询一个人的所有工作经历)的效率高。

14. 文件系统缓存是什么, 在用某种语言read file时, 还会从缓存中读取文件内容吗?

就是操作系统的 page cache。文件系统-> 外存,或者二级存储。page cache->内存,或者说主存。如果是 page cache 中已有的页,通过 read file 再去读时,是可以直接命中缓存的,不需要再去捞一遍了。

另外,在 Linux 6.3 测试,不同进程是可以共享 page cache 的。@Tang_D 同学做了相关的测试:https://gist.github.com/tang-hi/6555ec0a230c6f3602faf31963ee0e22

代码里用 mmap 是因为 read 没办法用 perf 测出去页率的:https://stackoverflow.com/questions/23317666/read-system-call-page-fault-doesnt-depend-on-file-size