深入理解 PostgreSQL 的架构和内部工作原理

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁
🦄 个人主页——libin9iOak的博客🎐
🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐
🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🍁🐥

文章目录

  • 深入理解 PostgreSQL 的架构和内部工作原理
    • 摘要:
    • 1. 引言
    • 2. 概述 PostgreSQL 架构
      • 客户端与服务器端交互
      • 查询处理器
      • 执行计划生成
      • 存储引擎
      • 事务管理与并发控制
      • 缓存管理
      • 后台进程
    • 3. 连接和会话管理
      • 连接到 PostgreSQL 服务器
      • 认证和权限验证
      • 建立会话
      • 连接池和连接限制
      • 4. 查询处理与执行
      • 解析查询语句
      • 查询优化器
      • 执行计划生成
      • 查询执行
      • 5. 事务管理与并发控制
      • 事务的 ACID 特性
      • 多版本并发控制(MVCC)
      • 处理高并发读写操作
    • 6. 存储引擎与物理存储结构
      • 表空间
      • 表的组织和存储
      • 索引
      • 数据文件
      • 高级存储技术
    • 7. 日志与恢复
      • Write-Ahead Logging(WAL)
      • 归档日志
      • 数据恢复和故障恢复
    • 8. 扩展性与高可用性
      • 读写分离
      • 分片
      • 流复制和逻辑复制
      • 自动故障转移
    • 9. 性能调优与监控
      • 性能调优原则与技巧
      • 性能监控工具与技术
    • 10. 结论
    • 11. 参考文献
  • 原创声明

在这里插入图片描述

深入理解 PostgreSQL 的架构和内部工作原理

摘要:

在各个领域广泛应用的 PostgreSQL 是一个强大的开源关系型数据库管理系统。本博客的主题是深入了解 PostgreSQL 的架构和内部工作原理,旨在帮助读者更好地理解其工作机制,从而优化和管理 PostgreSQL 数据库。

1. 引言

PostgreSQL,作为一款功能丰富、可靠稳定的数据库管理系统,已经被广泛应用于众多项目和企业中。为了深入理解 PostgreSQL 的内部工作原理,我们将介绍其整体架构以及客户端、服务器端和后台进程之间的交互关系。

2. 概述 PostgreSQL 架构

PostgreSQL是一个功能强大的开源关系型数据库管理系统,其架构包括多个组件,每个组件在数据库系统中发挥着关键的作用。了解 PostgreSQL 的整体架构对于深入理解其内部工作原理至关重要。

客户端与服务器端交互

在 PostgreSQL 中,客户端通过网络连接到服务器端来执行数据库操作。客户端可以是各种各样的应用程序,如Web应用、命令行工具或者图形界面应用。服务器端接收来自客户端的请求,并根据请求的内容执行相应的数据库操作。

查询处理器

当客户端发送 SQL 查询到服务器端时,查询处理器负责解析和处理这些查询。首先,它会对查询进行语法解析,确保查询语句的合法性。然后,查询处理器会根据查询的语义,生成执行计划。

执行计划生成

查询优化器是生成执行计划的关键组件。优化器会分析查询,考虑多种执行方案,并选择最优的执行计划。优化器的目标是通过选择最佳执行计划来提高查询的性能和效率。

存储引擎

执行计划被传递给存储引擎,存储引擎负责实际执行查询并从磁盘或内存中读取数据。PostgreSQL支持多种存储引擎,这使得它具备了较高的灵活性和扩展性。

事务管理与并发控制

PostgreSQL严格遵循ACID(原子性、一致性、隔离性和持久性)事务特性。事务管理器负责处理事务的提交和回滚,并确保多个并发事务之间的隔离性和一致性。

缓存管理

为了提高数据库查询的性能,PostgreSQL使用了缓存机制。它会将频繁使用的数据和查询结果存储在内存中,以便快速响应客户端的请求。

后台进程

除了上述关键组件之外,PostgreSQL还有一些重要的后台进程,用于执行诸如自动化清理、日志管理、并发控制等任务,以保证数据库的正常运行。

在后续的章节中,我们将详细探讨每个组件的工作原理,并了解 PostgreSQL 如何通过这些组件实现其强大的功能和高性能。通过深入理解 PostgreSQL 架构,您将能够更好地管理和优化您的 PostgreSQL 数据库。

3. 连接和会话管理

连接和会话管理是数据库操作中至关重要的环节。在本节中,我们将详细解释客户端如何连接到 PostgreSQL 服务器并建立会话,以及如何优化数据库的性能和资源利用率。

连接到 PostgreSQL 服务器

当客户端应用程序希望与 PostgreSQL 数据库进行交互时,它需要通过网络连接到 PostgreSQL 服务器。客户端会提供服务器的主机名(或 IP 地址)和端口号,以便建立连接。

认证和权限验证

一旦客户端与 PostgreSQL 服务器建立连接,服务器会要求客户端进行身份验证。这是为了确保只有授权的用户能够访问数据库。PostgreSQL 支持多种身份验证方法,包括基于密码的身份验证、证书认证等。

建立会话

在通过身份验证后,客户端与 PostgreSQL 服务器建立会话。会话是一种持久的连接,它允许客户端在多个查询之间保持状态。在会话期间,客户端可以执行多个查询,并在每个查询之间共享数据和状态。

连接池和连接限制

为了优化数据库的性能和资源利用率,通常会使用连接池技术。连接池允许多个客户端共享一组预先建立的数据库连接,从而避免了频繁地创建和销毁连接的开销。此外,为了控制数据库服务器的负载,可以对连接数进行限制,以防止过多的连接导致性能下降。

4. 查询处理与执行

一旦客户端与 PostgreSQL 建立会话,它可以发送 SQL 查询到服务器端以执行数据库操作。在本节中,我们将深入探讨查询处理的过程,以及如何优化查询执行,提高数据库性能。

解析查询语句

当服务器接收到客户端发送的 SQL 查询语句时,首先要对查询进行语法解析。解析器将查询语句转换为内部数据结构,以便数据库系统理解查询的含义。

查询优化器

一旦查询被解析,查询优化器便会介入工作。查询优化器的目标是选择最佳的执行计划,以最高效地执行查询。优化器考虑多种执行策略,并评估每种策略的成本,然后选择最优的执行计划。

执行计划生成

在优化器选择执行计划后,执行计划生成器将根据优化器的选择,生成实际执行查询的计划。执行计划是一组操作步骤,描述了数据库系统如何从存储中获取数据,并对数据进行处理,以产生最终的查询结果。

查询执行

最后,生成的执行计划会被传递给存储引擎,存储引擎会负责实际执行查询并返回结果给客户端。存储引擎会根据执行计划从磁盘或内存中读取数据,并执行各种数据库操作,如过滤、排序和聚合等。

在深入理解查询处理和执行的过程后,我们可以通过优化查询语句和索引设计等手段,提高数据库的性能和响应速度,从而更好地满足应用程序的需求。

5. 事务管理与并发控制

事务管理和并发控制是 PostgreSQL 强大功能的基础。在本节中,我们将详细讨论 PostgreSQL 是如何实现事务的 ACID 特性,并解释多版本并发控制(MVCC)的原理。

事务的 ACID 特性

PostgreSQL 严格遵循事务的 ACID 特性:

  • 原子性(Atomicity):事务被视为一个不可分割的操作单元,要么全部执行成功,要么全部失败回滚,保证数据库的一致性。
  • 一致性(Consistency):事务将数据库从一个一致性状态转移到另一个一致性状态,不会破坏数据完整性。
  • 隔离性(Isolation):事务的执行是相互隔离的,一个事务的执行不会影响其他事务的执行结果。
  • 持久性(Durability):一旦事务提交,其修改将永久保存在数据库中,即使发生系统故障也不会丢失。

多版本并发控制(MVCC)

为了实现高并发性能,PostgreSQL 使用了多版本并发控制(MVCC)机制。MVCC 允许多个事务并发执行,每个事务都能看到自己的数据库快照,从而实现了高度的隔离性。当事务需要修改数据时,它会创建数据的新版本,而不是直接修改原始数据,这保证了不同事务之间的隔离性和一致性。

处理高并发读写操作

MVCC 的使用使得 PostgreSQL 能够高效处理高并发读写操作。多个读操作可以同时进行,而不会相互干扰。对于写操作,PostgreSQL 使用锁和版本控制来确保数据的一致性和完整性。

通过深入理解事务管理和并发控制的原理,数据库管理员和开发人员可以更好地设计应用程序的数据库操作,避免冲突和数据错误,从而保证数据库系统的稳定和可靠性。

6. 存储引擎与物理存储结构

PostgreSQL的存储引擎对于优化数据库性能至关重要。在本节中,我们将深入探讨存储引擎以及物理存储结构的组织方式,包括表空间、表、索引和数据文件,同时介绍一些高级存储技术,如表分区和页压缩。

表空间

表空间是 PostgreSQL 中用于组织数据文件的逻辑容器。每个表空间可以包含一个或多个数据文件,这些数据文件可以位于不同的磁盘分区。通过合理规划表空间,可以将不同的数据对象分别存放在不同的物理位置,实现数据的有效管理和优化存储性能。

表的组织和存储

在 PostgreSQL 中,表是数据的逻辑组织单元,用于存储实际的数据记录。表的数据可以存储在多个数据文件中,这些数据文件由表空间管理。表的组织方式对数据库性能影响很大,因此选择合适的表结构和索引设计至关重要。

索引

索引是加快数据检索速度的重要手段。PostgreSQL支持多种类型的索引,如B树索引、哈希索引和GIN(Generalized Inverted Index)等。合理创建索引可以大大提高查询性能,但过多的索引也会增加数据维护的开销,因此需要根据实际需求进行权衡和优化。

数据文件

PostgreSQL使用数据文件来存储表中的数据。每个表的数据可以分布在多个数据文件中,每个数据文件通常对应一个表空间。通过将表数据划分到多个数据文件中,可以实现数据的并行读写,提高数据库的扩展性和性能。

高级存储技术

为了更好地管理数据和优化存储,PostgreSQL提供了一些高级存储技术。其中,表分区是一种将大表拆分为多个子表的方法,可以提高查询性能和数据维护的效率。另外,页压缩是一种压缩数据文件中的数据页,减少存储空间占用,但同时会增加数据读取时的CPU开销。

通过了解存储引擎和物理存储结构的组织方式,数据库管理员和开发人员可以更好地规划数据库的存储布局和索引设计,以提高数据库的性能和可维护性。

7. 日志与恢复

日志机制是确保数据库数据安全性和故障恢复的重要手段。在本节中,我们将解释 PostgreSQL 中的日志机制,并讨论 Write-Ahead Logging(WAL)和归档日志的作用,以及如何使用日志来实现数据恢复和故障恢复。

Write-Ahead Logging(WAL)

WAL是 PostgreSQL 中的一种重要日志技术。在每次数据修改之前,PostgreSQL会先将修改操作记录到WAL中,然后再进行实际的数据修改。这确保了在发生故障时,数据库可以通过重放WAL日志来恢复数据,从而保证数据的一致性和完整性。

归档日志

为了进一步保障数据的安全性,PostgreSQL还支持归档日志。归档日志是WAL日志的备份,可以将WAL日志复制到远程存储位置,从而防止数据丢失。在故障恢复时,可以使用归档日志进行数据恢复。

数据恢复和故障恢复

当数据库发生故障时,如硬件故障、崩溃或意外关闭,PostgreSQL可以使用WAL日志和归档日志进行数据恢复。通过回放WAL日志和应用归档日志,数据库可以将数据恢复到最近的一致状态,保障数据的完整性。

了解日志机制和故障恢复流程,对数据库管理员至关重要。通过合理配置日志设置和定期备份归档日志,可以最大程度地降低数据丢失的风险,并确保数据库的高可用性和稳定性。

8. 扩展性与高可用性

随着数据量的增长,扩展性和高可用性成为了数据库系统设计的重要考虑因素。在本节中,我们将介绍 PostgreSQL 的扩展性策略,包括读写分离和分片等方法,并提供实现高可用性的选项,如流复制、逻辑复制和自动故障转移等。

读写分离

为了应对高并发读写请求,PostgreSQL支持读写分离技术。通过将读操作和写操作分离到不同的数据库实例,可以减轻主数据库的负载,并提高读取性能。

分片

当单个数据库无法满足数据存储和查询的需求时,可以考虑使用分片技术。分片将大表拆分为多个子表,并将这些子表分布在不同的数据库节点上,从而实现数据的横向扩展。

流复制和逻辑复制

为了实现高可用性,PostgreSQL支持流复制和逻辑复制。流复制是将数据实时复制到多个备用数据库节点,从而在主数据库发生故障时,自动切换到备用节点。逻辑复制

则可以选择性地复制特定数据表或数据集合,提供更灵活的数据复制和同步选项。

自动故障转移

为了确保数据库系统的高可用性,自动故障转移是一种重要的实践方法。通过使用自动故障转移工具,如Patroni或Pacemaker,可以实现在主数据库发生故障时,自动将备用节点提升为主节点,从而实现数据库的自动切换和恢复。

通过合理规划和选择扩展性和高可用性的策略,数据库管理员可以确保 PostgreSQL 数据库能够随着业务需求的增长而持续稳定地运行。同时,高可用性的设计还能最大程度地减少数据库系统的宕机时间,保障数据的可靠性和一致性。

9. 性能调优与监控

性能调优是确保 PostgreSQL 数据库高效运行的关键步骤。在本节中,我们将提供一些性能调优的一般原则和技巧,帮助您优化数据库性能。同时,我们还将推荐一些常用的性能监控工具和技术,帮助管理员及时发现和解决性能问题。

性能调优原则与技巧

  1. 索引优化:合理创建索引可以显著提高查询性能。评估数据库的查询模式,选择适当的字段作为索引,并避免过多或不必要的索引,以减少维护开销。

  2. 查询优化:审查查询语句,确保使用高效的查询语法和 JOIN 操作,避免不必要的子查询或全表扫描。

  3. 内存管理:增加 PostgreSQL 的共享缓存大小,充分利用系统的内存资源,减少磁盘 I/O,提高查询性能。

  4. 参数调整:根据实际硬件和工作负载,调整 PostgreSQL 的配置参数,如并发连接数、查询缓存等,以获得最佳性能。

  5. 数据分区:对大型表进行分区,将数据分散到不同的表空间,提高查询效率和数据维护的灵活性。

性能监控工具与技术

  1. pg_stat_statements:这个扩展可以监控查询的执行情况,包括执行次数、平均执行时间等,帮助识别慢查询。

  2. pg_stat_activity:这个视图可以查看当前活跃的数据库会话和查询信息,帮助管理员及时发现并解决连接问题。

  3. pgBadger:这是一个用于解析 PostgreSQL 日志文件并生成报告的工具,可以帮助管理员追踪数据库的性能情况。

  4. pg_top:这是一个类似于Unix的top命令的工具,可以实时监控 PostgreSQL 数据库的活动情况。

  5. PostgreSQL自带日志:开启 PostgreSQL 的日志记录功能,可以记录数据库的查询和错误信息,帮助管理员进行故障排查和性能分析。

性能监控是 PostgreSQL 数据库维护的重要组成部分。通过使用这些工具和技术,管理员可以及时了解数据库的性能表现,发现潜在的性能问题,并进行相应的优化和调整。

10. 结论

在本博客中,我们深入理解了 PostgreSQL 的架构和内部工作原理。从概述 PostgreSQL 架构到连接和会话管理、查询处理与执行、事务管理与并发控制、存储引擎与物理存储结构、日志与恢复、扩展性与高可用性,每个主题都为我们揭示了 PostgreSQL 数据库的重要特性和机制。

深入理解这些原理有助于管理员更好地管理和优化 PostgreSQL 数据库,确保其高效稳定地运行。通过遵循性能调优原则和技巧,合理配置数据库参数,优化查询和索引,以及使用性能监控工具,管理员可以保持对数据库性能的持续关注,及时发现和解决性能问题,从而使 PostgreSQL 数据库成为高效可靠的数据存储平台。

希望本博客对您深入了解 PostgreSQL 并优化数据库性能有所帮助。继续学习和实践,将能够进一步掌握 PostgreSQL 的高级特性,并在实际应用中发挥其强大功能。祝您在 PostgreSQL 的学习和应用中取得成功!

11. 参考文献

在本博客的写作过程中,我们参考了以下相关书籍、文章和官方文档链接,推荐给读者进一步深入学习:

  • PostgreSQL官方文档
  • 《PostgreSQL 9 高级内核》- 刘铁岩
  • 《PostgreSQL 权威指南》- 安格尔 (Angulo)
  • 《PostgreSQL技术内幕》- 张文博

希望这篇博客能够帮助您更好地理解 PostgreSQL 的架构和内部工作原理,并在实践中应用这些知识来优化和管理您的数据库系统。祝您学习愉快!

原创声明

======= ·

  • 原创作者: 猫头虎

作者wx: [ libin9iOak ]


本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。

作者保证信息真实可靠,但不对准确性和完整性承担责任

未经许可,禁止商业用途。

如有疑问或建议,请联系作者。

感谢您的支持与尊重。

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/5618.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Postman怎么做接口测试-以简单的登录接口为例

我们就以登录某测试系统为例子,实现在Postman上做接口测试 一、首先打开系统首页首页,做一个登录操作(目的是获取接口url及参数):一般在公司做接口测试的时候页面还没有出来,我们需要根据接口文档进行接口…

kafka第三课-可视化工具、生产环境问题总结以及性能优化

一、可视化工具 https://pan.baidu.com/s/1qYifoa4 密码:el4o 下载解压之后,编辑该文件,修改zookeeper地址,也就是kafka注册的zookeeper的地址,如果是zookeeper集群,以逗号分开 vi conf/application.conf 启…

Python 逻辑回归:理论与实践

文章目录 1. 介绍1.1 什么是逻辑回归?1.2 逻辑回归的应用领域 2. 逻辑回归的原理2.1 Sigmoid 函数2.2 决策边界2.3 损失函数 3. 逻辑回归的实现3.1 数据准备3.2 创建逻辑回归模型3.3 模型训练3.4 模型预测3.5 模型评估 4. 可视化决策边界4.1 绘制散点图4.2 绘制决策…

基于SaaS模式的Java基层卫生健康云HIS系统源码【运维管理+运营管理+综合监管】

云HIS综合管理平台 一、模板管理 模板分为两种:病历模板和报表模板。模板管理是运营管理的核心组成部分,是基层卫生健康云中各医疗机构定制电子病历和报表的地方,各医疗机构可根据自身特点特色定制电子病历和报表,制作的电子病历…

Docker 容器生命周期:创建、启动、暂停与停止----从创建到停止多角度分析

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

代码随想录| 图论04 查并集 ●查并集理论知识 ●1971.寻找图中是否存在路径 ●684.冗余连接 ●685.冗余连接II

#查并集理论知识 并查集用处:解决连通性问题 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 思路:将三个元素A,B,C (分别是数字)放在同一个集合,其实就是将三个元素连通在一起&a…

Python 算法基础篇:插入排序和希尔排序

Python 算法基础篇:插入排序和希尔排序 引言 1. 插入排序算法概述2. 插入排序算法实现实例1:插入排序 3. 希尔排序算法概述4. 希尔排序算法实现实例2:希尔排序 5. 插入排序与希尔排序的对比总结 引言 插入排序和希尔排序是两种常用的排序算法…

017-从零搭建微服务-系统服务(四)

写在最前 如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 源码地址(后端):https://gitee.com/csps/mingyue 源码地址(前端):https://gitee.com/csps…

【NLP】如何使用Hugging-Face-Pipelines?

一、说明 随着最近开发的库,执行深度学习分析变得更加容易。其中一个库是拥抱脸。Hugging Face 是一个平台,可为 NLP 任务(如文本分类、情感分析等)提供预先训练的语言模型。 本博客将引导您了解如何使用拥抱面部管道执行 NLP 任务…

超详细图文教程:3DS Max 中创建低多边形游戏长剑模型

推荐: NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 在此,由两部分组成的教程的第一部分中,我将向您展示如何: 对剑柄进行建模剑的护手模型剑刃建模 1. 如何制作剑柄 步骤 1 在本教程中使用正交视图。要更改视图&#x…

AI时代带来的图片造假危机,该如何解决

一、前言 当今,图片造假问题非常泛滥,已经成为现代社会中一个严峻的问题。随着AI技术不断的发展,人们可以轻松地通过图像编辑和AI智能生成来篡改和伪造图片,使其看起来真实而难以辨别,之前就看到过一对硕士夫妻为了骗…

Flink-端到端精确一次(End-To-End Exactly-Once)

1.总结 目的:想要在故障恢复后不丢数据 输入端 保证可以重复发送数据如果是kafka,Flink负责维护offset,不用kafka维护设置kafka的隔离级别为:读已提交flink 开启检查点采用对齐或者不对齐的精确一次输出端 kafka 幂等事务两阶段…

一文了解Python中的while循环语句

目录 🥩循环语句是什么 🥩while循环 🥩遍历猜数字 🥩while循环嵌套 🥩while循环嵌套案例 🦐博客主页:大虾好吃吗的博客 🦐专栏地址:Python从入门到精通专栏 循环语句是什…

Mysql表锁与行锁

Mysql锁实战 前言:什么是锁一:全局锁1.1 概念1.2 作用1.3 使用1.4 特点 二:表级锁2.1 概念2.2 分类2.2.1 表锁2.2.2 元数据锁 MDL2.2.3 意向锁 三:行级锁3.1 行锁(Record Lock)3.2 间隙锁(Gap Lock)3.3 临键锁(Next-Key Lock): 四…

C# 委托详解

一.委托的概念 C#中委托也叫代理,委托提供了后期绑定机制(官方解释),功能类似于C中的函数指针,它存储的就是一系列具有相同签名和返回类型的方法的地址,调用委托的时候,它所包含的所有方法都会被执行。 二.委托的用法…

自然语言处理基础详解入门

1、自然语言的概念 自然语言是指人类社会约定俗成的,并且区别于人工语言(如计算机程序)的语言,,是自然而然的随着人类社会发展演变而来的语言,它是人类学习生活的重要工具。 2、自然语言处理概述 自然语言…

Redis【实践篇】之RedisTemplate基本操作

Redis 从入门到精通【应用篇】之RedisTemplate详解 文章目录 Redis 从入门到精通【应用篇】之RedisTemplate详解0. 前言1. RedisTemplate 方法1. 设置RedisTemplate的序列化方式2. RedisTemplate的基本操作 2. 源码浅析2.1. 构造方法2.2. 序列化方式2.3. RedisTemplate的操作方…

【数据可视化】基于Python和Echarts的中国经济发展与人口变化可视化大屏

1.题目要求 本次课程设计要求使用Python和ECharts实现数据可视化大屏。要求每个人的数据集不同,用ECharts制作Dashboard(总共至少4图),要求输入查询项(地点和时间)可查询数据,查询的数据的地理…

Stable Diffusion如何生成高质量的图-prompt写法介绍

文章目录 Stable Diffusion使用尝试下效果prompt的编写技巧prompt 和 negative promptPrompt格式Prompt规则细节优化Guidance Scale 总结 Stable Diffusion Stable Diffusion是一个开源的图像生成AI系统,由Anthropic公司开发。它基于 Transformer模型架构,可以通过文字描述生成…

Asp.net Core配置CORS 跨域无效(记录一下)

问题 学习老杨的英语网站项目,运行项目时,发现出现了跨域的问题。 然后自己建一项目,进行配置,测试,发现配置CORS 跨域时,发现跨域的配置无效,依旧报错。 解决 网上找了一天,然后…