手记

Oracle 数据库上云之路

数据是公司最为重要的资产之一,这些数据部分存储在数据库之中,金融,电信,能源等很多企业使用 Oracle 来承载他们的核心交易及业务系统,Oracle 的市场份额一度接近50%,维护 Oracle 系统通常需要专业 DBA,主机工程师,存储工程师,网络工程师合作,才能保证数据库系统稳定运行,这需要大量的投资,上云能大幅度提升数据库系统的稳定性,降低运维复杂度和强度,同时也能降低投资(这包括 License 和硬件的一次行投资以及持续的运维成本), 这有助于帮助客户更加专注于自身的业务。上云的过程中要确保数据完整一致上云,是 IT 部门非常重要的一个任务。

数据库迁移受限于很多约束条件,我把在迁移过程中需要考量的因素(源库及目标库)归纳如下:

  • 数据库规模

    • 数据规模,少量数据可以选择很多迁移方法,例如可以不受暂存空间限制,你可以选择导出导入,或者以不落地的形式之际将其通过create table as select的形式快速迁移走;当你的数据规模较大例如几个T,你能选择的迁移方法将会减少,可能你的服务器上暂时没有足够的空间用于存放你的导出文件,或者备份文件,通过网络传输导出文件或者备份文件的时间也会大幅度增加。

    • 元数据规模,在进行跨平台迁移时必须考虑元数据规模,因为迁移的时候是以“数据+元数据”的形式,而元数据的导出导入可能会比你导出数据的时间还要长。

  • 迁移范围

    • 全部迁移

    • 部分迁移,如果你的系统经历过多次迭代你也清楚数据库里部分schema的数据可以丢弃的时候,在迁移的时候为数据库瘦身通常是一个合适的时机。但是更多的时候,一个系统经历过多次迭代,开发人员通常以一种只增不减的策略对数据库进行管理,随着人员的进出,通常没有翔实的文档,这个时候进行全量迁移其实更为稳妥。

  • 中转存储,如果数据泵,rman是你的备选迁移方法,你不得不审视你的环境里是否有充足的空间以存储导出文件或者备份文件。这些存储空间,可能是本地磁盘,也可能是你的SAN存储,也可能是你的磁带库。

  • 停机时间,这通常是由业务决定的能留给你的最大操作窗口,但是经历过多次迁移的老司机通常会告诉你,你的真实停机操作窗口还应该减去开始迁移前必要的业务停机,以及迁移完毕后的必要的业务验证,以及你给自己设定的最后失败必须进行回退的时间。

  • 字节序

    • Big endian

    • Little endian

计算机底层存储数据的两种基本形式,由此也把计算机分成了两大阵营。关于字节序的详细解释,大家可以查阅维基百科,借用百科的两张图:

SQL> select * from v$transportable_platform;
PLATFORM_ID PLATFORM_NAME                            ENDIAN_FORMAT
----------- ---------------------------------------- --------------
         1 Solaris[tm] OE (32-bit)                  Big
         2 Solaris[tm] OE (64-bit)                  Big
         3 HP-UX (64-bit)                        Big
         4 HP-UX IA (64-bit)                        Big
         6 AIX-Based Systems (64-bit)            Big
         9 IBM zSeries Based Linux                  Big
         16 Apple Mac OS                        Big
         18 IBM Power Based Linux                  Big
         5 HP Tru64 UNIX                        Little
         7 Microsoft Windows IA (32-bit)            Little
         8 Microsoft Windows IA (64-bit)            Little
         10 Linux IA (32-bit)                        Little
         11 Linux IA (64-bit)                        Little
         12 Microsoft Windows x86 64-bit            Little
         13 Linux x86 64-bit                        Little
         15 HP Open VMS                              Little
         17 Solaris Operating System (x86)      Little
         19 HP IA Open VMS                        Little
         20 Solaris Operating System (x86-64)      Little

数据库版本

  • 升级

  • 同版本
    如果你希望在迁移的时候将数据库升级到更高版本以获得更好的技术支持或者使用一些新特性,通常建议在正式升级之前进行详细的验证测试。这通常需要你仔细阅读升级文档,以明确你的升级路径。

  • 应用系统依赖的JDK版本
    在升级的时候要确认你的应用系统依赖的JDK版本,因为每个数据库版本都有支撑的最低的JDK版本。

  • 网络

    • 裸设备

    • 文件系统

    • ASM(Automatic Storage Management)

    • 底层存储
      仔细核实您的底层存储模式,可能可以借用其自带的复制功能进行数据迁移;

    • 数据库平台转换,例如Oracle转换到Aurora。

    • sqlplus copy,比较适合少量数据(表一级)的在线迁移。

    • exp/imp,在Oracle 10g之前主流的逻辑数据导出导入工具。

    • rman(Recovery Manager)

    • Data pump,Oracle 10g及后续的版本主流到高效的数据导出导入工具。

    • SQL Developer,适合100G以下的数据库迁移。

    • SQL Loader,适合CSV等可以解析的数据迁移。

    • ETL/ELT,

    • CTAS(create table as select),比较适合少量数据(表一级)的在线迁移。

    • Oracle Golden Gate /IBM CDC/迪思杰/sharePlex,适合10TB以下数据的无缝迁移。

    • Oracle Data Guard,适合同构数据库的迁移。

    • ASM,适合存储迁移,或者基于存储异地配送的迁移方式。

    • LVM,适合存储迁移,或者基于存储异地配送的迁移方式,或者在线迁移。

    • Storage Copy,适合存储迁移,或者基于存储异地配送的迁移方式。

    • TTS/XTTS,跨平台迁移首选,后续会专门介绍。

    • rsync,适合有一定的停机窗口的数据迁移。

    • AWS Database Migration Service,这是一个强大的Service,后续会专门介绍。

    • 距离

    • 数据库存储方式

      虽然制约因素众多,但是迁移的方法也有很多,例如常见的迁移方法有:

    • 外围复制软件
      外围复制软件不同的版本支持不同版本的数据库,而且通常是以源/目标的形式组对部署。

    • 字符编码
      在迁移过程中要注意一下源库的字符集。

    • 存储
      在迁移过程中要评估你的当前存储的最大性能,因为导出数据是个典型的IO密集型操作,如果你不对你的导出操作进行带宽限制在某些时候可能影响你的业务系统。你可以在rman内限定你的带宽。

    • 22,sftp

    • 21,ftp

    • 1521,监听端口

    • 111/2049,NFS

    • 带宽

    • 防火墙
      如果你选择基于网络的传输你的备份文件或者进行不落地的迁移,你需要验证你的网络带宽是否充足,或者你的网络防火墙是否开放:


通常您需要详细调研源环境和目标环境,然后在诸多因素中做出权衡,选择满足各种制约因素的迁移策略和方法。
我们先从制约因素相对最少,且迁移方法最简单的开始,逐渐熟悉各种不同迁移方法的适应环境,然后逐步深入到在准生产环境中进行各种复杂的迁移。

Oracle数据库上云之路(一),引导大家构建一个数据中心/云上一致的数据库软件环境,以及基本到导出导入数据迁移方式。
Oracle数据库上云之路(二),引导大家使用工具进行简单的逻辑迁移。
Oracle数据库上云之路(三),引导大家使用golden gate进行无缝迁移。
Oracle数据库上云之路(四),引导大家进行跨平台迁移。

一. 数据库软件系统迁移

在上云的过程中,尽可能降低整体迁移的复杂度有利于提高迁移的成功率,而需要解决的首要问题是如何保持op(on premises)和cloud之间数据库软件系统的版本一致性,因为并不是每个公司都会维护一个良好的软件仓库,通常在op安装完软件或者打完补丁后,这些介质通常会被删除掉。当然您可以安装大版本号一致的软件,但是这可能导致新的数据库要进行升级(依赖于您选择的迁移方法)。

当然您可以原始地把整个软件拷贝到Cloud上,但是您很快会发现一个更为严重的问题,当您的数据库遭遇bug时,您无法为您的数据库系统更新补丁,因为opatch无法找到软件仓库(一个以xml规范存储数据库软件的版本信息仓库),所以我们需要一个简单的方法进行数据库软件系统的迁移。

以下操作适合BYOL(Bring yourself own license)用户。

(一)、 OP操作

以下操作均在op上进行。

简单地对op的数据库软件进行打包。

Bash

#tar -cvzf dbhome_1.tar /u01/app/oracle/product/11.2.0/dbhome_1

把压缩软件包上传到cloud上,通常您需要将压缩包上传到堡垒机或者public subnet的某台EC2上,然后再将其传输到private subnet的数据库EC2内。

(二)、 Cloud操作

以下操作均在cloud上进行。

假设您的EC2已挂载了两个ESB设备分别是/dev/sdb,/dev/sdc。

1. 安装必要的软件包

yum -y install libstdc++-devel.x86_64
yum -y install gcc-c++.x86_64
yum -y install libdmx.x86_64
yum -y install libXmu.x86_64
yum -y install xorg-x11-xauth.x86_64
yum -y install ksh.x86_64
yum -y install libXxf86dga.x86_64
yum -y install compat-libstdc++.x86_64
yum -y install libXxf86misc.x86_64
yum -y install xorg-x11-utils.x86_64
yum -y install libaio-devel.x86_64
yum -y install compat-libcap1.x86_64

2. 创建用户、组

#groupadd oinstall
#groupadd dba
#useradd -d /home/oracle -s /bin/bash -k /etc/skel -m -g oinstall -G dba oracle

3. 配置资源限制

#cat>>/etc/security/limits.conf<<HereDoc
oracle soft nofile 32768
oracle hard nofile 65536
oracle soft nproc 32768
oracle hard nproc 65536
oracle soft stack 10240
oracle hard stack 32768
HereDoc

4. 配置内核参数

#cat>>/etc/sysctl.conf<<HereDoc
fs.aio-max-nr = 1048576
fs.file-max = 6815744
kernel.shmall = 2097152
kernel.shmmax = 4294967295
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
net.ipv4.ip_local_port_range = 9000 65500
net.core.rmem_default = 262144
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576
HereDoc
#sysctl -p /etc/sysctl.conf

5. 规划目录结构

u01通常用来存储数据库软件,u02通常用来存储数据库。

#mkdir /u01
#mkdir /u02
cat>>/etc/fstab<<HereDoc
/dev/xvdb1  /u01    ext4    defaults 0 0
/dev/xvdc1  /u02    ext4    defaults 0 0
HereDoc
#mount /u01
#mount /u02
#chown -R oracle:oinstall /u01 /u02
#chmod -R 775 /u01 /u02

6. 解压数据库软件

$tar xzf dbhome_1.11.2.0.4.160419.tar.gz -C /u01/app/oracle/product/11.2.0/

7. 配置环境变量

$sed -i /PATH/d ~/.bash_profile$cat >>~/.bash_profile<<HereDoc
ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1
ORACLE_BASE=/u01/app/oracle/
PATH=\$PATH:\$ORACLE_HOME/bin:\$ORACLE_HOME/OPatch
export ORACLE_HOME ORACLE_BASE PATH
umask 022
HereDoc
$source ~/.bash_profile

8. 克隆数据库软件

由于EC2实例未配置swap空间,所以我们在克隆软件前先挂载并初始化一个swap空间。

#mkswap /dev/xvdf
#swapon /dev/xvdf
#swapon -s

然后开始克隆数据库软件。

$cd $ORACLE_HOME/clone/bin
$./clone.pl ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1 ORACLE_BASE=/u01/app/oracle OSDBA_GROUP=dba

根据提示以root用户身份执行脚本。

#/u01/app/oraInventory/orainstRoot.sh
#/u01/app/oracle/product/11.2.0/dbhome_1/root.sh

9. 创建监听

$netca -silent -responsefile /u01/app/oracle/product/11.2.0/dbhome_1/assistants/netca/netca.rsp

10. 创建空数据库

由于在云上无图形界面,所以我们先使用静默方式创建一个空的数据库。

$dbca -silent -createDatabase -templateName General_Purpose.dbc \
-gdbName orcl -sid orcl \
-sysPassword Welcome1 -systemPassword Welcome1 \
-emConfiguration NONE \
-datafileDestination /u02 \
-redoLogFileSize 128 \
-recoveryAreaDestination /u01/app/oracle/flash_recovery_area \
-storageType FS \
-characterSet UTF8 -nationalCharacterSet AL16UTF16 \
-listeners LISTENER \
-initParams FILESYSTEMIO_OPTIONS=setall \
-databaseType OLTP

 

二. 数据库上云

现在越来越多的公司将开发测试环境迁移到AWS云上,通过云的弹性扩展能力可以很好满足开发测试过程中对计算资源的弹性需求,例如压力测试所需要的各种压力机。构建一个云开发测试环境,涉及到迁移开发测试数据库,这通常是由DBA来负责,但是很多开发团队并没有专职的DBA,迁移开发测试数据库就落到了开发人员肩上,本章将介绍被广泛使用的方法:数据泵(data pump),让开发人员方便地把开发测试库迁移到云上。

(一)、 Data pump简介

Oracle Data pump是高效导出导入数据及元数据的工具,自Oracle 10g引入该工具,意味着您无法在低于10g的数据库内使用。数据泵支持并行导出导入,可以大幅度降低数据迁移的时间;同时它也支持导出数据的压缩,该特性对于无足够的暂存空间,通过网络或者便携式存储迁移大量数据特别有帮助。

(二)、 导出倒入模式

针对不同的迁移需求,数据泵支持不同的导出导入模式,

  • Full Export/Import Mode

  • Schema Mode

  • Table Mode

  • Tablespace Mode

  • Transportable Tablespace Mode

较为常使用的模式是Schema Mode(依据业务系统设计情况,可能需要补充一些元数据在导入前),可以快速将一个业务系统依赖的数据快速迁移至云环境。

(三)、 依赖权限

在进行数据导出导入时必须获得相应的授权,否则数据泵无法正常启动job。建议从您的dba或者安全管理员获取以下授权

  • DATAPUMP_EXP_FULL_DATABASE

  • DATAPUMP_IMP_FULL_DATABASE

同时,您也要获得数据库Directory(是操作系统层面的具体文件夹在数据库空间的映射)的读取写入权限。

如果您是dba,亦可以使用system完成数据的导出导入。

(四)、 常见的优化方法

为了降低导出导入过程所需的时间,通常建议使用并行技术,可以显著缩短导出导入所用的时间,并行度一般不超过物理CPU核数的2倍,例如您的服务器部署了英特尔具备HT技术的CPU,您的最大并行度即为您在系统里看到的CPU数。

压缩技术通常能大幅度降低暂存空间和对传输带宽的需求,使用压缩技术后您的最终导出数据大致为原数据的1/3以上(注:这是一个经验评估值,具体的压缩比例依赖于您的数据情况)。

(五)、 安全与加密

注意:如果您的数据库版本低于11g,建议您采取其他加密方法对导出文件进行加密。这是一个仅在11g及以后版本才支持的特性。

如果您打算使用大容量硬盘迁移您的数据,强烈建议您在导出数据时进行加密。(请遵循 aws export/import规范提交相关信息)

并在数据导出完成后,生成每一个文件的摘要信息。

1. 加密范围

数据泵支持对以下范围的数据进行加密:

ENCRYPTION = [ALL | DATA_ONLY | ENCRYPTED_COLUMNS_ONLY | METADATA_ONLY | NONE]

我个人强烈建议您选择ALL,这将加密您的所有数据。

2. 加密算法

默认加密算法为AES128。

ENCRYPTION_ALGORITHM = [AES128 | AES192 | AES256]

3. 加密模式

ENCRYPTION_MODE = [DUAL | PASSWORD | TRANSPARENT]

个人建议采用PASSWORD,DUAL/TRANSPARENT依赖wallet。

密码里尽量不要使用shell的保留字符,例如$,$?。

注意:加密password放在命令行里,不会记录在导出日志文件里。但是会记录在history里,所以导出完毕后记得将history清理干净。

(六)、 OP数据导出

1. 创建目录并授权

在主目录创建备份目录。

Bash

mkdir backup
在数据库内创建目录并授权。
SQL> create directory backup as '/home/oracle/backup';
Directory created.
SQL> grant read,write on directory backup to system;
Grant succeeded.

2. 导出数据

加密并行但是未压缩导出。

expdp system schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example%u.dmp logfile=example_exp_20180524.log parallel=2 ENCRYPTION=ALL ENCRYPTION_ALGORITHM=AES256 ENCRYPTION_MODE=PASSWORD ENCRYPTION_PASSWORD=AAEUeauaouaoeu13123133

Export: Release 11.2.0.4.0 - Production on Thu May 24 17:29:43 2018

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Starting "SYSTEM"."SYS_EXPORT_SCHEMA_01":  system/******** schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example%u.dmp logfile=example_exp_20180524.log parallel=2 ENCRYPTION=ALL ENCRYPTION_ALGORITHM=AES256 ENCRYPTION_MODE=PASSWORD ENCRYPTION_PASSWORD=******** 
Estimate in progress using BLOCKS method...
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 290 MB
…………………………
Master table "SYSTEM"."SYS_EXPORT_SCHEMA_01" successfully loaded/unloaded
******************************************************************************
Dump file set for SYSTEM.SYS_EXPORT_SCHEMA_01 is:
  /home/oracle/backup/example01.dmp
  /home/oracle/backup/example02.dmp
[oracle@devdb backup]$ ls -lah
total 58M
drwxr-xr-x.  2 oracle oinstall 4.0K May 24 17:29 .
drwx------. 25 oracle oinstall 4.0K May 24 17:15 ..
-rw-r-----.  1 oracle oinstall 564K May 24 17:30 example01.dmp
-rw-r-----.  1 oracle oinstall  58M May 24 17:30 example02.dmp
-rw-r--r--.  1 oracle oinstall  12K May 24 17:30 example_exp_20180524.log

加密并行压缩导出

[oracle@devdb ~]$ expdp system schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example_COMPRESSION_%u.dmp logfile=example_exp_COMPRESSION_20180524.log parallel=2 ENCRYPTION=ALL ENCRYPTION_ALGORITHM=AES256 ENCRYPTION_MODE=PASSWORD ENCRYPTION_PASSWORD=AAEUeauaouaoeu13123133 COMPRESSION=ALL

Export: Release 11.2.0.4.0 - Production on Thu May 24 17:38:27 2018

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Starting "SYSTEM"."SYS_EXPORT_SCHEMA_01":  system/******** schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example_COMPRESSION_%u.dmp logfile=example_exp_COMPRESSION_20180524.log parallel=2 ENCRYPTION=ALL ENCRYPTION_ALGORITHM=AES256 ENCRYPTION_MODE=PASSWORD ENCRYPTION_PASSWORD=******** COMPRESSION=ALL 
Estimate in progress using BLOCKS method...
Processing object type SCHEMA_EXPORT/TABLE/TABLE_DATA
Total estimation using BLOCKS method: 290 MB
……………………
******************************************************************************
Dump file set for SYSTEM.SYS_EXPORT_SCHEMA_01 is:
  /home/oracle/backup/example_COMPRESSION_01.dmp
  /home/oracle/backup/example_COMPRESSION_02.dmp
Job "SYSTEM"."SYS_EXPORT_SCHEMA_01" completed with 1 error(s) at Thu May 24 17:39:03 2018 elapsed 0 00:00:35

大家会看到使用压缩后导出数据仅为非压缩的1/3。

3. 清理命令行历史

因为在导出数据时使用了密码,养成一个好习惯,清空命令行历史有助于提升数据安全性。

[oracle@devdb ~]$ history -c
[oracle@devdb ~]$ history
    1  history

4. 生成摘要文件

可以使用sha256sum,sha1sum为文件生成摘要,这将有助于验证数据在传输过程中,文件是否是完整正确的。

[oracle@devdb backup]$ for i in `ls`
> do 
> sha256sum $i >> export.dig
> done
[oracle@devdb backup]$ more export.dig 
0939c592d1484c16433b7d983f4666d17aec97980a169dcce1c965caa92a0e32  example01.dmp
7016364a2207ab79c6594f2b4aae20ff91482720438d8182d7339bba767328c1  example02.dmp
cbd21df9756902887f164061354feaf339e5efb906ec414de8b75aa0236ac131  example_COMPRESSION_01.dmp
3c4f35dcf00c74f893030bc3aafe77c07c71c7fb8db61fb345b86d96ac3d692d  example_COMPRESSION_02.dmp
068c206ba00f49c0c364f23ba0901cc157e53d0213d8b2cbc682bdb0bae9cd19  example_exp_20180524.log
d732e571d8f66668ddf6e5a84642edc8abbd611e424ba53a0fb0be36084b0a5f  example_exp_COMPRESSION_20180524.log

(七)、 Cloud数据导入

在将导出数据文件上传到云后,及时校验文件,确保数据文件的一致性。

1. 校验文件

[root@ip-172-31-15-152 backup]# for i in `ls`
> do
> sha256sum $i >> cloud.dig
> done
[root@ip-172-31-15-152 backup]# sed -i /export.dig/d cloud.dig
[root@ip-172-31-15-152 backup]# diff cloud.dig export.dig 
[root@ip-172-31-15-152 backup]#

2. 将数据导入云端数据库

创建文件夹的步骤,参见op数据导出章节。

[oracle@ip-172-31-15-152 ~]$ impdp system schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example_COMPRESSION_%u.dmp logfile=example_imp_COMPRESSION_20180524.log parallel=2  ENCRYPTION_PASSWORD=AAEUeauaouaoeu13123133

Import: Release 11.2.0.4.0 - Production on Thu May 24 10:31:50 2018

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Master table "SYSTEM"."SYS_IMPORT_SCHEMA_01" successfully loaded/unloaded
Starting "SYSTEM"."SYS_IMPORT_SCHEMA_01":  system/******** schemas=BI,OE,HR,IX,SH,PM directory=backup dumpfile=example_COMPRESSION_%u.dmp logfile=example_imp_COMPRESSION_20180524.log parallel=2 ENCRYPTION_PASSWORD=********
…………
Job "SYSTEM"."SYS_IMPORT_SCHEMA_01" completed with 4 error(s) at Thu May 24 10:32:18 2018 elapsed 0 00:00:27

您会发现在导入的过程中会有报错(我们在一开始介绍数据泵导出导入模式时就提到了这一点),这是因为有一些元数据(文件夹,权限,角色,database link,scheduler job并没有在目标数据库创建,还有一些tablespace可能也没有创建),通常在数据迁移的过程中您需要把这些元数据提前导出。

嗯,这是一次演习,是时候把库清空了。不要每次都把库干掉啊(你懂的……)。

SQL> drop user BI cascade;
SQL> drop user OE cascade;
SQL> drop user HR cascade;
SQL> drop user IX cascade;
SQL> drop user SH cascade;
SQL> drop user PM cascade;

仔细翻阅导入日志,找出报错信息,补充元数据,然后再导入一次。


Amazon Web Services ( AWS )技术峰会 2018 中国站(上海)已经于 6 月 29 日圆满落幕,感谢您对 AWS 的关注和支持。精彩仍在继续,所建皆为不凡,AWS 技术峰会 2018 中国的第二站即将启航。AWS 技术峰会 2018 北京站,将在2018 年 8 月 9 日登陆北京国家会议中心!

想和我们一起#所建不凡#?快来注册#AWS技术峰会2018,前20名注册的小伙伴将可获得 $50亚马逊优惠券,抓住机会,赶快注册吧!https://awssummit.cn?tc=1s016B2uQi


0人推荐
随时随地看视频
慕课网APP