数据库中的XML支持与早期数据管理方法概述
1. 关系型数据库管理系统中的XML支持
虽然XML本身由标准定义,但特定数据库管理系统(DBMS)支持XML的方式却各不相同。下面来看看两种广泛使用的DBMS——DB2和Oracle对XML的支持情况。
1.1 DB2对XML的支持
- 存储方面 :DB2通过XML列数据类型支持XML存储。可以将XML数据类型分配给任何关系中的列,这样表的每一行该列就能存储一个XML文档。创建包含XML列的表的语法与使用其他SQL数据类型创建表的语法相同,例如:
CREATE TABLE stocked_products
(
store_numb int PRIMARY KEY,
UPC char (13) PRIMARY KEY,
Product_details XML
);
存储文档时,应用程序将文本发送到DB2服务器,服务器会检查文档是否正确(格式良好且有效),然后将其转换为层次化表示形式。这个层次结构与表的其他部分分开存储,但有内部数据结构将关系型和XML存储结构关联起来。
-
索引方面
:DB2的XML列数据类型不需要与XML模式关联,但模式可用于检查文档的正确性。索引能显著提高数据库查询性能,XML文档的数据埋在文档内部(即单个DB2列中),不过其层次化存储结构使索引成为可能。要为特定元素创建索引,用户必须提供通过层次结构到达所需数据的路径。例如,为产品名称创建索引的语句如下:
CREATE INDEX idx_product_name ON products
(product_details) GENERATE KEY USING XML PATTERN
'/product_details/product_name' AS SQL VARCHAR
(50);
这里索引的键由数据元素的路径指示,且必须为其指定一个SQL数据类型用于索引。如果XML文档中的值无法转换为索引数据类型,该值将不包含在索引中。
-
数据操作方面
:DB2通过SQL/XML和XQuery这两个扩展支持XML数据的操作。它们都是查询语言,但使用XQuery进行数据修改的支持仍在开发中。SQL/XML是SQL标准的扩展,不过和大多数DBMS一样,其实现是专有的。XQuery也是一个开放标准。SQL/XML包含对标准SQL的扩展,可用于检索整个XML文档或文档中的特定元素。引用XML文档中的特定元素时,需要包含通过文档存储层次结构到达该元素的路径,就像为单个数据元素创建索引时那样。
1.2 Oracle对XML的支持
-
存储选项
:Oracle为XML文档提供了三种存储选项:
- 将整个XML文档存储为字符大对象(CLOB)。由于文档是作为一个整体存储的,因此无法根据XML模式进行验证,并且在关系型查询中性能较慢,同时CLOB存储占用的磁盘空间最多。
- 使用XML模式解析文档(包括验证)并以二进制形式存储数据。这是最容易使用的存储方法,虽然在关系型查询中的性能不是最优的,但优于CLOB存储。
- 将解析后的数据以对象 - 关系型格式存储,这是关系型和面向对象数据建模的组合。向数据库添加XML文档时性能较慢,但查询性能最佳,且磁盘空间使用最少。
- 数据类型与插入操作 :和DB2一样,Oracle为关系中的列提供了一种数据类型(XMLType)来存储XML数据。关系中的XMLType列可以接受来自先前存储的CLOB、字符列(VARCHAR2)、格式正确的SQL INSERT语句或另一个XMLType列的查询的数据。例如,向库存产品表中添加一行的SQL INSERT语句如下:
INSERT INTO stocked_products
{
3,
'1010101010101',
XMLType (<product_details>
<product_name>gel pen</product_name>
<product_dept>Office Supplies</product_dept>
<product_desc>
<color>black</color>
<unit>box</unit>
<numb_per_pack>12</numb_per_pack>
</product_desc>
</product_details>)
};
- 查询支持 :Oracle的SQL实现允许使用标准SQL SELECT命令查询XMLType列。该产品还支持SQL/XML用于查询关系型数据以生成XML文档作为输出,并提供了XQuery实现。
2. 早期数据管理方法
在关系型数据模型出现之前,有多种数据管理方法,下面介绍一些早期的数据管理组织方式。
2.1 文件处理系统
- 早期文件处理 :早期的文件处理系统由一组数据文件(最常见的是文本文件)和直接操作这些文件而不借助DBMS的应用程序组成。文件采用非常精确的固定格式布局,每个单独的数据项(如名字、姓氏、街道地址等)称为字段,描述单个实体的数据收集到一个记录中,数据文件由多个记录组成。每个字段分配特定数量的字节,固定字段长度意味着字段之间或记录末尾不需要分隔符,不过有些数据文件也会包含分隔符。程序通过数据在文件中的字节位置来定位数据,假设文件中的第一条记录编号为0,程序可以通过以下计算定位任何字段的起始位置:
record_number * record_length + starting_position_of_field
这种文件结构很容易解析,也简化了操作文件的应用程序的编写过程。如果文件存储在磁带上,对记录的访问是顺序的;如果存储在磁盘上,软件可以进行直接访问读写。但无论哪种情况,程序都需要确切知道每个数据的存储位置,并负责发出适当的读写命令。
-
存在的问题
:这些系统存在很多问题,例如更改数据文件的布局(如更改字段或记录的大小)需要更改所有访问该文件的程序,并重新编写文件以适应新布局。按文件物理顺序顺序处理所有记录时访问速度很快,但根据某些匹配标准搜索特定记录时,必须顺序执行,这是一个非常缓慢的过程,即使对于存储在磁盘上的文件也是如此。不过文件处理系统的主要优点是成本低,安装计算机的组织通常拥有所需的一切:外部存储和编译器,而且创建相对容易,不需要太多预先规划。但由于不必要的数据重复、程序与物理文件布局的紧密耦合以及搜索文件时出现的严重性能问题,促使20世纪50年代和60年代的数据管理人员寻找替代方案。
2.2 ISAM文件
在数据库管理系统引入之前,IBM的程序员开发了一种增强的文件组织方式——索引顺序访问方法(ISAM)。ISAM文件必须存储在磁盘上,最初写入磁盘时,每个占用的柱面会预留多余空间,以便按顺序键顺序添加记录。当柱面填满时,记录会写入溢出区域,并链接回它们在文件主存储区域序列中的位置。当溢出区域填满时,文件必须重新分块,重新分块过程中,文件大小会增加,记录会被重写,每个占用的柱面再次预留扩展空间。重新分块期间,无法使用该文件进行数据处理。ISAM文件的索引可以与数据存储在同一个文件中,也可以存储在单独的文件中。当数据文件分开存储时,操作文件的函数将索引和数据视为一个逻辑文件。虽然ISAM文件在很大程度上已不再使用,但DBMS Informix(现在由IBM拥有)仍在使用其自己版本的ISAM——c - isam进行数据存储。
2.3 文件处理的局限性
- 不支持即席查询 :文件处理无法支持即席查询(即一时兴起产生的、无法预测且可能不会再次出现的查询)。因为数据文件由使用它的组织创建,不同组织的文件没有通用的布局,所以软件开发人员无法编写一种可以查询任何数据文件的语言。如今很多数据访问都需要即席查询功能,例如ATM机,其软件无法预测用户将访问哪个账户、谁会使用特定的机器以及用户会从机器请求什么,因此软件必须能够随时从任何位置访问任何账户持有人的数据并执行任何请求的操作。
- 跨文件处理困难 :当文件处理系统由多个文件组成时,很难验证文件之间的交叉引用,也难以执行需要多个文件数据的查询。这是一个主要的数据完整性问题,例如将客户数据存储在文件A中,订单存储在文件B中,需要确保文件B中的客户数据(即使只是客户编号)与文件A中的客户数据匹配。当数据存储在多个文件中时,很难进行这种验证,唯一的方法是编写一个同时使用两个文件的程序,并明确验证文件B中的客户数据是否与文件A中的数据匹配,但文件处理系统很少进行这种验证。同样,需要从多个文件中提取数据的查询或报告也很难准备,生成输出的应用程序必须创建为读取所有必要的文件,由于其复杂性,这样的程序很难调试和维护。
3. 桌面文件处理与相关概念混淆
数据管理软件存在一个问题,开发人员和用户常常不理解“数据库”一词的确切含义,导致该词被应用于任何管理存储在磁盘文件中的数据的软件,而不管该软件是否能处理逻辑数据关系。例如20世纪80年代初的pfs : File,它只是一个简单的文件管理器,没有办法表示多个实体或数据关系,但却被作为数据库管理系统进行营销,从而引发了市场上的混淆。像FileMaker Pro最初是文件管理器,后来升级到数据库状态,FileMaker公司在2008年推出的Bento,宣传为“个人数据库管理器”,但实际上它只是一个文件管理器。尽管“数据库”这个词被错误使用,但桌面文件管理器对于维护邮件列表、客户联系列表等应用程序仍然是有用的工具。在考虑购买产品时,必须特别注意其功能,确保消费者真正了解他们购买的产品及其实际伴随的限制。
4. 层次数据模型
- 特点 :第一个真正开发的数据库模型是层次数据模型,它于1966年作为商业产品的基础出现。和随后的两种网络数据模型一样,它是一种导航式数据模型,意味着访问路径受模式中预先声明的指针结构限制。使用层次数据模型设计的数据库仅限于一对多关系,且任何子实体不能有多个父实体。例如,在一个包含部门、员工和项目的ER图中,存在部门与员工、项目的关系,以及员工与项目的关系。理想情况下,希望使用复合实体来处理多对多关系,但层次数据模型不允许包含复合实体,只能复制实体实例,这意味着项目实例必须为每个参与该项目的员工复制一份,且项目和员工实体在部门层次结构中也会被复制。因此,层次结构本身包含大量重复数据,这使得层次数据库容易出现因不必要的数据重复而导致的数据一致性问题。此外,访问只能通过层次结构顶部的实体(根)进行,从每个根实例开始,访问路径是从上到下、从左到右。例如,在部门层次结构中,必须先通过部门,再到所有员工,最后才能到项目。按默认顺序遍历层次结构速度很快,但如果需要随机访问数据,速度会极慢,因为必须遍历层次结构中所需实例之前的每个实体实例才能到达所需实例。所以层次数据库非常适合按树遍历顺序进行批处理,但不适合需要即席查询的应用程序。不过,与文件处理系统(包括基于ISAM文件的系统)相比,层次模型是一个巨大的进步,它允许用户根据逻辑数据关系存储和检索数据,提供了逻辑和物理数据存储之间的一定独立性,在很大程度上减轻了应用程序程序员对物理文件布局的关注。
- IMS系统 :最成功的层次DBMS是IBM的IMS,它自1966年以来一直在处理高容量事务导向的数据处理。如今,IBM支持IMS遗留系统,但积极劝阻新安装。实际上,有很多工具可以帮助公司从IMS迁移到新产品或将IMS集成到更先进的软件中。IMS并不严格遵循理论上的层次数据模型,在某些非常严格的情况下允许有多个父实体。例如,在一个包含部门到项目层次结构和仅包含员工的层次结构的示例中,项目实体的多个父实体是允许的,因为第二个父实体(员工实体)在另一个层次结构中且在该层次结构中处于较高级别。尽管对多个父实体有严格限制,但这种规则的放宽在很大程度上消除了不必要的数据重复。IMS不支持查询语言,所有访问都通过通常用COBOL编写的应用程序进行。和真正的层次DBMS一样,它最适合按树遍历顺序进行批处理,在银行和保险公司等具有大量运营事务处理负载的大型企业中被大量使用。
5. 简单网络数据模型
-
特点
:在IBM开发IMS的同时,其他公司正在开发基于简单网络数据模型的DBMS。第一个基于该模型的DBMS于1967年出现(GE的IDS),它直接解决了层次数据模型的一些局限性,在业务使用方面,简单网络数据库是所有关系前数据模型中部署最广泛的。简单网络数据库支持实体之间的一对多关系,且对多个父实体没有限制。例如,员工/部门/项目数据库可以设计成项目作为部门和员工之间的复合实体,并且部门和员工之间有直接关系以实现更快的访问。与层次数据模型相比,简单网络是一个合乎逻辑的进化步骤,它消除了层次数据模型最严重的限制:不允许多个父实体,并且进一步分离了逻辑和物理存储。简单网络数据库通过直接在数据中嵌入指针或使用索引来实现数据关系。无论使用哪种策略,除非为特定类型的实体定义了快速访问路径,否则对数据的访问仅限于指针创建的预定义链接。简单网络有两种类型的快速访问路径:
- 哈希 :影响实体实例在数据文件中的放置策略。当实体实例被哈希到数据文件中时,DBMS使用键(一个或多个属性的值)计算物理文件定位器(通常称为数据库键)。要检索实例,DBMS重新计算哈希值。相关实体的实例会围绕其父实体聚集在数据文件中,这样既可以快速访问父实体,又可以将子实体与父实体放在同一磁盘页面上以加快检索速度。例如,数据库设计人员可能选择对部门实例进行哈希处理,并将项目围绕其部门聚集。
- 索引 :提供对包含二级键的实体实例的快速直接访问。如果实例没有进行哈希处理且没有索引,那么检索它们的唯一方法是通过遍历与父实体实例的关系。为了实现数据关系的遍历,简单网络DBMS必须跟踪其在数据库中的位置。对于每个针对数据库运行的程序,DBMS维护一组货币指示器,每个指示器是一个系统变量,包含特定类型的最后访问实体实例的数据库键。应用程序可以使用货币指示器的内容相对于程序在数据库中的先前位置进行数据访问。
-
存在的问题
:最初,简单网络DBMS不支持查询语言,但随着关系型数据模型变得更流行,许多供应商在其产品中添加了关系型风格的查询语言。如果简单网络数据库设计得像关系型数据库,那么它可以像关系型数据库一样被查询,但底层仍然是简单网络,因此数据库仍然受到简单网络的访问限制。简单网络数据库不容易维护,特别是更改数据库的逻辑设计可能会造成极大的干扰。首先,数据库必须离线,在更改完成之前不能对其进行任何处理。数据库离线后,会经历以下过程:
- 备份所有数据或将数据保存为文本文件。
- 删除当前的模式和数据文件。
- 编译新的数据库模式,通常包含在一个用数据定义语言(DDL)编写的文本文件中。
- 重新分配数据文件的空间。
-
重新加载数据文件。
在后来的简单网络DBMS中,这个过程在很大程度上由实用软件自动化,但由于大多数简单网络DBMS是基于大型机的,涉及大量数据,更改逻辑数据库可能需要很长时间。如今仍有一些简单网络数据库作为遗留系统在使用,但一个组织决定基于这种数据模型创建新数据库是非常不寻常的。
6. CODASYL标准
20世纪60年代中期,政府和行业专业人士组成了数据系统语言委员会(CODASYL),其目标是开发一种商业编程语言,最终结果是COBOL。在工作过程中,委员会意识到除了编程语言之外,还有另一个成果:简单网络数据库的规范。CODASYL分离出数据库任务组(DBTG),该组在1969年发布了其规范集。CODASYL规范提交给了美国国家标准协会(ANSI),ANSI对标准进行了一些修改,以进一步将数据库的逻辑设计与其物理存储布局分开,结果产生了两组非常相似但不完全相同的规范。需要注意的是,CODASYL是一个标准而不是产品,许多产品是为遵循CODASYL标准而开发的,也有一些简单网络DBMS采用简单网络数据模型但不遵循CODASYL标准。
一个遵循CODASYL标准的DBMS将简单网络视为一组称为集合的两级层次结构。例如,在一个包含部门、员工和项目的数据库中,需要两个集合:一个用于部门→员工和部门→项目,另一个用于员工→项目。关系中“一”端的实体称为集合的所有者,“多”端的实体称为集合的成员。任何集合中只能有一个所有者实体,但可以有多个成员实体。同一个实体可以是一个集合的所有者,也是另一个集合的成员,这样数据库设计人员就可以构建多层次的网络。访问方式要么是使用快速访问路径(哈希或索引)直接访问实体实例,要么是按遍历顺序访问。在CODASYL数据库中,集合的成员有一个由数据库设计人员指定的顺序。如果一个实体没有被赋予快速访问路径,那么检索实例的唯一方法是通过某些集合的所有者,并且除非所有这些实例都是同一个集合中具有相同所有者的成员,否则无法检索一个实体的所有实例。每个集合提供一个概念上的链表,从所有者实例开始,经过所有成员实例,最后链接回所有者。和层次数据库中层次结构的实例一样,集合的实例是不同且无关的。早期的CODASYL DBMS实际上将集合实现为链表,这导致数据文件中复杂的指针操作,特别是对于属于多个集合的实体。后来的产品使用索引来表示集合,数据库键作为指向所有者和成员记录存储位置的指针。对于不属于任何集合的实体(如某些部门实例),CODASYL数据库支持一种特殊类型的集合(通常称为系统集合),它只有一个所有者实例:数据库系统本身。属于该集合的实体的所有实例都连接到这个单一的所有者实例。员工和项目可能也会包含在系统集合中,以便能够访问所有员工和所有项目。系统集合的声明由数据库设计人员决定。任何遵循CODASYL标准的DBMS通常被称为CODASYL DBMS,这代表了市场上销售的最大数量的简单网络产品。可以说,最成功的CODASYL DBMS是IDMS,它最初由Cullinet开发,是一个大型机产品,在20世纪80年代很受欢迎。随着关系型DBMS开始主导市场,IDMS被赋予了一种类似关系型的查询语言,并作为IDMS/R进行营销。最终,Cullinet被卖给了Computer Associates,该公司以CA - IDMS的名称营销和支持该产品。
7. 复杂网络数据模型
复杂网络数据模型与简单网络同时开发,它允许直接的多对多关系,而不需要引入复合实体。其开发者的意图是消除简单网络数据模型对多对多关系的限制,但这种限制的消除代价高昂。在一个包含客户、订单和项目的数据库中,存在一些问题。首先,没有地方存储每个订购项目的数量等关系数据,这是用复合实体和两个一对多关系替换多对多关系的一个原因。其次,当添加另一个实体(如发货)时,每个项目可以出现在多个发货中,每个发货可以包含多个项目,此时会出现逻辑歧义。例如,无法确定发货属于哪个订单,从发货实例跟踪到项目实例后,无法知道哪个订单是正确的。针对这个问题有两个解决方案:
-
引入额外关系
:引入额外的关系来指示哪个发货来自哪个订单,但这会增加存储和检索数据的复杂性。
-
引入复合实体
:完全放弃使用复杂网络,引入复合实体将所有多对多关系简化为一对多关系,结果就是一个简单网络。由于维护多对多关系的复杂性和逻辑歧义的可能性,基于复杂网络数据模型的商业产品没有广泛成功。不过,该数据模型仍存在于文献中,为传统数据建模提供了一定的理论完整性。在数据模型的发展顺序中,关系型数据模型在网络数据模型之后出现,自关系型数据模型发展以来,又出现了两种额外的数据模型:面向对象数据模型和多维数据库(通常称为在线分析处理[OLAP]或星型模式数据库),多维数据库主要用于数据仓库。关系型DBMS可以表示简单网络中所有可接受的关系(包括复合实体),但以非导航的方式进行,和简单网络一样,它可以捕捉多对多关系的所有含义,同时避免数据歧义。
数据库中的XML支持与早期数据管理方法概述
8. 各数据管理方法对比
为了更清晰地了解不同数据管理方法的特点,下面通过表格进行对比:
| 数据管理方法 | 关系类型支持 | 数据重复情况 | 访问方式 | 查询灵活性 | 维护难度 | 适用场景 |
| — | — | — | — | — | — | — |
| 文件处理系统 | 无明确关系 | 可能存在大量重复 | 顺序或直接访问,依赖字节位置 | 不支持即席查询,限于预定义查询 | 更改布局困难,跨文件处理复杂 | 简单数据存储,对查询要求低 |
| ISAM文件 | 无明确关系 | 可能存在重复 | 支持顺序和索引访问 | 不支持即席查询,限于预定义查询 | 溢出区域处理和重块操作复杂 | 对顺序访问和部分索引访问有需求 |
| 层次数据模型 | 一对多,子实体单父实体 | 存在大量重复 | 导航式,从根开始 | 不适合即席查询,适合批处理 | 设计更改可能导致大量数据重复 | 批处理场景,关系简单且固定 |
| 简单网络数据模型 | 一对多,支持多父实体 | 相对较少 | 导航式,可通过哈希或索引加速 | 后期支持关系型查询,但受底层限制 | 逻辑设计更改干扰大 | 对关系表达有一定需求,业务规模适中 |
| CODASYL标准(简单网络) | 一对多,支持多父实体 | 相对较少 | 导航式,通过集合和快速路径 | 受集合和路径限制 | 设计更改需按流程操作 | 遵循标准的简单网络应用 |
| 复杂网络数据模型 | 多对多 | 可能存在复杂重复和歧义 | 复杂导航 | 存在逻辑歧义,查询困难 | 维护多对多关系复杂 | 理论研究,实际应用少 |
9. 操作步骤总结
在实际应用中,不同的数据管理方法有不同的操作步骤,下面进行总结:
9.1 DB2操作步骤
- 创建包含XML列的表 :
CREATE TABLE stocked_products
(
store_numb int PRIMARY KEY,
UPC char (13) PRIMARY KEY,
Product_details XML
);
- 为XML元素创建索引 :
CREATE INDEX idx_product_name ON products
(product_details) GENERATE KEY USING XML PATTERN
'/product_details/product_name' AS SQL VARCHAR
(50);
9.2 Oracle操作步骤
- 插入数据到XMLType列 :
INSERT INTO stocked_products
{
3,
'1010101010101',
XMLType (<product_details>
<product_name>gel pen</product_name>
<product_dept>Office Supplies</product_dept>
<product_desc>
<color>black</color>
<unit>box</unit>
<numb_per_pack>12</numb_per_pack>
</product_desc>
</product_details>)
};
9.3 简单网络DBMS逻辑设计更改步骤
- 备份所有数据或将数据保存为文本文件。
- 删除当前的模式和数据文件。
- 编译新的数据库模式,通常包含在一个用数据定义语言(DDL)编写的文本文件中。
- 重新分配数据文件的空间。
- 重新加载数据文件。
10. 数据管理方法的发展趋势
从早期的数据管理方法到现在,数据管理领域发生了巨大的变化。随着数据量的不断增加和业务需求的日益复杂,对数据管理的要求也越来越高。
早期的文件处理系统和ISAM文件由于其局限性,逐渐被更先进的数据库模型所取代。层次数据模型和网络数据模型在一定程度上解决了数据关系的表达问题,但仍然存在数据重复、查询灵活性差等问题。
关系型数据模型的出现是数据管理领域的一个重要里程碑,它以其简洁的结构、强大的查询能力和数据完整性保障,成为了主流的数据管理方式。此后,面向对象数据模型和多维数据库等新的数据模型也不断涌现,以满足不同场景下的数据管理需求。
未来,随着人工智能、大数据、物联网等技术的发展,数据管理将面临更多的挑战和机遇。例如,如何处理海量的非结构化数据、如何实现实时数据分析等。数据管理方法也将不断创新和发展,以适应不断变化的技术和业务环境。
11. 选择合适的数据管理方法
在选择数据管理方法时,需要综合考虑多个因素,以下是一些建议:
-
业务需求
:如果业务主要是简单的数据存储和顺序处理,文件处理系统或ISAM文件可能是合适的选择。如果业务涉及复杂的关系表达和查询,关系型数据库或更先进的数据模型可能更适合。
-
数据规模
:对于小规模的数据,简单的数据库模型可能足以满足需求。但对于大规模的数据,需要考虑数据的存储效率、查询性能等因素。
-
查询灵活性
:如果需要频繁进行即席查询,关系型数据库或支持高级查询语言的数据库模型更有优势。
-
维护成本
:不同的数据管理方法在维护成本上差异较大,例如简单网络数据库的逻辑设计更改可能需要较长时间和较高的成本。
12. 总结
本文介绍了关系型DBMS中XML的支持情况,包括DB2和Oracle的不同实现方式。同时,回顾了早期的数据管理方法,如文件处理系统、ISAM文件、层次数据模型、简单网络数据模型、CODASYL标准和复杂网络数据模型,并分析了它们的特点、优缺点和适用场景。
通过对这些数据管理方法的了解,我们可以更好地选择适合自己业务需求的数据管理方案。在实际应用中,需要根据具体情况权衡各种因素,以确保数据的高效存储、管理和利用。随着技术的不断发展,数据管理领域也将不断创新,为我们提供更多更好的选择。
下面是一个简单的mermaid流程图,展示了从早期数据管理方法到现代数据模型的发展过程:
graph LR
A[文件处理系统] --> B[ISAM文件]
B --> C[层次数据模型]
B --> D[简单网络数据模型]
D --> E[CODASYL标准]
D --> F[复杂网络数据模型]
C --> G[关系型数据模型]
D --> G
G --> H[面向对象数据模型]
G --> I[多维数据库]
这个流程图清晰地展示了不同数据管理方法之间的发展脉络和继承关系,帮助我们更好地理解数据管理领域的发展历程。
超级会员免费看
887

被折叠的 条评论
为什么被折叠?



