我的半年健身心得:重塑精力与效率之路

作为一个在互联网行业摸爬滚打多年的老兵,常年的久坐、长时间面对屏幕以及伴随而来的精神压力,是很多同行,包括我自己,都绕不开的日常。虽然体检报告上没有特别异常,但说实话,身体状态长期处于一种“够用但不好”的平庸状态:容易疲惫、眼带血丝,开会容易走神打哈欠、体重和体脂率悄悄攀升……我相信这可能是很多技术朋友们的写照。

坦白说,我的体质基础其实算是偏弱的。 比如,以前看到很多人能轻松以10-11(KM/h)的配速跑步,而我按配速10连续跑不到半小时,大部分时候只能维持在8-8.5左右(当然,找到适合自己的节奏最重要)。在开始系统健身前,我甚至连一个标准的引体向上都做不了。正因为起点不算高,我才觉得健身这件事对我个人,或许更具改变的意义。

转折发生在大概半年前。我决定不再敷衍,而是像对待一个重要项目一样,开始认真规划和执行我的健身计划。目标很明确:不追求成为健美先生,而是为了提升整体生命系统的效率和稳定性,让身体和大脑都能更好地支持高强度的工作和生活。

我设定的最低标准是:每周至少150分钟的中等强度有氧运动,外加1-2小时的力量训练。这里的“训练”是专注的、有计划性的,不包括日常零散的步行。

为什么是这个组合?科学依据与个人感悟

这个目标并非凭空而来。它符合世界卫生组织(WHO)等权威健康机构对成年人的基本运动建议。早些年我也曾“佛系”地跑过步,但回过头看,每周零星一两次、没有持续性和时长的保证,更多是心理安慰,远未达到能带来显著改变的“起步线”。

而加入力量训练,是基于我深入分析了解后做出的决定。它不仅仅是练肌肉,更是为了:

  1. 优化身体的“能量系统”:肌肉是储存糖原的主要场所,增加肌肉量有助于提高胰岛素敏感性,降低血糖波动和二型糖尿病的风险。这对于细粮高糖现代饮食结构的人来说,尤其重要。
  2. 构建强大的“身体架构”:力量训练提升关节的稳定性和整体的力量平衡,这是对抗衰老、预防伤病、保持中老年后身体机能不快速下滑的关键。把它想象成给你的身体这台精密仪器打好结构基础、加固关键部件。

而最令我惊喜的“可量化成果”,体现在最大摄氧量(VO2 Max)的显著提升上。短短半年,我的VO2 Max提升了约 15%,而大部分人这个指标是随年龄下降的。

借用一下定义:

最大摄氧量 (VO2 max) 是指人体在剧烈运动中所能消耗的最大氧气量。它是衡量有氧健身水平的指标,表明身体向工作肌肉输送和利用氧气的能力。最大摄氧量越高,通常意味着进行耐力活动的能力越强,患心血管疾病的风险也越低。

— 根据 Google Gemini

VO2 Max可以被视为你身体的“发动机功率”或者“处理氧气的峰值能力”。它的提升,直接体现在:

  • 有氧耐力显著增强,可以轻松完成1小时甚至更长时间不间断训练。
  • 日常精力大幅改善:这也是我最看重的生命时间的质量。比如开会时能注意力集中,很少出现犯困或打哈欠的情况;处理复杂问题时,感觉大脑供氧充足,思维更敏捷;一天工作下来,不再是电量耗尽的感觉,晚上睡觉时候身体不再像一个放了气的皮球。
  • 睡眠质量提升。
  • 长期来看,高VO2 Max是心血管健康的积极信号,甚至与预测寿命相关。

这些实实在在的改变,让我更加确信,把一定的时间和精力投入到健身上,是一笔高回报的投资,它直接服务于我的工作效率、生活品质甚至生命的质量。

实践心得:如何将健身融入日常?

理解了“是什么”和“为什么”,接下来的挑战就是“怎么做”——尤其是在我们这种工作节奏快、时间宝贵的行业里。分享几个我的实践心得,或许能给朋友们一些启发:

1. 时间管理:将其视为一项非协商的日程

150分钟有氧+1-2小时力量,每周总计约3-4小时的训练时间,加上路途往返和拉伸,单次可能需要1.5-2小时。这确实需要挤时间。我的做法是:

  • 排入固定日程:像重要的会议一样,将锻炼时间锁定在每周的日历中,比如下班后7-9点、周末的早晨等。一旦排定,除非有紧急情况,不随意取消。
  • 选择“近”和“快”:优先选择离家或公司最近的健身房,最大限度减少路途时间。家门口或办公室楼下健身房通常是最佳选择。
  • 提高优先级:在有限的闲暇时间里,健身的优先级需要高于刷手机、看剧甚至阅读等活动。一开始可能需要一些意志力,但当身体反馈给你积极信号时,动力会越来越强。

2. 地点选择:效率与效果优先

我在户外跑步和健身房之间做过尝试和权衡,最终选择了健身房,这主要出于效率和综合性考虑:

  • 时间效率:健身房通常是室内恒温,不受天气影响,启动阻力低(尤其冬天)。
  • 训练全面性:户外跑步难以进行系统的力量训练。健身房器械齐全,能同时满足有氧和力量的需求。
  • 有氧的可控性:在跑步机、椭圆机、单车等器械上,更容易维持目标心率区间,确保有氧训练的质量。
  • 当然,选择你最容易坚持的环境是第一位。如果户外跑步让你感到愉悦放松,更容易坚持,那就去户外。关键是开始并持续。

3. 心率是你的最佳“传感器”:理解Zone 2和HIIT

有氧训练并非心率越高越好。我的有氧时间里,大部分时间会放在Zone 2(约最大心率的70%-80%)。也有定义为60%-70%的,如果你觉得困惑,Zone 2的共识是,这个强度下,你通常还能维持基本的对话(即所谓的“跑步时能聊天”)。

  • 为什么是Zone 2? 它是建立有氧基础、提高身体脂肪供能效率、增强毛细血管网络的关键区域。想象它在为你身体的“发动机”构建更庞大高效的燃料输送系统。很多新手容易全程冲刺到Zone 3、4,那更多是提高“峰值性能”,而不是“基础排量”。
  • HIIT(高强度间歇训练):为了进一步提高VO2 Max这个“发动机功率上限”,我每周会加入至少一次HIIT。典型的做法是高强度(心率冲到Zone 4-5,约80%-90%最大心率)持续3分钟,然后低强度(心率降到Zone 1,约50%-60%最大心率)活动3分钟,重复5-8组。
  • 为了科学监控心率和运动效果,运动手环或手表几乎是必备工具。它们能实时显示心率及区间,长期记录还能看到VO2 Max等数据的变化。我日常使用Apple Watch设备来监控心率,包括记录睡眠。
  • 最高心率简易算法(HRmax) = 每分钟心跳(220 – 年龄)

4. 力量训练:务实为王,安全第一

力量训练的目标并非要追求极限重量或块头,而是提高功能性力量、改善身体成分和代谢健康。对于我们来说:

  • 选择基础动作:深蹲、卧推、硬拉(谨慎尝试)、划船、引体向上(或器械辅助)、肩推等复合动作是效率较高的选择,能锻炼到主要的肌群。
  • 重量适度,动作标准:安全永远是第一位的。先用轻重量掌握正确的动作模式,再逐步增加重量。宁可重量轻一点,也要保证动作不变形,避免受伤。
  • 无需频繁练到力竭:除非有特定的增肌目标,大部分时候训练到接近力竭(还能做1-2次的程度)即可。

5. 要不要教练?初期入门的好帮手

有氧入门相对容易,更在于坚持。但力量训练,尤其是自由器械,对动作标准要求高。

  • 优势:如果你预算允许,并且是力量训练新手,初期请10-20节私教课是非常值得的投资。一个好的教练(注意甄别,优先有运动科学背景、沟通好的)能帮你建立正确的动作模式,避免走弯路和受伤,这能为你省去很多麻烦。尤其是女性的健身者,力量训练可能更需要一些正确理念的指引。
  • 替代方案:如果不想请教练,互联网上有大量高质量的训练视频教程(建议选择有运动康复或健身认证背景的博主),多看多学,从小重量开始模仿练习。

6. 换个词:这不是“坚持”,而是“习惯”

“坚持”听起来是需要咬牙、反人性的。一旦松懈,就可能前功尽弃。我更喜欢把它看作是培养一种生活习惯,就像每天要洗脸刷牙一样自然。

  • 奖励机制:找到你运动后的“小奖励”。我的健身房有泳池和桑拿,因此大部分时候我练完会去放松一下,是很好的激励,并且桑拿还可以促进生长激素分泌。
  • 减少阻力:随时可以准备好运动装备出发、选择便捷的地点、有合适的搭子、找到喜欢的运动方式(听音乐、听书)都能降低启动的心理门槛。
  • 从小目标开始:如果设定“今天要练2小时”让你望而却步,那就改成“今天先去做30分钟有氧”。往往当你迈出第一步后,身体进入状态,内啡肽分泌,很容易就愿意完成更多。我很多时候只打算练30分钟,但是练完30分钟身体没有停下来的意愿,不知不觉就练了1小时甚至更多。

7. 让过程更有趣:知识的“加油站”

长时间的有氧训练可能略显枯燥。这正是利用“碎片化”脑力进行学习的好时机。

  • 听书/播客:我利用在跑步机或椭圆仪上的时间,听完了不少之前没时间读的书或播客,包括微信读书上很多很枯燥的比如凯恩斯,哈耶克等经济学图书。
  • 内容选择:选择那些信息密度适中、不需要高度专注消化的内容,比如财经评论、商业访谈、人物传记、科普类播客(AI相关非常热门),因为太烧脑的技术内容可能影响运动节奏。有趣的音频内容能让你觉得时间过得飞快,同时收获知识。Spotify、微信听书等平台有很多优质资源。

8. 关于分享与衡量:外部监督是动力,内在提升是关键

分享健身进展,是获得外部监督和持续动力的一种有效方式。朋友圈或社交网络上,经常能看到大家晒跑步轨迹、配速,或者展示健身后的肌肉线条、马甲线。这无可厚非,也是记录成果的一种方式。

但从我以提升整体功能性为目标的实践经验来看,真正关键的“指标”,不完全是这些“表面”的成果展示。

对于有氧训练,如我们前面强调的,真正决定心肺功能提升效果的,不是你跑了多远的距离或多快的配速(这些更多是结果),而是你持续训练的时长以及训练过程中的心率是否维持在目标区间(尤其 Zone 2)。如果你选择通过分享来激励自己或影响他人,不妨也关注你的运动时长和心率区间分布图。这能让你更关注训练的“过程质量”,有氧收益源于科学的训练过程。

对于力量训练,情况又有所不同。很多业余锻炼者,即使科学训练了一段时间,可能并不会像健身杂志封面人物那样迅速练出引人注目的、棱角分明的肌肉线条。如果过于看重外部视觉变化,看不到预期的“大块头”或“马甲线”,确实可能会感到沮丧,甚至因此羞于分享或产生灰心。

我想再次强调,我们健身目的是为了提升整体健康和功能性,是为了一个更好身体状况的自己。力量训练是为了提升身体的稳定性、力量基础和平衡能力,是为了让你在日常生活中更轻松、更不容易受伤,是为了为对抗衰老打下基础,而不是为了炫耀或者仅追求外部的认可和视觉效果。

所以,请关注那些更实在的“内部提升”:你在卧推时感觉身体更稳定了吗?深蹲时核心更扎实了吗?搬重物是否感觉更轻松了?长时间坐立办公后腰背是否比以前更舒适?这些功能性的改善和力量的增长,才是我们力量训练最有价值、也最值得骄傲的成果。

是否分享,如何分享,完全取决于你自己,不需要因为没有练出“杂志封面”般的身材而灰心。专注于自身的力量、平衡性及稳定性的提升,这本身就是一种巨大的成功。让外部监督成为助力,但最终的衡量标准,请回归到自身身体状况和能力的改善。

结语:这是一场对自身系统的“升级”

健身不应被视为时间消耗,而应看作是一场对你最宝贵的资产——身体和大脑——进行的系统性升级和维护。它带来的远不止体脂率的下降或VO2 Max的提升,更是精力、专注力、情绪稳定性的全面改善。这些提升,将直接转化为你精神力的竞争力,以及生活中享受美好的能力。

从今天开始,哪怕只是抽出30分钟,去楼下快走,或者在客厅做几个深蹲和俯卧撑,迈出这第一步。相信我,一旦你开始尝到身体机能变化的甜头,健身就会像刷牙一样,自然而然地融入你的生活,成为你提升自我、从容应对挑战的有力武器。

你的身体,是你大脑和心灵唯一的长期住所。好好爱护它,它会给你丰厚的回报。一起行动起来吧!

我们需要什么样的社会连接

年轻时,我也体验过深陷社会连接缺失的孤独。刚毕业找工作那段日子,觉得全世界都在忙碌,只有自己被困在空虚里,毫无着落。而在学生时代,暑假回乡的两个月也是如此,村庄的宁静反倒放大了一种无处安放的寂寞。那时的我,渴望有人能看见我的存在,哪怕只是简单聊上几句。

随着自己人生经验的增长,感悟却逐渐有些不同。一方面已经到了不惑的年龄,不需要刻意去增加社会连接来缓解自己的焦虑或证明自己的存在;处于 I 和 E 之间的性格也在另外一方面减少了对外界的依赖。自疫情开启远程工作以来,我身处一种疏远的社交环境,但并未感到困扰。视频会议和协作项目提供了某种联系,偶尔的线下聚会或行业活动,也让我感到被理解、被看见。这种状态似乎足够,却也让我反思:这样的连接真的能支撑我们走过所有时刻吗?一年多前,我参与的Web3项目遭遇黑客攻击,损失惨重。由于Web3的跨国性质,问题无法诉诸常规司法途径。面对挫折,我再次体会到那种孤独——没有团队的共鸣,没有朋友的倾听,仿佛又回到了年轻时的无助。那段时间,我感到孤立无援,但细想之下,孤独并非全然负面。它像一面镜子,逼迫我直面内心的缺失,也让我更珍惜那些微小的联结。我们害怕孤独,是因为它暴露了未被填满的角落,还是因为我们尚未学会与自己为伴?或许,社会连接的价值,不只在于驱散孤独,更在于帮我们找到与自己和解的勇气。

这些经历让我开始思考:我们到底需要什么样的社会连接?短暂的与社会疏离或许无妨,但当生活抛来真正的考验,连接的质量便显露无遗。我看到过一项研究:更丰富的社会联系与更健康的晚年密切相关。这或许能解释“丧偶效应”——老年伴侣一方离世后,另一方常在一两年内随之而去。逝者带走了最核心的情感支柱,留下的失衡足以撼动身心。孤独会不会成为比疾病更隐秘的威胁?或许未来,健康评估会将“高质量社会连接”视为与血压同等重要的指标。

在桑贾伊• 古普塔的《逆龄大脑:保持大脑年轻敏锐的新科学》中也有过这样一段话:

如果说填字游戏提高大脑功能的能力可以得100 分为B——,那什么能得A呢?与他人的联系。面对面的联系。2015 年的一项研究及其他许多研究告诉我们,拥有多样化的社交圈子可以提高大脑的可塑性,也有助于保持我们的认知能力。与他人互动交流不仅有助于减轻压力,增强我们的免疫系统,还可以降低我们认知能力下降的风险。

现代社会让“离群索居”变得容易。网上常有人讨论“去哪个城市躺平”,比如鹤岗这样的低成本小城。选择这些地方的人,社交往往极少,却似乎也能在经济独立的支撑下自得其乐。这种生活摆脱了职场无休止的奔波和复杂的人际羁绊,显得轻松自由。这不仅是对自主的追求,或许也是对传统社会期待的一种蔑视。我想起一个视频,讲述艺术家张二冬在终南山隐居十余年的故事。他几乎断绝了社会联系,却在自然中找到平静。与我们这些“入世”的忙碌相比,他的选择更像一种“出世”,在自然中寻得平静。但这份平静让我好奇:对于大部分人或者少部分愿意选择出世的人来说,抛开经济问题不说,极少的连接,真能满足我们内心的全部需求吗?当我们抛开复杂的关系,是否在拥抱自由,还是在逃避某种无法回避的羁绊?社会连接或许并非越多越好,但它有它存在的合理性,每个人是否真的可以完全割舍?即便我们自以为可以。

即便不选择隐居,普通人的社交也未必丰富。职场生活围绕家庭展开,有孩子的可能加入家长群,工作上则局限于几个熟面孔。若非职业需要,日子就在这些固定的圈子中流转。你有没有问过自己:身边有几人能在你最需要时伸出援手?对背井离乡在大城市打拼的人来说,这座城市往往只是落脚之地,其人际网络与“躺平”小城并无本质不同。这种疏离感让我遐想:若未来AI伴侣能取代日常寒暄,我们是否仍需真实的人际联结?当然,那些在一直生活在紧密社会连接关系中的人,或许难以体会这种“稀薄”。

“君子之交淡如水”听来高雅,但若没有持续的往来,纯粹的友谊往往脆弱。反观那些带着互惠色彩的关系,却常能经受时间考验。起初可能是“彼此有求”,但频繁的互动总会催生情谊。大学时的挚友,若无共同的牵绊,如今是否只剩节日问候?我曾以为理想的连接无需算计,但现实告诉我,互助或许更可靠。比如一起做项目,既能分担压力,也能在合作中找到默契。这种“绑定”并不浪漫,却能在疏远的现代社会中,帮我们抵御孤独,甚至重拾一种归属感。

那么,我们需要什么样的社会连接?若有一个能让你毫无保留倾诉的人——爱人、知己或手足——或许一人足矣。若没有这样的存在,像大多数人那样,在生活不同角落有几位有重叠甚至有利益关系的朋友,也已足够。关键不在于数量,而在于能否感到被看见、被理解。但我越来越相信,真正的挑战不在寻找合适的人,而在成为那个值得被连接的人。这需要我们不断成长,学会真诚付出,并掌握维系关系的智慧。

高质量连接的意义,或许不在于数量,而在于彼此点亮的瞬间。那些瞬间,让我们成为彼此的见证者,为生命增添独特的纹理。我们需要连接,是为了不孤独,还是为了成为更完整的自己?只有先学会看见自己——那些彷徨、失落甚至坚韧的时刻——才能敞开内心,让他人走近。即使没有这样的连接,孤独也只是人性的一部分,任何能带来片刻安宁的方式都值得尝试。但无论何时,我们都需保持一份警醒:孤独虽常见,却可能在不经意间,让我们变得脆弱。

2PC之踵?是时候升级二阶段提交协议了

感谢读者,能看到这篇文章,也许是通过 RSS 订阅或者是博客首页来的。博客过去很长时间没有更新,大部分随想都发表在微博,由于发的内容大多是碎碎念,建议大家也不用专门去拜访。在 2010 年时候,曾写过一篇多IDC的数据分布设计的文章提到过 2PC 等协议,最近在 Hackernews 上又有很多有关优化 2PC 讨论,讨论的源头主要由下面这篇文章引起的,因此作了翻译,供大家参阅。

两阶段提交协议(2PC)已经在企业软件系统中使用了三十多年。它是一种非常有影响力的协议,用于确保访问多个分区或分片中的数据的事务的原子性和持久性。它无处不在 – 无论是在旧的“古老的”分布式系统、数据库系统和文件系统,如Oracle,IBM DB2,PostgreSQL 和 Microsoft TxF(支持事务的 NTFS)还是在较年轻的“千禧”系统如 MariaDB、TokuDB、VoltDB、Cloud Spanner、Apache Flink、Apache Kafka 和 Azure SQL 数据库。如果您的系统支持跨分片/分区/数据库的 ACID 事务,那么它很可能会在后台运行 2PC(或其某些变体)。有时它甚至出现在“前台” – 旧版本的 MongoDB 要求用户在应用程序代码中为多文档事务实现 2PC。

在这篇文章中,我们将首先介绍一下 2PC:它是如何工作的以及它解决了什么问题。然后,我们将展示 2PC 的一些主要问题以及现代系统如何试图解决它。不幸的是,这些尝试的解决方案也带来了一些其他问题。在文章最后,我将说明下一代分布式系统应该避免使用 2PC,以及如何实现这一点。

2PC 协议概述

2PC 有很多变种,但基本协议的工作原理如下:

背景假设:一个事务相关的工作已经划分给存储该事务数据的分片节点。我们将在每个分片中执行的工作,称为节点“参与者”的工作。当事务准备好“提交”时,每个参与者都能够独立于彼此完成事务相应的职责。2PC 协议由单个独立的、可协调的节点发起(可能是参与者之一)。

2PC 协议的基本流程如下图所示。(协议从图的顶部开始,然后向下进行。)
2PC

阶段1:协调者询问每个参与者,是否已成功完成其对该事务的职责,并达到可以提交的状态。每个参与者都回答“同意”或“反对”。

阶段2:协调者统计所有回应,如果每个参与者都回答“同意”,那么就提交事务,否则就中止事务。协调者向每个具有提交最终决策权力的参与者发送消息,并接收参与者的确认消息。

此机制确保事务的原子性属性:整个事务将反映在系统的最终状态中,或者不反映在系统的最终状态中。即使只有一个参与者没有提交,那么整个事务将会被中止。换句话说:每个参与者对事务都有“否决权”。

它还确保了事务的持久性。每个参与者确保在阶段1响应“同意”之前,已将所有事务持久地写入存储。这使协调者对事务做出最终决定时,无需担心参与者在投票“同意”之后写入失败。在这篇文章中,当使用术语“持久写入”时,我们有目的地模糊化了两个区别 – 本地临时性存储,或是分布式的写入到多个分片以“持久化”。

除了持久地写入事务相关的数据之外,协议本身还需要额外的写入,在处理消息之前必须使其持久化。例如,一名参与者在第一阶段投票“同意”之前拥有否决权,但在此之后,它不能改变其投票结果。但如果它在投票“同意”后立即崩溃怎么办? 当它恢复时,它可能不知道它投了“同意”,仍然认为它拥有否决权,并继续流程并中止事务。为了防止这种情况,它必须在“同意”投票发给协调者之前,持久化相关投票结果。(除了这个例子,在标准的 2PC 流程中,还有另外两种消息需要发送前持久化操作。)

2PC 的问题

2PC 存在两个主要问题。第一个是众所周知的,并在所有讲述 2PC 的教科书中都进行了讨论。第二个不太知名,但仍然是一个大的问题。

众所周知的问题被称为“阻塞(block)问题”。当每个参与者都投了“同意”,但协调者在将最终决定的消息未能发送给至少一参与者之前就挂了,就会出现这种情况。问题的原因是,通过投票“同意”,每个参与者已经取消了否决事务的权力。但是,协调者仍有绝对权力来决定事务的最终状态。如果协调者在向至少一名参与者发送最终决定的消息之前挂了,那么参与者就无法做出最终决定 – 他们不能中止,因为协调者可能会在挂掉之前决定提交,并且他们无法提交,因为协调者可能决定在失败之前中止。因此,他们必须等待—等到协调者恢复—以便得到最终决定。与此同时,他们无法处理与停滞冲突的事务,因为该事务的写入的最终结果尚未确定。

阻塞问题有两种解决方案。方案一是修改核心协议以消除阻塞问题。不幸的是,这些修改降低了性能 – 通常通过添加额外的一轮通信来实现 – 因此很少在实践中使用。

方案二是保持协议不变,但降低协调者失败从而引发阻塞的可能性 – 例如,通过在副本共识协议上运行 2PC 并确保协议的重要状态被复制。不幸的是,这些解决方案再一次降低了性能,因为协议要求这些副本共识轮次按顺序进行,因此它们可能会给协议增加显著的延迟。

鲜为人知的问题是我称之为“拥堵(cloggage)问题”。在处理事务之后进行 2PC,必然增加事务的等待时间,它等于运行协议所花费的时间。延迟的增加对于许多系统来说已经是一个问题,但更大的问题是,工作节点必须到第二阶段中期才知道事务的最终结果。在他们得到最终结果之前,他们必须为可能中止事务的可能性做好准备,因此在事务得到确认之前,他们通常会暂停其他有冲突的事务进行。这些阻塞的事务同样会进一步阻止其他事务运行,依此类推,直到 2PC 完成,所有被阻止的事务才可以恢复。这些拥堵问题进一步增加了事务平均延迟,并且降低了整体的事务吞吐量。

总结我们上面讨论的问题:2PC 在四个方面污染了系统架构:延迟 (协议的时间加上冲突事务的停顿时间),吞吐量(因为它碰到冲突的事务会停顿),可扩展性 (系统越大,事务更需要多分区的支持,并且必须付出 2PC 的吞吐量和延迟成本以及可用性(前面提到的阻塞问题)。

没有人喜欢 2PC,但几十年来,人们都认为它是一种必要的妥协。

是时候改变了

三十多年来,业界一直在分布式系统中使用两阶段提交。我们已经意识到引入 2PC 会带来性能、可伸缩性和可用性问题,但在没有更好的替代方案之前,仍需要选择使用它。

真相就是,如果有更好的方案,2PC 就没必要存在了。为了实现这一目标,无论是在学术界(如SIGMOD 2016论文)和工业界都在进行尝试。通常的做法是避免分布式事务,例如通过在提交事务之前将数据重新分片,使得事务不再是分布式事务。不幸的是,这种重新分片的做法降低了系统的性能。

我倡导对分布式系统架构进行更深层次的优化。我坚持认为系统可以使用更简单和高效的提交协议,在保证 ACID 的同时,能够处理分布式事务。

一切问题的根源来自一个存在数十年的假设:事务可能随时以任何理由中止。即使我在相同的初始系统状态下运行相同的事务,在下午 2:00 它可能可以成功提交,但在 3:00 时却会提交失败。

为什么需要该假设?大多数架构师认为有以下几个原因。首先,节点可能在任何时候失败,包括在事务处理过程中。系统故障恢复过程中,由于无法获取故障前的内存状态,因此也无法恢复事务失败之前的现场。因此系统需要中止故障出现时所有相关事务。由于任何时候都可能发生故障,这意味着事务可能随时中止。

其次,大多数并发控制协议都需要能够随时中止事务。乐观协议在处理事务后执行“验证”,如果验证失败,则中止事务。悲观协议通常使用锁来防止并发异常,这种锁的使用可能会导致死锁,然后又需要通过中止(至少)事务的方法来解决死锁问题。由于可能随时出现死锁,因此事务需要保留随时中止的能力。

如果来重新审视两阶段提交协议,您将看到随时中止事务的可能性,是 2PC 协议中复杂和延迟的主要原因。参与者不能轻易地告诉其他方是否同意提交,因为他可能在此之后(在事务提交前)出现故障,然后在故障恢复期间中止此事务。因此,他们必须等到事务结束(当所有重要状态都已经持久化)并且严格按照两个阶段进行处理:在第一阶段,每个参与者公开放弃其控制以中止事务,然后才能进入第二阶段,作出最终决定并进行广播。

在我看来,我们需要从参与者中移除否决权,并且以系统无法在执行期间随时中止事务的假设来进行架构设计。只接受以业务逻辑需要来否决事务的情况。如果在给定数据库当前状态下,理论上可以提交事务情况下,无论发生何种类型的故障,该事务都必须可以提交。此外也不接受由于其他并发运行导致的竞争条件而不能最终提交或中止事务。

消除随意中止事务的灵活性听起来很难。我们将很快讨论如何实现这一目标。但首先让我们观察在不能随意中止事务的情况下,提交协议会如何变化。

当事务不能随意中止时,提交协议是什么样的

我们来看两个例子:

在第一个例子中,假设存储变量 X 的节点需要执行一个任务:将 X 的值更改为 42。假设在 X 上没有定义完整性约束或触发器(这可能会阻止系统将 X 设为 42)。在这种情况下,该参与方永远没有中止事务的权力。无论发生什么,该参与方必须将 X 更改为42,如果修改过程中出现系统故障,则必须在故障恢复后将 X 设成 42。由于参与方没有随意中止事务的能力,因此在提交协议期间,不需要检测参与方是否可以提交。

在第二个例子中,假设存储变量 Y 和 Z 值的节点接收到两个事务任务:从前一个 Y 值中减去 1 并将 Z 设置为 Y 的新值。此外,假设 Y 上存在完整性约束,表明 Y 永远不会低于 0(例如它代表零售应用程序中的库存)。因此,此参与方必须运行以下代码:

 IF (Y > 0)
              Subtract 1 from Y
ELSE
              ABORT the transaction
Z = Y

因为应用程序的逻辑需要这样做,所以必须赋予参与者中止事务的权力。但是这种权力是受限的。只有当 Y 的初始值为 0 时,才能中止该事务,否则必须提交。因此,参与方不必等到完成事务之后才知道它是否需要提交。相反:一旦它完成了事务中第一行代码的执行,它就已经知道了最终需要提交还是中止。这意味着相比于 2PC 而言,提交协议将能够更早地启动。

现在让我们将这两个例子组合成一个例子,其中一个事务由两个参与者执行 – 其中一个参与者正在完成第一个例子中描述的工作,另一个参与者正在完成第二个例子中描述的工作。由于我们保证原子性,第一个参与者不能简单地将 X 设置为 42,相反,它自己的工作依赖于 Y 的值。实际上,第一个参与者的事务代码变为:

temp = Do_Remote_Read(Y)
if (temp > 0)
    X = 42

请注意,如果第一个参与者的代码如上的话,那么另一个参与者的代码可以简化为:

IF (Y > 0)
         Subtract 1 from Y
         Z = Y

通过以这种方式编写事务代码,两个参与方都删除了显式中止逻辑。相反,两个参与方都有 if 语句来检查是否会导致原始事务中止的约束。如果原始事务中止,两个参与方最终都无所作为。否则,两个参与方都会根据事务逻辑更改其本地状态。

此时需要注意的一点是,在上面的代码中完全消除了对提交协议的需求。除了应用程序代码在给定数据状态下定义的条件逻辑以外的任何原因,系统都不允许中止事务。并且所有参与者都在这个相同的条件上调整他们的动作,这样他们就可以独立地决定,在由于当前系统状态而无法完成事务的情况下“什么也不做”。因此,已经消除了事务中止的所有可能性,并且在事务处理结束时不需要任何类型的分布式协议来做出关于事务组合的最终决定。2PC 的所有问题都已消除。因为没有协调者,所以也没有阻塞(block)问题。因为所有必要的检查都与事务处理时候完成,而非在事务完成之后检查,所以没有拥堵(cloggage)问题。

此外,只要不允许系统因应用程序逻辑之外的任何原因而中止事务,总是可以像上面那样重写任何事务以替换代码中的中止逻辑,即 if 语句有条件地检查中止条件。此外,可以在重写应用程序代码的情况下实现此目的。(有关如何执行此操作的详细信息超出了本文的范围,但可以高屋建瓴地总结为:当一个节点执行了导致中止的条件逻辑时,它可以设置特殊的标记,其他节点可以在远程读取这些标记。)

实质上:在事务处理系统中有两种类型中止:(1)由数据状态引起的中止和(2)由系统本身引起的中止(例如故障或死锁)。如上所述,类别(1)总是可以根据数据的条件逻辑来编写。因此,如果您可以消除类别(2)中止,则可以消除提交协议。

所以现在,我们所要做的就是解释如何消除类别(2)中止。

消除系统本身中止

我花了将近十年的时间来设计没有系统引发中止的系统。此类系统的示例是 Calvin,CalvinFS,Orthrus,PVW 以及惰性处理事务系统。这一特性的推动力来自于— Calvin —因为它是一个确定性数据库系统。确定性数据库保证在给定一组定义的输入请求的情况下,数据库中只有一个可能的最终数据状态。因此,如果将相同的输入发送到系统的两份不同的副本,两份副本将独立地处理该输入,并将最终达到一致的结果。

系统本身中止,例如系统故障或并发控制竞态条件,从根本上说是不确定性事件。一个副本很可能碰见系统调用失败或进入竞态条件,而另一个副本则不会。如果允许这些非确定性事件导致事务中止,则一个副本会中止事务而另一个副本将提交事务 – 这是对确定性的违背。因此,我们必须以系统故障和竞态条件不能导致事务中止的方式设计 Calvin。对于并发控制,Calvin 使用了避免死锁技术的悲观锁定,该技术确保系统永远不会陷入由于死锁导致的事务中止的状况。面对系统故障,Calvin 无法从中断的位置重启事务(因为在故障期间失去了内存状态)。尽管如此,通过从相同的原始输入重新启动事务,它依然能够完成该事务的处理而不必中止它。

这些解决方案(包括防止死锁以及故障重启恢复事务),都不局限于在确定性数据库系统中使用。在非确定性系统中,如果失败期间,丢失的事务状态被其他非故障节点侦测到,那么事务重启变得略微棘手。但是也有一些简单的方法来解决这个问题,但这些方法已经超出了本文讨论的范围。实际上,我上面提到的其他系统都是非确定性系统。一旦我们意识到消除系统本身故障所带来的威力,我们就将设计植入到 Calvin 之后构建的每个系统中 – 甚至是非确定性系统。

结论

系统架构师继续在分区系统中使用 2PC 的好处微乎其微。我认为,忽略系统本身中止以及状态写入故障是更好的前进方法。确定性数据库系统(如 Calvin 或 FaunaDB)总是会规避系统本身中止,因此通常可避免 2PC。但是这种优势仅发挥在确定性数据库是一个巨大的浪费。从非确定性系统中消除系统本身引起的中止并不困难。最近的项目表明,甚至可以在不使用悲观并发控制技术的系统中消除系统引起的中止。例如,我们上面链接的 PVW 和惰性事务处理系统都使用多版本并发控制(MVCC)的变体。FaunaDB 使用乐观并发控制的变体。

在我看来,几乎没有理由坚持过时的系统性中止假设,当系统在单台机器上运行时,这种假设是合理的。然而,很多现代系统已经扩展到到多台可以故障隔离的机器上。维持该假设就需要成本高昂类似 2PC 的协调和提交协议。2PC 的性能问题一直是非 ACID 系统兴起的主要推动力,这些系统放弃了强一致性保证,以达到更好的可扩展性、可用性和性能。2PC 太慢了!它增加了所有事务的延迟,不仅仅是协议本身的时间占用,还阻止了访问相同数据的其他事务的并发执行。此外,2PC 还限制了可伸缩性(通过降低并发性)和可用性(我们上面讨论的阻塞问题)。前进的道路已经很明确:我们需要在设计系统时重新审视过时的假设,并对两阶段提交说“再见”!

本文作者 DANIEL ABADI,翻译自 It’s Time to Move on from Two Phase Commit方圆对本文翻译亦有贡献。