Blog

圣人有情无情的争论

Content #

《世说新语·伤逝篇》还有一则含义深长的小故事:

王戎丧儿万子,山简往省之,王悲不自胜。简曰:“孩抱中物,何至于此!”王曰:“圣人忘情,最下不及情;情之所钟,正在我辈。”简服其言,更为之恸。

《晋书·王衍传》的记载与此不同,说这是王衍与山简的对答。山简是山涛的儿子。王戎的儿子王绥,字万子,死时已经十九岁,称之为“孩抱中物”似乎不合情理,所以《晋书》记为王衍的幼子较为可靠些。不过我们仍可以看作是他们共同的思想。

这故事也涉及到魏晋玄学所争论的一个重要问题,即“圣人”是否有情。先是何晏提出圣人没有喜怒哀乐等感情,王弼起来驳斥,认为圣人与众人一样,也有喜、怒、哀、乐、怨等“五情”;与众人不同处在于他虽有情而又不为情所累。正因为他与众人“五情”同,所以能够感受万物,理解众人的喜怒哀乐;正因为他不为情所累,所以能够超脱万物,超越众人。显然,王弼的观点更为通达,更为合乎情理,也就更容易为名士们接受。

这里所说的“圣人”,指禀有至高德性能够通于“大道”的人,其实也就是理想人格。讨论圣人是否有情,其实也就是讨论一般人是否应当有情,应当如何评价情。认为人应无情,其说出于老庄。《庄子·德充符篇》有段话讲得比较明确集中,原文不易读,我们直接把陈鼓应的译文抄在下面:

惠子对庄子说:“人是没有情的吗?”庄子说:“是的。”惠子说:“人若没有情,怎么能称为人?”庄子说:“道给了人容貌,天给了人形体,怎么能不称为人?”惠子说:“既然称为人,怎么没有情?”庄子说:“这不是我所说的‘情’。我所说的情,乃是说人不以好恶损害自己的本性,经常顺任自然而不用人为去增益。”

所以先秦道家以有情为有累,无情为无累。有情是指背离人的原初素朴心理的物欲追逐,以及由这物欲满足与否而引发的喜怒哀乐,等等;无情则是指安时处顺,一任自然的逍遥境界和自由天地,是“喜怒哀乐不入于胸次”(庄子语)的无心无为状态。王弼等人从老庄的理论出发,却又做了一个修正,认为人既应顺任自然,而情正是一种自然本性,但众人易为情所累,理想人格则不累于情。王戎虽然也讲“圣人忘情”,但那是一般人所达不到的理想境界,“最下不及情”则失去人之为人的起码条件,二者都为突出“情之所钟,正在我辈”,即一个正常的人(实指名士)是应有血肉之情的。

圣人有情无情的争论与现实生活中自然与名教、情与礼的冲突密切相关。在魏晋名士看来,任情就是顺任自然,用名教、礼法节制情、约束情则有悖于自然。所以“有情”论在肯定和提升情的价值之时,也高扬了人的个性。但如果不能保持在一种精神自由的境界内而向庸俗方面发挥,那么这种理论恰好给纵情放诞的行为提供了依据。

From #

三语掾

Content #

《晋书》卷四十九《阮瞻传》上记载了一个小故事:

(阮瞻)见司徒王戎,戎问曰:“圣人贵名教,老庄明自然,其旨同异?”瞻曰:“将无同。”戎咨嗟良久,即命辟之。时人谓之“三语掾”。太尉王衍亦雅重之。

这是后来王戎、王衍做了高官以后的事情。阮瞻是阮籍的从孙,也是一位清谈名士。“圣人”指孔子,“名教”即礼教,是儒家思想的核心,而老庄则以“自然”即行为的自由无羁与之分庭抗礼。“将无同”是当时的口语,即“大致相同”之意。阮瞻认为名教与自然是大旨相同并行不悖的。王戎很赏识此语,就凭着这三个字将他聘为自己的掾属,故当时艳称为“三语掾”。

《世说新语·文学篇》也记载了这个故事,不过发问的是太尉王衍,回答的是阮瞻的从兄弟阮修。到底哪种记载正确,已无法确定了。但既然古人已把事情记混,足见王戎、王衍的思想倾向是一致的。

故事虽然简单,答语更仅寥寥三字,却涉及到中国思想史特别是魏晋思想史上的大问题,也难怪王戎、王衍如此看重。

早在先秦时期,礼法与自然便是几个主要学派争论的核心问题,既涉及到治国平天下的方略,也涉及到做人应世的态度。在那动荡不宁的时世,儒、法、道家各自开出拯时救弊的药方。儒家重礼,法家重法,道家重自然,都想平治天下,收拾人心。汉代罢黜百家独尊儒术,礼教获得完全的胜利。汉末动乱以来,这个矛盾又突出起来。到曹魏正始年间,司马氏标榜名教以树立威望,暗中做篡位的准备。亲近曹氏的何晏、王弼宣称名教出于自然,以自然为本,以名教为末,有暗中对抗司马氏的用心。所以何晏最终被杀,王弼以早卒幸免,不过他们也并未把名教与自然对立起来。将二者对立起来的是嵇康、阮籍。不管他们内心的想法到底如何,嵇康提出的“越名教而任自然”便使二者处于水火不容之地,阮籍在实际生活中更做出许多有悖名教的狂怪举止,被当时和后世的礼法之士骂为名教的罪人,甚至比为桀、纣一类恶棍。王戎虽与嵇、阮同为竹林七贤,王衍虽号称放达名士,但从他们赞同“将无同”三字来看,可知他们的思想已与嵇、阮不同,与何、王也有差异。他们认为自然与名教是可以调和的,也没有本、末的分别。这是进入晋代以后普遍的社会思潮,以便协调儒、道的矛盾,协调名士放达与司马氏政权的矛盾。当时有位与王衍齐名的乐广也说过一句有名的话:“名教中自有乐地。”“乐地”就是生活的放任与快乐,这是在不违背名教的前提下也可以求得的。

不过在当时以至于整个两晋南朝,士人思想和言行的主流毕竟更倾向于“自然”,而不同程度地漠视礼法。

From #

ZAB与Raft的对比

Content #

ZAB 和 Raft 从核心原理上做对比。

  1. 领导者选举

ZAB 采用的“见贤思齐、相互推荐”的快速领导者选举(Fast Leader Election)。 Raft 采用的是“一张选票、先到先得”的自定义算法。 Raft 的领导者选举,需要通讯的消息数更少,选举也更快。

  1. 日志复制

Raft 和 ZAB 相同,都是以领导者的日志为准来实现日志一致,而且日志必须是连续的,也必须按照顺序提交。

  1. 读操作和一致性

ZAB 的设计目标是操作的顺序性,在 ZooKeeper 中默认实现的是最终一致性,读操作可以在任何节点上执行;而 Raft 的设计目标是强一致性(也就是线性一致性),所以 Raft 更灵活, Raft 系统既可以提供强一致性,也可以提供最终一致性。

  1. 写操作

Raft 和 ZAB 相同,写操作都必须在领导者节点上处理。

  1. 成员变更

Raft 和 ZAB 都支持成员变更,其中 ZAB 以动态配置(dynamic configuration)的方式实现的。那么当你在节点变更时,不需要重启机器,集群是一直运行的,服务也不会中断。

  1. 设计

相比 ZAB,Raft 的设计更为简洁,比如 Raft 没有引入类似 ZAB 的成员发现和数据同步阶段,而是当节点发起选举时,递增任期编号,在选举结束后,广播心跳,直接建立领导者关系,然后向各节点同步日志,来实现数据副本的一致性。 ZAB 的成员发现,可以和领导者选举合到一起,类似 Raft,在领导者选举结束后,直接建立领导者关系,而不是再引入一个新的阶段;数据同步阶段,是一个冗余的设计,可以去除的,因为 ZAB 不是必须要先实现数据副本的一致性,才可以处理写请求,而且这个设计是没有额外的意义和价值的。

另外,ZAB 和 ZooKeeper 强耦合,你无法在实际系统中独立使用;而 Raft 的实现(比如 Hashicorp Raft)是可以独立使用的,编程友好。

Viewpoints #

From #

加餐 | ZAB协议(三):如何处理读写请求?

...

ZAB节点运行时的四种状态

Content #

ZAB 定义了 4 种状态,来标识节点的运行状态。

  1. ELECTION(选举状态)表明节点在进行领导者选举;
  2. DISCOVERY(成员发现状态)表明节点在协商沟通领导者的合法性;
  3. SYNCHRONIZATION(数据同步状态)表明集群的各节点以领导者的数据为准,修复数据副本的一致性;
  4. BROADCAST(广播状态)表明集群各节点在正常处理写请求。

只有当集群大多数节点处于广播状态的时候,集群才能提交提案。

Viewpoints #

From #

加餐 | ZAB协议(二):如何从故障中恢复?

SSD硬盘容易越用越慢(写入放大)

Content #

当 SSD 硬盘的存储空间被占用得越来越多,每一次写入新数据,我们都可能没有足够的空白。我们可能不得不去进行垃圾回收,合并一些块里面的页,然后再擦除掉一些页,才能匀出一些空间来。

这个时候,从应用层或者操作系统层面来看,我们可能只是写入了一个 4KB 或者 4MB 的数据。但是,实际通过 FTL 之后,我们可能要去搬运 8MB、16MB 甚至更多的数据。

“实际的闪存写入的数据量 / 系统通过 FTL 写入的数据量 = 写入放大”,可以看到,写入放大的倍数越多,实际的 SSD 性能也就越差,会远远比不上实际 SSD 硬盘标称的指标。

而解决写入放大,需要我们在后台定时进行垃圾回收,在硬盘比较空闲的时候,就把搬运数据、擦除数据、留出空白的块的工作做完,而不是等实际数据写入的时候,再进行这样的操作。

Viewpoints #

From #

47 | SSD硬盘(下):如何完成性能优化的KPI?

SSD硬盘不知道操作系统删除了文件

Content #

操作系统的逻辑层和 SSD 的逻辑层里的块状态,是不匹配的。

我们在操作系统里面去删除一个文件,其实并没有真的在物理层面去删除这个文件,只是在文件系统里面,把对应的 inode 里面的元信息清理掉,这代表这个 inode 还可以继续使用,可以写入新的数据。这个时候,实际物理层面的对应的存储空间,在操作系统里面被标记成可以写入了。

所以,其实我们日常的文件删除,都只是一个操作系统层面的逻辑删除。这也是为什么,很多时候我们不小心删除了对应的文件,我们可以通过各种恢复软件,把数据找回来。同样的,这也是为什么,如果我们想要删除干净数据,需要用各种“文件粉碎”的功能才行。

这个删除的逻辑在机械硬盘层面没有问题,因为文件被标记成可以写入,后续的写入可以直接覆写这个位置。但是,在 SSD 硬盘上就不一样了。我在这里放了一张详细的示意图。我们下面一起来看看具体是怎么回事儿。

一开始,操作系统里面有好几个文件,不同的文件我用不同的颜色标记出来了。下面的 SSD 的逻辑块里面占用的页,我们也用同样的颜色标记出来文件占用的对应页。

当我们在操作系统里面,删除掉一个刚刚下载的文件,比如标记成黄色 openjdk.exe 这样一个 jdk 的安装文件,在操作系统里面,对应的 inode 里面,就没有文件的元信息。

但是,这个时候,我们的 SSD 的逻辑块层面,其实并不知道这个事情。所以在,逻辑块层面,openjdk.exe 仍然是占用了对应的空间。对应的物理页,也仍然被认为是被占用了的。

这个时候,如果我们需要对 SSD 进行垃圾回收操作,openjdk.exe 对应的物理页,仍然要在这个过程中,被搬运到其他的 Block 里面去。只有当操作系统,再在刚才的 inode 里面写入数据的时候,我们才会知道原来的些黄色的页,其实都已经没有用了,我们才会把它标记成废弃掉。

所以,在使用 SSD 的硬盘情况下,你会发现,操作系统对于文件的删除,SSD 硬盘其实并不知道。这就导致,我们为了磨损均衡,很多时候在都在搬运很多已经删除了的数据。这就会产生很多不必要的数据读写和擦除,既消耗了 SSD 的性能,也缩短了 SSD 的使用寿命。

为了解决这个问题,现在的操作系统和 SSD 的主控芯片,都支持 TRIM 命令。这个命令可以在文件被删除的时候,让操作系统去通知 SSD 硬盘,对应的逻辑块已经标记成已删除了。现在的 SSD 硬盘都已经支持了 TRIM 命令。无论是 Linux、Windows 还是 MacOS,这些操作系统也都已经支持了 TRIM 命令了。

Viewpoints #

From #

47 | SSD硬盘(下):如何完成性能优化的KPI?

...

FTL和磨损均衡

Content #

让 SSD 硬盘各个块的擦除次数,均匀分摊到各个块上。这个策略,就叫作磨损均衡(Wear-Leveling)。实现这个技术的核心办法,就是添加一个间接层,即 FTL 闪存转换层。

我们通过一个页表映射虚拟内存页和物理页,在 FTL 里面,存放了逻辑块地址(Logical Block Address,简称 LBA)到物理块地址(Physical Block Address,简称 PBA)的映射。

操作系统访问的硬盘地址,其实都是逻辑地址。只有通过 FTL 转换之后,才会变成实际的物理地址,找到对应的块进行访问。操作系统本身,不需要去考虑块的磨损程度,只要和操作机械硬盘一样来读写数据就好了。

操作系统所有对于 SSD 硬盘的读写请求,都要经过 FTL。FTL 里面又有逻辑块对应的物理块,所以 FTL 能够记录下来,每个物理块被擦写的次数。如果一个物理块被擦写的次数多了,FTL 就可以将这个物理块,挪到一个擦写次数少的物理块上。但是,逻辑块不用变,操作系统也不需要知道这个变化。

Viewpoints #

From #

47 | SSD硬盘(下):如何完成性能优化的KPI?