2026-05-28 10:56:39
CAP定理(CAP theorem)又被称作布鲁尔定理(Brewer’s theorem),是加州大学伯克利分校的计算机科学家埃里克·布鲁尔(Eric Brewer)在2000年的ACM PODC上提出的一个猜想。2002年,麻省理工学院的赛斯·吉尔伯特(Seth Gilbert)和南希·林奇(Nancy Lynch)发表了布鲁尔猜想的证明,使之成为分布式计算领域公认的一个定理。对于设计分布式系统的架构师来说,CAP是必须掌握的理论。
布鲁尔在提出CAP猜想的时候,并没有详细定义Consistency、Availability、Partition Tolerance三个单词的明确定义,因此如果初学者去查询CAP定义的时候会感到比较困惑,因为不同的资料对CAP的详细定义有一些细微的差别,例如:
Consistency: where all nodes see the same data at the same time.
Availability: which guarantees that every request receives a response about whether it succeeded or failed.
Partition tolerance: where the system continues to operate even if any one part of the system is lost or fails.
(https://console.bluemix.net/docs/services/Cloudant/guides/cap_theorem.html#cap-)
Consistency: Every read receives the most recent write or an error.
Availability: Every request receives a (non-error) response – without guarantee that it contains the most recent write.
Partition tolerance: The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.
(https://en.wikipedia.org/wiki/CAP_theorem#cite_note-Brewer2012-6)
Consistency: all nodes have access to the same data simultaneously.
Availability: a promise that every request receives a response, at minimum whether the request succeeded or failed.
Partition tolerance: the system will continue to work even if some arbitrary node goes offline or can’t communicate.
(https://www.teamsilverback.com/understanding-the-cap-theorem/)
为了更好地解释CAP理论,我挑选了Robert Greiner(http://robertgreiner.com/about/)的文章作为参考基础。有趣的是,Robert Greiner对CAP的理解也经历了一个过程,他写了两篇文章来阐述CAP理论,第一篇被标记为“outdated”(有一些中文翻译文章正好参考了第一篇),我将对比前后两篇解释的差异点,通过对比帮助你更加深入地理解CAP理论。
第一版解释:
Any distributed system cannot guaranty C, A, and P simultaneously.
(http://robertgreiner.com/2014/06/cap-theorem-explained/)
简单翻译为:对于一个分布式计算系统,不可能同时满足一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三个设计约束。
第二版解释:
In a distributed system (a collection of interconnected nodes that share data.), you can only have two out of the following three guarantees across a write/read pair: Consistency, Availability, and Partition Tolerance - one of them must be sacrificed.
(http://robertgreiner.com/2014/08/cap-theorem-revisited/)
简单翻译为:在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。
对比两个版本的定义,有几个很关键的差异点:
第二版定义了什么才是CAP理论探讨的分布式系统,强调了两点:interconnected和share data,为何要强调这两点呢? 因为分布式系统并不一定会互联和共享数据。最简单的例如Memcache的集群,相互之间就没有连接和共享数据,因此Memcache集群这类分布式系统就不符合CAP理论探讨的对象;而MySQL集群就是互联和进行数据复制的,因此是CAP理论探讨的对象。
第二版强调了write/read pair,这点其实是和上一个差异点一脉相承的。也就是说,CAP关注的是对数据的读写操作,而不是分布式系统的所有功能。例如,ZooKeeper的选举机制就不是CAP探讨的对象。
相比来说,第二版的定义更加精确。
虽然第二版的定义和解释更加严谨,但内容相比第一版来说更加难记一些,所以现在大部分技术人员谈论CAP理论时,更多还是按照第一版的定义和解释来说的,因为第一版虽然不严谨,但非常简单和容易记住。
第二版除了基本概念,三个基本的设计约束也进行了重新阐述,我来详细分析一下。
1.一致性(Consistency)
第一版解释:
All nodes see the same data at the same time.
简单翻译为:所有节点在同一时刻都能看到相同的数据。
第二版解释:
A read is guaranteed to return the most recent write for a given client.
简单翻译为:对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。
第一版解释和第二版解释的主要差异点表现在:
第一版从节点node的角度描述,第二版从客户端client的角度描述。
相比来说,第二版更加符合我们观察和评估系统的方式,即站在客户端的角度来观察系统的行为和特征。
第一版的关键词是see,第二版的关键词是read。
第一版解释中的see,其实并不确切,因为节点node是拥有数据,而不是看到数据,即使要描述也是用have;第二版从客户端client的读写角度来描述一致性,定义更加精确。
第一版强调同一时刻拥有相同数据(same time + same data),第二版并没有强调这点。
这就意味着实际上对于节点来说,可能同一时刻拥有不同数据(same time + different data),这和我们通常理解的一致性是有差异的,为何做这样的改动呢?其实在第一版的详细解释中已经提到了,具体内容如下:
A system has consistency if a transaction starts with the system in a consistent state, and ends with the system in a consistent state. In this model, a system can (and does) shift into an inconsistent state during a transaction, but the entire transaction gets rolled back if there is an error during any stage in the process.
参考上述的解释,对于系统执行事务来说,在事务执行过程中,系统其实处于一个不一致的状态,不同的节点的数据并不完全一致,因此第一版的解释“All nodes see the same data at the same time”是不严谨的。而第二版强调client读操作能够获取最新的写结果就没有问题,因为事务在执行过程中,client是无法读取到未提交的数据的,只有等到事务提交后,client才能读取到事务写入的数据,而如果事务失败则会进行回滚,client也不会读取到事务中间写入的数据。
2.可用性(Availability)
第一版解释:
Every request gets a response on success/failure.
简单翻译为:每个请求都能得到成功或者失败的响应。
第二版解释:
A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).
简单翻译为:非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
第一版解释和第二版解释主要差异点表现在:
第一版是every request,第二版强调了A non-failing node。
第一版的every request是不严谨的,因为只有非故障节点才能满足可用性要求,如果节点本身就故障了,发给节点的请求不一定能得到一个响应。
第一版的response分为success和failure,第二版用了两个reasonable:reasonable response 和reasonable time,而且特别强调了no error or timeout。
第一版的success/failure的定义太泛了,几乎任何情况,无论是否符合CAP理论,我们都可以说请求成功和失败,因为超时也算失败、错误也算失败、异常也算失败、结果不正确也算失败;即使是成功的响应,也不一定是正确的。例如,本来应该返回100,但实际上返回了90,这就是成功的响应,但并没有得到正确的结果。相比之下,第二版的解释明确了不能超时、不能出错,结果是合理的,注意没有说“正确”的结果。例如,应该返回100但实际上返回了90,肯定是不正确的结果,但可以是一个合理的结果。
3.分区容忍性(Partition Tolerance)
第一版解释:
System continues to work despite message loss or partial failure.
简单翻译为:出现消息丢失或者分区错误时系统能够继续运行。
第二版解释:
The system will continue to function when network partitions occur.
简单翻译为:当出现网络分区后,系统能够继续“履行职责”。
第一版解释和第二版解释主要差异点表现在:
第一版用的是work,第二版用的是function。
work强调“运行”,只要系统不宕机,我们都可以说系统在work,返回错误也是work,拒绝服务也是work;而function强调“发挥作用”“履行职责”,这点和可用性是一脉相承的。也就是说,只有返回reasonable response才是function。相比之下,第二版解释更加明确。
第一版描述分区用的是message loss or partial failure,第二版直接用network partitions。
对比两版解释,第一版是直接说原因,即message loss造成了分区,但message loss的定义有点狭隘,因为通常我们说的message loss(丢包),只是网络故障中的一种;第二版直接说现象,即发生了分区现象,不管是什么原因,可能是丢包,也可能是连接中断,还可能是拥塞,只要导致了网络分区,就通通算在里面。
虽然CAP理论定义是三个要素中只能取两个,但放到分布式环境下来思考,我们会发现必须选择P(分区容忍)要素,因为网络本身无法做到100%可靠,有可能出故障,所以分区是一个必然的现象。如果我们选择了CA而放弃了P,那么当发生分区现象时,为了保证C,系统需要禁止写入,当有写入请求时,系统返回error(例如,当前系统不允许写入),这又和A冲突了,因为A要求返回no error和no timeout。因此,分布式系统理论上不可能选择CA架构,只能选择CP或者AP架构。
1.CP - Consistency/Partition Tolerance
如下图所示,为了保证一致性,当发生分区现象后,N1节点上的数据已经更新到y,但由于N1和N2之间的复制通道中断,数据y无法同步到N2,N2节点上的数据还是x。这时客户端C访问N2时,N2需要返回Error,提示客户端C“系统现在发生了错误”,这种处理方式违背了可用性(Availability)的要求,因此CAP三者只能满足CP。

2.AP - Availability/Partition Tolerance
如下图所示,为了保证可用性,当发生分区现象后,N1节点上的数据已经更新到y,但由于N1和N2之间的复制通道中断,数据y无法同步到N2,N2节点上的数据还是x。这时客户端C访问N2时,N2将当前自己拥有的数据x返回给客户端C了,而实际上当前最新的数据已经是y了,这就不满足一致性(Consistency)的要求了,因此CAP三者只能满足AP。注意:这里N2节点返回x,虽然不是一个“正确”的结果,但是一个“合理”的结果,因为x是旧的数据,并不是一个错乱的值,只是不是最新的数据而已。

理论的优点在于清晰简洁、易于理解,但缺点就是高度抽象化,省略了很多细节,导致在将理论应用到实践时,由于各种复杂情况,可能出现误解和偏差,CAP理论也不例外。如果我们没有意识到这些关键的细节点,那么在实践中应用CAP理论时,就可能发现方案很难落地。
而且当谈到数据一致性时,CAP、ACID、BASE难免会被我们拿出来讨论,原因在于这三者都是和数据一致性相关的理论,如果不仔细理解三者之间的差别,则可能会陷入一头雾水的状态,不知道应该用哪个才好。
今天,我来讲讲CAP的具体细节,简单对比一下ACID、BASE几个概念的关键区别点。
埃里克·布鲁尔(Eric Brewer)在《CAP理论十二年回顾:“规则”变了》(http://www.infoq.com/cn/articles/cap-twelve-years-later-how-the-rules-have-changed)一文中详细地阐述了理解和应用CAP的一些细节点,可能是由于作者写作风格的原因,对于一些非常关键的细节点一句话就带过了,这里我特别提炼出来重点阐述。
CAP关注的粒度是数据,而不是整个系统。
原文就只有一句话:
C与A之间的取舍可以在同一系统内以非常细小的粒度反复发生,而每一次的决策可能因为具体的操作,乃至因为牵涉到特定的数据或用户而有所不同。
但这句话是理解和应用CAP理论非常关键的一点。CAP理论的定义和解释中,用的都是system、node这类系统级的概念,这就给很多人造成了很大的误导,认为我们在进行架构设计时,整个系统要么选择CP,要么选择AP。但在实际设计过程中,每个系统不可能只处理一种数据,而是包含多种类型的数据,有的数据必须选择CP,有的数据必须选择AP。而如果我们做设计时,从整个系统的角度去选择CP还是AP,就会发现顾此失彼,无论怎么做都是有问题的。
以一个最简单的用户管理系统为例,用户管理系统包含用户账号数据(用户ID、密码)、用户信息数据(昵称、兴趣、爱好、性别、自我介绍等)。通常情况下,用户账号数据会选择CP,而用户信息数据会选择AP,如果限定整个系统为CP,则不符合用户信息数据的应用场景;如果限定整个系统为AP,则又不符合用户账号数据的应用场景。
所以在CAP理论落地实践时,我们需要将系统内的数据按照不同的应用场景和要求进行分类,每类数据选择不同的策略(CP还是AP),而不是直接限定整个系统所有数据都是同一策略。
CAP是忽略网络延迟的。
这是一个非常隐含的假设,布鲁尔在定义一致性时,并没有将延迟考虑进去。也就是说,当事务提交时,数据能够瞬间复制到所有节点。但实际情况下,从节点A复制数据到节点B,总是需要花费一定时间的。如果是相同机房,耗费时间可能是几毫秒;如果是跨地域的机房,例如北京机房同步到广州机房,耗费的时间就可能是几十毫秒。这就意味着,CAP理论中的C在实践中是不可能完美实现的,在数据复制的过程中,节点A和节点B的数据并不一致。
不要小看了这几毫秒或者几十毫秒的不一致,对于某些严苛的业务场景,例如和金钱相关的用户余额,或者和抢购相关的商品库存,技术上是无法做到分布式场景下完美的一致性的。而业务上必须要求一致性,因此单个用户的余额、单个商品的库存,理论上要求选择CP而实际上CP都做不到,只能选择CA。也就是说,只能单点写入,其他节点做备份,无法做到分布式情况下多点写入。
需要注意的是,这并不意味着这类系统无法应用分布式架构,只是说“单个用户余额、单个商品库存”无法做分布式,但系统整体还是可以应用分布式架构的。例如,下面的架构图是常见的将用户分区的分布式架构。

我们可以将用户id为0 ~ 100的数据存储在Node 1,将用户id为101 ~ 200的数据存储在Node 2,Client根据用户id来决定访问哪个Node。对于单个用户来说,读写操作都只能在某个节点上进行;对所有用户来说,有一部分用户的读写操作在Node 1上,有一部分用户的读写操作在Node 2上。
这样的设计有一个很明显的问题就是某个节点故障时,这个节点上的用户就无法进行读写操作了,但站在整体上来看,这种设计可以降低节点故障时受影响的用户的数量和范围,毕竟只影响20%的用户肯定要比影响所有用户要好。这也是为什么挖掘机挖断光缆后,支付宝只有一部分用户会出现业务异常,而不是所有用户业务异常的原因。
正常运行情况下,不存在CP和AP的选择,可以同时满足CA。
CAP理论告诉我们分布式系统只能选择CP或者AP,但其实这里的前提是系统发生了“分区”现象。如果系统没有发生分区现象,也就是说P不存在的时候(节点间的网络连接一切正常),我们没有必要放弃C或者A,应该C和A都可以保证,这就要求架构设计的时候既要考虑分区发生时选择CP还是AP,也要考虑分区没有发生时如何保证CA。
同样以用户管理系统为例,即使是实现CA,不同的数据实现方式也可能不一样:用户账号数据可以采用“消息队列”的方式来实现CA,因为消息队列可以比较好地控制实时性,但实现起来就复杂一些;而用户信息数据可以采用“数据库同步”的方式来实现CA,因为数据库的方式虽然在某些场景下可能延迟较高,但使用起来简单。
放弃并不等于什么都不做,需要为分区恢复后做准备。
CAP理论告诉我们三者只能取两个,需要“牺牲”(sacrificed)另外一个,这里的“牺牲”是有一定误导作用的,因为“牺牲”让很多人理解成什么都不做。实际上,CAP理论的“牺牲”只是说在分区过程中我们无法保证C或者A,但并不意味着什么都不做。因为在系统整个运行周期中,大部分时间都是正常的,发生分区现象的时间并不长。例如,99.99%可用性(俗称4个9)的系统,一年运行下来,不可用的时间只有50分钟;99.999%(俗称5个9)可用性的系统,一年运行下来,不可用的时间只有5分钟。分区期间放弃C或者A,并不意味着永远放弃C和A,我们可以在分区期间进行一些操作,从而让分区故障解决后,系统能够重新达到CA的状态。
最典型的就是在分区期间记录一些日志,当分区故障解决后,系统根据日志进行数据恢复,使得重新达到CA状态。同样以用户管理系统为例,对于用户账号数据,假设我们选择了CP,则分区发生后,节点1可以继续注册新用户,节点2无法注册新用户(这里就是不符合A的原因,因为节点2收到注册请求后会返回error),此时节点1可以将新注册但未同步到节点2的用户记录到日志中。当分区恢复后,节点1读取日志中的记录,同步给节点2,当同步完成后,节点1和节点2就达到了同时满足CA的状态。
而对于用户信息数据,假设我们选择了AP,则分区发生后,节点1和节点2都可以修改用户信息,但两边可能修改不一样。例如,用户在节点1中将爱好改为“旅游、美食、跑步”,然后用户在节点2中将爱好改为“美食、游戏”,节点1和节点2都记录了未同步的爱好数据,当分区恢复后,系统按照某个规则来合并数据。例如,按照“最后修改优先规则”将用户爱好修改为“美食、游戏”,按照“字数最多优先规则”则将用户爱好修改为“旅游,美食、跑步”,也可以完全将数据冲突报告出来,由人工来选择具体应该采用哪一条。
ACID是数据库管理系统为了保证事务的正确性而提出来的一个理论,ACID包含四个约束,下面我来解释一下。
Atomicity(原子性)
一个事务中的所有操作,要么全部完成,要么全部不完成,不会在中间某个环节结束。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
Consistency(一致性)
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
Isolation(隔离性)
数据库允许多个并发事务同时对数据进行读写和修改的能力。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
Durability(持久性)
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
可以看到,ACID中的A(Atomicity)和CAP中的A(Availability)意义完全不同,而ACID中的C和CAP中的C名称虽然都是一致性,但含义也完全不一样。ACID中的C是指数据库的数据完整性,而CAP中的C是指分布式节点中的数据一致性。再结合ACID的应用场景是数据库事务,CAP关注的是分布式系统数据读写这个差异点来看,其实CAP和ACID的对比就类似关公战秦琼,虽然关公和秦琼都是武将,但其实没有太多可比性。
BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency),核心思想是即使无法做到强一致性(CAP的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性。
1.基本可用(Basically Available)
分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
这里的关键词是“部分”和“核心”,具体选择哪些作为可以损失的业务,哪些是必须保证的业务,是一项有挑战的工作。例如,对于一个用户管理系统来说,“登录”是核心功能,而“注册”可以算作非核心功能。因为未注册的用户本来就还没有使用系统的业务,注册不了最多就是流失一部分用户,而且这部分用户数量较少。如果用户已经注册但无法登录,那就意味用户无法使用系统。例如,充了钱的游戏不能玩了、云存储不能用了……这些会对用户造成较大损失,而且登录用户数量远远大于新注册用户,影响范围更大。
2.软状态(Soft State)
允许系统存在中间状态,而该中间状态不会影响系统整体可用性。这里的中间状态就是CAP理论中的数据不一致。
3.最终一致性(Eventual Consistency)
系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
这里的关键词是“一定时间” 和 “最终”,“一定时间”和数据的特性是强关联的,不同的数据能够容忍的不一致时间是不同的。举一个微博系统的例子,用户账号数据最好能在1分钟内就达到一致状态,因为用户在A节点注册或者登录后,1分钟内不太可能立刻切换到另外一个节点,但10分钟后可能就重新登录到另外一个节点了;而用户发布的最新微博,可以容忍30分钟内达到一致状态,因为对于用户来说,看不到某个明星发布的最新微博,用户是无感知的,会认为明星没有发布微博。“最终”的含义就是不管多长时间,最终还是要达到一致性的状态。
BASE理论本质上是对CAP的延伸和补充,更具体地说,是对CAP中AP方案的一个补充。前面在剖析CAP理论时,提到了其实和BASE相关的两点:
CAP理论是忽略延时的,而实际应用中延时是无法避免的。
这一点就意味着完美的CP场景是不存在的,即使是几毫秒的数据复制延迟,在这几毫秒时间间隔内,系统是不符合CP要求的。因此CAP中的CP方案,实际上也是实现了最终一致性,只是“一定时间”是指几毫秒而已。
AP方案中牺牲一致性只是指分区期间,而不是永远放弃一致性。
这一点其实就是BASE理论延伸的地方,分区期间牺牲一致性,但分区故障恢复后,系统应该达到最终一致性。
综合上面的分析,ACID是数据库事务完整性的理论,CAP是分布式系统设计理论,BASE是CAP理论中AP方案的延伸。
来源:从 0 开始学架构
2026-05-14 20:49:18
普通的电脑主机都是一个硬盘挂接到一个总线上,这里是一对一的关系。如果某一处发生故障,可能导致整个网络瘫痪,我们称这个存储网络中存在单点故障。随着现代信息技术的发展,在IT基础设施运行过程中,对存储网络的安全性和稳定性要求越来越高。在存储网络中,为了避免单点故障,高可靠的存储网络除了对设备和器件做了冗余设计,同时通过多条冗余路径的互联来规避链路的单点故障。而到了有光纤组成的SAN环境,或者由iSCSI组成的IPSAN环境,由于主机和存储通过了光纤交换机或者多块网卡及IP来连接,这样的话,就构成了多对多的关系。也就是说,主机到存储之间的IO有多条路径可以选择,每个主机到所对应的存储可以经过几条不同的路径。利用冗余设计满足存储网络的高可靠性和高性能,就需要通过多路径技术来实现。这种冗余设计既能提高整个存储网络运行的可靠性,又能利用冗余设计达到更高的性能要求。
多路径磁盘(Multipath Disk)的主要作用是提供冗余和负载均衡,以提高存储系统的可靠性和性能。具体来说:
冗余/高可用性:当服务器和存储设备之间存在多条物理路径(例如,通过多个HBA卡、交换机或控制器连接)时,多路径技术可以确保在一条路径发生故障时,自动切换到另一条可用路径,从而避免单点故障,保证业务连续性。
负载均衡:多路径软件可以将I/O流量分散到多条路径上,从而提高整体的带宽和性能。有些多路径实现还支持动态负载均衡,根据路径的当前负载情况调整流量分配。
路径管理:多路径软件可以检测路径的状态,并自动处理路径故障和恢复。当一条路径恢复后,它可以被重新加入使用。
设备统一命名:通过多路径软件,操作系统可以看到一个统一的设备名(如/dev/mapper/mpathb),而不是多个独立的设备节点(如/dev/sdj、/dev/sdk等)。这简化了管理,因为无论使用哪条路径,都访问同一个存储设备。
提高性能:通过并发使用多条路径,可以增加存储访问的带宽,特别是对于高I/O需求的应用程序。
在Linux中,多路径功能通常由device-mapper-multipath包提供,它创建了一个虚拟设备(在/dev/mapper下)来代表多路径磁盘。这样,上层应用(如LVM、文件系统等)就可以像使用普通磁盘一样使用多路径设备,而不必关心底层路径的变化。
在配置LVM时,必须使用多路径设备(如/dev/mapper/mpathb)作为物理卷,而不是使用单个路径的设备节点(如/dev/sdj)。这是因为如果使用单个路径设备,当该路径故障时,LVM将无法访问该物理卷,即使还有其他路径可用。而使用多路径设备,即使一条路径故障,多路径软件会自动切换到其他路径,LVM不会受到影响。因此,多路径磁盘在需要高可用性和高性能的企业级存储环境中非常重要。
主要作用:防止单点故障 物理拓扑: 服务器 ├── HBA卡1 → 光纤交换机1 → 存储控制器A └── HBA卡2 → 光纤交换机2 → 存储控制器B ↓ 同一存储LUN
路径冗余:提供多条物理路径访问同一存储设备

自动故障切换:当某条路径故障时,自动切换到正常路径
在单路径组网中,主机HBA或iSCSI启动器在链路断开时会进行一段时间的尝试重连(即防闪断机制,一般30秒或60秒),如果重连成功则I/O能够继续执行;而在多路径组网中,由于存在备用路径,所以通常会设置缩短HBA或iSCSI的重连时间,以使多路径软件能够快速感知I/O失败并切换路径,从而减小上层应用I/O阻塞的时间。

零中断访问:应用无感知的路径切换,保证业务连续性
UltraPath在路径故障时可以自动将I/O转移到其他可用路径,流程如下图所示:
1)应用向UltraPath生成的虚拟磁盘下发I/O;
2)UltraPath将I/O转发给一个path 1(即SCSI设备);
3)路径故障导致该path 1上I/O失败;
4)UltraPath将I/O重新下发给另一个path 2;
5)path 2返回I/O成功;
6)UltraPath向上层应用返回I/O成功。

I/O流量分发: 读取请求:path1 → path2 → path3 → path1... 写入请求:path2 → path3 → path1 → path2...
带宽聚合:多路径并发传输,提高吞吐量
性能优化:根据算法(RR、队列深度等)分配I/O请求
避免单路径瓶颈:防止单个HBA卡或端口成为性能瓶颈

路径检测:持续监控各路径健康状态
自动恢复:故障路径修复后自动重新启用
动态优化:根据路径性能动态调整流量分配
# 典型SAN多路径配置 服务器:2个HBA卡 → 2台光纤交换机 → 存储双控制器 设备映射: /dev/sdc (HBA1 → 交换机1 → 控制器A) /dev/sdd (HBA1 → 交换机2 → 控制器B) /dev/sde (HBA2 → 交换机1 → 控制器A) /dev/sdf (HBA2 → 交换机2 → 控制器B) 多路径聚合后:/dev/mapper/mpath0 (包含以上4条路径)
# iSCSI多路径配置 服务器:2个网卡 → 2台交换机 → 存储双IP端口 路径: eth1 → 192.168.1.10 → LUN1 eth1 → 192.168.2.10 → LUN1 eth2 → 192.168.1.10 → LUN1 eth2 → 192.168.2.10 → LUN1
| 场景 | 无多路径 | 有多路径 | 优势体现 |
|---|---|---|---|
| HBA卡故障 | 存储访问中断 | 自动切换到另一HBA卡 | 业务不中断 |
| 光纤交换机故障 | 存储不可用 | 使用另一交换机路径 | 高可用性 |
| 存储控制器故障 | 存储离线 | 切换到备用控制器 | 持续可用 |
| 高并发I/O | 单路径带宽限制 | 多路径聚合带宽 | 性能提升 |
| 维护操作 | 需停机维护 | 可在线更换部件 | 运维便利 |
DM-Multipath (device-mapper-multipath)
RHEL/CentOS默认
配置简单,功能完善
EMC PowerPath
商业软件
高级功能,厂商支持
VMware PSA/NMP
虚拟化环境专用
与vSphere深度集成
| 模式 | 原理 | 适用场景 |
|---|---|---|
| 故障切换 | 主路径故障时切换到备用 | 高可用性优先 |
| 轮询(RR) | 均匀分发I/O到所有路径 | 性能优先 |
| 队列深度 | 按路径队列深度分配 | 动态负载均衡 |
| 最小队列 | 选择队列最少的路径 | 实时负载优化 |
linux中大多已经默认安装了multipath,只需设置开机启动即可.
systemctl enable multipathd.service
$multipath -T
defaults {
verbosity 2
polling_interval 5
max_polling_interval 20
reassign_maps "no"
multipath_dir "//lib/multipath"
path_selector "service-time 0" #选择那一条路径进行下次IO操作
path_grouping_policy "multibus" #路径组策略
uid_attribute "ID_SERIAL"
prio "const"
prio_args ""
features "0"
path_checker "tur" # 决定路径状态的方法
alias_prefix "mpath"
failback "immediate" #故障恢复的模式
rr_min_io 1000 # 在当前的用户组中,在切换到另外一条路径之前的IO请求的数目
rr_min_io_rq 1
max_fds "max"
rr_weight "uniform"
no_path_retry "fail" #默认fail,在disable queue之前系统尝试使用失效路径的次数的数值
queue_without_daemon "no"
flush_on_last_del "no"
user_friendly_names "yes"
fast_io_fail_tmo 5
bindings_file "/etc/multipath/bindings"
wwids_file "/etc/multipath/wwids"
prkeys_file "/etc/multipath/prkeys"
log_checker_err always
all_tg_pt "no"
retain_attached_hw_handler "yes"
detect_prio "yes"
detect_checker "yes"
force_sync "no"
strict_timing "no"
deferred_remove "no"
config_dir "/etc/multipath/conf.d"
delay_watch_checks "no"
delay_wait_checks "no"
san_path_err_threshold "no"
san_path_err_forget_rate "no"
san_path_err_recovery_time "no"
marginal_path_err_sample_time "no"
marginal_path_err_rate_threshold "no"
marginal_path_err_recheck_gap_time "no"
marginal_path_double_failed_time "no"
find_multipaths "on"
uxsock_timeout 4000
retrigger_tries 0
retrigger_delay 10
missing_uev_wait_timeout 30
skip_kpartx "no"
disable_changed_wwids ignored
remove_retries 0
ghost_delay "no"
find_multipaths_timeout -10
enable_foreign ""
marginal_pathgroups "no"
}
# 后端设备(不包含以下设备节点)
blacklist {
devnode "^sda"
devnode "^hd[a-z]"
devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
devnode "^dcssblk[0-9]*"
devnode "^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]"
devnode "^(td|hd|vd)[a-z]"
devnode "^cciss!c[0-9]d[0-9]*"
}
device {
vendor "SGI" #厂商名称,可通过multipath –v3获取到
product "Universal Xport" #产品型号
getuid_callout "/sbin/scsi_id -g -u -s /block/%n" #获得唯一设备号使用的默认程序
}
device {
vendor "^DGC"
product "LUNZ"
}
device {
vendor "EMC"
product "LUNZ"
}
device {
vendor "DELL"
product "Universal Xport"
}
device {
vendor "IBM"
product "Universal Xport"
}
device {
vendor "IBM"
product "S/390"
}
device {
vendor "LENOVO"
product "Universal Xport"
}
device {
vendor "(NETAPP|LSI|ENGENIO)"
product "Universal Xport"
}
device {
vendor "STK"
product "Universal Xport"
}
device {
vendor "SUN"
product "Universal Xport"
}
device {
vendor "(Intel|INTEL)"
product "VTrak V-LUN"
}
device {
vendor "Promise"
product "VTrak V-LUN"
}
device {
vendor "Promise"
product "Vess V-LUN"
}
}
blacklist_exceptions {
property "(SCSI_IDENT_|ID_WWN)"
}
devices {
device {
vendor "COMPELNT"
product "Compellent Vol"
path_grouping_policy "multibus"
no_path_retry "queue"
}
}
overrides {
...
}
multipaths {
multipath {
wwid "36000d3100366e6000000000000000021" #磁盘的WWID
alias "k8sapp"
}
multipath {
wwid "36000d3100366e6000000000000000020"
alias "k8slog"
}
}
# 查看详细路径拓扑 multipath -ll 输出示例: mpathb (3600a0b80001234567890123456789012) dm-1 IBM,2145 size=100G features='1 queue_if_no_path' hwhandler='1 alua' wp=rw |-+- policy='service-time 0' prio=50 status=active | |- 2:0:0:1 sdc 8:32 active ready running | `- 3:0:0:1 sde 8:64 active ready running `-+- policy='service-time 0' prio=10 status=enabled |- 2:0:1:1 sdd 8:48 active ready running `- 3:0:1:1 sdf 8:80 active ready running
路径状态解读:
status=active:当前活动路径
status=enabled:备用可用路径
prio=50:优先级较高(主路径)
prio=10:优先级较低(备路径)
RTO(恢复时间目标)降低:路径切换秒级完成
RPO(恢复点目标)保障:数据访问不中断
# 在线维护示例 # 1. 更换HBA卡时 multipath -f /dev/mapper/mpath0 # 临时移除设备 # 更换硬件... multipath -v2 # 重新扫描识别 # 业务无感知完成维护
# 多路径策略配置示例 /etc/multipath.conf: defaults: polling_interval 10 path_selector "round-robin 0" path_grouping_policy multibus rr_min_io 100 rr_weight priorities failback immediate
物理层冗余:多路径(多条物理路径) 逻辑层冗余:LVM镜像(多个PV镜像) 应用层:业务系统
先配置多路径,后配置LVM
LVM只操作多路径设备(/dev/mapper/*)
定期监控路径状态
多路径磁盘的核心价值在于:
可靠性:消除单点故障,提供存储高可用
性能:聚合带宽,提升I/O吞吐量
可维护性:支持在线维护,降低运维窗口
可管理性:统一设备视图,简化存储管理
在企业级存储环境中,多路径是确保关键业务连续性和性能的基础必备技术,与LVM、RAID等技术协同工作,构建稳定可靠的存储架构。
参考:
2026-05-05 20:50:03
从单机到多节点分布式推理部署,架构上最大的变化是从"单机多卡"的内部通信变成了"跨节点"的网络通信。核心在于如何配置分布式节点和并行策略。
核心架构变化:从TP到PP+TP
在单机多卡部署时,我们主要使用 张量并行(Tensor Parallelism,TP),它在单节点内通过高速总线(如NVLink)拆分模型权重,通信开销极低。
但在多节点场景下,跨节点的网络带宽远低于节点内总线,如果继续单独使用TP,大量的数据传输会成为性能瓶颈。因此,多节点部署的核心策略是 流水线并行(Pipeline Parallelism,PP)+ 张量并行(Tensor Parallelism,TP) 的组合。
PP (Pipeline Parallelism, 流水线并行):将模型的不同层切分到不同节点上。数据像一个流水线一样,在节点间依次处理。这能有效减少跨节点的数据传输量。

TP (Tensor Parallelism, 张量并行):在每个节点内部,继续将切分到的层(Layer)拆分到多张GPU上,充分利用节点内的高速互联。

简单来说,策略就是:节点之间用PP,节点内部用TP。一个部署任务的总GPU数 = PP大小 × TP大小。
分布式推理:vLLM 集成 Ray
使用vllm做分布式推理,目前主流方案是与 Ray 分布式计算框架集成,将 Ray 作为分布式执行的后端调度器。在多节点场景下,Ray 负责管理集群资源、在节点间协调任务,而 vLLM 则作为推理引擎运行在 Ray 的 Worker 上,利用 Ray 分配好的 GPU 资源进行模型推理 。这两者形成了一种“资源调度(Ray)+ 计算引擎(vLLM)”的经典协作模式。
以下是它们集成的分层架构示意图:

下面从具体的配置步骤展开,涵盖从 Ray 集群搭建到 vLLM 集成以及生产环境调优的完整流程。
在启动 vLLM 服务之前,需要在所有节点上安装并启动一个 Ray 集群。这是多节点部署的前提条件 。
1. 安装 Ray:在所有节点上执行以下命令安装 Ray。
pip install "ray[default]"
2. 启动 Head 节点:在选为主节点(管理节点)的机器上执行。
ray start --head --port=6379
也可以指定其他端口,6379 是 Ray 的默认端口。执行成功后,终端会显示 Ray 集群的地址,类似 192.168.1.10:6379 。
3. 连接 Worker 节点:在其余所有的 Worker 节点上,执行以下命令连接到 Head 节点。将 <HEAD_NODE_IP> 替换为 Head 节点的实际 IP 地址。
ray start --address=<HEAD_NODE_IP>:6379
执行完这些步骤后,一个多节点的 Ray 集群就运行起来了,此时 vLLM 就可以接入并使用集群内的所有 GPU 资源。
Ray 集群就绪后,只需在 Head 节点上正常启动 vLLM 服务,并通过关键参数指定使用 Ray 作为分布式执行后端。
以 Qwen3 模型部署为例,使用2个节点,每个节点配有4张GPU,部署Qwen3-32B模型。以下是一个生产环境级别的vLLM启动命令示例:
vllm serve /path/to/Qwen3-32B \ --tensor-parallel-size 4 \ # 每个节点内使用4卡进行TP --pipeline-parallel-size 2 \ # 共有2个节点进行PP --distributed-executor-backend ray \ # 多节点必须使用Ray作为后端 --max-model-len 8192 \ # 根据实际场景设置,过长会消耗大量显存 --gpu-memory-utilization 0.90 \ # 保留部分显存余量 --max-num-seqs 256 \ # 控制并发量,避免OOM --trust-remote-code \ # Qwen3需要此参数 --enforce-eager \ # 多节点复杂图下可能更稳定(可选) --swap-space 16 \ # 设置CPU交换空间(单位:GiB) --api-key $YOUR_API_KEY # 设置访问认证token --enable-auto-tool-choice \ --tool-call-parser hermes \ --reasoning-parser qwen3 \ --disable-custom-all-reduce ......
| 参数 | 作用与说明 |
|---|---|
--distributed-executor-backend ray |
这是集成的核心开关。 强制 vLLM 使用 Ray 来管理跨节点的所有分布式工作进程(workers)。如果没有这个参数,vLLM 默认会使用 Python 的多进程(mp)后端,但这只能用于单机部署,无法跨节点通信 。 |
--tensor-parallel-size (或 -tp) |
设置张量并行的规模,即每个模型层(Layer)会被切分到几张 GPU 上。在多节点环境中,这个值乘以节点数,通常等于希望用于单个模型副本的总 GPU 数量。例如,-tp 4 意味着每个节点内部的 4 张 GPU 会通过高速互联协同工作,共同计算模型的一部分 。 |
--pipeline-parallel-size (或 -pp) |
设置流水线并行的规模,即模型的不同层被切分到几个节点上。这个值通常等于总节点数。在上面的例子中,-pp 2 表示模型的不同阶段会分布在 2 个节点上,数据以流水线方式依次通过这两个节点 。 |
vLLM 在启动时会自动连接已经运行的 Ray 集群,无需额外指定 Ray 地址 。
在多节点、高性能生产环境中,网络通信往往是性能瓶颈。需要通过环境变量来精细控制底层通信库(如 NCCL)的行为,以确保数据能在节点间高速传输 。
建议在启动 vLLM 服务的 Head 节点上,预先设置以下环境变量:
| 环境变量 | 生产环境推荐配置 | 作用说明 |
|---|---|---|
NCCL_SOCKET_IFNAME |
ib0 (如果使用 InfiniBand) 或 eth0
|
指定 NCCL 通信使用的网络接口。如果节点间有专用的 InfiniBand (IB) 高速网络,务必设置为此 IB 网卡的名称,以获得最佳性能 。 |
NCCL_IB_DISABLE |
0 (如果使用 IB) 或 1 (如果使用以太网) |
控制是否禁用 InfiniBand。0 表示启用 IB,1 表示禁用。需要与 NCCL_SOCKET_IFNAME 的设置保持一致 。 |
NCCL_DEBUG |
INFO (用于调试) 或 WARN (生产环境) |
设置 NCCL 的日志级别。在初次部署或遇到通信问题时,建议设为 INFO 以查看详细的连接和初始化信息。稳定运行后可以调高日志级别以减少输出 。 |
VLLM_HOST_IP |
当前节点的 IP 地址 | 在某些网络复杂的环境(如 Kubernetes)中,明确指定 vLLM 用于节点间通信的 IP 地址,可以避免自动获取错误 IP 导致的连接问题 。 |
NCCL_IB_HCA |
mlx5_0,mlx5_1,... 或 mlx5_*
|
当每个 GPU 有独立的 IB 网卡时,可以用此变量指定具体的 RDMA 网卡设备,实现网卡与 GPU 的亲和性绑定,进一步提升性能 。 |
环境变量可以在启动 vLLM 的终端中直接 export,或者将它们写入启动脚本的开头。例如:
# 设置 NCCL 超时时间,防止通信卡死 export NCCL_TIMEOUT=1800 # 设置共享内存大小,vLLM 会用到 /dev/shm 进行数据交换 export VLLM_SHM_SIZE=16GB export NCCL_SOCKET_IFNAME=ib0 export NCCL_IB_DISABLE=0 export NCCL_DEBUG=INFO vllm serve /path/to/Qwen3-32B \ --tensor-parallel-size 4 \ --pipeline-parallel-size 2 \ --distributed-executor-backend ray \ --trust-remote-code
量化技术的应用:对于Qwen3-32B这样的模型,在生产环境中强烈建议使用量化版本(如FP8、AWQ、GPTQ)。例如,Qwen/Qwen3-32B-FP8 可以显著降低模型显存占用和跨节点通信的数据量,从而允许更大的并发度或更低的延迟。启动时需配合 --kv-cache-dtype fp8 和 --quantization fp8 等参数。
性能调优测试:生产环境没有“一键最优解”。需要根据实际的请求流量、输入输出长度、SLA要求,对 TP 和 PP 的组合进行基准测试。例如,在8卡H100的节点上,对于Qwen3-32B,是选择 TP=8, PP=1(单节点)还是 TP=4, PP=2(双节点),需要通过压力测试来决定。
除了上述配置,在生产环境中还需要留意以下几点:
共享内存:确保所有 vLLM 容器或进程挂载了 /dev/shm(共享内存),并且大小足够(例如 16GB)。vLLM 在数据处理和进程通信中会大量使用共享内存 。在 Docker 或 Kubernetes 中,这通常需要显式配置。
环境一致性:确保所有节点上的 Python 环境、vLLM 版本、Ray 版本以及模型路径完全一致 。任何不一致都可能导致无法预料的错误。
权限设置:在 Kubernetes 等容器化环境中,可能需要为容器添加 IPC_LOCK 权限,以允许 Ray 和 vLLM 锁定内存中的页面,这对性能至关重要 。
监控与健康检查:利用 Ray 的仪表盘(默认在 Head 节点的 8265 端口)监控集群状态。同时,为 vLLM 服务配置健康检查端点(如 /health),以便于服务发现和自动恢复 。
在开始之前,有几个关键点需要先明确:
硬件假设:本次配置以 2 个节点、每个节点 4 张 Hopper 系列 GPU(如 H100)为例。这是 DeepSeek-V3.2 的常见部署场景。
核心优化原则:对于 DeepSeek-V3.2,官方强烈推荐采用“数据并行 + 专家并行”(DP+EP)模式,而不是单纯的张量并行(TP)。因为其核心算子主要针对 TP=1 进行了优化,DP+EP 模式能避免因注意力头填充导致的性能开销,提供更优的吞吐量 。
基础环境:请确保 Ray 集群已在所有节点上正确启动和配置,vLLM 将通过 --distributed-executor-backend ray 参数连接并使用该集群 。
以下命令需在 Ray Head 节点上执行。它将启动一个使用 2 个节点(共 8 张 GPU)、并启用工具调用和生产级性能优化的 DeepSeek-V3.2 服务。
# 设置访问 API 密钥 (推荐从环境变量读取) export VLLM_API_KEY="sk-your-secret-production-token" vllm serve deepseek-ai/DeepSeek-V3.2 \ # 模型加载相关 --model deepseek-ai/DeepSeek-V3.2 \ --tokenizer-mode deepseek_v32 \ --trust-remote-code \ --dtype auto \ --kv-cache-dtype fp8 \ \ # 并行策略相关 (核心: Ray + DP + EP) --distributed-executor-backend ray \ --data-parallel-size 8 \ --enable-expert-parallel \ --tensor-parallel-size 1 \ \ # 工具调用相关 --enable-auto-tool-choice \ --tool-call-parser deepseek_v32 \ --reasoning-parser deepseek_v3 \ --chat-template /path/to/xxx_deepseek.jinja \ # 可选 \ # 性能与内存调优 --max-model-len 8192 \ --gpu-memory-utilization 0.90 \ --max-num-seqs 256 \ --enable-chunked-prefill \ --enable-prefix-caching \ \ # 服务器及API配置 --host 0.0.0.0 \ --port 8000 \ --served-model-name deepseek-v3-2 \ --api-key $VLLM_API_KEY \ --disable-log-requests
这类参数确保 vLLM 能正确识别和加载 DeepSeek-V3.2 的特殊架构。
| 参数 | 说明 | 生产环境建议 |
|---|---|---|
--model |
指定模型ID或本地路径。 | 必填。生产环境建议使用已下载到本地共享存储的路径,避免启动时从 Hugging Face 拉取,以加快部署速度并提高稳定性。 |
--tokenizer-mode |
设置分词器模式。 |
必须设置为 deepseek_v32,以适配 DeepSeek-V3.2 独特的对话模板 。vLLM 通过此参数进行特殊适配,以确保对话格式正确。
|
--trust-remote-code |
允许加载远程自定义代码。 | 必须添加。DeepSeek 模型依赖此项加载其配置文件。因为它们可能包含未合并到主分支的配置或建模文件。为了安全和稳定,建议在确认代码来源可信后使用。 |
--dtype |
模型权重和激活的数据类型。 | 设为 auto 即可自动使用模型默认的 bfloat16。 |
--kv-cache-dtype |
KV缓存的数据类型。 | 根据场景选择。fp8(默认)可缓存更多 token,适合长上下文;bfloat16 无量化开销,适合短请求 。生产环境建议进行 A/B 测试确定。 |
这是与单机部署最大的区别所在。采用 DP + EP + TP=1 的黄金组合。
| 参数 | 说明 | 生产环境建议 |
|---|---|---|
--distributed-executor-backend |
分布式执行后端。 |
必须设置为 ray。这是 vLLM 能够跨节点调度的基础 。 |
--data-parallel-size (-dp) |
数据并行度。 | 设置为集群中总的 GPU 数量(例如 8)。它会将整个模型复制多份,并行处理不同批次的数据,极大提升吞吐量 。 |
--enable-expert-parallel (-ep) |
启用专家并行。 | 强烈建议添加。对于 MoE 模型 DeepSeek-V3.2,此参数会将不同的专家网络分布到不同 GPU 上,与 DP 结合实现最佳性能 。 |
--tensor-parallel-size (-tp) |
张量并行度。 |
设置为 1。这是实现 DP+EP 模式的关键,将 TP 关闭,让核心算子在单 GPU 上高效运行 。 |
为什么是 DP=8, EP=8, TP=1?
这相当于在 8 张 GPU 上创建了 8 个模型副本(DP),同时每个副本的 MoE 专家层也被分布到所有 8 张 GPU 上(EP),而每个 GPU 上的模型其他部分则独立运行(TP=1)。这种模式完美匹配了 DeepSeek-V3.2 的 MoE 架构,是官方推荐的服务模式 。

Each GPU holds a complete copy of the model but processes a different batch of data. This is the simplest and most common strategy.

Models with a Mixture-of-Experts (MoE) architecture contain many "expert" sub-models. Only a subset of these experts is activated to process each request. Therefore, these expert sub-models can be stored on different GPUs. When an inference workload requires a specific expert, the data is routed to the relevant GPU.
让模型具备使用外部工具的能力。
| 参数 | 说明 | 生产环境建议 |
|---|---|---|
--enable-auto-tool-choice |
启用自动工具调用。这是开启模型自主选择是否调用工具以及调用哪个工具功能的总开关。 | 必填。告诉 vLLM 允许模型在认为合适时生成工具调用。 |
--tool-call-parser |
指定用于解析模型输出的工具调用解析器。不同模型的工具调用输出格式不同 |
必须设置为 deepseek_v32,以确保能正确解析 DeepSeek 模型生成的工具调用指令 。 |
--reasoning-parser |
指定推理过程解析器。 |
设置为 deepseek_v3,DeepSeek 模型支持“思考模式”(thinking mode),在输出最终答案前会进行内部推理。此参数确保 vLLM 能正确处理这部分内容。在API响应中,思考内容会放在 message.reasoning 字段,而非 DeepSeek 官方API的 reasoning_content 字段。
|
--chat-template |
指定自定义的对话模板文件(.jinja)路径。 |
通常不需要手动设置,因为 --tokenizer-mode deepseek_v32 已处理了模板问题。但如果遇到工具调用格式问题,可以检查是否需要自定义模板。
|
生产环境稳定性和效率的保障。
| 参数 | 说明 | 生产环境建议 |
|---|---|---|
--max-model-len |
模型最大序列长度。 | 根据实际业务需求设定(如 8192),不要无脑拉满。值越小,KV 缓存占用越少,可支持的并发度越高 。 |
--gpu-memory-utilization |
vLLM 可使用的 GPU 显存上限。 | 推荐设置为 0.85 - 0.95。预留部分显存给 CUDA 内核和其他开销,防止 OOM 。 |
--max-num-seqs |
单批次最大并行处理的序列数。 | 在多节点和 MoE 模型下,过大的 batch 可能导致 CUDA 错误。建议从较小的值开始(如 256)进行压力测试,再逐步增加 。 |
--enable-chunked-prefill |
启用分块预填充。 | 推荐开启(vLLM V1 中默认开启)。它能将长 prompt 的预填充分块,与解码请求混合 batch,显著提升 GPU 利用率和解码延迟 。 |
--enable-prefix-caching |
启用前缀缓存。如果请求的prompt前缀(如系统提示词、few-shot示例)相同,可以复用KV缓存,极大提升首字延迟和吞吐量。 | 推荐开启。对于多轮对话或共享系统 prompt 的场景,能复用 KV 缓存,大幅降低首字延迟 。在RAG或多轮对话等场景中效果显著,建议开启。 |
对外提供服务的关键设置。
| 参数 | 说明 | 生产环境建议 |
|---|---|---|
--host 和 --port
|
服务监听地址和端口。 |
默认通常是 127.0.0.1:8000。如需对外提供服务,需设置为 0.0.0.0:8000 以监听所有网络接口,方便内部其他服务调用。
|
--served-model-name |
API 接口中暴露的模型名称。 | 可自定义,如 deepseek-v3-2-prod,便于进行模型版本管理和优雅的无感升级。 |
--api-key |
API 访问认证密钥。 |
强烈建议设置。在生产环境中,这是防止服务被未授权访问的第一道防线。客户端请求时需在 Header 中添加 Authorization: Bearer <你的密钥> 。 |
--disable-log-requests |
禁用每个请求的详细日志输出。 | 推荐添加。在生产环境中,可以大幅减少日志 I/O 和存储压力,避免敏感信息泄露。 |
客户端调用示例:当设置了 --api-key 后,客户端代码(如 Python)需要这样配置请求头:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("VLLM_API_KEY"), # 使用与服务端相同的 key
base_url="http://<你的模型服务IP>:8000/v1"
)
在需要启用“思考模式”时,需通过 extra_body 传递参数,这与 DeepSeek 官方 API 略有不同 :
response = client.chat.completions.create(
model="deepseek-v3-2",
messages=[{"role": "user", "content": "..."}],
tools=[...], # 调用工具定义
extra_body={"chat_template_kwargs": {"thinking": True}} # 关键点
)
# 获取思考内容:response.choices[0].message.reasoning
关于 DeepGEMM:DeepSeek 模型依赖 DeepGEMM 库进行高效的 MoE 和 MQA 计算。默认安装即可。如果在 H20 等特定 GPU 上遇到性能问题或过长的预热时间,可以尝试通过设置环境变量 VLLM_USE_DEEP_GEMM=0 来禁用它 。
监控与告警:结合 Prometheus 和 Grafana 监控体系,关注 vLLM 暴露的指标,特别是抢占次数(preemption count)。如果抢占频繁,说明显存不足,需调整 max_num_seqs 或 gpu_memory_utilization 。
参考:
2026-04-28 10:59:03
DM8数据库的备份主要分为物理备份和逻辑备份两大类,两者适用的场景和命令不同。
物理备份:直接备份数据库的物理文件(如数据文件、日志文件等)。这种方式速度快,是保障数据安全最直接的手段,常用于全库级别的灾难恢复。
逻辑备份:使用 dexp 工具将数据库对象(如表、视图、存储过程等)的结构和数据导出为一个独立的文件。这种方式非常灵活,可以指定备份库、用户、模式或表,常用于数据迁移或特定对象的备份。
物理备份分为冷备(数据库关闭)和热备(数据库运行中)两种。其中,热备需要提前开启数据库的归档模式。
冷备需要在数据库服务关闭的状态下进行,操作简单,不需要开启归档。
使用 dmrman 命令行工具
进入达梦数据库安装目录的 bin 文件夹,执行 dmrman 命令进入交互界面,然后执行备份命令。
# 进入bin目录 cd /dm8/bin # 执行dmrman ./dmrman # 在RMAN交互界面中执行全量备份 RMAN> backup database '/dm8/data/DAMENG/dm.ini' full backupset '/dm8/backup/full_bak';
参数说明:
/dm8/data/DAMENG/dm.ini:数据库实例配置文件路径,请根据实际路径修改。
full:执行全量备份。
backupset:指定备份集存放的目录。
热备在数据库运行时进行,不影响业务,但必须确保数据库已开启归档模式。
开启归档模式(SQL命令行)
在执行任何备份前,务必完成以下检查,否则备份可能失败或无法恢复。
-- 1. 将数据库切换至 Mount 状态 ALTER DATABASE MOUNT; -- 2. 开启归档模式 ALTER DATABASE ARCHIVELOG; -- 3. 添加本地归档目录(请替换 /dm8/arch 为实际路径,需要提前创建并授予 dmdba 用户读写权限。) ALTER DATABASE ADD ARCHIVELOG 'DEST = /dm8/arch, TYPE = local, FILE_SIZE = 1024, SPACE_LIMIT = 2048'; -- 4. 切回 Open 状态 ALTER DATABASE OPEN; -- 验证是否成功(返回 'Y' 表示成功) SELECT ARCH_MODE FROM V$DATABASE;
DEST 指定归档日志存放路径,请确保目录已创建且有写入权限。
使用 SQL 命令备份
归档开启后,可以在 disql 命令行工具中直接执行备份命令。
# 1. 登录 disql cd /dm8/bin ./disql SYSDBA/SYSDBA@localhost:5236 # 2. 执行全量备份(备份集存至 /dm8/backup/full) SQL> BACKUP DATABASE FULL BACKUPSET '/dm8/backup/full' COMPRESSED LEVEL 6; # 说明:COMPRESSED LEVEL 6 为中等压缩,可节省磁盘空间 [citation:9] # 3. 执行增量备份(基于上次全量备份,备份变化的数据) # 增量备份命令会自动找到最近的基准备份 SQL> BACKUP DATABASE INCREMENT BACKUPSET '/dm8/backup/inc_20250326';
dexp)逻辑备份使用 dexp 工具,数据库必须处于运行状态。dexp 支持四种级别的备份。
| 备份级别 | 参数 | 说明 | 命令示例 |
|---|---|---|---|
| 全库 | FULL=Y |
导出整个数据库的所有对象 | ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup FILE=full.dmp LOG=full.log FULL=Y |
| 按用户 | OWNER=<用户名> |
导出一个或多个用户拥有的所有对象 | ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup FILE=owner.dmp LOG=owner.log OWNER=USER01 |
| 按模式 | SCHEMAS=<模式名> |
导出一个或多个模式下的所有对象 | ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup FILE=schema.dmp LOG=schema.log SCHEMAS=DMHR |
| 按表 | TABLES=<表名> |
导出一张或多张指定的表 | ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup FILE=table.dmp LOG=table.log TABLES=DMHR.EMPLOYEE |
通用命令格式与参数:
./dexp USERID=<用户名>/<密码>@<IP>:<端口> DIRECTORY=<备份目录> FILE=<导出文件名> LOG=<日志文件名> <备份级别参数> # 全库导出(一般用于迁移,生产环境频率较低) ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup/logic FILE=full_db.dmp LOG=full_db.log FULL=Y # 关键表定时导出(例如每天凌晨导出核心业务表) ./dexp SYSDBA/SYSDBA DIRECTORY=/dm8/backup/logic FILE=order_table.dmp LOG=order_table.log \ TABLES="PROD.ORDER_TABLE,PROD.ORDER_DETAIL"
USERID:连接数据库的认证信息,如果是本地数据库,@IP:端口 可以省略。
DIRECTORY:指定备份文件存放的目录。
FILE:指定导出的文件名。
LOG:指定记录导出过程的日志文件。
生产环境的首要原则是不影响业务,因此首选联机备份(热备)。针对不同场景,方案选择如下:
| 方案类型 | 适用场景 | 核心要求 | 恢复粒度 | 操作复杂度 |
|---|---|---|---|---|
| 物理热备(推荐) | 全库恢复、表空间恢复 | 必须开启归档日志 | 库级、表空间级 | 中,支持脚本自动化 |
| 逻辑备份(辅助) | 误删数据恢复、数据迁移、部分表恢复 | 数据库正常运行即可 | 库级、用户级、模式级、表级 | 低,dexp/dimp命令简单 |
| 物理冷备(备用) | 数据库迁移、重大变更前的保险 | 需停止数据库服务 | 库级 | 低,但会中断业务 |
生产环境建议:以“每周一次物理全量热备 + 每天一次物理增量热备”为主,辅以“关键业务表每日逻辑导出”的双重保险策略 。
备份集有效性检查:定期使用 dmrman 中的 CHECK BACKUPSET 命令验证备份集是否损坏。
异地备份:生产环境的备份集务必通过脚本(如 rsync、scp)同步至另一台服务器或对象存储,防止物理硬件损坏。
关键参数配置:
COMPRESSED:备份时开启压缩(级别5-6),显著节省磁盘空间 。
PARALLEL:多核服务器可设置并行度(如4或8),加快备份速度 。
保留周期:建议全量备份保留30天,归档日志保留7-15天(视磁盘空间而定)。
| 报错信息 | 原因分析 | 解决方案 |
|---|---|---|
数据库未开启归档模式 |
执行热备前未配置归档 | 参考“第二步”开启归档,并重启数据库服务生效 |
DMAP服务未启动 |
物理备份或还原时缺少辅助进程 | 切换到 dmdba 用户,执行 DmAPService start
|
无效的备份集目录 |
跨平台或跨版本还原 | 确保操作系统位数和数据库大版本一致(Linux到Linux,x64到x64) |
表空间恢复失败 |
尝试恢复系统表空间(如SYSTEM) | 系统表空间只能通过整库恢复来还原 |
备份恢复是数据库运维的“生命线”,建议在生产环境实施前,先在测试环境完整演练一遍备份和恢复流程。
2026-04-22 00:07:43
Temperature(温度):控制概率分布的“陡峭”程度,影响整体随机性。
Top-p(也称核采样):限制候选词的累积概率范围,动态过滤掉极不可能的选项
Top-k:截断采样策略,用于控制模型生成 token 时的候选词范围
Temperature(温度)
作用:控制输出分布的"尖锐度"
模型在生成每个 token 时,会先计算所有候选词的概率分布。Temperature 会对这个分布做如下变换:
P'(word) ∝ P(word)^(1/T)
Temperature 值 / 效果 / 适用场景
T = 0(或极低) — 始终选概率最高的词,输出完全确定 · 代码生成、数学计算、需要确定性答案的任务
T = 0.1~0.3 — 高度保守,几乎总是选最优解 · 事实问答、信息抽取、严格格式输出
T = 0.5~0.7 — 平衡随机性,主流默认值 · 通用对话、写作辅助、大多数场景
T = 0.8~1.0 — 明显增加多样性 · 创意写作、头脑风暴、角色扮演
本质: 低温度让分布更"尖锐",高温度让分布更"平缓"。
作用:动态截断低概率词
与 Temperature 固定缩放不同,Top_p 按概率从高到低累加,直到累计概率达到 p 值,只保留这些词,从保留的这些词中采样:
例如 top_p=0.9: 选词 A(40%) + B(30%) + C(20%) = 90% → 保留 词 D(10%) 被截断
Top_p 值 / 效果 / 特点
0.1 ~ 0.3 — 极度保守,只选最高概率词 · 类似低 temperature,但更动态
0.7 ~ 0.9 — 主流推荐值 · 在多样性和质量间取得平衡
0.9 ~ 0.95 — 允许更多低概率词 · 创意性更强,偶尔会跑偏
1.0 — 不做截断,等价于关闭 · 不推荐,可能采样到无意义词
优势: 比 Temperature 更"智能"——当模型很确定时自动收窄,不确定时自动放宽。
需要精确、低风险 → 低 temperature(0.1~0.3)+ 低 top-p(0.1~0.5)
需要创意、多样性 → 高 temperature(0.8~1.2)+ 高 top-p(0.9~1.0)
平衡模式(多数日常对话)→ temperature 0.7~0.8,top-p 0.9
| 任务类型 | temperature | top-p | 说明 |
|---|---|---|---|
| 代码生成、数学解题 | 0.1~0.3 | 0.1~0.3 | 需要确定性高 |
| 事实问答、摘要 | 0.3~0.5 | 0.5~0.7 | 允许少量变化 |
| 通用客服/聊天 | 0.6~0.8 | 0.8~0.9 | 平衡流畅与多样性 |
| 故事/诗歌创作 | 0.8~1.2 | 0.9~1.0 | 鼓励惊喜 |
| 头脑风暴/创意构思 | 1.0~1.4 | 1.0 | 最大自由度,注意偶尔乱码 |
temperature = 0.7 top_p = 0.9
这是大多数 API 的默认值,适合 80% 的场景
不要同时设极值:T=0 + top_p=0.1 会导致输出极度单调
Temperature 优先调:多数情况下调 T 就够了,Top_p 保持 0.9 不动
需要确定性时用 T=0:此时 Top_p 失效(贪婪解码优先)
不同模型敏感度不同:同样参数在不同模型上效果可能差异较大
batch 生成时注意:同一 prompt 多次调用,参数相同也会得到不同结果
Temperature → "敢不敢冒险":越低越保守,越高越大胆
Top_p → "备选池多大":越低选择越少,越高越自由
两者配合 → T 定基调,Top_p 做微调
top_k 参数详解在基于 Transformer 的大语言模型(如 GPT、LLaMA 等)文本生成中,top_k 是一个常用的采样参数,用于控制输出 token 的随机性和多样性。它直接限制了模型每一步可选的候选词范围。
top_k:在每一步生成时,只保留概率最高的 k 个 token,然后在这 k 个 token 中重新归一化概率并进行采样(或配合其他策略比如温度采样)。
如果 top_k=1,则退化为贪婪搜索,每次都选最高概率的 token。
如果 top_k 很大(比如等于词表大小),则等价于不做 top_k 过滤(仅靠温度等控制)。
注意:
top_k是一个“硬截断”机制——不考虑 k 个候选之外的所有 token,即使它们的概率也没有低到微不足道。
假设词表中有 10 个 token,模型原始输出的 logits(未归一化分数)经过 softmax 得到概率分布:
token: A B C D E F G H I J
prob: 0.3 0.2 0.15 0.1 0.08 0.05 0.04 0.03 0.03 0.02
设置 top_k=3:
选择概率最高的 3 个 token:A(0.3), B(0.2), C(0.15)
对这三个概率重新归一化(除以和 0.65):
A: 0.462, B: 0.308, C: 0.231
根据这个新的分布随机采样得到下一个 token。
top_k 值 |
效果 |
|---|---|
| 1 | 确定性输出,每次结果相同,适合有标准答案的任务(如数学计算、代码生成中的简单逻辑)。 |
| 10~40 | 常用范围。适度限制低概率 token,降低“跑题”或乱码风险,同时保留一定创造性。 |
| 50~100 | 更开放,允许更多低概率词被选到,多样性高,但可能偶尔产生不连贯内容。 |
| 词表大小 | 实际上禁用 top_k 过滤,所有 token 都有机会被选中。 |
top_k 与 top_p 的区别| 参数 | 方式 | 动态性 | 控制目标 |
|---|---|---|---|
| top_k | 固定候选数量 | 候选池大小固定 | 绝对去掉尾部低概率 token |
| top_p | 累计概率阈值(如 0.9) | 候选池大小根据分布自适应 | 只保留概率质量集中的一部分 token |
示例对比(依然用上面的概率分布):
top_k=3 → 固定 3 个候选
top_p=0.6 → 从高到低累加概率:A(0.3) → B(0.5) → C(0.65) 超过 0.6,所以选中 A+B,候选池大小=2
实际建议:常同时使用 top_k 和 top_p,例如 top_k=50 + top_p=0.95,先按 top_k 截断,再按 top_p 过滤,可以使候选更加稳妥又不过于机械。
temperature 的交互temperature 在 softmax 前对 logits 做缩放:低温度使概率分布更陡(偏向高概率 token),高温度使分布更平坦。
top_k 作用于缩放后的概率分布。
如果温度很低,原始概率已经尖锐,再应用 top_k 可能只剩下 1~2 个候选(近似贪婪)。
如果温度很高,分布平坦,top_k 可以排除长尾中的极低概率词,防止生成完全随机的无意义内容。
常用组合:
创意写作:temperature=0.9,top_k=50,top_p=0.95
精确问答:temperature=0.2,top_k=10,top_p=0.9
完全确定性:top_k=1(忽略 temperature)
优点:
简单有效,能显著降低生成“无意义词”的概率。
计算成本低(只是截取前 k 个,重新归一化)。
直观易调:k 越大,多样性越高。
缺点:
固定大小的 k 不能适应不同分布形状。当概率质量异常集中时,k 个 token 中后面的 token 可能概率极低,仍会选到;当分布非常平缓时,k 个 token 会漏掉大量中等概率的合理词。
需要手动调整,不同任务最优 k 不同。
| 任务类型 | 推荐 top_k | 理由 |
|---|---|---|
| 代码生成 / 算术 | 1 或 10~20 | 高确定性,避免错误 token。 |
| 翻译 / 摘要 | 20~40 | 需要忠实原文,但允许适当措辞变化。 |
| 聊天机器人(通用) | 40~60 | 平衡合理性与趣味性。 |
| 创意故事 / 诗歌 | 60~100 | 鼓励新颖表达,但保留基础连贯性。 |
| 超长文本生成 | 50~80 | 避免长期退化(重复/空洞),同时不过分散乱。 |
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("gpt2")
tokenizer = AutoTokenizer.from_pretrained("gpt2")
inputs = tokenizer("The quick brown fox", return_tensors="pt")
outputs = model.generate(
**inputs,
max_new_tokens=20,
do_sample=True, # 启用采样,否则 top_k 无效
top_k=50, # 只从概率最高的 50 个 token 中选
top_p=0.95, # 可选,配合使用
temperature=0.8
)
top_k 是控制生成多样性与连贯性的一个简单但强大的参数。
通过限制每一步的候选 token 数量,可以有效剔除极端低概率的无意义词。
与 top_p、temperature 组合使用可以更精细地调节输出质量。
没有绝对最优值,通常需要通过实验针对具体任务调参。
理解 top_k 的本质后,就可以根据模型行为(是否出现不合理 token、是否重复、是否缺乏创意)来快速调整该参数,并与其他采样策略配合达到最佳生成效果。