Oracle 11.2.0.3 补丁:OPatch 与 inventory 验收
简介:面向 Oracle 数据库运维人员与 DBA 的 11.2.0.3 版本补丁集更新包,对应补丁编号 20760997,适配 Linux x86-64 平台,即 DATABASE PATCH SET UPDATE 11.2.0.3.15,其中整合了 2015 年 7 月发布的关键补丁更新(CPU JUL2015)。该补丁集集中修复已知安全漏洞、性能缺陷与稳定性问题,适合需要为 11.2.0.3 环境补齐安全基线的生产库维护场景,也便于备考 OCP 或研究 Oracle 补丁机制的技术人员对照学习。压缩包为 zip 格式,体积约 100.73MB,上游未提供逐项文件清单,从包内结构看主要包含补丁元数据说明与补丁实体文件两部分,前者用于查询适用性、依赖关系与安装前置条件,后者用于实际应用。目前已有 291 人学习下载。读者可借此了解 Oracle PSU 的版本命名规则、CPU 与 PSU 的对应关系,掌握补丁适用平台与版本匹配的判断方法,并据元数据核对先决条件与依赖,为实际打补丁前的备份与兼容性检查提供依据。
1. 一个补丁包放错位置,能让你停库停半天
一个p20760997_112030_Linux-x86-64.zip放在/tmp里,看着就是一个普通压缩包,不少人第一反应是unzip完进目录直接opatch apply,然后被OPatch version或Oracle Home is not registered顶回来,库已经停了,业务在等,手里只剩一个报错。Oracle patch 这件事,难度从来不在命令本身,而在前置条件:平台对不对、OPatch 版本够不够、inventory 认不认这个 home、补丁前的备份落没落地。这套流程在 11.2.0.3 的 Linux x86-64 环境里出现频率极高,尤其是接手别人维护的老库、临时补一个包、或者 RAC 只打了一半节点的时候。下面按我实际执行的顺序走一遍,从读文件名到打完验收,中间的血泪经验一并写进去。
2. 环境先列清:11.2.0.3 这套 Oracle patch 的版本对照
补丁打不上的第一大类原因,是环境根本没对齐。有人拿 AIX 的包往 Linux 上装,有人拿 11.2.0.4 的 OPatch 去处理 11.2.0.3 的 home,还有人ORACLE_HOME指向了一个已经不在 inventory 里的目录。这些事在apply之前全都查得出来,花十分钟核对,比事后回滚省一整天。
2.1 从文件名读出三件事:补丁号、版本线、平台
p20760997_112030_Linux-x86-64.zip这个命名不是随便起的,三段信息各自管一件事,任何一个对不上都别往下走。
| 文件名片段 | 含义 | 核对方式 |
|---|---|---|
20760997 | 补丁号,apply和rollback都用这个号 | 解压后目录名也是它,README.txt里会写明适用版本 |
112030 | 对应 11.2.0.3.0 版本线 | opatch lsinventory里的 Oracle Home 版本必须是 11.2.0.3 |
Linux-x86-64 | 平台与位数,64 位 Linux | uname -m输出x86_64才匹配 |
版本线是最容易翻车的一项。11.2.0.3 的 home 上打 11.2.0.4 的补丁,prereq阶段就会拦下来,提示版本不匹配;反过来,如果 home 已经升到了 11.2.0.4,这个包也不能用。平台同样不能想当然,x86-64只覆盖 64 位 Linux,32 位系统、AIX、HP-UX、Solaris 各有各的包,文件名不同,混用会直接报平台校验失败。
拿到包之后,第一件事是解压到补丁号目录再看README.txt,里面写清楚了这个包是单补丁还是汇总补丁、需不需要先装前置补丁、打完要不要跑数据字典脚本。我一般会把 README 里的“Pre-installation”和“Post-installation”两段单独截出来放在便签里,后面一步一步对着勾。
2.2 OPatch 版本不够新,后面的命令全是白跑
OPatch 是打补丁的执行器,它的版本决定了能不能识别新格式的补丁包。11.2.0.3 环境里常见的 OPatch 版本是 11.2.0.3.x,而这个补丁包通常要求 OPatch 不低于某个 11.2.0.3 的子版本,具体下限在 README 里写明。版本不够时的典型表现是apply一上来就报 OPatch 版本不受支持,或者prereq阶段直接退出,命令看着执行了,实际什么都没干。
先查当前版本:
# 必须以 oracle 用户执行,root 下 opatch 会拒绝工作 $ $ORACLE_HOME/OPatch/opatch version OPatch Version: 11.2.0.3.8拿到版本号后跟 README 要求的下限对比。不够就升级 OPatch,常见做法是从补丁 6880880 对应的 11.2 版本线取最新的 OPatch 压缩包,替换掉$ORACLE_HOME/OPatch整个目录。整个替换而不是覆盖解压,是因为旧目录里可能残留opatch.ini之类的本地配置,混在一起排查起来很麻烦。
# 1. 先整体备份旧 OPatch,出问题能一把换回去 mv $ORACLE_HOME/OPatch $ORACLE_HOME/OPatch.bak.$(date +%Y%m%d) # 2. 解压新版 OPatch 到 ORACLE_HOME,压缩包内层目录名就是 OPatch cd $ORACLE_HOME unzip -q /u01/soft/p6880880_112000_Linux-x86-64.zip # 3. 属主和权限位要归位,否则后面 apply 写 inventory 会失败 chown -R oracle:oinstall $ORACLE_HOME/OPatch chmod -R 775 $ORACLE_HOME/OPatch # 4. 复查版本,确认已经是新版本再继续 opatch version压缩包文件名以实际下载到的为准,重点是版本线必须和$ORACLE_HOME一致。这里有个细节:mv之后$ORACLE_HOME/OPatch目录是空的,解压必须在$ORACLE_HOME下执行,让压缩包自己展开成OPatch目录,不要手动改名,改名容易把内层的opatch可执行文件路径搞乱。升级完顺手跑一次opatch lsinventory,能正常输出就说明新 OPatch 已经认到了这个 home。
提示:OPatch 升级失败时,直接
rm -rf $ORACLE_HOME/OPatch再把OPatch.bak.日期改回来,两分钟恢复原状。这就是先备份整个目录的价值,比事后分析文件差异快得多。
2.3 空间、用户、环境变量的前置核对
OPatch 在apply过程中会把原文件备份到$ORACLE_HOME/.patch_storage,同时往 inventory 里写记录,空间不够会在中途失败,而且失败后 home 处于部分打补丁的状态,最难受。装之前把这几项过一遍。
| 检查项 | 命令 | 合格标准 |
|---|---|---|
| 执行用户 | id | 必须是安装 Oracle 的属主用户,通常是oracle,不能是 root |
| 所属组 | id | 主组与 inventory 中记录一致,一般是oinstall |
ORACLE_HOME | echo $ORACLE_HOME | 指向实际 home,且该目录在 inventory 中已注册 |
ORACLE_SID | echo $ORACLE_SID | 单实例下必填,RAC 下用于定位当前节点实例 |
| home 所在分区剩余 | df -h $ORACLE_HOME | 至少留出补丁包解压后体积的 2~3 倍,按 G 级别准备 |
/tmp剩余 | df -h /tmp | 不少于 1~2G,OPatch 运行时会用临时目录 |
| umask | umask | 0022或更严格,避免生成组可写文件导致后续校验告警 |
环境变量这块有个经典坑:ORACLE_HOME和 inventory 里登记的实际路径不一致,比如软链接指向、末尾多了斜杠、或者从别的 home 复制了一份.bash_profile。opatch判断 home 依据的是 inventory 记录,不是环境变量,两者对不上就会报 home 未注册。遇到这种先看$ORACLE_HOME/oraInst.loc里的inventory_loc指向哪里,再用-invPtrLoc显式指定,别急着重装。
# 一次性把关键环境信息打出来,贴在变更单里备查 echo "USER=$(id -un) GROUP=$(id -gn)" echo "ORACLE_HOME=$ORACLE_HOME" echo "ORACLE_SID=$ORACLE_SID" echo "umask=$(umask)" df -h $ORACLE_HOME /tmp cat $ORACLE_HOME/oraInst.loc这几行输出看着朴素,出事的时候能救命。补丁打到一半报错,回看这份快照就知道当时环境是什么样,不用凭记忆猜。
3. 预检与备份:让 opatch 先把话说清楚
预检这一步很多人跳过,直接apply,结果报错信息绕一大圈才指回前置条件。OPatch 自带的检查命令就是干这个的,跑一遍几分钟,比停库之后被打回来划算得多。
3.1 用 lsinventory 摸清家底
lsinventory是 OPatch 的“黑匣子”,它列出当前 home 装了哪些补丁、组件版本、inventory 位置。打补丁前必须留一份快照,这份快照既是基线,也是回滚后对比的依据。
# 完整清单输出到备份目录,文件名带日期便于回溯 mkdir -p /u01/backup/patch_$(date +%Y%m%d) $ORACLE_HOME/OPatch/opatch lsinventory -detail \ > /u01/backup/patch_$(date +%Y%m%d)/lsinv_before.txt # 顺手确认目标补丁号当前是否已经装过,避免重复打 grep -i "20760997" /u01/backup/patch_$(date +%Y%m%d)/lsinv_before.txt-detail会输出组件级别的详细信息,文件会比较大,但值得。第二个grep是我固定动作:如果补丁号已经出现在清单里,说明这个 home 之前打过,重复apply会报补丁已存在,需要先判断是打完没记录还是另一套 home,别盲目重装。清单里同时能看到Oracle Home的绝对路径和版本号,正好和 2.1 里的版本核对呼应。
3.2 解压与属主:别让 root 留下痕迹
补丁包一般用 root 从跳板机传过来,落到/u01/soft之类的目录。解压这一步建议直接用 oracle 用户做,避免解压出来的文件属主是 root,后面 OPatch 读不了或者写不进.patch_storage。
# 切到 oracle 用户后执行,解压目录按补丁号命名,方便多补丁并存 mkdir -p /u01/soft/20760997_src cd /u01/soft/20760997_src unzip -q /u01/soft/p20760997_112030_Linux-x86-64.zip # 解压后会出现以补丁号命名的目录,确认里面有 README.txt 和 files 目录 ls -l /u01/soft/20760997_src/20760997解压出来应该是一个20760997目录,里面通常有README.txt、etc、files这几项。etc/config/inventory.xml描述了补丁对 home 的要求,files下是按组件分目录存放的替换文件。如果解压出来是散落的一堆文件而不是单一目录,说明包结构不对或者解压参数用错了,别继续,先确认包完整性和解压路径。
权限方面,chown -R oracle:oinstall /u01/soft/20760997_src做一遍不亏。有些环境/u01/soft的父目录权限收紧到 700,导致 OPatch 在预检阶段读不到files目录,报的错却像是补丁包损坏,排查方向很容易跑偏。
3.3 冲突检查:先问 OPatch 能不能装
apply之前的冲突检查是必做项,OPatch 会比对补丁涉及的文件与当前 home 中的文件,判断有没有被别的补丁改过、有没有冲突组件。
# 进入补丁目录,-ph 指向当前目录,即补丁所在路径 cd /u01/soft/20760997_src/20760997 $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./输出里如果出现Conflict字样,说明 home 里有别的补丁动过同一批文件,需要按 README 的说明决定是先回滚旧补丁还是换用合并包。输出Prereq "checkConflictAgainstOHWithDetail" passed才算过关。这条命令不会修改任何东西,纯粹是检查,多跑几次也没副作用,环境不确定的时候我一般跑两遍,第一遍确认输出格式,第二遍对着看具体组件。
另一个常用的是组件版本预检,确认补丁要求的组件在当前 home 里都有:
# 按补丁自带的 prereq 清单做一次完整校验 $ORACLE_HOME/OPatch/opatch prereq CheckComponents -ph ./这一步失败通常意味着 home 缺组件或者版本不符,比如只装了数据库没装某些选件。这种问题不是靠改参数能绕过的,要么补装组件,要么换一个匹配的补丁包。
3.4 停库、停监听、双份备份
到了真正动手前,把服务停干净,备份做两份:一份是 inventory 快照(已经在 3.1 做了),一份是 home 的文件系统级备份。apply自身会在.patch_storage里保留原文件,但那只覆盖补丁涉及到的文件,出大事的时候不够用。
# 单实例:先停监听再停库,顺序反了会有连接残留 lsnrctl stop sqlplus / as sysdba <<'EOF' shutdown immediate exit EOF # RAC 环境用 srvctl,节点逐个停,确认全部实例关闭后再动 home # srvctl stop database -d ORCL -o immediate # srvctl stop listener -n node1停库之后确认没有残留进程占着 home,ps -ef | grep ora_应该只剩后台无关键程或干脆干净。然后做文件系统备份,tar压缩一份放到独立分区,别放在$ORACLE_HOME下面,否则等于没备份。
# 备份 ORACLE_HOME 关键目录,排除体积大且可重建的目录可以显著缩短时间 cd $ORACLE_HOME tar -czf /u01/backup/patch_$(date +%Y%m%d)/oh_before.tgz \ --exclude='./.patch_storage' \ --exclude='./oraInventory' \ ./bin ./lib ./rdbms ./OPatch ./inventory只备bin、lib、rdbms、OPatch、inventory这几块,是因为补丁改动的就是这些位置,全量备份一个几十 G 的 home 时间成本太高,而真正需要回滚文件时这几块基本够用。如果磁盘宽裕,全量备一份更稳妥,这个取舍看你的维护窗口长度。
4. 正式 apply:命令、参数与打完必须补的 SQL
前面全过了,apply本身是一条命令的事,关键在于参数别写错、打完的收尾别漏。
4.1 opatch apply 的参数逐个拆
# 在补丁目录内执行,-oh 显式指定 home,-local 表示只处理当前节点 cd /u01/soft/20760997_src/20760997 $ORACLE_HOME/OPatch/opatch apply \ -oh $ORACLE_HOME \ -local \ -silent \ /u01/soft/20760997_src/20760997| 参数 | 作用 | 什么时候改 |
|---|---|---|
-oh | 指定目标 Oracle Home | home 有多个时必须显式写,避免打错库 |
-local | 只处理当前节点的本地 home | 单实例固定用它;RAC 下按节点逐个打 |
-silent | 减少交互输出,适合脚本化 | 首次操作建议去掉,看着输出走更安心 |
-invPtrLoc | 指定oraInst.loc路径 | 报 home 未注册、inventory 找不到时用 |
-report | 只报告不实际修改 | 想先看会改哪些文件时用,正式打时去掉 |
-silent在首次打补丁时我一般不加,让它把每一步的文件替换和备份路径打出来,输出里能看到Backing up和Applying的进度,中途卡住一眼就知道停在哪。等这套流程在同一个环境跑顺了,再考虑加-silent缩短执行时间。
apply正常结束会输出补丁号、成功信息和 inventory 更新提示。如果最后一行是OPatch succeeded,说明补丁文件和 inventory 都已更新。中途失败时不要急着重跑,先看日志:日志路径在输出末尾会打印,通常在$ORACLE_HOME/cfgtoollogs/opatch/下,按时间戳找最新的那个,里面记录了具体在哪个组件、哪个文件上失败。
4.2 RAC 下 -local 与 -all_nodes 的取舍
RAC 环境里$ORACLE_HOME分两种:数据库 home 和 Grid Infrastructure home,补丁类型不同,打法也不同。数据库 home 的补丁通常做法是逐节点-local执行,一个节点打完再打下一个,保证同一时刻只有一个实例受影响;也有环境用-all_nodes一次性推到所有节点,速度快但要求各节点 home 路径、权限、inventory 完全一致,不一致时会报某个节点失败,剩下的节点状态不明,收拾起来更麻烦。
我的习惯是逐节点打,理由是排障边界清晰:打完一个节点立刻验证补丁号,确认没问题再动下一个。打之前还要确认节点间时间同步、/u01路径一致、oracle 用户的 uid/gid 一致,这三项不一致是 RAC 打补丁最常见的翻车点。
GI home 的补丁会更麻烦,需要先停掉对应节点的 CRS 栈,打完再拉起来,而且必须在所有节点都完成后再统一验证。这一块不建议在业务高峰前临时操作,维护窗口要留够,单节点失败就整体停下。
4.3 打完补丁必须跑的 SQL:catbundle 与 utlrp
补丁文件替换完不等于打完,数据字典也要跟着更新。11.2.0.3 上打完补丁后要跑catbundle.sql,再用utlrp.sql编译无效对象。
# 确认库已启动到 open 状态,数据字典脚本要在库可用时执行 cd $ORACLE_HOME/rdbms/admin $ORACLE_HOME/bin/sqlplus / as sysdba <<'EOF' @catbundle.sql psu apply @utlrp.sql EOFcatbundle.sql的作用是把补丁带来的数据字典变更应用到库里,参数psu apply表示按补丁集方式应用,这个参数在 11.2 系列上固定这么写。脚本执行过程中会创建一些对象、更新注册表信息,输出里能看到create or replace和commit的记录,中途报ORA-错误就要停下来看具体对象,不能忽略继续。
utlrp.sql负责重新编译失效对象,跑完会输出无效对象数量。补丁刚打完时这个数字通常不为零,跑完应该是 0 或者只剩极少数与业务无关的对象。如果编译后还有大量无效对象,先查dba_objects里status='INVALID'的清单,看看是不是某个组件没更新到位。
-- 补丁落地后确认注册表已记录本次动作 select action_time, action, namespace, version, id from dba_registry_history order by action_time desc; -- 查当前无效对象数量,编译后应为 0 或极少 select count(*) from dba_objects where status = 'INVALID';dba_registry_history里的记录是打完补丁的“收据”,id字段对应补丁号,action显示APPLY。如果这条记录不存在,说明catbundle.sql没有正确执行,补丁文件虽然换了,库层面并没有认账,后面查版本会前后不一致。
5. 避坑记录:五条从报错到救回的血泪经验
补丁这事,成功路径就那么一条,失败姿势千奇百怪。下面五条是我在 11.2.0.3 的 Linux x86-64 环境里真实遇到过的,按“现象、原因、解决”记下来。
5.1 OPatch 版本过旧:apply 刚起步就退出
现象:opatch apply执行后立刻输出OPatch version ... is not supported或This patch requires OPatch version ...,补丁目录都没进。
原因:home 里的 OPatch 还是几年前的版本,不认识这个补丁包的打包格式,或者达不到 README 里要求的最低版本。
解决:先opatch version记录当前值,再按 2.2 的步骤整体替换 OPatch,替换前mv备份旧目录。升级后重跑prereq再apply。这个坑的隐蔽之处在于报错信息里提到的版本号很容易被当成补丁版本,实际说的是 OPatch 版本,看错方向就会去重下补丁包,白折腾一轮。
5.2 inventory 不认 home:报 Oracle Home is not registered
现象:opatch lsinventory报Oracle Home is not registered或者Cannot find inventory,但目录明明存在,环境变量也指向它。
原因:$ORACLE_HOME/oraInst.loc里的inventory_loc指向的 inventory 文件不存在,或者这个 home 当初是拷贝过来的、从没在 inventory 里注册过。另一种情况是同一台机器上存在多份oraInst.loc,OPatch 读到了错的那份。
解决:cat $ORACLE_HOME/oraInst.loc看inventory_loc指向哪里,去那个路径确认ContentsXML/inventory.xml里有没有当前 home 的条目。条目存在但 OPatch 读不到,就在命令后加-invPtrLoc $ORACLE_HOME/oraInst.loc显式指定。home 确实没注册的,不要手工改 inventory.xml 硬塞,正确做法是用attachHome注册,改坏了整个 inventory 都得重建。
5.3 空间不足导致中途失败:home 处于半打状态
现象:apply跑到一半报写入失败或No space left on device,此时部分文件已被替换,lsinventory里没有补丁记录,库起不来或者起来后报错。
原因:$ORACLE_HOME所在分区剩余空间不够,OPatch 需要同时存放备份和替换文件,峰值占用远大于补丁包本身。
解决:先看df -h $ORACLE_HOME,腾出空间或者临时扩分区,然后从.patch_storage里恢复被替换的文件,或者直接用 3.4 的文件系统备份整体还原,再重新apply。这个坑的教训是前置检查里那条“按补丁包体积的 2~3 倍准备空间”不是保守估计,是下限。
5.4 用 root 打补丁:属主错乱比报错更麻烦
现象:root 下执行opatch apply,OPatch 直接拒绝;有人用sudo -u oracle之外的方式绕过,结果$ORACLE_HOME下部分文件属主变成 root,后续数据库启动时报权限错误。
原因:OPatch 明确要求以 home 属主用户运行,root 或错误用户执行会破坏文件属主和权限位。
解决:立即chown -R oracle:oinstall $ORACLE_HOME把属主改回来,重点检查bin、lib、rdbms/admin这几块,然后终止操作,切到 oracle 用户重来。预防办法很简单:登录后先id确认身份,别在 root 会话里 source oracle 的环境变量就以为切过去了,id -un输出才是真的。
5.5 RAC 只打了一个节点:版本不一致引发诡异报错
现象:节点一打完补丁能正常启动,节点二没打,实例间通信或切换时出现版本不一致相关报错,严重时节点二起不来。
原因:RAC 要求所有节点的数据库 home 补丁级别一致,只打一部分节点会破坏这个前提。
解决:把剩下的节点按同样流程补齐,补齐前先确认节点一的补丁号已经写进 inventory。如果节点二已经起不来,先停掉节点二的实例,打完补丁再拉起。这条的预防成本极低:打之前把所有节点的opatch lsinventory | grep 补丁号输出并排比一遍,缺哪个打哪个。
5.6 回滚:rollback 命令与 -id 参数
补丁打坏了,第一选择是回滚而不是重装。OPatch 保留了补丁涉及文件的原始副本,rollback就是把它们换回去。
# 停库停监听之后执行,-id 后面跟补丁号,必须和 apply 时的号一致 $ORACLE_HOME/OPatch/opatch rollback \ -id 20760997 \ -oh $ORACLE_HOME \ -local # 回滚后数据字典也要回退,按补丁 README 说明执行对应的回滚脚本回滚成功后lsinventory里这个补丁号会消失,但数据字典层面如果已经跑过catbundle.sql,通常是无法用脚本回退的,这一点必须在打补丁前就想清楚。所以我的原则是:apply之前那份文件系统备份比任何回滚命令都可靠,rollback处理的是文件层面,库层面的变更要靠备份加恢复。回滚完重新启动库,把 5.5 里的版本一致性检查再跑一遍,确认所有节点回到了同一个状态。
6. 验收:从 lsinventory 到数据库起的四层检查
补丁打完,别急着把库交给业务,按四层往下验,每层都有明确的通过标准。
| 层级 | 检查动作 | 通过标准 |
|---|---|---|
| 文件层 | opatch lsinventory -detail | 输出里能看到补丁号 20760997,无冲突告警 |
| 字典层 | 查dba_registry_history | 有对应的APPLY记录,版本号与补丁匹配 |
| 对象层 | @utlrp.sql后统计无效对象 | 无效对象数为 0 或仅剩已知无关对象 |
| 服务层 | 启动监听和实例,连一次业务库 | 实例 open,监听状态 READY,业务连接正常 |
文件层最容易过,也最容易骗人:lsinventory里有记录只代表 OPatch 认了账,不代表库认了账。字典层那条dba_registry_history记录才是库层面的凭证,两者都过才算补丁真正落地。对象层经常被跳过,跑完utlrp.sql看到无效对象数不为零也不当回事,结果业务跑几天报某个视图不可用,回头查才发现是补丁后没编译干净。服务层则是最后一道,启动顺序按监听、实例来,起来之后用业务账号连一次,跑一条最简单的查询,比只看lsnrctl status更实在。
# 四层验收一次性跑完,输出贴进变更记录 $ORACLE_HOME/OPatch/opatch lsinventory -detail | grep -i 20760997 $ORACLE_HOME/bin/sqlplus -S / as sysdba <<'EOF' select action_time, action, id from dba_registry_history order by action_time desc; select count(*) invalid_cnt from dba_objects where status = 'INVALID'; select status from v$instance; EOF lsnrctl status | grep -i "ready"这套检查里的一个技巧是:把补丁前的lsinv_before.txt和补丁后的清单做一次 diff,只看差异部分。补丁正常落地时差异应该集中在目标补丁号和它涉及的组件上,如果 diff 出来一堆不相关的组件版本变化,说明这个 home 之前的状态就不干净,得先弄清楚那些变化是怎么来的,再决定要不要继续。我吃过一次亏,diff 里冒出一个从没见过的组件版本变更,追下去发现是上一任维护者手工替换过文件但没走 OPatch,这种历史包袱不查清楚,后面每次打补丁都会踩。
从那以后我每次打补丁,不管多急,都强制走一遍“停库、快照、备份、prereq、apply、catbundle、四层验收”这套固定动作,一步不省。急的时候省下的十分钟,往往要用一个维护窗口来还。希望这套流程能帮到你,少走一次半夜回滚的弯路。
本文还有配套的精品资源,点击获取