系统概述

元数据

master主要存储三种元数据:文件和chunk的命名空间(namespace)、文件到chunk的映射和chunk的每个副本的位置。
所有元数据被存储在master的内存中。前两种类型(文件和快的命名空间、文件到chunk的映射)还通过将变更(mutation)
记录到一个操作日志(operation log)的方式持久化存储在master的磁盘上,并在远程机器上备份。通过日志,我们可以简单、
可靠地更新master的状态,即使master故障也没有数据不一致的风险。master不会持久化存储chunk的位置信息,
而是在启动时和当chunkserver加入集群时向chunkserver询问其存储的chunk信息。

内存数据结构

大多数文件包含多个chunk,所以大部分chunk是满的,仅最后一个chunk被部分填充。并且因为采用了前缀压缩的方式紧凑地存储文件名,每个文件的命名空间数据通常需要少于64字节。

chunk位置

master不会持久化保存哪台chunkserver含有给定的chunk的副本的记录,而是简单地在启动时从chunkserver获取信息
优点:实现简单

操作日志

操作日志包含重要的元数据变更的历史记录。这是GFS的核心。它不仅是元数据中唯一被持久化的记录,还充当了定义并发操作顺序的逻辑时间线。带有版本号的文件和chunk都在他们被创建时由逻辑时间唯一、永久地确定
我们将操作日志备份到多台远程主机上,且只有当当前操作记录条目被本地和远程主机均写入到了磁盘后才能向客户端发出响应。
todo 跟raft log有啥区别?
checkpoint: btree, async exec

一致性模型

在一系列变更执行成功后,被变更的文件区域状态为defined的,且该区域中包含最后一次变更写入的数据。这一点是GFS通过以下方式实现的:
(a)对chunk执行变更时,其所有副本按照相同的顺序应用变更(章节3.1)
(b)使用chunk版本号(chunk version)来检测因chunkserver宕机而错过了变更的陈旧的chunk副本(章节4.5)。陈旧的chunk副本永远不会在执行变更时被使用,也不会在master返回client请求的chunk的位置时被使用。它们会尽早地被作为垃圾回收。

由于client会缓存chunk的位置,在缓存信息刷新前,client可能会访问陈旧的副本。这个时间窗口会受缓存过期时间和下一次打开文件限制(下一次打开文件会清除文件的所有chunk位置信息)。
GFS通过master和所有chunkserver周期性握手的方式来确定故障的chunkserver,并通过校验和(checksunmming)的方式检测数据损坏

GFS应用程序可以通过一些简单的技术来使用其宽松的一致性模型,且这些技术已经因其他目标而被使用,如:依赖append而不是overwrite、检查点、自验证写入(writing self-validating)、自标识记录(self-identifying records)。

系统交互

租约与变更顺序

租约本质是授予某chunk变更数据的权限(选主),避免网络分区造成的脑裂
改变chunk或元数据的操作被称为“变更”,如write或append。chunk变更时,其每个副本都会应用变更。我们使用租约(lease)来维护副本间变更顺序的一致性。master向其中一份副本授权一个变更的租约,我们称这个副本为primary(译注:有时也可代指primary副本所在的chunkserver)。primary为应用于该chunk的所有变更选取顺序。所有副本都会按照这个顺序来应用变更。因此,全局的变更顺序首先由master选取的租约授权顺序定义,接着在租约内由primary选取的顺序编号定义。

这种租约机制是为了最小化master管理负载而设计的。租约的初始超时时间为60秒。然而,一旦chunk被变更,primary就可以向master请求延长租约时间,或者(通常为)接受来自master的租约时间延长操作。这些租约延长请求和租约授权请求依赖master与chunkserver间周期性地心跳消息来实现。有时master可能会在租约过期前视图撤销租约(例如,当master想禁止对正在被重命名的文件进行变更时)。即使master与一个primary的通信丢失,master仍可以在旧租约过期后安全地向另一个副本授权新的租约。

假如 master与primary 的网络断开了,但client与primary网络正常,在该租约内也无法写入数据,因为写入时primary需要与其他secondary通信,其他secondary可能已经被master选取为新的primary,此时secondary拒绝写入。
假如master和primary,secondary的网络全断开了,系统在当前租约内可用,数据保持一致,租约过期后系统不可用。

数据流

数据流沿着一条精心挑选的chunkserver链以流水线的方式线性推送。我们的目标是充分利用每台机器的网络带宽,避免网络瓶颈和高延迟的链路,并最小化推送完所有数据的时延。

为了尽可能地避免网络瓶颈和高延迟的数据链路(例如,交换机间链路(inter-switch)经常同时成为网络瓶颈和高延迟链路),每台机器会将数据传递给在网络拓扑中最近的的且还没有收到数据的机器。

最后,我们通过流水线的方式通过TCP连接传输数据,以最小化时延。当chunkserver收到一部分数据时,它会立刻开始将数据传递给其他chunkserver。

原子性record append

多生产者单消费者 模型中使用,想到了kafka的单个分区
然而在record append中,client仅需指定待追加的数据。GFS会为其选择一个偏移量,在该偏移量处至少一次地原子性地将数据作为一个连续的字节序列追加到文件,并将该偏移量返回给client。

快照

使用类似AFS[5]的标准的写入时复制技术来实现快照

小于64M的文件也占用一个

GFS在逻辑上用一个完整路径名到元数据的查找表来表示命名空间。通过前缀压缩技术,这个查找表可在内存中高效地表示。

fileName -> {chunk_list}
chunk {
handler_id
location
}

todo 前缀压缩
todo 校验和计算方式

Java 不提供指针来直接访问内存,程序内存更加安全。可以使用线性分配器分配内存(因为线性分配器需要与具有拷贝特性的垃圾回收算法配合,所以 C 和 C++ ,
go等需要直接对外暴露指针的语言就无法使用该策略)

阅读全文 »

1、想要提高应用的性能,可以引入「缓存」来解决

2、引入缓存后,需要考虑缓存和数据库一致性问题,可选的方案有:「更新数据库 + 更新缓存」、「更新数据库 + 删除缓存」

3、更新数据库 + 更新缓存方案,在「并发」场景下无法保证缓存和数据一致性,解决方案是加「分布锁」,但这种方案存在「缓存资源浪费」和「机器性能浪费」的情况

4、采用「先删除缓存,再更新数据库」方案,在「并发」场景下依旧有不一致问题,解决方案是「延迟双删」,但这个延迟时间很难评估

5、采用「先更新数据库,再删除缓存」方案,为了保证两步都成功执行,需配合「消息队列」或「订阅变更日志」的方案来做,本质是通过「重试」的方式保证数据最终一致

6、采用「先更新数据库,再删除缓存」方案,「读写分离 + 主从库延迟」也会导致缓存和数据库不一致,缓解此问题的方案是「延迟双删」,凭借经验发送「延迟消息」到队列中,延迟删除缓存,同时也要控制主从库延迟,尽可能降低不一致发生的概率

参考
https://developer.baidu.com/article/detail.html?id=294416

事务消息

rocketmq事务消息

RocketMQ采用了2PC的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息,流程如下图所示:
img.png
其具体工作流程分为正常事务消息的发送及提交和不正常情况下事务消息的补偿流程:

阅读全文 »

设计原理

栈区的内存一般由编译器自动分配和释放,其中存储着函数的入参以及局部变量,这些参数会随着函数的创建而创建,函数的返回而消亡,一般不会在程序中长期存在
这种线性的内存分配策略有着极高地效率,但是工程师也往往不能控制栈内存的分配,这部分工作基本都是由编译器完成的。

阅读全文 »

Go 语言的调度器通过使用与 CPU 数量相等的线程减少线程频繁切换的内存开销,同时在每一个线程上执行额外开销更低的 Goroutine 来降低操作系统和硬件的负载

阅读全文 »

img_1.png

实体

实体就是领域中需要唯一标识的领域概念

值对象

值对象没有唯一标识,这是它和实体的最大不同。

阅读全文 »

内存分配是 Go 语言运行时内存管理的核心逻辑,运行时的内存分配器使用类似 TCMalloc 的分配策略将对象根据大小分类,并设计多层级的组件提高内存分配器的性能。

阅读全文 »

go map

  1. 数组+链表法实现
  2. 通过hash(key)将元素分散在不同的桶中,每个桶有8个cell,hash值低位决定桶序号,高位标识同一个桶中的不同key
  3. 每个桶有个overflow指针,当8个key都被填满时,会新建一个溢出桶出来,并将overflow指向它
  4. 扩容分为等量扩容(overflow > 2 ^ B)和2倍容量扩容((count / 2 ^ B) > 装载因子)
  5. 扩容是渐进式的,避免一次性迁移太多key引发性能问题,bucket迁移发生在赋值,删除期间,每次最多搬迁两个bucket
阅读全文 »

  • 延迟满足感
  • 懒惰(体力的、思考的、情绪的)是万恶之源,所以修炼很多时候就是在克服惰性。
  • 年轻人不要试图追求安全感,特别是年轻的时候,周遭环境从来都不会有绝对的安全感,如果你觉得安全了,很有可能开始暗藏危机。真正的安全感,
    来自你对自己的信心,是你每个阶段性目标的实现,而真正的归属感,在于你的内心深处,对自己命运的把控,因为你最大的对手永远是自己。
  • 其实,德州扑克和人生一样:应该:理解不确定性、专注有可能的事情、理智评估概率、能舍才能得、避免意气用事。
  • 凡事就怕不认真,不思考。好多问题我应该能知道的,只是之前没有认真看,认真想,想当然(不是没时间)。延迟满足感是一项长期修炼。
  • 加强专注力训练
  • 保证足够睡眠是积极高效的第一步。
  • 我今天的处境都是因为我有些应该做的事情我没有做,不应该做的事情我就全做,所以要改变现状,就要从自己开始。’
  • 一点不要含糊,含糊代表着侥幸、代表着自我欺骗、代表着自我感觉飘然。
  • 乔布斯说stay hungry,我以为饥渴有三个层次:贪婪、成就动机、好奇心 。三者分别关注:瞬间的结果,持续的过程,和远大的未知。
    三者也恰好对应了三种人:卑劣的投机者,艰辛的攀登者,与幸福的探索者。保持好奇心
  • 人生的本质是追寻自我的提升。包括思想、能力、意志等等。这些发展好了,一切随之而来。偏偏大多数人追求的是短期的公司、职位、薪水,运气好的能有所发展,
    运气差的会迷失方向流于平庸。