Decode360's Blog

业精于勤而荒于嬉 QQ:150355677 MSN:decode360@hotmail.com

  BlogJava :: 首页 :: 新随笔 :: 联系 ::  :: 管理 ::
  397 随笔 :: 33 文章 :: 29 评论 :: 0 Trackbacks
[Oracle10G_R2]02.可管理性特性

    由于具备自动存储管理命令行工具、直连 SGA 访问、支持联机分区重定义等特性,这个自我管理的数据库变得更加强大。
 
    这一部分涉及:
 
    ● ASM 命令行工具
    ● 删除空数据文件
    ● 针对挂起/低速系统的直连 SGA 访问
    ● 联机重新定义分区
    ● 在内存中检查数据块完整性,而非在磁盘上检查
    ● 联机限制更改
    ● 更快的启动
    ● 在 Oracle Enterprise Manager 中管理多个对象
    ● 自动的 Segment Advisor
    ● 基于事件的调度
 

ASM 命令行工具
 
    Oracle 自动存储管 (ASM;请参阅本系列的 1 )是 Oracle 数据库 10g 第 1 版中引入的专用文件系统,为数据文件的管理提供了急需的支持。
 
    ASM 通过 SQL 命令管理,必要时也可以通过 Oracle Enterprise Manager 界面管理。同样,您也可以通过 SQL 接口或 GUI 看到它。大多数 DBA 都能接受该方法,但对那些不熟悉 SQL 的系统管理员来说学习 SQL 是件不大情愿的事。而作为一名 DBA,您可能不大愿意授予非 DBA Oracle Enterprise Manager 的访问权限。
 
    在 Oracle 数据库 10g 第 2 版中,一个新的 ASM 命令行工具消除了这个隔阂。此接口称为 asmcmd,通过它您可以对 ASM 磁盘组中存储的数据文件(类似于文件系统和相应的文件)执行大量操作。该工具基于 Perl,因此后者应位于相应的路径中。如果未正确设置 Perl 的路径,则可能需要创建一个指向 Perl 所在目录的软链接,或只需修改文件 asmcmd 以反映 Perl 可执行文件的正确路径。
 
    切记将 ORACLE_SID 设置为 ASM 实例(通常为 +ASM),而不是在服务器上运行的实际数据库实例。通过键入以下指令调用该命令
 
asmcmd -p
 
    使用 -p 选项可以在提示中显示当前路径。
 
    现在,尝试一些非常简单的命令。调用命令行提示 (ASMCMD >) 后,键入 ls 查看已挂载的所有磁盘组。
 
ASMCMD [+] > ls
DGROUP1/
DGROUP10/
DGROUP2/
DGROUP3/
DGROUP4/
DGROUP5/
DGROUP6/
DGROUP7/
DGROUP8/
DGROUP9/
 
    可以在此处看到在 ASM 实例中创建和挂载的所有磁盘组(DGROUP1 至 DGROUP10)。
 
    现在,考察磁盘组 DGROUP1。可以使用 cd 命令像更改目录那样更改此磁盘组。
 
ASMCMD [+] > cd dgroup1
 
    甚至可以像在类 UNIX 或 Windows 这样的操作系统中那样,通过键入 cd .. 转到父目录。现在,确认在该磁盘组中创建了哪些文件。
 
ASMCMD [+dgroup1] > ls
ORCL/
 
    好了,该磁盘组的下面增加了一个目录 ORCL。从它后面的正斜线 (/) 即可看出它是一个目录。使用 cd 命令进入该目录,然后执行命令 ls 显示内容。
 
ASMCMD [+dgroup1] > cd orcl
ASMCMD [+dgroup1/orcl] > ls
CONTROLFILE/
PARAMETERFILE/
control01.ctl => +DGROUP1/ORCL/CONTROLFILE/Current.256.551928759
spfileorcl.ora => +DGROUP1/ORCL/PARAMETERFILE/spfile.257.551932189
ASMCMD [+dgroup1/orcl] >
 
    除了 cd 和 ls 命令外,还可以使用其他类 UNIX 的命令,如 rm(用于删除目录或文件)、mkdir(用于创建目录)和 find(用于查找文件和目录)。
 
    以下是一些其他命令:
 
    lsdg(list diskgroup 的缩写)—— 要查看该 ASM 实例挂载的磁盘,使用 lsdg 命令。
 
ASMCMD [+] > lsdg
State    Type    Rebal  Unbal  Sector  Block       AU  Total_MB  Free_MB  Req_mir_free_MB  Usable_file_MB  Offline_disks  Name
MOUNTED  EXTERN  N      N         512   4096  1048576       100       40                0              40              0  DGROUP1/
MOUNTED  EXTERN  N      N         512   4096  1048576       100       33                0              33              0  DGROUP10/
MOUNTED  EXTERN  N      N         512   4096  1048576       100       41                0              41              0  DGROUP2/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      787                0             787              0  DGROUP3/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      537                0             537              0  DGROUP4/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      928                0             928              0  DGROUP5/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      742                0             742              0  DGROUP6/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      943                0             943              0  DGROUP7/
MOUNTED  EXTERN  N      N         512   4096  1048576      1000      950                0             950              0  DGROUP8/
MOUNTED  EXTERN  N      N         512   4096  1048576       100       33                0              33              0  DGROUP9/
 
    除了显示磁盘名称以外,lsdg 还显示了其他相关信息,如已分配的空间大小、可用空间大小和脱机磁盘。该信息简化了问题的诊断。
 
    du(disk utilization 的缩写)—— 由于您已经在 ASM 磁盘上填充了数据,因此可能要查明磁盘组内部占用的空间大小。为此,可以像在 UNIX、Linux 或 Windows 中那样使用 du 命令。要了解目录内部已使用的空间大小,只需使用
 
ASMCMD [+] > du /dgroup1
Used_MB      Mirror_used_MB
      9                   9
 
    以上命令显示已经使用了 9MB。由于您已经使用了外部镜像,因此所使用的磁盘空间总大小仍为 9MB (Mirror_used_MB)。如果使用了 ASM 磁盘的标准冗余参数,则该数字将有所不同。
 
    help —— 没有帮助那还叫什么工具!您不必记住每一个命令。只需键入 help 即可显示一个命令列表。然后,可以键入 help <command> 查看特定命令的信息。例如,在此您要查看 mkalias 命令。
 
ASMCMD [+] > help mkalias
mkalias <system_alias> <user_alias>
 
Create the specified user_alias for the system_alias.The user_alias
must reside in the same diskgroup as the system_alias, and only one
user_alias is permitted per file.The SQLPLUS equivalent is "alter
diskgroup <dg_name> add alias <user_alias> for <system_alias>".
 
    您可以看到,这个丰富的命令集使 ASM 成为一个可管理性很高的文件系统,您甚至不需要研究 SQL 接口或 Oracle Enterprise Manager。还可以将这些命令轻松置于 shell 脚本中,从而为更多用户所接受。



删除空数据文件
 
    假设您刚刚向错误目录或表空间中添加了一个数据文件 —— 一个很常见的错误。并不会丢失一切;由于该数据文件不包含任何数据,因此您可以轻松地删除它,是这样吗?
 
    遗憾的是,您无法删除它。在 Oracle 数据库 10g 第 2 版之前,删除数据文件的唯一有效方法就是删除整个表空间,然后在没有该特定文件的情况下重新构建它。如果表空间包含数据,则必须通过既费时又费力的过程将数据存储到单独的位置并恢复它。除了不方便以外,该过程还使表空间不可用。
 
    幸运地是,在 Oracle 数据库 10g 第 2 版中,该过程已得到简化:您可以只删除数据文件。例如,以下命令将从表空间和服务器中删除指定的数据文件。
 
alter tablespace users drop datafile '/tmp/users01.dbf'
/
 
    但存在一些限制:1数据文件必须为空才能删除。无法删除表空间中的最后一个数据文件;必须删除表空间本身。同时,表空间必须处于联机和读写状态。



针对挂起/低速系统的直连 SGA 访问
 
    当用户经常抱怨数据库运行速度慢并经常超时时,大多数 DBA 最先想到的就是以 SYSDBA 连接到数据库并检查等待事件。但如果实例挂起,甚至您无法登录时又会怎样?这种情况下,即使最强大、最优秀的故障诊断查询也无济于事。
 
    在 Oracle 数据库 10g 第 2 版中,Oracle Enterprise Manager Grid Control 可以在苛刻环境下根据您的请求直接连接到 SGA,从而从进程状态中直接收集数据。这个所谓的内存访问模式增强了您有效使用 Oracle Enterprise Manager 的能力,即使在实例经历严重问题的情况下也是如此。而最重要的是,这是在 SQL 访问基本无法实现的情况下自动执行的。
 
    以下是它的工作方式:在 Oracle Enterprise Manager 用户界面中,选择 Performance 选项卡,并向下滚动到该页面底部标记为“Related Links”的部分,这将显示一个如下所示的屏幕。
 
 
    注意名为“Monitor in Memory Access Mode”的链接。单击该链接将显示一个如下所示的屏幕。注意“View Mode”下拉菜单,其中的“Memory Access”选项处于选中状态。
 
 
    可以使用“View Mode”下拉菜单控制 Oracle Enterprise Manager 获取数据的方式。在本示例中,它显示从内存获取的数据(“Memory Access”)。还可以在此处选择“SQL Access”以从性能视图中进行选择。
 
    请注意,内存访问模式并不是 SQL 访问的替代模式;它只适合在 SQL 访问不可用的紧急情况下使用。此外,内存访问模式只提供对分析挂起会话有帮助的数据子集。(有关该主题的详细信息请见下个部分 — “性能”特性。)



联机重新定义分区
 
    当大多数 DBA 没有停机时间来更改表时(如对其进行分区),他们将使用联机重新定义工具 DBMS_REDEFINITION。使用该工具,您可以更改对象定义,同时使它们保持可访问状态。
 
    但 DBMS_REDEFINITION 有一个限制,这使它在某些情况下无法提供帮助。例如,您可能要将表分区移动到不同的表空间。为此,必须移动整个表,即使已将其分区。如果该表很大,此方法将产生大量的重做和撤消操作,且无法利用现有分区。但如果一次可以移动一个分区,则可以显著减少时间、空间和重做/撤消要求。
 
    使用 Oracle 数据库 10g 第 2 版,您可以完全实现该目标:可以通过 Oracle Enterprise Manager 或命令行重新定义一个表分区。
 
    我们来看一个使用命令行的示例。在这里,您有一个名为 ACCOUNTS 的表,它包含 11 个分区,所有这些分区都位于同一表空间 USERS 中。您要将它们移到一个专门为该表创建的新表空间 ACCDATA 中。您将以一次移动一个分区的方式移动该表。
 
    首先,创建一个中间表,其结构与表 ACCOUNTS 相同但现在将数据置于 ACCDATA 表空间上。
 
SQL> create table accounts_int
2  tablespace accdata
3  as
4  select * from accounts
5  where 1=2
6  /
 
    注意分区现在所在的位置:
 
SQL> select partition_name, tablespace_name, num_rows
2  from user_tab_partitions
3  /
 
PARTITION_NAME                 TABLESPACE_NAME                  NUM_ROWS
------------------------------ ------------------------------ ----------
P1                             USERS                                1014
P2                             USERS                                1042
P3                             USERS                                1002
P4                             USERS                                 964
P5                             USERS                                 990
P6                             USERS                                1042
P7                             USERS                                 915
P8                             USERS                                 983
P9                             USERS                                1047
P10                            USERS                                1001
PMAX                           USERS                                   0
 
11 rows selected.
 
    所有分区均位于 USERS 表空间中。现在,将第一个分区 P1 移到表空间 ACCDATA 中。
 
SQL> begin
2     dbms_redefinition.start_redef_table (
3        uname => 'ARUP',
4        orig_table => 'ACCOUNTS',
5        int_table  => 'ACCOUNTS_INT',
6        part_name  => 'P1'
7     );
8  end;
9  /
 
PL/SQL procedure successfully completed.
 
    注意第 6 行,其中的 part_name 参数指定要重新组织的分区。如果省略该参数,则将同时重新定义所有分区。
 
    现在,将中间表与原始表同步。(仅当要对表 ACCOUNTS 进行更新时才需要执行该操作。)
 
SQL> begin
2     dbms_redefinition.sync_interim_table (
3        uname => 'ARUP',
4        orig_table => 'ACCOUNTS',
5        int_table  => 'ACCOUNTS_INT',
6        part_name  => 'P1'
7     );
8  end;
9  /
 
PL/SQL procedure successfully completed.
 
    最后,完成重新定义过程。
 
SQL> begin
2     dbms_redefinition.finish_redef_table (
3        uname => 'ARUP',
4        orig_table => 'ACCOUNTS',
5        int_table  => 'ACCOUNTS_INT',
6        part_name  => 'P1'
7     );
8  end;
9  /
 
PL/SQL procedure successfully completed.
 
    确认分区 P1 确已移到表空间 ACCDATA 中。
 
SQL> select partition_name, tablespace_name, num_rows
2  from user_tab_partitions
3  /
 
PARTITION_NAME                 TABLESPACE_NAME                  NUM_ROWS
------------------------------ ------------------------------ ----------
P1                             ACCDATA                              1014
P2                             USERS                                1042
P3                             USERS                                1002
P4                             USERS                                 964
P5                             USERS                                 990
P6                             USERS                                1042
P7                             USERS                                 915
P8                             USERS                                 983
P9                             USERS                                1047
P10                            USERS                                1001
PMAX                           USERS                                   0
 
11 rows selected.
 
    行了!对其他分区重复该过程。
 
    相比而言,如果您重新组织了整个表,则将 (a) 需要一个大小与整个表相等的空间,(b) 为整个表生成重做(必须存在),否则将产生错误。但通过对单个分区执行此过程,将减少单个分区的空间要求,并减少只为该分区生成的重做。
 
    使用这个强大而有用的特性,您可以联机重新组织非常大的对象(就像大多数已分区对象一样)。此外,还应注意如何将统计信息复制到已重新定义的表上(如以上查询中的 NUM_RWS 值所示);您不必为新建的表或分区重新生成统计信息。



在内存中检查数据块完整性,而非在磁盘上检查
 
    活动的数据库实例将大量数据从用户会话移动到缓冲区缓存,从缓存移动到磁盘,反之亦然。这些移动可能使数据块容易遭到损坏。
 
    Oracle 通过在将数据块写入磁盘之前计算数据值的校验和确保数据块的完整性。同时将该检验和值写入磁盘。当从磁盘中读取该块时,读取过程将再次计算校验和,然后与存储的值进行对比。如果值已损坏,校验和将不同,从而表明数据已经损坏。
 
    由于大多数操作发生在内存中,因此在源本身(即缓冲区缓存)中执行该检查应谨慎。在 Oracle 数据库 10g 第 2 版中,还可以通过将初始化参数 DB_BLOCK_CHECKSUM 设置为 FULL 来执行内存检查。
 
    在进行此设置后,Oracle 将在进行任何更改之前计算校验和并将该校验和与存储的值进行比较。该方法可以发现内存本身中的任何数据损坏,并在数据损坏时报告错误,这对防止磁盘级别的数据损坏以及防止将该损坏传播到备用数据库很帮助。
 
    请注意,默认情况下,该参数设置为 FALSE,这与早期版本不同,那时该参数设置为 TRUE。



联机限制更改
 
    当需要更改在创建数据库期间定义的参数(MAXDATAFILES、MAXLOGFILES 等)时,您有哪些选择?在 Oracle 数据库 10g 第 2 版之前,唯一的选择就是遵循以下步骤:
 
    1、备份要跟踪的控制文件。
    2、在跟踪文件中修改要更改的参数。
    3、关闭数据库。
    4、开始挂载。
    5、重新创建控制文件。
    6、在RESETLOGS模式下打开数据库。
 
    不用说,该方法降低了可用性。此外,由于 RMAN 将有关备份的元数据保存在控制文件和目录中,因此该信息将在此过程中丢失。由于控制文件是在 RESETLOGS 模式下创建的,因此某些备份信息也将丢失。
 
    在 Oracle 数据库 10g 第 2 版中,您无需重新创建控制文件便可更改这些参数。这样,您将不会丢失其中存储的 RMAN 信息。



更快的启动
 
    2GB 内存即被视为够大的年代已像恐龙一样成为了历史。现在,100GB 的大型缓冲区缓存并不鲜见。实例启动时,初始化这样大小的缓冲区缓存可能需要数分钟甚至数小时。
 
    如果您深入研究这种情况将会发现,在数据库实例启动时无需启动整个缓冲区缓存。当实例启动时,缓冲区缓存是空的;随着用户从表中选择数据,该缓冲区缓存将逐渐填满。因此,在实例启动时无需初始化整个缓冲区缓存。
 
    在 Oracle 数据库 10g 第 2 版中,此行为在启动逻辑中受到控制。当启动实例时,只初始化 10% 的缓冲区缓存;剩余缓冲区缓存在检查点进程打开数据库之后才被初始化。这个新方法显著减少了实例启动的时间。
 
    但请注意,在初始化整个缓冲区之前,无法使用缓冲区缓存大小自动调整功能。



在 Oracle Enterprise Manager 中管理多个对象
 
    如果某个模式中的多个对象均无效,您通常怎么办?您很有可能创建一个 SQL 脚本来动态生成另一个编译这些无效对象的脚本。至少,在缺少第三方工具的情况下,这个方法还不错。
 
    但如果可以为此使用 Oracle Enterprise Manager Grid Control 岂不是更好?不是只选择一个无效对象并单击编译,而是同时选择多个无效对象并通过一次单击操作编译它们?
 
    在 Oracle 数据库 10g 第 2 版中,您确实可以这么做。如下所示,您所要做的就是选中对象旁边的复选框。然后,就可以从“Actions”旁边的下拉列表中选择“Compile”来同时编译所有对象。
 
 
    除了编译以外,您还可以执行大量其他操作,如创建 DDL 或删除对象。



XML 格式的审计跟踪
 
    如果您长期以来一直在使用 Oracle 中内置的审计工具则可能会注意到,一个非常有用的特性就是能够将审计跟踪写入一个文件系统中。将条目写入文件系统而非数据库本身中可以建立额外级别的安全性。
 
    您所要做的就是设置两个初始化参数:
 
audit_file_dest = '/auditfs'
audit_trail = xml
 
    并重新启动该实例。但这两个参数不是动态的;一旦设置,将把审计跟踪写入目录 /auditfs 中。参数 audit_file_dest 是可选的;默认为 $ORACLE_HOME/rdbms/audit 目录。这些审计跟踪文件将为扩展名为 .xml 的 XML 文件。
 
    以下是一个 XML 格式的审计跟踪示例:
 
- <Audit xmlns="http://xmlns.oracle.com/oracleas/schema/dbserver_audittrail-10_2.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.oracle.com/oracleas/schema/dbserver_audittrail-10_2.xsd">
<Version>10.2</Version>
- <AuditRecord>
<Audit_Type>8</Audit_Type>
<EntryId>1</EntryId>
<Extended_Timestamp>2005-03-05T20:52:51.012045</Extended_Timestamp>
<DB_User>/</DB_User>
<OS_User>oracle</OS_User>
<Userhost>oradba</Userhost>
<OS_Process>18067</OS_Process>
<Terminal>pts/0</Terminal>
<Instance_Number>0</Instance_Number>
<Returncode>0</Returncode>
<OS_Privilege>SYSDBA</OS_Privilege>
<Sql_Text>CONNECT</Sql_Text>
</AuditRecord>
</Audit>
 
    这些跟踪文件可被 XML 分析器轻松分析,从中提取有用的信息。您甚至可以将它们作为 XML 类型加载到数据库中,然后使用 XML 查询(详见本系列第 1 部分中的介绍)在 SQL 内部查询它们。
 
    在 Oracle 数据库 10g 第 2 版中,可以将此 XML 文件与 SQL 查询相结合,从中进行选择,就好像它是来自一个 SQL 源似的。还有一个预定义的动态视图 V$XML_AUDIT_TRAIL,用于从固定表 X$XML_AUDIT_TRAIL 中进行选择。此动态视图在结构方面类似于常规审计跟踪视图 DBA_AUDIT_TRAIL。
 
    对于 DBA 而言,使用 XML 格式的审计跟踪就可以通过第三方的 XML 分析器和编辑器操作文件,并可以通过接受 XML 作为输入的工具发布报表。您不再需要编写自己的分析器来解释这些审计跟踪文件。



自动的 Segment Advisor
 
    您如何知道哪些段在最高使用标记下具有大量可用空间,并能从重新组织中受益?
 
    可以使用 Oracle 数据库 10g 中提供的 Oracle Enterprise Manager 界面指定特定的表空间来发现可能的符合要求的段,但如果您的数据库有几百个表空间,则不可能每天执行该操作。即使您能做,但也不是每个表空间都有需要重新组织的段的。因此,如果有一个自动工具可以提前扫描这些段并报告需要重新组织的段岂不很好?
 
    在 Oracle 数据库 10g 第 2 版中,所附带的程序包 DBMS_SPACE 提供了该功能。内置函数 ASA_RECOMMENDATIONS 显示了段;由于这是一个管道函数,因此必须按如下所示使用它:
 
select * from table (dbms_space.asa_recommendations());
 
    由于列数较多,因此看清楚输出不太容易。以下只是一个以垂直格式显示的记录。
 
TABLESPACE_NAME       :USERS
SEGMENT_OWNER         :ARUP
SEGMENT_NAME          :ACCOUNTS
SEGMENT_TYPE          :TABLE PARTITION
PARTITION_NAME        :P7
ALLOCATED_SPACE       : 0
USED_SPACE            : 0
RECLAIMABLE_SPACE     : 0
CHAIN_ROWEXCESS       : 17
RECOMMENDATIONS       :The object has chained rows that can be removed
by re-org.
C1                    :
C2                    :
C3                    :
TASK_ID               : 261
MESG_ID               : 0
 
    此处您将看到,模式 ARUP 中表 ACCOUNTS 的分区 P7 包含链接行。执行重新组织将帮助加快该分区全表扫描的速度。
 
    此信息由在预定义的维护时间窗(周末下午 10 点至早上 6 点以及周六中午 12 点至周一中午 12 点)内运行的自动调度作业收集;您可以使用 Oracle Enterprise Manager 更改这些时间窗。在此时间内,该作业将对段进行扫描以获取符合要求的段。如果扫描无法及时完成,该作业将暂停并在第二天的时间窗继续扫描。
 
    该作业存储有关在名为 wri$_segadv_objlist 的表中检查的段和表空间的信息。可以查看有关在视图 DBA_AUTO_SEGADV_CTL 中检查的段的信息。



基于事件的调度
 
    Oracle 数据库 10g 第 1 版中引入的 Oracle Scheduler 是下一代作业调度系统,它取代了 DBMS_JOB 附带的程序包。与该程序包相比,此 Scheduler 工具有一些显著的优点(详见此处最初的介绍)。
 
    在 Oracle Scheduler 的第一版中,作业基于时间并根据时间触发。但如果要使触发器基于事件该怎么办?例如,当帐户的 Account Manager 更改时,您可能希望一个批处理程序自动执行以重新计算收入并重新发布报表。
 
    可以在 Oracle 数据库 10g 第 2 版的 Scheduler 工具中实现此类基于事件的触发。事件通过 Advanced Queueing (AQ)(其中有效载荷是一个对象类型)传递给 Scheduler。因此,您首先需要创建一个 AQ(如 proc_queue),在这里将把任意这样的事件排队。然后,您必须基于此事件创建一个调度。
 
begin
dbms_scheduler.create_event_schedule (
schedule_name   => 'accadmin.acc_mgr_change',
start_date      => systimestamp,
event_condition => 'tab.user_data.event_name = ''acc_mgr_change''',
queue_spec      => 'proc_queue');
end;
 
    接着,您将创建一个作业以遵循此调度。您也可以直接调度一个作业而不用先创建一个调度。
 
begin
dbms_scheduler.create_job (
job_name        => acc_mgr_change,
program_name    => acc_mgr_change_procs,
start_date      => 'systimestamp,
event_condition => 'tab.user_data.event_name = ''acc_mgr_change''',
queue_spec       => 'proc_queue'
enabled          => true);
end;
 
    默认值是 UNLIMITED。
 
    如果事件(而不是特定时间)是触发作业的决定因素,则基于事件的调度很有帮助。
 
 
 
 
posted on 2009-08-23 23:24 decode360 阅读(624) 评论(0)  编辑  收藏 所属分类: 08.DBA

只有注册用户登录后才能发表评论。


网站导航: