Monthly Archive: June 2021

0

Linearizability一致性介绍二

我们在前面《分布式系统中的Linearizability一致性的概念介绍》介绍了Linearizability的基本概念,本文就来详细介绍一下我们如何来实现Linearizability。 我们再来简单回忆一下Linearizability的介绍,他其实就是说所有的replica都像只有一个一样,那么我们是否有个暴力解,就是真的只有一个拷贝,没有replica,这样不就是Linearizable的了?你是对的,哈哈,不过这个显然不是我们想要的答案,毕竟这样一来,如果这个节点出了任何问题,你整个读写就都不能继续了。 那么我们先来看看各种分布式的模型,看看他们能不能Linearizability: 单leader的replication 在单leader的系统中,假如读都是从leader来的话,或者你使用同步更新replica,那是有可能实现Linearizability的,但是注意也只是有可能,毕竟有可能leader出问题,比如leader自己还认为自己是leader,但事实上已经不是了,这种情况就有可能不是linearizability了。...

1

分布式系统中的Linearizability一致性的概念介绍

分布式系统中一致性一直是一个大家热衷讨论的话题,这里的一致性是指假如你同时到两个节点读取数据,你很可能看到的是不同的数据。毕竟发生在一个节点上的写操作同步到另外一个节点总是需要时间的。 我们最常见的说法就是“最终一致性”,也就是说假如没有写操作,所有的节点在一段时间之后就会一致了。目前大多数的数据库都是支持这种一致性的,但是这个一致性非常弱。你仔细想想它其实什么都没有保证,比如你写了一个数,你再去读,读到什么值根本就没有任何保证,只是说你最终能读到一致的值,这个最终是多长时间之后谁也不知道。所以说这样的“最终一致性”其实给应用开发带来了很多困难,也有可能导致很多Bug。 那么有没有什么更强的一致性保证呢?答案是当然有,但是需要注意的是一致性越强,它的性能或者错误容忍度就会越差,毕竟十全十美总是很难。本文就来介绍一种强一致性技术:Linearizability。 概述 我们上面提到在一个“最终一致性”的系统中,你同时访问不同的数据replica,得到的值可能是不同的。那么能否有一种机制保证我们任何时候访问同一个数据replica得到的值一直都是相同的呢?这种保证的就是Linearizability背后的思想:任何时候整个系统就像只有一个拷贝一样,不管你访问哪一个replica,得到的结果都是一样的。...

0

一文带你了解分布式系统中的真真假假

我们知道分布式系统中各个服务器都是通过网路进行连接的,这样导致的结果就是你很难知道各个服务器的真实状况,比如你判断另外一台服务器是否有问题的唯一办法就是发送一个请求给他,只有收到了回应,你就认为它是好的,假如没有收到回应,你就很难判断对面的服务器是否有问题,因为这个没有回应很可能是发生了网络故障,也可能是对端机器真的出问题了。因此,在分布式系统中我们如何来准确判断这些问题呢?本文就来详细介绍相关的方法。 基于多数的(Majority)事实 很多时候我们一个节点可能不是真的有问题,比如说它正在进行GC,那么在GC的这段时间内它就不能回应任何请求,这个时候从节点本身的来看,它自己是很ok的,没有任何问题。然而从别的节点来看,这个GC的节点就和出问题的节点一模一样,发请求它不回,重试也没有反应。所以别的节点就会认为它是有问题的。从这个角度来看,节点本身其实也是很难知道自己是否问题的。 现在比较流行判断节点是否有问题的算法都是基于多数的决策,比如说我有5个节点,那么大家一起来投票,假如有超过一定数量的节点(一般来说超过半数,这里就是有三个节点)认为它有问题,那么我们就认为这个节点是真的有问题。哪怕这个节点本身是没有问题的,但是只要有多数认为有问题,我们就认为它有问题。这里使用多数来决定是因为多数就意味着不会有冲突,因为一个系统中不可能存在两个多数,只可能有一个。 Leader和Lock...

2

分布式系统之不可靠时钟揭秘

时钟是一个我们常常会使用的东西,比如我们会用它来确定一个事情发生的时间或者说一个请求花费的时间。然而在分布式系统中,每个机器都有他们自己的时钟,通常来说是由它们本身的硬件来决定的(比如晶振等),它们都不是精确准确的,所以每个机器之间的时钟都或多或少有点差别。所以当我们需要使用不同机器的时钟时,比如比较两个发生在不同机器上事情发生的先后顺序的时候,就很难说哪一个事情是真正先发生哪一个是后发生的。本文就来介绍一下一般如何处理这个问题。 单调时钟(Monotonic)和当天的时间(Time-of-Day Clock) 在现代计算机上,一般有两种时钟,一个是单调时钟另外一个是当天时间。虽然两者都是时钟,但它们其实有很大的差别。 当天时间...

0

分布式系统之怎么都不可靠的网络

当我们聊到分布式系统和单机程序不同之处时第一反应就是多台机器之间的网络问题。多台机器之间的网络连接给我们带来了很多便利,比如我们可以把多个不同地方的机器互联,再也不用担心单台机器带来的性能瓶颈等等。但同时也给我们带来了很多意想不到的问题,本文就来详细介绍为什么我们说网络是不可靠的。 简单Request可能遇到的问题 我们首先来看一下当一个节点发送一个request到另外一个节点,可能会遇到的问题(如下图所示): 你的请求可能直接丢失了。(比如发送时网络突然断了) 你的请求可能会被堵塞在一个queue中,一段时间之后才会被发送。(比如网络负载很重或者接收端直接过载等)...

0

一文带你深入理解Serializable隔离最新技术SSI

我们已经在前文了解了数据库的弱隔离以及Serializable隔离的两种技术(串行执行和两阶段锁),每个人都想用Serializable隔离,毕竟它很好的处理了各种冲突。但很多时候又被逼无奈选择弱隔离,原因也很简单,Serializable隔离好虽然好,但是性能消耗太大了,选择它就意味着选择了低的性能。所以人们一直在感叹假如有一种方法能做到Serializable隔离,性能损耗又不大的话就好了。皇天终归不负有心人,一个新的算法横空出世,它就是Serializable Snapshot isolation,简称SSI,它的优点就是能够牺牲很小的性能达到Serializable隔离的效果,这个算法是2008才出现的,不过已经被运用在PostgreSQL的版本9.1和FoundationDB中了。本文就来详细介绍一下这一最新的技术。 悲观和乐观的同步控制 我们在《Transaction...

1

Transaction Serializable隔离之两阶段锁

我们在前面的《Transaction Serializable隔离之串行执行》中介绍了Serializable隔离的第一个实现方法:串行执行。本文来介绍第二个实现方法:两阶段锁(Two-Phase Locking)。这个方法也是一个由古到今一直流行的方法,需要注意的是他和我们通常说的两阶段提交(Two-Phase Commit)是完全不同的两个概念,我们会在后面的文章中再详细介绍两阶段提交的概念。 我们都知道锁的作用:就是有transaction想同时写一个object,这个锁可以保证两个写中的一个必须等待另外一个完成。两阶段锁也是类似的,只是说它更加强一点。假如一个object没有人在写,那么多个读可以同时进行。但是只要有人想要写这个object,那么就有下面这样的限制:...

2

Transaction Serializable隔离之串行执行

我们在前面几篇Transaction弱隔离相关的文章中介绍了读提交,snapshot隔离,write skew和Phantoms。但这些总给我们一种感觉就是他们只能解决部分问题,而且使用起来很不方便,需要注意很多方面。那么有没有什么好的办法来一次性解决所有问题呢?答案是有的,那就是我们今天要介绍的史上最强,超级无敌的Serializable隔离。 Serializable隔离一般会认为是最强的隔离级别。它保证了哪怕有多个transaction并行执行,它的结果和串行执行的结果是一样的。而这个是由数据库本身来保证的,也就是说数据库处理了所有的冲突情况。 既然Serializable隔离这么强,那么大家都用它就好了啊,我们为什么还需要考虑别的呢?显然世界上不会有免费的午餐,想要得到强大的力量,必然就要付出相应的代价。要想了解这个代价是什么,我们首先要来看看一般来说都是怎么实现Serializable隔离的,大概的方法有下面这些: 真正的串行执行所有的transaction。...

1

Transaction弱隔离之Write Skew和Phantoms

我们在前面的《Transaction弱隔离之读提交的介绍和实现》和《Transaction弱隔离之更新的丢失》中分别介绍了脏写和更新丢失。他们都是有两个写同时发生,从而产生了冲突。那么对于这种情况,我们必然需要进行保护和处理,可以是数据库来自动处理也可以是手动的加一些保护比如锁或者原子写操作等。然而上面提到的两种写冲突就是全部了吗?现实世界显然更加残酷,本文就来具体看看别的冲突的例子。 如下图所示,假设你正在维护一个医院的值班系统,这个系统一般来说会安排几个医生一起来值班,当然它的最低要求是必须有一个医生值班。所以说,当轮到你值班的时候,假如还有别的医生在值班,你就可以从这个系统中请假。请假的操作也很简单,就是假如系统中显示有两个及以上的医生在值班,那么就允许你更新自己的状态成不值班。 现在我们假设Alice和Bob突然同时有事,一个身体不舒服,一个想去和女朋友约会,他们在下班之前的那一刻同时准备到系统中来请假,并且同时执行了请假的操作。然后就有了两个transaction同时执行,他们都是读的snapshot的内容,所以在更新的之前on call count都是大于等于2的,然后他们同时更新了他们自己的状态到请假(on_call...

2

Transaction弱隔离之更新的丢失

我们在前文《Transaction弱隔离之读提交的介绍和实现》和《Snapshot的隔离和Repeatable的读》中介绍了read commit和snapshot隔离。这两篇文章其实更多地讨论的是当有写发生的时候如何进行读,除了脏写之外没有更多地讨论如何处理两个写同时发生的情况。其实当有两个写同时发生的时候还会有很多问题需要处理,其中最常见的就是两个写有冲突,这个时候就会发生更新丢失的问题。 我们举个例子,比如一个transaction先从数据库读一个数据,然后修改这个值,再把它写回数据库(读-修改-写的模式)。当有两个transaction同时发生这件事的时候,其中一个transaction的写就会丢失。这种pattern其实在现实中很常见的: 比如说你需要增加一个计数器或者更新账户的余额,需要先读出原来的值,计算出新的值,然后更新。 更新一个复杂值的一个部分,比如说你在JSON文档里面加一个元素,需要首先解析这个文档,做修改,然后写回去。...