拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Linux环境下CommVault备份与恢复Oracle数据库的实践指南

简介在Linux服务器上使用CommVault统一备份平台保护Oracle数据库时可参考这份PDF文档。文档面向数据库管理员与运维工程师完整覆盖了从安装准备、软件部署到备份策略配置及灾难恢复的实操流程。安装前需重点确认CommVault版本与数据库版本兼容并让Oracle处于自动归档模式同时在CommServer和本机的/etc/hosts中写入正确的IP与主机名映射。文档还以RMAN的NOCATALOG方式为主线逐步说明如何创建Oracle子客户端、设定备份频率与保留期限并详细演示了控制文件恢复、将数据库启动到MOUNT状态、恢复数据文件与归档日志、重建REDOLOG并打开数据库等关键步骤。全包仅含1个PDF文件大小2.02MB便于移动阅读或打印对照。目前已吸引305人浏览学习适合正在搭建或维护CommVault与Oracle备份体系的IT人员。掌握这些方法后可显著提升数据库故障场景下的恢复效率保障业务连续性。1. 当备份 LUN 消失时你才意识到 CommVault 为 Oracle 准备的逃生通道很多 DBA 第一次接触 CommVault 不是因为“需要更好的备份”而是因为“磁带库满了”、“备份窗口超时了”或者“恢复时发现备份集少了一个归档日志”。CommVault Simpana现在叫 Commvault Complete Backup Recovery在 Linux 平台上备份 Oracle核心不是它多聪明而是它把 Oracle 的 RMAN 通道变成了自己的数据管道备份由 Oracle 发起目标不是本地磁盘而是 CommServe 里的 MediaAgent。这意味着备份流、目录信息、归档日志管理都可以被统一调度恢复时也能直接拉取而不必在服务器上翻找残留的 dump 文件。这里讨论的“CommVault Oracle 数据库的备份及恢复 For Linux”本质上是回答一个问题在 Linux 下如何用 CommVault 接管 Oracle 的 RMAN 备份并且出故障时还能正常恢复。本文按最容易踩坑的顺序把安装、配置、备份、恢复、排错里应该盯住的细节讲清楚。2. 先搞懂 CommVault 在 Linux 上备份 Oracle 的原理和必备组件2.1 备份的真正执行者是 RMANCommVault 只是给了它一个网络磁盘2.1.1 Oracle 备份机制与 RMAN 通道的基本认识Oracle 数据库自身的备份工具 RMANRecovery Manager是事实标准。它通过分配通道channel读取数据文件、控制文件、SPFILE 和归档日志然后把内容写入一个备份目标。通常情况下RMAN 把内容写到本地文件系统或者磁带设备但 RMAN 还支持一种特殊设备类型SBT_TAPESystem Backup to Tape。这种设备类型并不要求真有一台磁带机只要系统里存在一个符合 Oracle SBT 接口的动态库RMAN 就会把数据交给这个库去处理。CommVault 正是通过这个接口介入的。它在 Linux 服务器上安装一个叫作 File System iDataAgent 或 Oracle iDataAgent 的客户端组件其中包含一个 SBT 库。RMAN 通道配置为sbt_tape备份的数据会从 Oracle 进程传给 CommVault 的库再由库传给 MediaAgent最终落在磁盘库或磁带库上。2.1.2 CommVault 三个核心组件的角色划分要在这套体系里定位问题必须分清三个组件的职责组件角色常见安装位置CommServe管理整个备份环境的数据库和任务调度中心存放所有备份策略、客户端列表、任务历史Windows Server常见或 Linux 上的 CommServe 组件MediaAgent负责实际接收和写出备份数据连接磁盘库或磁带库Linux 或 Windows一台或多台iDataAgent客户端安装在 Oracle 数据库服务器上提供文件系统备份能力和 Oracle SBT 库与数据库同机部署在 Oracle 备份场景中我们可以把关系理解为RMAN 是备份的发动机CommVault 的 SBT 库是油管MediaAgent 是油箱CommServe 是仪表盘。任何一环断裂备份显示成功但恢复时都会出问题。2.1.3 为什么 Linux 上配置比 Windows 更容易出现小毛病Windows 环境下CommVault 的 SBT 库会自动注册到 Oracle 的hsots或通过oracle用户的环境变量找到Linux 下则不同。SBT 库是.so文件必须被oracle用户的可执行环境加载。这意味着LD_LIBRARY_PATH里要能正确搜索到该库的目录该.so文件对oracle用户必须有读和执行权限RMAN 配置中的PARMS需要明确指定SBT_LIBRARY路径CommVault 的后台进程必须在oracle用户下能启动否则 RMAN 通道无法初始化。这些细节不一定写在初版的安装手册里但实际配置时几乎不可避免。2.2 安装 Oracle iDataAgent 时不该按默认走的几个配置点2.2.1 一次性安装成功的环境检查顺序在 Linux 上安装 CommVault Oracle iDataAgent 之前我会先做以下检查# 检查内核版本和 glibcCommVault 对 32 位库有一些依赖 uname -a ldd --version | head -1 # 检查 oracle 用户是否存在并确认属组 id oracle # 检查 Oracle 环境变量是否能被 su 到 oracle 后正常加载 su - oracle -c echo \$ORACLE_HOME; echo \$ORACLE_SID这里每一步都值得说明。ldd --version是确认系统 glibc 版本不是让读者看具体数字而是提示如果 CommVault 的二进制包要求特定 glibc 版本提前发现比装到一半报错要省时。id oracle是检查 Oracle 安装用户因为 CommVault 的服务有时候需要用数据库属主身份去访问ORACLE_HOME下的文件。su命令中的反斜杠是为了防止在双引号里直接展开当前 shell 的变量确保拿到的是 oracle 用户实际的环境变量。2.2.2 安装包的选择和备份路径建议Linux 上安装 Oracle iDataAgent 时安装包一般按操作系统版本区分比如 RHEL 7 和 RHEL 8 会使用不同的二进制集。安装过程中会要求指定以下内容CommServe 主机名和网络端口客户端名称建议用主机名不要用 IP便于后续迁移数据路径缓冲目录一般建议放在空间充足的目录比如/usr/commvault/Data或单独挂载点是否启用文件系统备份功能如果只做 Oracle 备份可以不启用文件系统 Agent。缓冲目录的规划常被忽视。Oracle 备份数据以流的方式先写到 CommVault 的缓冲文件再传给 MediaAgent。如果缓冲目录和 Oracle 数据文件在同一文件系统并且磁盘接近满备份很可能出现不明原因失败。我一般建议把缓冲目录放置在独立的挂载点或至少和数据文件所在的文件系统分开。数据路径建议用 df 命令确认df -h /usr/commvault/Data输出结果中需要关注Avail一列正常情况下该目录应至少有数据库总体积的 10% 空间可用这是为了应付并发备份时缓冲文件占用。如果空间不足宁可调整备份并发数也不要把缓冲目录塞到/根分区。2.3 配置 RMAN 与 CommVault 的 SBT 接口最小可用配置2.3.1 一个能直接跑的 RMAN 备份脚本模板安装完成并启动 CommVault 客户端服务后需要验证 SBT 库路径。在 Linux 上SBT 库的常见路径格式如下ls -l $ORACLE_HOME/lib/libobk.so如果显示的是一个符号链接说明 Oracle 已经能够找到 SBT 库入口。接下来要验证 CommVault 提供的库是否真的存在。通常它位于 CommVault 安装目录中如find /opt/commvault -name *sbt*.so 2/dev/null在确认库文件路径后写一个最小 RMAN 脚本测试备份通道。以下脚本展示了最简洁的调用方式rman target / catalog rcvcat/xxxorapkg log/tmp/rman_test.log EOF RUN { ALLOCATE CHANNEL ch1 DEVICE TYPE sbt PARMSSBT_LIBRARY/opt/commvault/libobk.so, ENV(CV_JobID1001); BACKUP DATABASE PLUS ARCHIVELOG DELETE INPUT; RELEASE CHANNEL ch1; } EOF对这段脚本的说明ALLOCATE CHANNEL中的SBT_LIBRARY必须指向 CommVault 实际的库文件这里只是一个路径示例。ENV参数里的CV_JobID是 CommVault 关联任务编号用的如果不设置备份也能执行但后续从 CommCell 控制台查看任务内容时可能缺少关联信息。BACKUP DATABASE PLUS ARCHIVELOG DELETE INPUT是备份数据库并附带归档日志的常用写法DELETE INPUT保证备份成功后清理归档避免文件系统被灌满。这个脚本仅用于验证生产环境不建议每次都手工执行而是通过 CommVault 的策略去调用。2.3.2 验证 SBT 通道是否正常的三个步骤执行上面的脚本后不要只看是否报错还要主动做三个检查第一查看/tmp/rman_test.log里是否出现channel ch1: starting和channel ch1: finishedgrep -E channel ch1|ORA- /tmp/rman_test.log若看到ORA-27211或ORA-19506通常表示 SBT 库加载失败重点回查LIBRARY路径。第二登录 CommCell 控制台找到刚才的任务查看备份数据是否出现在 MediaAgent 对应的存储策略中。这一步确认数据从 Oracle 发出后真正达到了 CommVault 的存储。第三用RMAN list backup;验证 Oracle 侧的可恢复性视图里已经注册了备份集这一步防止出现“CommVault 有数据但数据库不认识”的诡异情况。2.3.3 备份用户权限和环境变量的隐性要求Linux 上由 CommVault 启动 RMAN 任务时不是以 DBA 交互方式运行而是通过客户端服务调用。这就意味着执行备份的oracle用户必须能无密码切换到数据库环境CommVault 服务进程要么以 root 启动后 dropped 到 oracle 权限要么直接以 oracle 用户启动相关 agentORACLE_HOME、ORACLE_SID、PATH必须在非交互式登录 shell 下也能读取。su - oracle -c source ~/.bash_profile; which rman; echo \$ORACLE_SID上面的命令检查了非交互式环境下 RMAN 是否在PATH中、数据库 SID 是否被正确导出。source ~/.bash_profile是模拟 oracle 用户登录后加载环境变量的动作which rman输出应该有实际路径echo $ORACLE_SID的结果不能为空。否则 CommVault 在调度任务时可能报告RMAN-04006: error from auxiliary database或者找不到命令。3. 设计 Linux 下 Oracle 备份策略从全备到归档把窗口和空间一起算进去3.1 备份策略的分层设计3.1.1 全备份、增量备份与归档日志备份的取舍CommVault 对 Oracle 的备份策略通常由三部分组成数据文件的全备份或增量备份、归档日志备份、控制文件备份。RMAN 对增量备份的机制是基于块的 Change Tracking 或比较 SCN与 CommVault 本身的关系不大但在 CommVault 配置中需要指定“备份级别”为 Incremental。Linux 环境下常见的组合是每周日凌晨 1 点0 级全备份周一至周六凌晨 1 点1 级增量备份每 30 分钟或每小时归档日志备份。为什么这样设计全备份的耗时取决于数据量增量备份适合压缩备份窗口但要承担恢复时前滚日志的时间成本。归档日志的备份频率决定了数据库能丢失多少数据。比如把归档日志备份间隔设为 30 分钟那么理论上最多丢失 30 分钟的已提交事务。3.1.2 Retention保留周期与存储策略的关系备份保留多久取决于业务对恢复时间的要求而不是存储空间能撑多久。CommVault 的存储策略属性里包含保留时间、副本数量和保存格式。需要明白一个要点CommVault 的保留周期不是按自然日简单删除文件而是标记为可回收同时要求至少保留一个完整恢复链。Oracle 备份的恢复链依赖控制文件或恢复目录记录的信息。如果把全备份的保留周期设为 30 天但归档日志只保留 7 天那么到第 20 天需要恢复到第 10 天的某个时间点时会因为缺少第 10 天到第 15 天之间的归档日志导致恢复链断裂。恢复链示意 0级备份第1天00:00 - 归档日志第1天00:30 - ... - 归档日志第20天10:00为满足“恢复到任意时间点”归档日志的保留周期必须覆盖到当前并且至少要与全备份的保留周期保持一致。可以在 CommVault 里设置归档日志备份独立于数据文件备份的保留时间但要保证它不小于数据库可回溯时间的业务要求。3.2 用 CommVault 的策略配置实现上述方案3.2.1 创建 Oracle 备份策略的关键参数说明在 CommCell 控制台创建备份策略时有几个参数不是随便填的Backup Content选择 Oracle 数据库实例输入 SID 和 ORACLE_HOME 路径Storage Policy指定数据写入哪个磁盘库或磁带库Data Transfer Option选择“使用数据多路复用”这会提高并发扇出但会占用额外网络连接Compression建议启用Oracle 数据块重复度较高压缩比通常能达到 2:1 以上Encryption按合规要求启用但启用后对还原效率有 5% 到 15% 的影响。3.2.2 归档日志备份的独立配置在 CommVault 的备份策略中归档日志备份可以作为一个独立子策略存在。它有两个调度入口。第一个是周期性备份例如每 30 分钟执行一次适合日志产生量均匀的系统。第二个是事件触发式备份例如通过 CommVault 的文件系统 Agent 监控归档日志目录的变化但需要额外配置一般生产环境不采用。事件触发式有个典型问题如果 DBA 手动切换了日志ALTER SYSTEM SWITCH LOGFILE事件触发的间隔可能滞后而周期性备份在一个固定时间点检查并备份所有新增日志。3.2.3 备份窗口与资源冲突的预判方法备份与业务高峰如果撞车容易造成 I/O 争用。可以用下面的查询估算需要多少备份时间SELECT ROUND(SUM(bytes)/1024/1024/1024, 2) AS data_gb FROM v$datafile;这条 SQL 查询数据文件总大小。得到的数据量除以实际备份吞吐预估时间。实际备份吞吐可以从 CommVault 历史任务报告中读取。若 2TB 的数据量上一次全备份耗时 4 小时就可以算出平均吞吐率。基于这个数再根据磁盘库的写入性能决定是否错峰。这类计算虽然粗略但在确定备份窗口时非常实用。3.3 控制文件与 SPFILE 的备份保护控制文件和 SPFILE 虽然在常规 RMAN 备份中会自动包含但在 CommVault 策略里要注意不能遗漏“控制文件自动备份”选项。Linux 下 RMAN 的默认配置是CONTROLFILE AUTOBACKUP OFFCommVault 的预检脚本一般会检查该参数。如果没有开启恢复时可能出现找不到控制文件的尴尬处境。rman target / RMAN CONFIGURE CONTROLFILE AUTOBACKUP ON; RMAN CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE SBT FORMAT %F;CONFIGURE CONTROLFILE AUTOBACKUP ON开启了控制文件自动备份。FORMAT FOR DEVICE TYPE SBT FORMAT %F指定备份格式%F是包含数据库标识DBID的唯一文件名这样 CommVault 和 RMAN 能自动识别。没有这一步手动恢复时就需要额外找控制文件的备份集位置。4. 在 Linux 上执行 Oracle 恢复操作从文件级还原到时间点恢复4.1 恢复模式的选择与 PreView 功能4.1.1 分清 Restore 与 Recover 的区别无论用不用 CommVaultOracle 恢复都分为 Restore 和 Recover 两步。Restore 是把备份里的数据文件取回来放回原路径Recover 是应用归档日志或增量备份把数据文件推进到目标时间点。CommVault 的界面上把这两步整合在向导中但仍然需要搞清楚每一步的作用。很多 Linux 新手在恢复时报错ORA-01194: file 1 needs more recovery就是因为只做了 Restore 没做 Recover。4.1.2 CommVault 的 Browse 与 PreView 机制CommVault 提供了一个“浏览备份”功能允许在发起恢复前预览备份集里的内容不需要真正下载数据。对于大库来说这个功能非常重要因为可以避免恢复一个错误的时间点。在 CommCell 控制台选择 Oracle 实例 - Browse - 选择备份时间 - 展开数据库文件树可以看到备份集中包含哪些数据文件、归档日志和控制文件。使用这个功能不会消耗磁盘空间但会扫描 MediaAgent 上的索引耗时取决于备份集的数量。4.2 完整的恢复流程通过 CommVault 恢复整个数据库到原主机4.2.1 恢复前的准备工作列表在执行恢复之前我通常会按照以下顺序操作。先检查当前数据库的动态视图确认数据结构是否发生了不可接受的改变SELECT name, open_mode FROM v$database; SELECT checkpoint_time FROM v$datafile_header WHERE file# 1;第一条 SQL 用于确认数据库目前是否处于关闭或 mount 状态。第二条 SQL 展示文件头检查点时间用来判断数据文件是否比备份集新。如果数据文件比备份集新恢复时要用UNDO TABLESPACE回滚可能导致恢复流程复杂此时可以与业务方确认是否需要执行不完全恢复。如果不需要直接使用REWIND之类的功能从备份恢复到某个历史点。4.2.2 通过 CommVault 发起恢复的操作步骤准备就绪后在 CommVault 控制台发起一个恢复操作。选择恢复类型为“数据库恢复”指定恢复目标为主机上的原实例或一个临时实例。如果恢复到原实例CommVault 会把备份集映射回原来的文件路径。如果恢复到新主机要修改以下参数数据文件目标路径控制文件位置日志文件目标位置。CommVault 的 Oracle 恢复向导中有“重定位文件”的选项这里可以指定DBID和新的存储路径。如果 Oracle 使用 ASM那么目标路径应该写成DATA/prod/datafile/xxx.dbf的格式。4.2.3 手动运行 RMAN 脚本方式在某些故障场景下CommVault 的恢复向导可能无法完成例如 CommServe 不可用但 MediaAgent 和备份数据在可以手动执行 RMAN 脚本调用 SBT 通道。示例rman target / catalog rcvcat/xxxorapkg log/tmp/restore_$(date %Y%m%d).log EOF RUN { ALLOCATE CHANNEL ch1 DEVICE TYPE sbt PARMSSBT_LIBRARY/opt/commvault/libobk.so; RESTORE DATABASE; RECOVER DATABASE; RELEASE CHANNEL ch1; } EOF这段脚本会从备份中恢复整个数据库并自动应用归档日志。RESTORE DATABASE将控制文件记录的所有数据文件从备份中取回RECOVER DATABASE会自动应用所有可用的归档日志直到一致状态。此方法能解决控制台前端故障的问题但要求操作者能拿到备份集的元数据DBID、备份时间范围。如果不知道 DBID 和控制文件备份位置手动恢复会非常困难此时可通过 CommVault 客户端工具查询最近的备份索引。4.3 时间点恢复PITR的细节与风险4.3.1 误删数据后恢复到过去某一时刻时间点恢复在业务场景里多用于误删数据、错误执行 DDL。操作流程在 RMAN 中如下RUN { ALLOCATE CHANNEL ch1 DEVICE TYPE sbt PARMSSBT_LIBRARY/opt/commvault/libobk.so; SET UNTIL TIME TO_DATE(2025-01-15 14:30:00,YYYY-MM-DD HH24:MI:SS); RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN RESETLOGS; RELEASE CHANNEL ch1; }这里的SET UNTIL TIME指定恢复停止的时间点。因为 PITR 会丢失该时间点之后的变更所以执行前需要由业务方书面确认。ALTER DATABASE OPEN RESETLOGS是重置在线日志的序列号执行后数据库的日志序列从 1 开始旧日志不再适用。注意如果把生产库当作测试库进行恢复建议改DBNAME或使用SET NEWNAME重定向防止与生产库互相干扰。4.3.2 基于 SCN 的恢复场景如果报错信息里有明确的 SCN可以通过 SCN 而不是时间进行恢复。举例来说应用日志中显示的数据异常点在 SCN 2345678可以使用SET UNTIL SCN 2345678; RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN RESETLOGS;基于 SCN 的恢复比基于时间的恢复更精确。但前提是 SCN 对应的数据变更已经写入了备份的归档日志并且备份集中包含了该 SCN 之前的所有日志。如果 CommVault 的归档日志备份频率太低例如每天一次那么 SCN 时间点可能落在未备份的日志中恢复时会报缺少归档日志。5. 备份验证与恢复演练Linux 环境下的自动化验证技巧5.1 每条备份任务结束后的验证清单不能等到真的宕机才检验备份的有效性。建议在每次备份任务完成后用 RMAN 自带的校验功能检查备份集rman target / EOF VALIDATE BACKUPSET COMPLETED AFTER SYSDATE-1; EOFVALIDATE BACKUPSET会读取备份集内的每个数据块并校验逻辑损坏。只消耗读 I/O不占用额外存储。进一步地可以校验数据库的可恢复性RMAN RESTORE DATABASE PREVIEW;RESTORE DATABASE PREVIEW并不真正恢复文件它只读取控制文件与备份元数据判断所有数据文件、归档日志是否都在备份中。如果这条命令执行成功且不报错说明从备份元数据视图上看数据库可完整恢复。这是最便宜也最可靠的验证方法。5.2 自动化的恢复演练脚本为了确保 CommVault 备份不只是“写入成功”而确实能“恢复成功”建议每个月做一次恢复演练。在一台与生产环境隔离的机器上安装好同版本的 Oracle并用下面的脚本做自动化恢复#!/bin/bash export ORACLE_SIDtestdb export ORACLE_HOME/u01/app/oracle/product/19.0.0/dbhome_1 export PATH$ORACLE_HOME/bin:$PATH rman target / EOF STARTUP NOMOUNT; RESTORE CONTROLFILE FROM AUTOBACKUP; ALTER DATABASE MOUNT; RESTORE DATABASE; RECOVER DATABASE; ALTER DATABASE OPEN RESETLOGS; EOF脚本说明先启动实例到 NOMOUNT 状态然后从控制文件自动备份中恢复控制文件。ALTER DATABASE MOUNT挂载数据库后RESTORE DATABASE将所有数据文件恢复回来RECOVER DATABASE应用归档日志最终ALTER DATABASE OPEN RESETLOGS打开数据库。这套流程等同于一次完整的灾难恢复操作执行前需要确认ORACLE_SID与生产不同避免 CommVault 客户端把测试库当作生产库。5.3 备份恢复过程中的常见报错与解决方向5.3.1 ORA-27211 与 SBT 库加载失败ORA-27211通常表示无法加载 SBT 库。首先检查库路径是否存在ls -l /opt/commvault/libobk.so注意这个路径是示例应该以你的安装目录为准。如果存在再看权限如果路径不对或 CommVault 客户端没有启动RMAN 报错几乎必然。检查 CommVault 客户端服务ps -ef | grep -i cvfwd该进程是 CommVault 文件系统的守护进程其运行状态影响 SBT 库的初始化。如果进程不在先启动客户端服务再重试。5.3.2 恢复时找不到归档日志恢复时出现RMAN-06053: unable to perform media recovery或ORA-00283常见原因是归档日志备份策略的覆盖范围小于全备份保留周期。查询备份中的归档日志情况RMAN LIST ARCHIVELOG ALL;在输出中观察最早的日志是否覆盖到全备份时间之前。如果归档日志缺失恢复就无法继续。补救方式是先从头做一次 0 级全备份并保留当前所有归档日志不要急着删除。5.3.3 恢复后数据库无法打开如果恢复结束时执行ALTER DATABASE OPEN报ORA-01113说明数据文件与控制文件检查点不一致需要再次RECOVER DATABASE。如果仍然失败就检查是否丢失了某个归档日志或者是否误用了RESTORE DATABASE UNTIL TIME但后续没有执行RECOVER。操作顺序在 Oracle 中不可颠倒必须 Restore 后再 Recover然后才能考虑打开。5.4 用脚本定期验证备份元数据的完整性CommVault 自带一些命令行工具常用的是qlist系列。Linux 上可以这样查看最近的 Oracle 备份任务状态/opt/commvault/Base/qlist job -c jobtype3 -j statuscompleted这里jobtype3通常表示 Oracle 备份任务不同版本有差异需以实际安装为准statuscompleted过滤出成功的任务。如果没有任何输出说明备份任务要么没有执行要么状态不是 completed需要查看日志。qlist是 CommVault 提供的基础命令具体参数可通过qlist -h查看不同版本的字段名可能有差异但这条命令适合快速侧写。更重要的是可以把以上校验写成 cron 脚本每天早上自动汇总前一天的备份结果。简单脚本如下#!/bin/bash LOG/backup/check_$(date %F).log /opt/commvault/Base/qlist job -c jobtype3 -j statuscompleted $LOG if [ -s $LOG ]; then echo Oracle backup jobs completed. $LOG else echo No completed Oracle backup jobs found! $LOG echo Check CommVault and Oracle alert log. $LOG fi-s $LOG是判断文件非空若空说明没有成功任务输出告警。这套逻辑可以作为每日巡检的一部分比纯靠监控页面更能提前发现问题。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门