::: tip 缓存方案设计

  • 什么数据应该缓存
  • 什么时机触发缓存和以及触发方式是什么
  • 缓存的层次和粒度( 网关缓存如 nginx,本地缓存如单机文件,分布式缓存如redis cluster,进程内缓存如全局变量)
  • 缓存的命名规则和失效规则
  • 缓存的监控指标和故障应对方案
  • 可视化缓存数据如 redis 具体 key 内容和大小
    :::

::: tip 数据库自身不是有缓存吗?为什么还要引入redis等缓存

  • mysql第一种缓存叫sql语句结果缓存,但条件比较苛刻,程序员不可控,我们的dba线上都关闭这个功能,具体实现可以查一下
  • mysql第二种缓存是innodb buffer pool,缓存的是磁盘上的分页数据,不是sql的查询结果,sql的执行过程省不了。而mc,redis这些实际上都是缓存sql的结果,两种缓存方式,性能差很远。

因此,可控性,性能是数据库缓存和独立缓存的主要区别
:::


::: tip 商品列表被缓存,但是商品价格需要极大的实时性,如何处理?
通常有几种做法:

  • 同步刷新缓存:当更新了某些信息后,立刻让缓存失效。
    这种做法的优点是用户体验好,缺点是修改一个数据可能需要让很多缓存失效
  • 适当容忍不一致:例如某东的商品就是这样,我查询的时候显示有货,下单的时候提示我没货了
  • 关键信息不缓存:库存,价格等不缓存,因为这类信息查询简单,效率高,关系数据库查询性能也很高
    :::

::: tip 高性能架构设计

  • 尽量提升单服务器的性能,将单服务器的性能发挥到极致。
  • 如果单服务器无法支撑性能,设计服务器集群方案

架构设计决定了系统性能的上限,实现细节决定了系统性能的下限
:::


::: tip 并发模型设计关键点

  • 服务器如何管理连接。
  • 服务器如何处理请求。
    :::

::: tip I/O 模型:阻塞、非阻塞、同步、异步
概念
IO操作分两个阶段
1、等待数据准备好(读到内核缓存)
2、将数据从内核读到用户空间(进程空间)

一般来说1花费的时间远远大于2。
1上阻塞2上也阻塞的是同步阻塞IO
1上非阻塞2阻塞的是同步非阻塞IO - Reactor模型
1上非阻塞2上非阻塞是异步非阻塞IO - Proactor模型

理解
1、假如我们去饭店点餐,饭店人很多,如果我们付了钱后站在收银台等着饭端上来我们才离开,这就成了同步阻塞
2、如果我们付了钱后给你一个号就可以离开,饭好了老板会叫号,你过来取。这就成了同步非阻塞 - Reactor模型
3、如果我们付了钱后给我一个号就可以坐到坐位上该干啥干啥,饭好了老板会把饭端上来送给你。这就成了异步非阻塞 - Proactor模型
:::


::: tip 负载均衡访问性能

  • Nginx - 5w/s
  • LVS - 80w/s
  • F5 - 200w ~ 800w/s

主流的配置 32C - 48G
云主机的性能可以按照同配置的物理机80%的性能来估算
:::


::: tip CAP 定理(CAP theorem)- 布鲁尔定理

  • Consistenc(一致性):对某个指定的客户端来说,读操作保证能够返回最新的写操作结果。
  • Availability(可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
  • Partition(分区容错性):当出现网络分区后,系统能够继续“履行职责”。

在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉及读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition Tolerance)三者中的两个,另外一个必须被牺牲。

  • 分布式系统并不一定会互联和共享数据,如:Memcache 的集群
  • 关注对数据的读写操作,所有功能,如:ZK的选举
    :::

::: tip CAP 应用
不可实现的CA:因为网络本身无法做到 100% 可靠,分区是一个必然的现象。故假设放弃P(分区容错性)时,为了保证C(一致性),就会禁止写入操作,此时违反了A(可用性),故CA架构不存在。

CP:假设为了保证C(一致性)。当某一节点发生分区后,会停止写入数据,此时违背A(可用性),因此可以达到CP架构

AP:假设为了保证A(可用性)。当某一节点发生分区后,会继续写入数据导致数据不一致,此时违背C(一致性),但是依然返回了合理的数据,只是因为分区而不是最新值而已,因此可以达到AP架构。
:::


::: tip CAP 概念补充
适用场景:分布式系统有多种类型类型

  • 有异构的,比如节点之间是上下游依赖的关系
  • 有同构的,比如分区/分片型的、副本型的(主从、多主)。

CAP定理的适用场景是副本型的这种。

一致性的概念: CAP中的一致性应该是指顺序一致性,一致性从强到弱:

  • 线性一致性
  • 顺序一致性
  • 因果一致性
  • 单调一致性
  • 最终一致性

CAP中的一致性:与ACID中的一致性的区别。

  • 事务中的一致性,是指满足完整性约束条件。
  • CAP中的一致性,是指读写一致性。

CAP中的可用性:与我们常说的高可用的区别。

  • 比如HBase、MongoDB属于CP架构,Cassandra、CounchDB属于AP系统,能说后者比前者更高可用么?应该不是。
  • CAP中的可用性,是指在某一次读操作中,即便发现不一致,也要返回响应,关键是在合理时间内返回合理响应。
  • 我们常说的高可用,是指部分实例挂了,能自动摘除,并由其它实例继续提供服务,关键是冗余。

哪些情况属于网络分区

  • 网络故障造成的分区,属于。
  • 节点应用出现问题导致超时,属于。
  • 节点宕机或硬件故障,不属于。
    :::

::: tip CAP 关键细节点

  • CAP 关注的粒度是数据,而不是整个系统。
    • 在 CAP 理论落地实践时,需要将系统内的数据按照不同的应用场景和要求进行分类,每类数据选择不同的策略(CP 还是 AP),例如:注册信息(CP)和阅读信息(AP)
  • CAP 是忽略网络延迟的。
    • 实际情况下,数据不能够瞬间复制到所有节点。
    • 完美的CA无法做到多点写入,但是能做到单点写入多点读取(最终一致)
  • 正常运行情况下,不存在 CP 和 AP 的选择,可以同时满足 CA
    • 只能选择 CP 或者 AP的前提是发生了分区
    • 既要考虑分区发生时选择 CP 还是 AP,也要考虑分区没有发生时如何保证 CA
  • 放弃并不等于什么都不做,需要为分区恢复后做准备
    • 分区期间放弃 C 或者 A,并不意味着永远放弃 C 和 A
    • 典型方案:分区期间记录一些日志,当分区故障解决后,系统根据日志进行数据恢复,使得重新达到 CA 状态
    • 其他方案:设置数据合并规则、产生冲突日志人工处理
      :::

::: tip ACID 一点概念

  • Atomicity(原子性):一个事务中的所有操作,要么全部完成,要么全部不完成。中途出现错误,会回滚到事务开始的状态,像整个事务没有发生过一样。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏,满足预定约束的合法状态。
  • Isolation(隔离性):允许多个并发事务同时对数据进行读写和修改的能力。不同隔离级别,会产生不同的问题。
    • 读未提交(Read uncommitted):会出现脏读
    • 读提交(read committed):会出现不可重复读
    • 可重复读(repeatable read):会出现幻读,Mysql因为有next-key(间隙锁)机制,此隔离界别下不会出现幻读
    • 串行化(Serializable):会出现性能下降
  • Durability(持久性):
    :::

::: tip 不可重复读和幻读的区别

  • 不可重复读:单次事务中,多次相同的查询出现了不同的结果。重点针对相同查询结果不一致。
  • 幻读:单次事务中,查询结果一致,但是插入或者删除数据时会报错。例如:其他事务已经插入id为1的数据,当前事务查询无id为1数据,但是插入时会失败。
    :::

::: tip ACID 与 CAP 的C(一致性)

  • ACID的一致性:关心的是单个事务执行前后的数据完整性,是否达到合乎规范的数据格式。
  • CAP的一致性:关心的是分布式系统中对数据的读写一致性,也就是分布式节点数据的一致性。
    :::

::: tip BASE 理论
BASE 理论本质上是对 CAP 的延伸和补充,更具体地说,是对 CAP 中 AP 方案的一个补充
产生背景:

  • CAP 理论是忽略延时的,而实际应用中延时是无法避免的,哪怕只是几毫秒的时间也达不到C的要求,因此CP方案本质也是在一定时间内的CP,也就是最终一致性
  • AP 方案中牺牲一致性只是指分区期间,而不是永远放弃一致性。分区故障恢复后,系统应该达到最终一致性。

BASE 概念点

  • Basically Available - 基本可用 :分布式系统在出现故障时,允许损失部分可用性,即保证核心可用
  • Soft State - 软状态 :允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
  • Eventual Consistency - 最终一致性 :系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。
    :::

::: tip ACID - CAP - BASE

  • ACID 是数据库事务完整性的理论
  • CAP 是分布式系统设计理论
  • BASE 是 CAP 理论中 AP 方案的延伸
    :::

::: tip 分布式系统设计核心诉求 - 『可用性』
分布式系统的两大初衷:横向扩展(scalability)和高可用性(availability)

  • 横向扩展:解决单点瓶颈问题,增加高并发请求下的『可用性』
  • 高可用性:解决单点故障(SPOF)问题,进而保证部分节点故障时的『可用性』

可用性正是 CAP 中的 A:用户访问系统时,可以在合理的时间内得到合理的响应。
:::


::: tip 分布式系统如何保证核心诉求 - 『可用性』

  • 使用多个节点组成分布式系统
  • 每个节点各自维护一份数据,不管用户访问哪一个节点,原则上都应该读取到相同的数据,正是CAP中的 C(一致性)
  • 节点收到写入请求后,必须将数据同步到其他节点,以保证一致性

但是,由于CAP理论是忽略数据同步的耗时,现实中的分布式系统无法保证任何时刻的绝对「一致性」。
所以,分布式系统只能做到经过一定时间后的最终一致性,而不同业务系统对上述耗时的敏感度不同。
:::


::: tip 网络分区现象 - CAP中的分区容错性
分布式系统中,节点之间的数据同步是基于网络的。
由于网络本身固有的不可靠属性,极端情况下会出现网络不可用的情况,进而将网络两端的节点孤立开来,这就是所谓的“网络分区”现象。“网络分区”理论上是无法避免的,虽然实际发生的概率较低、时长较短。没有发生“网络分区”时,系统可以做到同时保证「一致性」和「可用性」。

发生网络分区时,系统中多个节点的数据一定是不一致的。
此时可以选择对用户表现出「一致性」,代价是牺牲「可用性」:将未能同步得到新数据的部分节点置为“不可用状态”,访问到这些节点的用户显然感知到系统是不可用的。
也可以选择对用户表现出「可用性」,代价是牺牲「一致性」:此时系统中各个节点都是可用的,只是返回给用户的数据是不一致的。
这里的选择,就是 CAP 中的 P。
:::


::: tip 一个分布式系统,需要考虑实现的三点
任何一个正常运行的分布式系统,起源于CA状态,发生分区时可能经过CP和AP状态,最后回到CA状态。

  • 正常运行时的CA状态。
  • 发生分区时转变为CP或AP状态。
  • 分区解决时如何恢复为CA状态。
    :::

::: tip 高可用和高性能哪个更复杂?
高可用更复杂一些

  • 高可用的异常的场景很多,只要有一个场景没考虑到,架构就存在可用性隐患。
  • 根据墨菲定律“可能出错的事情最终都会出错”,架构隐患总有一天会导致系统故障
    :::

::: tip FMEA - 可用性分析的方法论 - 概述
FMEA(Failure mode and effects analysis,故障模式与影响分析):通过对系统范围内潜在的故障模式加以分析,并按照严重程度进行分类,以确定失效对于系统的最终影响。
FMEA 并不能指导我们如何做架构设计,而是当我们设计出一个架构后,再使用 FMEA 对这个架构进行分析,看看架构是否还存在某些可用性的隐患。

在架构设计领域,FMEA 的具体分析方法是:

  • 给出初始的架构设计图
  • 假设架构中某个部件发生故障
  • 分析此故障对系统功能造成的影响
  • 根据分析结果,判断架构是否需要进行优化。

:::


::: tip FMEA 分析表的分析点

  • 功能点:从用户角度来看的功能点,例如:登录、注册。而Redis缓存功能、Json解析则不是。
  • 故障模式:
    • 描述系统会出现什么样的故障,并不需要给出真正的故障原因,具体原因在故障原因中描述。
    • 使用尽量精确、量化的描述,避免使用泛化的描述。
      ✖ Mysql响应时间过长
      ✖ Mysql磁盘坏道、慢查询
      ✔ Mysql响应时间达到3秒
  • 故障影响:描述发生故障模式时,功能点具体会受到什么影响。量化描述,但不需要精确量化。
    常见影响有:功能点偶尔不可用、功能点完全不可用、部分用户功能点不可用、功能点响应缓慢、功能点出错等。
    ✖ 大部分用户登录功能不可用
    ✔ 70%用户登录功能不可用
  • 严重程度:站在业务的角度故障的影响程度,一般分为“致命 / 高 / 中 / 低 / 无”五个档次。
    严重程度按照这个公式进行评估:严重程度 = 功能点重要程度 × 故障影响范围 × 功能点受损程度。
    • 致命:超过 70% 用户无法登录。
    • 高:超过 30% 的用户无法登录。
    • 中:所有用户登录时间超过 5 秒。
    • 低:10% 的用户登录时间超过 5 秒。
    • 中:所有用户都无法修改资料。
    • 低:20% 的用户无法修改头像
  • 故障原因:故障模式中只描述了具体的现象,但是多个故障原因都可以造成同样的现象,对功能点的影响也就相同。
    • 不同的故障原因发生概率不相同
    • 不同的故障原因检测手段不一样
    • 不同的故障原因的处理措施不一样
  • 故障概率:一般分为 『 高 / 中 / 低 』 三档,以下为需要重点关注的三点
    • 硬件:随着使用时长的增加,故障概率会逐步增加。
    • 开源系统:成熟的开源系统Bug率低,有使用经验的开源系统Bug率低。
    • 自研系统:成熟的自研系统Bug率更低。
  • 风险程度:风险程度就是综合严重程度和故障概率来一起判断某个故障的最终等级,风险程度 = 严重程度 × 故障概率。因此可能某个故障的严重等级很高,但由于故障概率极其低,反而最终的风险程度很低。反之,严重等级低的故障,最终风险程度也可能很高。
  • 已有措施:针对具体的故障原因,系统现在是否提供了某些措施来应对,包括:检测告警、容错、自恢复等。
    • 检测告警:最简单的方法,机器自身不处理,发出告警信息交由人工处理。
    • 容错:机器能够使用备份手段处理,例如:Mysql备份等。
    • 自恢复:机器能够在故障结束后,自行恢复业务。例如:连接池打满导致无响应,请求压力下降后机器恢复正常访问。只能实现业务恢复,而不是将故障本身解决,例如:磁道损坏。
  • 规避措施:规避措施指为了降低故障发生概率而做的一些事情,可以是技术手段,也可以是管理手段。
    • 技术手段:为了Mysql数据丢失,增加一台副本Mysql,实现主备架构。
    • 管理手段:为了降低磁盘坏道的概率,强制统一更换服务时间超过 2 年的磁盘。
  • 解决措施:解决措施指为了能够解决问题而做的一些事情,一般都是技术手段。
    如果某个故障既可以采取规避措施,又可以采取解决措施,那么一般会优先选择解决措施。而对于磁道损坏、开源系统bug等问题,一般选择规避措施。
    • 为了解决密码暴力破解,增加密码重试次数限制。
    • 为了解决拖库导致数据泄露,将数据库中的敏感数据加密保存。
    • 为了解决非法访问,增加白名单控制。
  • 后续规划:综合以上的分析,可以看到在哪些故障上,还缺少哪些措施、哪些措施含有不足,再结合风险等级进行排序,给出后续的改进规划。改进规划可以是技术手段、管理手段,也可以是规避措施、解决措施。
    • 地震导致数据丢失:异步灾备
    • 敏感数据泄露:数据加密
    • 机器断电数据丢失:建立备份数据库

FMEA分析表示例{{{width=“auto” height=“auto”}}}

注意:FMEA只适合分析高可用,不适合分析高性能等架构
:::


::: tip 存储高可用 - 双机架构
存储高可用的复杂性主要体现在:如何应对复制延迟和中断导致的数据不一致问题
:::


::: tip 存储高可用方案思考点

  • 数据如何复制?
  • 各个节点的职责是什么?
  • 如何应对复制延迟?
  • 如何应对复制中断?
    :::

::: tip 存储高可用 - 主备复制优缺点
优点

  • 客户端无需感知备机,人工切换主备机器时,对于客户端来说,只是改变了访问的ip地址。
  • 只需要进行数据复制,无需进行状态判断、主备切换这类复杂操作

缺点

  • 备机只能备份,不提供读写操作,有点浪费硬件成本
  • 故障无法自己解决,需要人工干预,处理时长较长,人工恢复容易出错
    :::

::: tip 存储高可用 - 主从复制优缺点
优点

  • 主机故障时,从机可以提供读取操作
  • 从机提供了读取功能,发挥了硬件性能

缺点

  • 客户端需要感知到从机的状态,将请求分发到主机或从机,复杂度比主备高
  • 主从复制延迟高时,会出现数据读取不一致情况
  • 出现故障需要人工干预

适用于读多写少的业务情况
:::


::: tip 存储高可用 - 双机切换设计点
主要为了解决主备复制、主从复制的两个共性问题

  • 主机故障,无法提供写入操作
  • 主机出现故障,需要人工干预恢复

切换方案关键点

  • 主备间状态判断:
    • 状态传递的渠道:是相互间互相连接,还是第三方仲裁?
    • 状态检测的内容:例如机器是否掉电、进程是否存在、响应是否缓慢等。切换决策。
  • 切换决策
    • 切换时机:什么情况下备机应该升级为主机?
      • 主机掉电后就升级
      • 主机上的进程不存在就升级
      • 主机响应时间超过 2 秒就升级
      • 3 分钟内主机连续重启 3 次就升级
    • 切换策略:原来的主机故障恢复后,要再次切换使原来的主机继续做主机,还是原来的主机故障恢复后自动成为新的备/从机?
    • 自动程度:切换是完全自动的,还是半自动的?例如:需要人工做最终的确认操作(例如:单击“切换”按钮)。
  • 数据冲突解决
    • 当原有故障的主机恢复后,新旧主机之间可能存在数据冲突,这时应该怎么处理?
      :::

::: tip 存储高可用 - 双机切换 - 常见架构
根据状态传递渠道的不同,常见的主备切换架构有三种形式

  • 互连式:主备机直接建立一个传递状态信息的通道。
  • 中介式:在主备两者之外引入第三方中介,主备机之间不直接连接,而都去连接中介,并且通过中介来传递状态信息。
  • 模拟式:主备机之间并不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模拟的读写操作,根据读写操作的响应情况来判断主机的状态。
    :::

::: tip 存储高可用 - 双机切换 - 互连式实现方式
主机与备机直接建立一个传递状态信息的通道。这个通道可以有多种具体实现。

  • 可以是网络连接,例如:各开一个端口,也可以是非网络连接,用串口线连接。
  • 可以是主机发送状态给备机,也可以是备机到主机来获取状态信息。
  • 可以和数据复制通道共用,也可以独立一条通道。
  • 状态传递通道可以是一条,也可以是多条,还可以是不同类型的通道混合,例如:网络 + 串口。

为了充分利用切换方案能够自动决定主机的优势,客户端也会有一些相应的改变。

  • 为了切换后不影响客户端的访问,主机和备机之间共享一个对客户端来说唯一的地址,例如:虚拟 IP,主机只需要绑定这个虚拟 IP。
  • 客户端同时记录主备机的地址,哪个能访问就访问哪个。备机虽然能收到客户端的操作请求,但是会直接拒绝。
    :::

::: tip 存储高可用 - 双机切换 - 互连式缺点

  • 如果状态传递这一步骤出现问题,例如:网线损坏。那么备机会认为主机已经故障,从而自己升级为主机,最终出现两个主机(脑裂现象)。
  • 虽然可以通过增加多个通道传递状态,但是当备机从多个通道收到不同状态时,如何决策也是一大难题。并且没有解决本质问题(脑裂现象)。
    :::

::: tip 存储高可用 - 双机切换 - 中介式优点

  • 连接管理更简单
    • 主备机只需将状态上报给中介者。
    • 中介者负责建立和管理多种类型的状态传递连接通道,考虑多种类型的连接通道的状态信息如何决策。
  • 状态决策更简单。默认规则:初始状态,所有机器都是备机。只要与中介断开链接,都自动降级为备机。
    • 主机与中介断开后,中介立刻通知备机升级为主机
    • 如果是网络问题导致主机与中介断开,主机自己会降级为备机。与中介重新连接后,以备机的身份上报给中介。
    • 如果是停电重启或进程重启,旧主机因为初始状态为备机,与中介重新连接后,依然保持备机身份不变。
    • 连接正常的情况下,中介根据预先制定的规则,例如:停止响应3秒。则通知主机降级为备机,备机升级为主机。
      :::

::: tip 存储高可用 - 双机切换 - 中介式缺点

  • 增加的机器成本
  • 如果中介本身宕机,则系统会处于双备状态,只能提供读取服务
  • 为了双机高可用而引入中介者,那如何实现中介者的高可用?如果对中介者设计双机高可用方案,则陷入了死循环。

中介者的高可用设计方案

  • ZooKeeper(推荐)
  • Keepalived
  • Nacos
    :::

::: tip 存储高可用 - 双机切换 - 模拟式
主备机之间并不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模拟的读写操作,根据读写操作的响应情况来判断主机的状态。
优点

  • 主备机中只有数据复制通道,没有复杂的状态传递通道

缺点

  • 备机只能获取到响应信息,例如:404、响应时长等。基于有效的状态来做状态决策,可能会出现偏差。
    :::

::: tip 存储高可用 - 主主复制
两台机器都是主机,互相将数据复制给对方,客户端可以任意挑选其中一台机器进行读写操作。
优点

  • 两台都是主机,不存在切换的概念。
  • 客户端无须区分不同角色的主机,随便将读写操作发送给哪台主机都可以。

缺点

  • 必须保证数据能够双向复制,但是有各种情况不能够双向复制。
    • 用户注册的用户id如果是自增长,就不能双向复制,会发生id冲突。
    • 库存、余额等共享数据不能双向复制,例如:同一用户含有1000余额,发起两笔500的支付,A机器处理一笔扣款500,同步余额500给B机器,B机器处理一笔扣款500,同步余额500给A机器,此时支付了1000,用户依然还有500余额。

因此,主主复制架构对数据的设计有严格的要求,一般适合于那些临时性、可丢失、可覆盖的数据场景。例如:

  • 用户登录产生的 session 数据(可以重新登录生成)。
  • 用户行为的日志数据(可以丢失)。
  • 论坛的草稿数据(可以丢失)。
    :::

::: tip 软件系统的高可用性的X个9
X个9表示在软件系统1年时间的使用过程中,系统可以正常使用时间与总时间(1年)之比,常见的X是代表数字3~5。

  • 3个9:(1-99.9%)36524=8.76小时,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是8.76小时。
  • 4个9:(1-99.99%)36524=0.876小时=52.6分钟,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是52.6分钟。
  • 5个9:(1-99.999%)36524*60=5.26分钟,表示该软件系统在连续运行1年时间里最多可能的业务中断时间是5.26分钟。

为什么没有1~2个9或者6个9?

  • 1个9:(1-90%)*365=36.5天
  • 2个9:(1-99%)*365=3.65天
  • 6个9:(1-99.9999%)365246060=31秒

36.5天、3.65天的不可用状态,还算不上高可用。
而5个9到6个9的可靠性提升的话,后者需要付出比前者几倍的成本,所以通常之谈3~5个9
:::

::: tip
单机可靠性只有2个9
:::


::: tip 康威定律

  • 第一定律 组织沟通方式会通过系统设计表达出来。
  • 第二定律 时间再多一件事情也不可能做的完美,但总有时间做完一件事情。
  • 第三定律 线型系统和线型组织架构间有潜在的异质同态特性。
  • 第四定律 大的系统组织总是比小系统更倾向于分解。
    :::
文章作者: Administrator
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 AE86小行星
喜欢就支持一下吧