[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-detail-ssr-48055426582907916":3},{"id":4,"title":5,"tag":6,"createTime":7,"updateTime":7,"renderedHtml":8,"description":9,"ogImageUrl":10},"48055426582907916","CAP 定理","分布式系统","2023-11-28","\u003Ch2 id=\"h2-0-consistency\">一致性（Consistency）\u003C\u002Fh2>\n\u003Ch3 id=\"h3-1-\">实现的目标\u003C\u002Fh3>\n\u003Cp>分布式系统中的数据一致性是指系统中存储的数据在多个副本之间能够保持一致的特性。用户对数据的修改操作，要么在所有的数据副本中执行成功，要么在所有的数据副本中执行失败，也就是一致性要求对所有数据节点里面的数据副本的修改是原子操作。所以，一致性的实现目标是确保分布式系统中的所有数据副本，在经过一系列操作之后，能够达到一个一致的状态。这意味着对数据的每次更新都必须立即传播到所有的副本中，或者对外的读操作必须能够等到数据完全达到一致之后才能进行。\u003C\u002Fp>\n\u003Ch3 id=\"h3-2-\">技术实现\u003C\u002Fh3>\n\u003Cp>要实现上面的两个目标，需要实现如下技术：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>同步复制\u003C\u002Fstrong>：数据写入主节点后，必须在复制到从节点后才能返回成功。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>读写分离\u003C\u002Fstrong>：读操作和写操作可能会被路由到不同的节点来保证一致性。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>分布式锁\u003C\u002Fstrong>：确保同时只有一个节点可以操作数据。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>版本控制\u003C\u002Fstrong>：对数据的每次修改都带有版本号，以帮助解决冲突。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>一致性协议\u003C\u002Fstrong>：如 Paxos 或 Raft，用于在不同节点之间达成一致性决定。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>事务\u003C\u002Fstrong>：确保操作的原子性，即要么全部完成要么全部不做。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 id=\"h3-3--acid-\">和 ACID 中的一致性的区别\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>CAP 定理的一致性更强调的是数据在多个副本之间的一致性。\u003C\u002Fli>\n\u003Cli>ACID 事务的一致性指的是确保数据库在事务开始之前和事务结束之后都处于一致性状态。也就是说，事务不会破坏数据库的完整性约束。例如，如果有一个规则说数据库中的账户余额不能为负数，那么事务在执行过程中必须保证不会产生负数的余额，这保证了事务的一致性。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"h2-4-availability\">可用性（Availability）\u003C\u002Fh2>\n\u003Ch3 id=\"h3-5-\">实现的目标\u003C\u002Fh3>\n\u003Cp>可用性是指任何非故障节点都应该在有限的时间内给出请求的响应，不论请求是否成功。所以，可用性的实现目标是确保系统始终能够对客户端请求作出响应，即使这些响应可能不包含最新的数据更新。用户不会遇到系统不响应的情况，从而保证了用户体验和系统功能的持续性。\u003C\u002Fp>\n\u003Ch3 id=\"h3-6-\">技术实现\u003C\u002Fh3>\n\u003Cp>为了实现高可用性，可以采用多种技术和策略，包括但不限于：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>冗余\u003C\u002Fstrong>：在系统中部署多个冗余组件，如服务器、数据库等，以便在某个组件失败时，其他组件可以接管其工作。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>故障检测和恢复\u003C\u002Fstrong>：实现机制来监控系统的健康状况，并在检测到故障时迅速采取行动，如重启服务或切换到备份系统。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>负载均衡\u003C\u002Fstrong>：使用负载均衡器可以将客户端请求分发到多个服务器，避免任何单点过载，并在服务器不可用时转移流量。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>数据复制\u003C\u002Fstrong>：通过在多个位置复制数据，可以确保即使某些节点不可用，客户端仍然可以从其他节点获取数据。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>优雅降级\u003C\u002Fstrong>：当系统压力过大时，可暂时关闭一些非核心功能，以保证核心功能的可用性。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"h3-7-\">可用性的特点\u003C\u002Fh3>\n\u003Cp>可用性的特点主要包括：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>响应性\u003C\u002Fstrong>：\u003Ccode>系统必须在合理的时间内响应客户端的请求\u003C\u002Fcode>。这个合理的时间是根据业务来定的。业务说必须 100 毫秒内返回，合理的时间就是 100 毫秒，如果业务定的 100 毫秒，结果却在 1 秒才返回，那么这个系统就不满足可用性。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>持续性\u003C\u002Fstrong>：\u003Ccode>系统必须要能够在故障发生时继续运行，即使是部分性能或功能受限\u003C\u002Fcode>。比如某个节点不能正常接收请求了（宕机或系统崩溃了），而其他节点依然能正常接收请求，那么，我们说系统依然是具有可用性的。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>可靠性\u003C\u002Fstrong>：\u003Ccode>系统应该尽可能地减少错误响应，并在出现故障时能够可靠地恢复\u003C\u002Fcode>。比如 Redis 的哨兵集群中的\u003Ccode>主从同步机制\u003C\u002Fcode>很好的减少了系统错误结果的响应；\u003Ccode>自动故障迁移机制\u003C\u002Fcode>能在系统出现故障时自动的恢复。但是可靠性并不一定保证系统返回的数据就是正确的，如果某个节点的数据不是最新的，那就返回一个旧的数据，如果连旧数据都没有，那就返回一个默认的数据，也即一定要有一个响应结果。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"h2-8-partition-tolerance\">分区容错性（Partition tolerance）\u003C\u002Fh2>\n\u003Ch3 id=\"h3-9-\">实现的目标\u003C\u002Fh3>\n\u003Cp>分布式的存储系统会有很多的节点，这些节点都是通过网络进行通信。而网络是不可靠的，当节点和节点之间的通信出现了问题，此时，就称当前的分布式存储系统出现了分区。需要注意的是，分区并不一定是由网络故障引起的，也可能是因为机器故障。总之，节点通信出现了问题，那么就出现了分区。\u003C\u002Fp>\n\u003Cp>分区容错性是指当发生网络分区时（即节点之间无法通信），在丢失任意多消息的情况下，系统仍然能够正常工作。不能因为出现了分区问题，整个分布式节点全部就熄火了。所以，分区容错性的实现目标：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>维持服务运行\u003C\u002Fstrong>：即使在某些组件无法通信或访问时，系统仍然能够对外提供服务。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>最小化操作失败\u003C\u002Fstrong>：尽可能保证用户的请求被响应，而不是因为网络分区而失败。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>系统恢复\u003C\u002Fstrong>：在网络重新连通之后，系统应能够同步分区期间的数据变更，恢复数据的一致状态。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 id=\"h3-10-\">技术实现\u003C\u002Fh3>\n\u003Cp>实现分区容错性需要的技术：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>复制\u003C\u002Fstrong>：通过在多个节点上存储数据的副本，即使某些节点无法访问，数据仍然可用。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>数据分片\u003C\u002Fstrong>：将数据分布在不同的节点上，这样即使部分节点不可用，其他节点上的数据仍然可以访问。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>一致性协议\u003C\u002Fstrong>：如 Raft 和 Paxos，它们能够处理节点宕机和网络分区问题，并在节点恢复后同步状态。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>故障检测和恢复机制\u003C\u002Fstrong>：系统应能够自动检测节点或网络故障，并采取措施以保持系统的可用性和一致性。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>负载均衡\u003C\u002Fstrong>：在网络分区发生时，能够重新分配请求到可用的节点上，以保持服务水平。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>消息队列和日志\u003C\u002Fstrong>：通过记录操作日志，即使在分区情况下，也能够在网络恢复后同步操作。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 id=\"h3-11-\">分区容错性的特点\u003C\u002Fh3>\n\u003Col>\n\u003Cli>\u003Cstrong>健壮性\u003C\u002Fstrong>：系统对网络故障和节点失效具有较强的抵抗力。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>自动恢复\u003C\u002Fstrong>：系统能够自动处理分区事件，并在问题解决后自动恢复正常状态。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>数据冗余\u003C\u002Fstrong>：数据在多个节点间有冗余副本，提供高可用性。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>延迟一致性\u003C\u002Fstrong>：在某些情况下，分区容错性可能导致系统暂时处于非一致性状态，但最终会恢复一致性（最终一致性）。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"h2-12-cap-\">CAP 定理\u003C\u002Fh2>\n\u003Ch3 id=\"h3-13-ca\">CA\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>CA 满足但 P 不满足：指的是在没有网络分区的情况下（这种情况现实中并不存在），系统是可以做到既保证一致性又保证可用性的。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>假设有一个分布式数据库系统，该系统部署在一个内部网络中，网络非常可靠，不会出现网络分区的情况。在这种情况下，使用 Paxos 或 Raft 这样的一致性协议，这些协议可以在没有网络分区的环境下保证数据的强一致性；并且在没有网络分区的情况下，可以采用读写分离的架构来提升性能。写操作仍然需要同步到多个副本以保证一致性，但读操作可以从任何一个副本中完成，这样可以提高读操作的可用性。\u003C\u002Fp>\n\u003Ch3 id=\"h3-14-cp\">CP\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>CP 满足但 A 不满足：指的是既要求数据具有强一致性又要求分区容错，那么就要放弃可用性了。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>因为当网络分区发生时，要求数据在各个服务器上是强一致的（C），然而网络分区（P）会导致数据同步时间无限延长，直到网络修复或者机器重启完成后，数据同步才会完成。那么如此一来可用性就得不到保障了（!A）。\u003C\u002Fp>\n\u003Ch3 id=\"h3-15-ap\">AP\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>AP 满足但 C 不满足：指的是既要求系统高可用又要求分区容错，那么就要放弃一致性了。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>因为一旦发生网络分区（P），节点之间将无法通信，为了满足高可用（A），每个节点只能用本地数据（旧数据或者默认数据）提供服务，这样就会导致数据的不一致（!C）。\u003C\u002Fp>\n\u003Ch3 id=\"h3-16-\">补充\u003C\u002Fh3>\n\u003Col>\n\u003Cli>正如热力学第二定律揭示了任何尝试发明永动机的努力都是徒劳的一样，CAP 原理明确指出了完美满足 CAP 三种属性的分布式系统是不存在的。了解 CAP 原理的目的在于，其能够帮助我们更好地理解实际分布式协议实现过程中的取舍。\u003C\u002Fli>\n\u003Cli>在现实当中的分布式系统中，由于网络分区的问题发生的概率非常低，所以，当没有出现分区问题的时候，系统就应该有完美的数据一致性和可用性。\u003C\u002Fli>\n\u003Cli>CAP 定理的三种特性不是 bool 类型的，不是只有一致和不一致，可用和不可用，分区和没分区的这类二选一的选项，而是这三种特性都是范围类型。在实际的系统设计和实现中，这些特性往往是可以调节和权衡的。一致性可以有不同的级别，例如：强一致性、弱一致性和最终一致性；可用性也可以有不同的定义，例如：高可用性和部分可用性。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"h2-17-cap-\">CAP 的不足\u003C\u002Fh2>\n\u003Col>\n\u003Cli>CAP 定理本身是没有考虑网络延迟的问题的，它认为一致性是立即生效的，但是，要保持一致性，是需要时间成本的，这就导致往往分布式系统多选择 AP 方式。\u003C\u002Fli>\n\u003Cli>由于时代的演变，CAP 定理在针对所有分布式系统的时候，出现了一些力不从心的情况，导致很多时候它自己会把以前很严谨的数学定义改成了比较松弛的业务定义，类似于我们看到，CAP 定理把一致性、可用性、分区容错都变成了一个范围属性，而这和 CAP 定理本身这种数学定理般的称呼是有冲突的，出现了不符合数学严谨定义的问题。\u003C\u002Fli>\n\u003Cli>在实践中以及后来 CAP 定理的提出者也承认，一致性和可用性并不仅仅是二选一的问题，只是一些重要性的区别，当强调一致性的时候，并不表示可用性是完全不可用的状态。比如，Zookeeper 只是在 master 出现问题的时候，才可能出现几十秒的不可用状态，而别的时候，都会以各种方式保证系统的可用性。而强调可用性的时候，也往往会采用一些技术手段，去保证数据最终是一致的。CAP 定理并没有给出这些情况的具体描述。\u003C\u002Fli>\n\u003Cli>CAP 理论从工程角度来看只是一种状态的描述，它告诉大家当有错的时候，分布式系统可能处在什么状态。但是，状态是可能变化的。状态间如何转换，如何修补，如何恢复是没有提供方向的。\u003C\u002Fli>\n\u003C\u002Fol>\n","一致性（Consistency） 实现的目标 分布式系统中的数据一致性是指系统中存储的数据在多个副本之间能够保持一致的特性。用户对数据的修改操作，要么在所有的数据副本中执行成功，要么在所有的数据副本中执行失败，也就是一致性要求对所有数据节点里面的数据副本的修改是原子操作。所以，一致性的实现目标是确保分布式系统中的所有数",""]