
数据工程大数据序列化数据分析【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址https://gitcode.com/gh_mirrors/arrow13/arrow点击查看免费下载本文基于 Apache Arrow 官方协议文档 docs/source/format/FlightSql.rst 展开结合仓库中的协议定义 format/FlightSql.proto、时序图与 Java 实现进行纵深解读。读完本文你将掌握 Flight SQL 的命令分类、消息封装约定、SQL 元数据命令的返回 Schema、查询执行与预编译语句的完整流程以及会话管理机制并能在仓库源码中定位到对应实现。协议概览让数据库通过 Arrow 内存格式与 Flight RPC 对话Arrow Flight SQL 是一套用于通过 Arrow 内存格式和 Flight RPC 框架与 SQL 数据库交互的协议。它将 SQL 数据库的能力映射为 Flight 的 RPC 方法数据库厂商只需按规范实现服务端 RPC 方法无需为每种语言分别编写客户端驱动程序而数据库客户端则可以直接使用现成的 Flight SQL 客户端如仓库中的 FlightSqlClient.java连接任何实现了所需端点的数据库。从源码结构看协议的核心承载形式有两层传输层复用 Arrow Flight 的既有 RPC 方法GetFlightInfo、GetSchema、DoGet、DoPut、DoAction详见 docs/source/format/Flight.rst 与 format/Flight.proto消息层通过 Protobuf 定义各类 command 消息打包后放入 FlightDescriptor 或 Flight Action 中传递定义集中在 format/FlightSql.protopackage arrow.flight.protocol.sql。协议的整体设计目标是SQL 查询结果、元数据乃至批量导入的数据都以 Arrow 列式数据流的形式传输从而消除行式序列化如 JDBC/ODBC 的逐行取值带来的转换开销。RPC 方法与消息封装约定Flight SQL 的所有命令都遵循一套统一的封装约定理解它是阅读后续所有命令的前提。元数据与查询命令打包进 FlightDescriptor绝大多数命令包括全部 SQL 元数据命令和查询执行命令通过GetFlightInfo与GetSchema两个 RPC 方法使用。封装方式为将命令对应的 Protobuf 请求消息装入google.protobuf.Any序列化后填入CMD 类型FlightDescriptor 的cmd字段。客户端侧的具体做法可以在 FlightSqlClient.java 中看到execute()方法构造CommandStatementQuery后通过Any.pack(builder.build()).toByteArray()生成 descriptor再调用底层 Flight 客户端的client.getInfo(descriptor, options)。若命令用于GetFlightInfo服务端返回FlightInfo客户端用其中的Ticket配合DoGetRPC 拉取 Arrow 数据——SQL 元数据本身就是以 Arrow 数据形式返回的与查询结果的处理方式完全一致若命令用于GetSchema服务端返回该命令固定的 Arrow Schema协议对每个命令的返回 Schema 都有严格规定详见下文。Action 命令打包进 Flight Action以Action开头的命令如创建/关闭预编译语句改用DoActionRPC 方法命令消息装入google.protobuf.Any后序列化并填入 Flight Action 的body字段Action 的type字段设置为命令名例如对于ActionClosePreparedStatementRequesttype应为ClosePreparedStatement。在 Java 实现中prepare()方法正是以FLIGHT_SQL_CREATE_PREPARED_STATEMENT作为 Action type将ActionCreatePreparedStatementRequest的 Any 包装结果作为 body 发起client.doAction见 FlightSqlClient.java。更新类命令通过 DoPut 返回结果执行更新如CommandStatementUpdate、CommandStatementIngest的命令使用DoPutRPC客户端消费完整 FlightData 数据流后服务端返回 Flight RPC 的PutResult其中app_metadata字段携带 Flight SQL 的DoPutUpdateResult消息包含被影响的行数record_count-1表示行数未知见 format/FlightSql.proto。SQL 元数据命令数据库目录信息的标准查询接口Flight SQL 提供一组命令来获取数据库服务端的目录catalog元数据涵盖了 JDBCDatabaseMetaData类接口的大多数能力。下表汇总了文档中列出的九个命令及各自含义命令作用CommandGetCatalogs列出数据库中可用的 catalog定义因厂商而异CommandGetCrossReference列出给定表中引用给定父表列的外键列CommandGetDbSchemas列出数据库中可用的 schema指表的集合分组不是Arrow schemaCommandGetExportedKeys列出引用给定表主键列的外键列CommandGetImportedKeys列出给定表的外键CommandGetPrimaryKeys列出给定表的主键CommandGetSqlInfo获取数据库服务端及其支持的 SQL 特性元数据CommandGetTables列出数据库中的表CommandGetTableTypes列出数据库中的表类型列表因厂商而异所有命令均可在GetFlightInfo/GetSchema上使用且返回 Schema 由协议固定。下面结合 format/FlightSql.proto 中的消息定义给出各命令的返回 Schema 细节。目录与 Schema 列举CommandGetCatalogsproto 定义返回单列catalog_name: utf8 not null结果按catalog_name排序CommandGetDbSchemasproto 定义返回catalog_name: utf8可空与db_schema_name: utf8 not null两列按 catalog 再按 schema 排序。请求消息支持catalog可选空字符串表示检索无 catalog 的 schema省略则不做过滤与db_schema_filter_pattern过滤模式模式串中%匹配任意长度子串、_匹配任意单个字符——这与 ODBC 的搜索模式约定一致。表与表类型CommandGetTableTypesproto 定义返回单列table_type: utf8 not null按table_type排序。TABLE、VIEW、SYSTEM TABLE是常见取值CommandGetTablesproto 定义返回catalog_name、db_schema_name、table_name: utf8 not null、table_type: utf8 not null若请求了include_schema还会追加table_schema: bytes not null列——该字段以 IPC 消息承载 Schema.fbs 中描述的 Arrow Schema。请求参数包括catalog、db_schema_filter_pattern、table_name_filter_pattern三者均支持%/_通配、table_types重复字段用于过滤以及include_schema。特别值得注意的是返回的 table schema 字段上可以携带以下元数据键同样适用于CommandStatementQuery/CommandPreparedStatementQuery的返回 SchemaARROW:FLIGHT:SQL:CATALOG_NAME表的 catalog 名ARROW:FLIGHT:SQL:DB_SCHEMA_NAME数据库 schema 名ARROW:FLIGHT:SQL:TABLE_NAME表名ARROW:FLIGHT:SQL:TYPE_NAME数据源特有的数据类型名ARROW:FLIGHT:SQL:PRECISION列精度/大小ARROW:FLIGHT:SQL:SCALE列小数位ARROW:FLIGHT:SQL:IS_AUTO_INCREMENT1表示自增列0否则ARROW:FLIGHT:SQL:IS_CASE_SENSITIVE1表示大小写敏感ARROW:FLIGHT:SQL:IS_READ_ONLY1表示只读列ARROW:FLIGHT:SQL:IS_SEARCHABLE1表示可通过 WHERE 子句检索这些元数据为 JDBC/ODBC 驱动映射列级属性提供了标准通道。主键与外键CommandGetPrimaryKeysproto 定义返回catalog_name、db_schema_name、table_name: utf8 not null、column_name: utf8 not null、key_name: utf8、key_sequence: int32 not null按 catalog、schema、table、key_name 再按 key_sequence 排序。请求参数为catalog可选、db_schema可选与必填的tableCommandGetExportedKeys/CommandGetImportedKeys/CommandGetCrossReferenceproto 定义三者都返回成对的主键/外键列描述pk_catalog_name、pk_db_schema_name、pk_table_name、pk_column_name、fk_catalog_name、fk_db_schema_name、fk_table_name、fk_column_name、key_sequence、fk_key_name、pk_key_name、update_rule、delete_rule。其中update_rule与delete_rule为uint8取值对应 UpdateDeleteRules 枚举0CASCADE、1RESTRICT、2SET NULL、3NO ACTION、4SET DEFAULT。三者的语义差异在于查询方向CommandGetExportedKeys给定表作为父表列出引用其主键的外键由catalog/db_schema/table指定父表CommandGetImportedKeys给定表作为子表列出其自身的外键由catalog/db_schema/table指定子表CommandGetCrossReference同时指定父表pk_catalog/pk_db_schema/pk_table与子表fk_catalog/fk_db_schema/fk_table列出子表中引用父表主键或唯一约束列的外键列。CommandGetSqlInfo服务端能力自描述CommandGetSqlInfoproto 定义是元数据命令中最重要的一员其请求消息CommandGetSqlInfo仅含一个repeated uint32 info字段若省略则返回全部元数据否则只返回指定 ID 的元数据项。其返回 Schema 固定为 info_name: uint32 not null, value: dense_union string_value: utf8, bool_value: bool, bigint_value: int64, int32_bitmask: int32, string_list: liststring_data: utf8, int32_to_int32_list_map: mapkey: int32, value: listint32 每行对应一条元数据信息。info_name的取值由 SqlInfo 枚举 定义其 ID 区间划分非常清晰模仿 ODBC 的SQLGetInfo()函数ID 区间类别示例0–499服务器信息FLIGHT_SQL_SERVER_NAME(0)、FLIGHT_SQL_SERVER_VERSION(1)、FLIGHT_SQL_SERVER_ARROW_VERSION(2)、FLIGHT_SQL_SERVER_READ_ONLY(3)、FLIGHT_SQL_SERVER_SQL(4)、FLIGHT_SQL_SERVER_SUBSTRAIT(5)、FLIGHT_SQL_SERVER_SUBSTRAIT_MIN/MAX_VERSION(6/7)、FLIGHT_SQL_SERVER_TRANSACTION(8)、FLIGHT_SQL_SERVER_CANCEL(9)、FLIGHT_SQL_SERVER_BULK_INGESTION(10)、FLIGHT_SQL_SERVER_INGEST_TRANSACTIONS_SUPPORTED(11)、FLIGHT_SQL_SERVER_STATEMENT_TIMEOUT(100)、FLIGHT_SQL_SERVER_TRANSACTION_TIMEOUT(101)500–999SQL 语法信息SQL_DDL_CATALOG(500)、SQL_DDL_SCHEMA(501)、SQL_DDL_TABLE(502)、SQL_IDENTIFIER_CASE(503)、SQL_IDENTIFIER_QUOTE_CHAR(504)、SQL_KEYWORDS(508)、SQL_NUMERIC_FUNCTIONS(509)、SQL_STRING_FUNCTIONS(510)、SQL_SYSTEM_FUNCTIONS(511)、SQL_DATETIME_FUNCTIONS(512)、SQL_SEARCH_STRING_ESCAPE(513)、SQL_SUPPORTED_GROUP_BY(522)、SQL_SUPPORTED_GRAMMAR(525)、SQL_ANSI92_SUPPORTED_LEVEL(526)、SQL_OUTER_JOINS_SUPPORT_LEVEL(528)、SQL_SUPPORTED_SUBQUERIES(538)、SQL_SUPPORTED_UNIONS(540) 等1000–9999保留区间默认项—≥10000自定义扩展服务端可选用必须从 10000 开始SqlInfo枚举取值类型多样布尔型如是否只读、是否支持 SQL、字符串型服务器名、标识符引号字符、int32 位掩码型如SQL_SUPPORTED_GRAMMAR的位组合1MINIMUM、2CORE、4EXTENDED、int64 型各类长度上限如SQL_MAX_COLUMNS_IN_SELECT(547)以及 map 型如SQL_SUPPORTS_CONVERT(517)返回mapint32, listint32。位掩码型返回值的解析方式在 proto 注释中均有逐位示例例如SQL_SUPPORTED_GROUP_BY返回 1 表示支持SQL_GROUP_BY_UNRELATED返回 2 表示支持SQL_GROUP_BY_BEYOND_SELECT返回 3 表示两者皆支持。SqlInfo之外proto 还定义了配套的细粒度枚举用于解释这些信息包括SqlSupportedTransactionNONE/TRANSACTION/SAVEPOINT三级事务支持SqlSupportedCaseSensitivity、SqlNullOrdering、SupportedSqlGrammar、SupportedAnsi92SqlGrammarLevel、SqlOuterJoinsSupportLevel、SqlSupportedGroupBy、SqlSupportedSubqueries、SqlSupportedUnions、SqlTransactionIsolationLevel、SqlSupportedResultSetType、SqlSupportedResultSetConcurrency 等XdbcDataType 与 XdbcDatetimeSubcode与 JDBC/ODBC 规范保持一致的 SQL 类型编号及 datetime/interval 子类型Nullable、Searchable描述列空值约束与 WHERE 子句可检索性。CommandGetXdbcTypeInfo数据类型支持描述除文档正文列出的九个命令外协议文件还定义了CommandGetXdbcTypeInfoproto 定义用于获取后端支持的数据类型信息。其请求可携带可选的data_type指定要查询的特定类型返回 Schema 极为详尽包含type_name、data_typeXdbcDataType 枚举值、column_size、literal_prefix/literal_suffix、create_params、nullable、case_sensitive、searchable、unsigned_attribute、fixed_prec_scale、auto_increment、local_type_name、minimum_scale/maximum_scale、sql_data_type、datetime_subcode、num_prec_radix、interval_precision等列完整对应 ODBC 的SQLGetTypeInfo结果集。查询执行即席查询、更新与批量导入Flight SQL 的查询执行命令复用前述封装约定覆盖即席查询、无结果更新、批量数据导入三类场景。CommandStatementQuery即席查询GetFlightInfo执行即席 SQL 查询随后客户端调用DoGet拉取结果GetSchema仅返回查询结果的 Arrow Schema。消息定义为 CommandStatementQueryquery为 SQL 文本可选的transaction_id表示将该查询纳入某个事务执行未设置则自动提交。配套的 TicketStatementQuery 是GetFlightInfo返回的 Ticket 内容协议明确要求客户端将其视为一次性、不透明的值不得尝试解析。对应的 Java 客户端 API 为execute(query)与getExecuteSchema(query)见 FlightSqlClient.java。CommandStatementSubstraitPlanproto 定义则是该命令的 Substrait 变体用SubstraitPlan内含序列化的substrait.Plan与版本号字符串version见 format/FlightSql.proto代替 SQL 文本。CommandStatementUpdate无结果的更新语句用于执行不返回结果集的 SQL 更新UPDATE/INSERT/DELETE等DoPut执行查询并返回受影响行数CommandStatementUpdate 定义。行数通过DoPutUpdateResult.record_count传递-1表示未知。Java 客户端的executeUpdate()方法即对应此命令返回long类型的受影响行数见 FlightSqlClient.java。CommandStatementIngest批量数据导入批量导入是 Flight SQL 加速数据写入的重要能力DoPut将 FlightData 流中的 Arrow record batches 加载到指定目标表并通过DoPutUpdateResult返回导入的行数。CommandStatementIngest 定义 提供了丰富的导入控制参数字段类型说明table_definition_optionsTableDefinitionOptions目标表不存在/已存在时的处理策略tablestring数据要载入的目标表schemaoptional string目标表所在 schema未设置用后端默认值catalogoptional string目标表所在 catalog未设置用后端默认值temporarybool是否存入临时表后端定义命名空间会话结束自动删除transaction_idoptional bytes纳入指定事务出错/取消时不提交optionsmapstring, string后端特有选项预留字段号 1000其中TableDefinitionOptions内嵌两个枚举TableNotExistOptionTABLE_NOT_EXIST_OPTION_CREATE(1) 不存在则创建TABLE_NOT_EXIST_OPTION_FAIL(2) 不存在则报错UNSPECIFIED(0) 仅用于占位客户端不应使用TableExistsOptionTABLE_EXISTS_OPTION_FAIL(1) 已存在则失败TABLE_EXISTS_OPTION_APPEND(2) 追加写入TABLE_EXISTS_OPTION_REPLACE(3) 先删后建UNSPECIFIED(0) 不应使用。该命令的完整调用链可参考仓库中的时序图源文件 CommandStatementIngest.mmdDoPut(CommandStatementIngest)→ 客户端推送 FlightData 流 → 服务端返回PutResult{DoPutUpdateResult{RecordCount}}。预编译语句创建、绑定参数、执行与关闭预编译语句Prepared Statement是 Flight SQL 中流程最完整、状态管理最复杂的部分涉及DoAction、DoPut、GetFlightInfo/GetSchema四种 RPC 的组合。创建ActionCreatePreparedStatementRequest通过DoAction发送ActionCreatePreparedStatementRequestproto 定义query为 SQL 文本可带transaction_id另有 Substrait 变体 ActionCreatePreparedSubstraitPlanRequest。响应为 ActionCreatePreparedStatementResult包含三个字段prepared_statement_handle服务端返回的不透明句柄用于后续标识该预编译语句dataset_schema结果集 Schema 的 IPC 封装来自 Schema.fbsparameter_schema绑定参数的 Schema若有。协议在此给出两条重要提示由于结果集 Schema 可能依赖绑定参数dataset_schema只是服务端的最佳猜测客户端不得假定该 Schema 与最终实际返回的数据一致对于无具体类型的绑定参数如SELECT ?协议目前未规定参数 Schema 的表示方式建议服务端使用 union 类型枚举可能的类型或用 NAnull类型作为通配/占位。绑定参数与执行CommandPreparedStatementQueryCommandPreparedStatementQueryproto 定义仅含prepared_statement_handle有三种用法DoPut向预编译语句绑定参数值可选的响应消息 DoPutPreparedStatementResult 中可能返回更新后的句柄prepared_statement_handle。更新句柄机制的意义在于支持无状态服务服务端可以把已绑定的参数编码进新句柄客户端后续请求都携带新句柄。一旦收到新句柄所有旧句柄立即失效。值得注意的是DoPut 返回的句柄本身可以再次传给下一次 DoPut 以绑定新参数集服务端负责检测客户端未使用更新句柄的情况并返回错误GetFlightInfo执行预编译语句并取回结果语句执行后可以复用GetSchema获取结果集预期 Schema若客户端此前已通过 DoPut 绑定参数服务端应将参数值纳入考虑。无结果执行与关闭CommandPreparedStatementUpdateproto 定义执行不返回结果的预编译语句。DoPut时执行并返回受影响行数语句可复用ActionClosePreparedStatementRequestproto 定义关闭预编译语句并释放服务端资源。可通过FLIGHT_SQL_SERVER_STATEMENT_TIMEOUTSqlInfo ID100查询/控制句柄超时0 表示无超时句柄被使用时服务端应重置计时。完整调用链时序见 CommandPreparedStatementQuery.mmdDoAction(CreatePreparedStatementRequest)→Result{handle}→ 对每次执行循环DoPut(CommandPreparedStatementQuery) FlightData 流 →可选DoPutPreparedStatementResult{handle}→GetFlightInfo(CommandPreparedStatementQuery)→ 对每个 endpoint 执行DoGet(ticket)→ 最后DoAction(ClosePreparedStatementRequest)。Java 客户端的prepare()方法返回PreparedStatement内部类实现AutoCloseable封装了上述全部流程见 FlightSqlClient.java。事务与取消相关 Action 命令协议文件还定义了与查询生命周期配套的 Action 命令事务能力是否支持可由 SqlInfo ID8FLIGHT_SQL_SERVER_TRANSACTION查询取值见 SqlSupportedTransactionActionBeginTransactionRequest→ActionBeginTransactionResult定义开启事务返回不透明transaction_id事务可被EndTransaction操作超时自动回滚ActionBeginSavepointRequest→ActionBeginSavepointResult定义在事务内创建保存点仅在服务端支持保存点时可用ActionEndTransactionRequest定义END_TRANSACTION_COMMIT(1) 提交或END_TRANSACTION_ROLLBACK(2) 回滚成功后事务句柄及其全部保存点失效ActionEndSavepointRequest定义END_SAVEPOINT_RELEASE(1) 释放或END_SAVEPOINT_ROLLBACK(2) 回滚到保存点回滚会使其后创建的保存点失效。Java 客户端对应方法为beginTransaction()、beginSavepoint()、commit()、rollback()见 FlightSqlClient.java。另外ActionCancelQueryRequest/ActionCancelQueryResultproto 定义提供显式取消运行中查询的能力幂等但其消息已标注deprecated自 13.0.0 起协议建议改用 Flight 核心协议中的CancelFlightInfoaction 配合DoAction使用。Flight Server 会话管理SetSessionOptions / GetSessionOptions / CloseSessionFlight SQL 提供会话管理命令用于设置和更新影响服务端行为的会话变量例如catalog与schema当前查询针对的 catalog/schema。三个命令的语义如下SetSessionOptions按名称/值设置服务端会话选项GetSessionOptions获取当前会话选项包括客户端设置的以及服务端默认或隐式设置的CloseSession关闭并使当前会话上下文失效。对应的消息定义位于 Flight 核心协议 format/Flight.protoSetSessionOptionsRequest/GetSessionOptionsRequest/CloseSessionRequest等Flight SQL 服务端与客户端直接复用。Java 客户端封装见 FlightSqlClient.java。文档对会话使用给出了三条实践指导尽早设置客户端应尽可能在任何查询及其他命令之前设置选项因为某些服务端实现要求选项恰好设置一次且必须早于任何可能触发其隐式设置的活动兼容 JDBC/ODBC 驱动强烈建议服务端接受选项值的字符串表示以便连接串中的参数无需转换即可透传同时建议非强制接受其他数值类型并转换为选项的首选类型会话持久化会话在客户端与服务端之间通过实现定义机制保持典型实现是RFC 6265 Cookie。服务端可以把其他连接状态与会话令牌不透明地捆绑在一起——在设计会话生命周期与语义时应兼顾这些附加用途例如CloseSession也应使通过会话上下文持久化的任何认证上下文失效。会话可由一次非空或空的SetSessionOptions调用触发也可由服务端自选时机建立。四个核心流程时序图协议文档用四份 Mermaid 时序图位于 docs/source/format/FlightSql/直观刻画了典型交互流程CommandGetTables.mmd列出可用表。GetFlightInfo(CommandGetTables)→FlightInfo{Ticket}→DoGet(Ticket)→ FlightData 数据流。这一模式适用于全部元数据命令CommandStatementQuery.mmd执行即席查询。GetFlightInfo(CommandStatementQuery)→FlightInfo{endpoints:[FlightEndpoint,…]}→ 对每个 endpoint 循环DoGet(endpoint.ticket)拉取数据流——多 endpoint 意味着查询结果可以分布在多个数据流中如分区/分片场景CommandPreparedStatementQuery.mmd预编译语句完整生命周期即上文预编译语句一节的调用链CommandStatementIngest.mmd批量导入。DoPut(CommandStatementIngest) FlightData 流 →PutResult{DoPutUpdateResult{RecordCount: int64}}。仓库中的实现验证与后续阅读协议在仓库中有多语言实现其中 Java 实现最能体现上述全部约定客户端FlightSqlClient.java 覆盖元数据查询、即席查询、更新、预编译语句、事务、会话管理全部 API服务端接口FlightSqlProducer.java 定义服务端需实现的全部回调含 SQL 元数据、预编译语句、导入、事务与会话管理另有 BasicFlightSqlProducer.java 作为骨架实现SqlInfoBuilder.java 用于构建 SqlInfo 返回数据。其他语言的协议生成代码同样位于仓库中如 Go 的github.com/apache/arrow/go/arrow/flight/gen/flight包、Python/C/Ruby 等均有对应绑定。若想深入理解底层 RPC 语义可继续阅读 docs/source/format/Flight.rstFlight RPC 框架与 format/Flight.proto核心消息定义。结语Flight SQL 的设计精髓在于协议最小化 复用最大化数据库只需实现规范规定的 RPC 端点客户端即可获得统一的 SQL 交互能力所有结果与元数据都以 Arrow 列式格式流动消除了传统驱动层的行式转换瓶颈。无论是构建新的 SQL 数据库服务端、为现有数据库编写 Flight SQL 适配层还是开发跨数据库的客户端工具本文梳理的命令清单、消息封装约定与源码映射都能作为直接的实现参考。赞分享数据工程大数据序列化数据分析【免费下载链接】arrowApache Arrow is a multi-language toolbox for accelerated data interchange and in-memory processing项目地址https://gitcode.com/gh_mirrors/arrow13/arrow点击查看免费下载相关推荐Apache Arrow Flight RPC 协议全解析方法、消息格式与实现要点Apache Arrow Flight RPC 协议全解析方法、消息格式与实现要点 Apache Arrow Flight 是构建在 gRPC 与 Arrow数据工程数据分析大数据Apache Arrow Arrow Flight RPC协议实战指南Apache Arrow Arrow Flight RPC协议实战指南 Apache Arrow Flight RPC协议是一个专为大规模数据分析场景设计的高性数据工程大数据序列化数据分析Apache Arrow Flight SQL 协议深度解析基于 Flight RPC 的数据库访问规范与实现Apache Arrow Flight SQL 协议深度解析基于 Flight RPC 的数据库访问规范与实现 导读 Arrow Flight SQL 是 A大数据数据分析数据工程序列化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考