一键自动安装Oracle数据库脚本

一键自动安装Oracle数据库脚本

𝓓𝓸𝓷 Lv6

一、一键安装Oracle数据库

1.自动安装Oracle11g数据库脚本

版本: Oracle11g 11.2.0.4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
#!/bin/bash
#
# Oracle Database 11g Release 2 (11.2.0.4) Enterprise Edition
# CentOS 7.x x86_64 单机自动安装脚本(生产级全功能修正版)
#
# SID : yxdb
# DB_NAME : yxdb
# ORACLE_BASE : /u01/app/oracle
# ORACLE_HOME : /u01/app/oracle/product/11.2.0/db_1
# DATA_DIR : /data
# CHARACTERSET : ZHS16GBK
# NATIONALCHARSET : AL16UTF16
# MEMORY : ASMM
# SGA_TARGET : 16GB
# PGA_AGGREGATE_TARGET : 6GB
# REDO : 8 groups x 1024MB
#
# 执行用户:root
# 执行方式:bash install_oracle11g.sh
#

set -Eeuo pipefail
umask 022

# ============================================================
# 1. 基础配置
# ============================================================

SID="yxdb"
DB_NAME="yxdb"

ORACLE_USER="oracle"
ORACLE_GROUP="oinstall"
DBA_GROUP="dba"
OPER_GROUP="oper"

ORACLE_BASE="/u01/app/oracle"
ORACLE_HOME="${ORACLE_BASE}/product/11.2.0/db_1"
INVENTORY="/u01/app/oraInventory"

DATA_DIR="/data"

SOFT_DIR="/opt/soft"
INSTALL_DIR="${SOFT_DIR}/oracle_install"
INSTALL_SOURCE="${INSTALL_DIR}/database"

ZIP1="p13390677_112040_Linux-x86-64_1of7.zip"
ZIP2="p13390677_112040_Linux-x86-64_2of7.zip"

FRA_DEST="${ORACLE_BASE}/fast_recovery_area"

LISTENER_PORT=1521

CHARSET="ZHS16GBK"
NATIONAL_CHARSET="AL16UTF16"

# ============================================================
# 2. 数据库内存配置 (32G物理内存服务器推荐)
# ============================================================

PROCESSES=1000

SGA_TARGET_MB=16384
SGA_MAX_SIZE_MB=16384
PGA_AGGREGATE_TARGET_MB=6144

SGA_TARGET_BYTES=$((SGA_TARGET_MB * 1024 * 1024))
SGA_MAX_SIZE_BYTES=$((SGA_MAX_SIZE_MB * 1024 * 1024))
PGA_AGGREGATE_TARGET_BYTES=$((PGA_AGGREGATE_TARGET_MB * 1024 * 1024))

TOTAL_TARGET_MB=$((SGA_TARGET_MB + PGA_AGGREGATE_TARGET_MB))

DBCA_MEMORY_PERCENT=80
DBCA_TOTALMEMORY_MB=0

FRA_SIZE_MB=$((TOTAL_TARGET_MB * 3 / 2))

# ============================================================
# 3. Redo配置
# ============================================================

REDO_SIZE_MB=1024
REDO_GROUPS=8

# ============================================================
# 4. 其他配置
# ============================================================

LOG="/var/log/oracle11g_install.log"
PASSWORD_FILE="/root/oracle11g_credentials.txt"
DB_PASSWORD=""

# ============================================================
# 5. 基础函数
# ============================================================

log() {
echo "[$(date '+%F %T')] $*"
}

die() {
log "ERROR: $*"
exit 1
}

run_as_oracle() {
su - "${ORACLE_USER}" -c "$*"
}

# ============================================================
# 6. 日志配置
# ============================================================

touch "${LOG}"
chmod 600 "${LOG}"
exec > >(tee -a "${LOG}") 2>&1

trap 'log "安装失败,行号:${LINENO},请检查日志:${LOG}"' ERR

log "================================================"
log "Oracle 11.2.0.4 单机自动安装开始"
log "================================================"

# ============================================================
# 7. 系统检查
# ============================================================

[ "$(id -u)" -eq 0 ] || die "必须使用 root 用户执行"
[ "$(uname -m)" = "x86_64" ] || die "仅支持 x86_64"

if ! grep -qiE 'release 7\.' /etc/redhat-release 2>/dev/null; then
die "当前系统不是 CentOS/RHEL 7.x"
fi

MEM_MB=$(awk '/MemTotal/ {print int($2 / 1024)}' /proc/meminfo)
log "服务器物理内存:${MEM_MB}MB"

if [ "${MEM_MB}" -lt 26000 ]; then
die "物理内存不足,建议至少26GB,当前${MEM_MB}MB"
fi

if [ "${TOTAL_TARGET_MB}" -gt $((MEM_MB * 85 / 100)) ]; then
die "SGA+PGA超过物理内存的85%,不允许继续安装"
fi

[ "${SGA_TARGET_MB}" -gt 0 ] || die "SGA_TARGET必须大于0"
[ "${SGA_MAX_SIZE_MB}" -ge "${SGA_TARGET_MB}" ] || die "SGA_MAX_SIZE不能小于SGA_TARGET"
[ "${PGA_AGGREGATE_TARGET_MB}" -gt 0 ] || die "PGA_AGGREGATE_TARGET必须大于0"

DBCA_TOTALMEMORY_MB=$((MEM_MB * DBCA_MEMORY_PERCENT / 100))

log "DBCA TOTALMEMORY:${DBCA_TOTALMEMORY_MB}MB"
log "最终 SGA:${SGA_TARGET_MB}MB"
log "最终 PGA:${PGA_AGGREGATE_TARGET_MB}MB"

# ============================================================
# 8. 安装包检查
# ============================================================

[ -s "${SOFT_DIR}/${ZIP1}" ] || die "安装包不存在:${SOFT_DIR}/${ZIP1}"
[ -s "${SOFT_DIR}/${ZIP2}" ] || die "安装包不存在:${SOFT_DIR}/${ZIP2}"

command -v unzip >/dev/null 2>&1 || die "缺少 unzip"
command -v openssl >/dev/null 2>&1 || die "缺少 openssl"
command -v bc >/dev/null 2>&1 || {
log "安装 bc"
yum install -y bc
}

# ============================================================
# 9. 磁盘空间检查
# ============================================================

mkdir -p /u01 /data

U01_AVAIL=$(df -Pm /u01 | awk 'NR==2 {print $4}')
DATA_AVAIL=$(df -Pm /data | awk 'NR==2 {print $4}')

[ "${U01_AVAIL}" -ge 50000 ] || die "/u01 可用空间不足50GB,当前${U01_AVAIL}MB"
[ "${DATA_AVAIL}" -ge 30000 ] || die "/data 可用空间不足30GB,当前${DATA_AVAIL}MB"

log "/u01 可用空间:${U01_AVAIL}MB"
log "/data 可用空间:${DATA_AVAIL}MB"

# ============================================================
# 10. 冲突检查
# ============================================================

[ ! -e "${ORACLE_HOME}" ] || die "ORACLE_HOME已经存在:${ORACLE_HOME}"
[ ! -e "${INVENTORY}" ] || die "Inventory已经存在:${INVENTORY}"
[ ! -d "${DATA_DIR}/${DB_NAME}" ] || die "数据库目录已经存在:${DATA_DIR}/${DB_NAME}"

if id "${ORACLE_USER}" >/dev/null 2>&1; then
die "Oracle用户已经存在,请检查原有安装环境"
fi

if ss -lnt | grep -qE ":${LISTENER_PORT}[[:space:]]"; then
die "监听端口${LISTENER_PORT}已经被占用"
fi

log "系统环境检查通过"

# ============================================================
# 11. 创建Oracle用户及用户组
# ============================================================

groupadd "${ORACLE_GROUP}"
groupadd "${DBA_GROUP}"
groupadd "${OPER_GROUP}"

useradd \
-g "${ORACLE_GROUP}" \
-G "${DBA_GROUP},${OPER_GROUP}" \
-d /home/oracle \
-s /bin/bash \
-m "${ORACLE_USER}"

# ============================================================
# 12. 创建安装目录
# ============================================================

mkdir -p \
"${ORACLE_BASE}" \
"${ORACLE_HOME}" \
"${INVENTORY}" \
"${DATA_DIR}" \
"${INSTALL_DIR}" \
"${FRA_DEST}"

chown -R "${ORACLE_USER}:${ORACLE_GROUP}" \
"${ORACLE_BASE}" \
"${INVENTORY}" \
"${INSTALL_DIR}"

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DATA_DIR}"

chmod 775 \
"${ORACLE_BASE}" \
"${INVENTORY}" \
"${DATA_DIR}" \
"${FRA_DEST}"

# ============================================================
# 13. 安装系统依赖
# ============================================================

log "安装Oracle依赖包"

yum install -y \
binutils \
compat-libcap1 \
compat-libstdc++-33 \
gcc \
gcc-c++ \
glibc \
glibc-devel \
ksh \
libaio \
libaio-devel \
libgcc \
libstdc++ \
libstdc++-devel \
libXi \
libXtst \
make \
sysstat \
unixODBC \
unixODBC-devel \
unzip \
net-tools \
bc \
openssl \
which

# ============================================================
# 14. 计算共享内存参数
# ============================================================

PAGE_SIZE=$(getconf PAGE_SIZE)
SHMMAX_BYTES=$((MEM_MB * 1024 * 1024 * 90 / 100))
SHMALL_PAGES=$((SHMMAX_BYTES / PAGE_SIZE))
MEMLOCK_KB=$((SGA_MAX_SIZE_BYTES / 1024))

log "共享内存参数计算完成"

# ============================================================
# 15. 配置内核参数
# ============================================================

cat > /etc/sysctl.d/99-oracle.conf <<EOF
fs.aio-max-nr = 1048576
fs.file-max = 6815744
kernel.shmall = ${SHMALL_PAGES}
kernel.shmmax = ${SHMMAX_BYTES}
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
EOF

sysctl --system

# ============================================================
# 16. 配置Oracle用户资源限制
# ============================================================

cat > /etc/security/limits.d/oracle.conf <<EOF
oracle soft nofile 1024
oracle hard nofile 65536
oracle soft nproc 16384
oracle hard nproc 16384
oracle soft stack 10240
oracle hard stack 32768
oracle soft memlock ${MEMLOCK_KB}
oracle hard memlock ${MEMLOCK_KB}
EOF

# ============================================================
# 17. 检查并确保/dev/shm足够大 (针对32G物理内存安全扩容至24G)
# ============================================================

SHM_REQUIRED_BYTES=$((24 * 1024 * 1024 * 1024))
SHM_CURRENT_BYTES=$(df -B1 /dev/shm | awk 'NR==2 {print $2}')

if [ "${SHM_CURRENT_BYTES}" -lt "${SHM_REQUIRED_BYTES}" ]; then
log "/dev/shm容量不足,开始调整为24G"
mount -o remount,size="24G" /dev/shm || die "/dev/shm扩容失败"
if grep -qE '^[^#]*[[:space:]]/dev/shm[[:space:]]' /etc/fstab; then
sed -i '/^[^#]*[[:space:]]\/dev\/shm[[:space:]]/d' /etc/fstab
fi
echo "tmpfs /dev/shm tmpfs defaults,size=24G 0 0" >> /etc/fstab
fi

log "/dev/shm配置完成"

# ============================================================
# 18. 配置Oracle环境变量
# ============================================================

cat > /home/oracle/.bash_profile <<EOF
export TMP=/tmp
export TMPDIR=/tmp
export ORACLE_BASE=${ORACLE_BASE}
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export ORACLE_UNQNAME=${DB_NAME}
export PATH=\$ORACLE_HOME/bin:\$PATH
export LD_LIBRARY_PATH=\$ORACLE_HOME/lib:/lib:/usr/lib
export CLASSPATH=\$ORACLE_HOME/jlib:\$ORACLE_HOME/rdbms/jlib
export NLS_LANG=AMERICAN_AMERICA.${CHARSET}
export PS1='[\\u@\\h \\W]\\$ '
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" /home/oracle/.bash_profile
chmod 644 /home/oracle/.bash_profile

# ============================================================
# 19. 生成数据库密码
# ============================================================

DB_PASSWORD="Yx$(openssl rand -hex 6)"
[ "${#DB_PASSWORD}" -ge 12 ] || die "数据库密码长度不足"
[ "${#DB_PASSWORD}" -le 16 ] || die "数据库密码超过16个字符"

cat > "${PASSWORD_FILE}" <<EOF
Oracle 11g Database Credentials
DB_NAME: ${DB_NAME}
SID: ${SID}
SYS_PASSWORD: ${DB_PASSWORD}
SYSTEM_PASSWORD: ${DB_PASSWORD}
LISTENER_PORT: ${LISTENER_PORT}
EOF

chmod 600 "${PASSWORD_FILE}"
log "数据库密码生成成功"

# ============================================================
# 20. 解压Oracle安装介质
# ============================================================

if [ ! -x "${INSTALL_SOURCE}/runInstaller" ]; then
log "开始解压Oracle安装介质"
run_as_oracle "
cd '${INSTALL_DIR}'
unzip -oq '${SOFT_DIR}/${ZIP1}'
unzip -oq '${SOFT_DIR}/${ZIP2}'
"
fi

[ -x "${INSTALL_SOURCE}/runInstaller" ] || die "runInstaller不存在"
chown -R "${ORACLE_USER}:${ORACLE_GROUP}" "${INSTALL_DIR}"

# ============================================================
# 21. Oracle软件安装响应文件
# ============================================================

DB_INSTALL_RSP="${INSTALL_DIR}/db_install.rsp"
cat > "${DB_INSTALL_RSP}" <<EOF
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0
oracle.install.option=INSTALL_DB_SWONLY
ORACLE_HOSTNAME=$(hostname -f)
UNIX_GROUP_NAME=${ORACLE_GROUP}
INVENTORY_LOCATION=${INVENTORY}
SELECTED_LANGUAGES=en,zh_CN
ORACLE_HOME=${ORACLE_HOME}
ORACLE_BASE=${ORACLE_BASE}
oracle.install.db.InstallEdition=EE
oracle.install.db.EEOptionsSelection=false
oracle.install.db.optionalComponents=
oracle.install.db.DBA_GROUP=${DBA_GROUP}
oracle.install.db.OPER_GROUP=${OPER_GROUP}
oracle.install.db.CLUSTER_NODES=
oracle.install.db.isRACOneInstall=false
oracle.install.db.racOneServiceName=
oracle.install.db.config.starterdb.type=GENERAL_PURPOSE
oracle.install.db.config.starterdb.globalDBName=${DB_NAME}
oracle.install.db.config.starterdb.SID=${SID}
oracle.install.db.config.starterdb.characterSet=${CHARSET}
oracle.install.db.config.starterdb.memoryOption=false
oracle.install.db.config.starterdb.memoryLimit=
oracle.install.db.config.starterdb.installExampleSchemas=false
oracle.install.db.config.starterdb.enableSecuritySettings=true
oracle.install.db.config.starterdb.password.ALL=
oracle.install.db.config.starterdb.password.SYS=
oracle.install.db.config.starterdb.password.SYSTEM=
oracle.install.db.config.starterdb.password.DBSNMP=
oracle.install.db.config.starterdb.password.SYSMAN=
oracle.install.db.config.starterdb.control=DB_CONTROL
oracle.install.db.config.starterdb.gridcontrol.gridControlServiceURL=
oracle.install.db.config.starterdb.automatedBackup.enable=false
oracle.install.db.config.starterdb.storageType=FILE_SYSTEM_STORAGE
oracle.install.db.config.starterdb.fileSystemStorage.dataLocation=${DATA_DIR}
oracle.install.db.config.starterdb.fileSystemStorage.recoveryLocation=${FRA_DEST}
oracle.install.db.config.asm.diskGroup=
MYORACLESUPPORT_USERNAME=
MYORACLESUPPORT_PASSWORD=
SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
DECLINE_SECURITY_UPDATES=true
oracle.installer.autoupdates.option=SKIP_UPDATES
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DB_INSTALL_RSP}"
chmod 600 "${DB_INSTALL_RSP}"

# ============================================================
# 22. 静默安装Oracle数据库软件
# ============================================================

log "开始安装Oracle 11.2.0.4数据库软件"
INSTALL_LOG="${INSTALL_DIR}/oracle_install.log"

if ! su - "${ORACLE_USER}" -c "
cd '${INSTALL_SOURCE}'
./runInstaller \
-silent \
-ignorePrereq \
-ignoreSysPrereqs \
-ignoreDiskWarning \
-waitforcompletion \
-responseFile '${DB_INSTALL_RSP}'
" > "${INSTALL_LOG}" 2>&1; then
tail -100 "${INSTALL_LOG}"
die "Oracle软件安装失败,请检查${INSTALL_LOG}"
fi

[ -x "${ORACLE_HOME}/bin/sqlplus" ] || die "Oracle sqlplus不存在"
log "Oracle数据库软件安装成功"

# ============================================================
# 23. 执行Root脚本
# ============================================================

[ -f "${INVENTORY}/orainstRoot.sh" ] && "${INVENTORY}/orainstRoot.sh"
[ -f "${ORACLE_HOME}/root.sh" ] && "${ORACLE_HOME}/root.sh"

# ============================================================
# 24. 配置监听器
# ============================================================

NETCA_RSP="${INSTALL_DIR}/netca.rsp"
cp "${INSTALL_SOURCE}/response/netca.rsp" "${NETCA_RSP}"
chown "${ORACLE_USER}:${ORACLE_GROUP}" "${NETCA_RSP}"
chmod 600 "${NETCA_RSP}"
NETCA_LOG="${INSTALL_DIR}/netca.log"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
'${ORACLE_HOME}/bin/netca' -silent -responseFile '${NETCA_RSP}'
" > "${NETCA_LOG}" 2>&1; then
tail -100 "${NETCA_LOG}"
die "NETCA监听器配置失败"
fi

# ============================================================
# 25. 检查监听器
# ============================================================

su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
lsnrctl status
"

# ============================================================
# 26. DBCA建库响应文件
# ============================================================

DBCA_RSP="${INSTALL_DIR}/dbca_create.rsp"
cat > "${DBCA_RSP}" <<EOF
[GENERAL]
RESPONSEFILE_VERSION = "11.2.0"
OPERATION_TYPE = "createDatabase"
[CREATEDATABASE]
GDBNAME = "${DB_NAME}"
SID = "${SID}"
TEMPLATENAME = "General_Purpose.dbc"
SYSPASSWORD = "${DB_PASSWORD}"
SYSTEMPASSWORD = "${DB_PASSWORD}"
DATAFILEDESTINATION = "${DATA_DIR}"
STORAGETYPE = "FS"
CHARACTERSET = "${CHARSET}"
NATIONALCHARACTERSET = "${NATIONAL_CHARSET}"
DATABASETYPE = "OLTP"
EMCONFIGURATION = "NONE"
RECOVERYAREASIZE = "${FRA_SIZE_MB}"
RECOVERYAREADESTINATION = "${FRA_DEST}"
AUTOMATICMEMORYMANAGEMENT = "TRUE"
TOTALMEMORY = "${DBCA_TOTALMEMORY_MB}"
SAMPLESCHEMA = "FALSE"
LISTENERS = "LISTENER"
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DBCA_RSP}"
chmod 600 "${DBCA_RSP}"

# ============================================================
# 27. 检查DBCA程序及模板
# ============================================================

[ -x "${ORACLE_HOME}/bin/dbca" ] || die "DBCA程序不存在"
[ -f "${ORACLE_HOME}/assistants/dbca/templates/General_Purpose.dbc" ] || die "General_Purpose.dbc模板不存在"
mkdir -p "${DATA_DIR}" "${FRA_DEST}"
chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DATA_DIR}" "${FRA_DEST}"

# ============================================================
# 28. DBCA创建数据库
# ============================================================

log "开始执行DBCA创建数据库"
DBCA_LOG="${INSTALL_DIR}/dbca_create.log"
touch "${DBCA_LOG}"
chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DBCA_LOG}"
chmod 600 "${DBCA_LOG}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
export LD_LIBRARY_PATH='${ORACLE_HOME}/lib:/lib:/usr/lib'
export NLS_LANG=AMERICAN_AMERICA.${CHARSET}
'${ORACLE_HOME}/bin/dbca' \
-silent \
-createDatabase \
-responseFile '${DBCA_RSP}' \
-redoLogFileSize '${REDO_SIZE_MB}'
" > "${DBCA_LOG}" 2>&1; then
tail -200 "${DBCA_LOG}"
die "DBCA建库失败,请检查${DBCA_LOG}"
fi

# ============================================================
# 29. 等待数据库启动
# ============================================================

log "等待数据库启动"
DB_READY=0
for i in $(seq 1 60); do
DB_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT STATUS FROM V\$INSTANCE;
EXIT;
SQL
" 2>/dev/null | tr -d '[:space:]' || true)

if [ "${DB_STATUS}" = "OPEN" ]; then
DB_READY=1
break
fi
sleep 10
done

[ "${DB_READY}" -eq 1 ] || die "数据库启动超时"

# ============================================================
# 30. 检查数据库字符集
# ============================================================

CHARSET_SQL="${INSTALL_DIR}/charset_check.sql"
cat > "${CHARSET_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT PARAMETER || '=' || VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET') ORDER BY PARAMETER;
EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${CHARSET_SQL}"
CHARSET_RESULT=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
sqlplus -L -s / as sysdba @'${CHARSET_SQL}'
")

echo "${CHARSET_RESULT}" | grep -q "${CHARSET}" || die "数据库字符集不是${CHARSET}"
echo "${CHARSET_RESULT}" | grep -q "${NATIONAL_CHARSET}" || die "国家字符集不是${NATIONAL_CHARSET}"

# ============================================================
# 31. 配置ASMM内存参数(已修复:清除AMM并平滑过渡到ASMM)
# ============================================================

log "开始配置ASMM内存参数"
MEMORY_SQL="${INSTALL_DIR}/memory_config.sql"

cat > "${MEMORY_SQL}" <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE

-- 1. 先将内存上限和目标值显式归零,解除 AMM 对内存的独占锁
ALTER SYSTEM SET memory_target=0 SCOPE=SPFILE;
ALTER SYSTEM SET memory_max_target=0 SCOPE=SPFILE;

-- 2. 设置 ASMM 所需的目标和最大值参数
ALTER SYSTEM SET sga_max_size=${SGA_MAX_SIZE_BYTES} SCOPE=SPFILE;
ALTER SYSTEM SET sga_target=${SGA_TARGET_BYTES} SCOPE=SPFILE;
ALTER SYSTEM SET pga_aggregate_target=${PGA_AGGREGATE_TARGET_BYTES} SCOPE=SPFILE;

-- 3. 其他系统参数
ALTER SYSTEM SET processes=${PROCESSES} SCOPE=SPFILE;
ALTER SYSTEM SET open_cursors=600 SCOPE=BOTH;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${MEMORY_SQL}"
chmod 600 "${MEMORY_SQL}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
sqlplus -L -s / as sysdba @'${MEMORY_SQL}'
"; then
die "ASMM内存参数配置失败"
fi

log "ASMM内存参数配置完成"

# ============================================================
# 32. 重启数据库使内存参数生效
# ============================================================

log "重启数据库,使ASMM参数生效"
RESTART_SQL="${INSTALL_DIR}/database_restart.sql"
cat > "${RESTART_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SHUTDOWN IMMEDIATE;
STARTUP;
EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${RESTART_SQL}"
chmod 600 "${RESTART_SQL}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
sqlplus -L -s / as sysdba @'${RESTART_SQL}'
"; then
die "数据库重启失败,请检查Oracle启动日志及内存参数"
fi

# ============================================================
# 33. 等待数据库重新启动
# ============================================================

DB_READY=0
for i in $(seq 1 60); do
DB_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT STATUS FROM V\$INSTANCE;
EXIT;
SQL
" 2>/dev/null | tr -d '[:space:]' || true)

if [ "${DB_STATUS}" = "OPEN" ]; then
DB_READY=1
break
fi
sleep 10
done

[ "${DB_READY}" -eq 1 ] || die "ASMM配置后数据库启动失败"

log "数据库重新启动成功"

# ============================================================
# 34. 检查监听器状态
# ============================================================

log "检查监听器状态"
su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
lsnrctl status
"

# ============================================================
# 35. 安装完成
# ============================================================

log "================================================"
log "Oracle 11.2.0.4 数据库安装成功"
log "================================================"

echo
echo "DB_NAME : ${DB_NAME}"
echo "ORACLE_SID : ${SID}"
echo "ORACLE_BASE : ${ORACLE_BASE}"
echo "ORACLE_HOME : ${ORACLE_HOME}"
echo "DATA_DIR : ${DATA_DIR}"
echo "CHARACTERSET : ${CHARSET}"
echo "NATIONAL_CHARSET : ${NATIONAL_CHARSET}"
echo "MEMORY : ASMM"
echo "SGA_TARGET : ${SGA_TARGET_MB}MB"
echo "SGA_MAX_SIZE : ${SGA_MAX_SIZE_MB}MB"
echo "PGA_AGGREGATE_TARGET : ${PGA_AGGREGATE_TARGET_MB}MB"
echo "REDO : ${REDO_GROUPS} x ${REDO_SIZE_MB}MB"
echo "FRA : ${FRA_DEST}"
echo "FRA_SIZE : ${FRA_SIZE_MB}MB"
echo "LISTENER_PORT : ${LISTENER_PORT}"
echo "PASSWORD_FILE : ${PASSWORD_FILE}"
echo "INSTALL_LOG : ${LOG}"

log "Oracle数据库安装全部完成"
log "================================================"

exit 0
2.自动部署Oracle12c数据库脚本

版本: Oracle12c 12.1.0.2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
#!/bin/bash
#
# Oracle Database 12c Release 1 (12.1.0.2) Enterprise Edition
# CentOS 7.6 x86_64 单机数据库一键安装
#
# SID : cyhis
# DB_NAME : cyhis
# DB_UNIQUE_NAME : cyhis(DBCA 建库后会按引擎规范化,本脚本不再中途改回,详见第16.5节说明)
# ORACLE_BASE : /u01/app/oracle
# ORACLE_HOME : /u01/app/oracle/product/12.1.0/db_1
# DATA : /data
# CHARACTERSET : ZHS16GBK
# NATIONAL CHAR : AL16UTF16
# SGA : 8GB
# PGA : 2GB
# REDO : 8 groups, 1024MB each
# MEMORY : ASMM(sga_target=8G / pga_aggregate_target=2G,memory_target=0)
#
# 修复要点 v2(对应日志:ORA-00823: Specified value of sga_target greater than sga_max_size):
# 1) 【致命原因】建库阶段不再用 -initParams 硬塞内存参数。
# 12.1 的 DBCA 先往它生成的初始化文件里写一套自己的内存参数
# (sga_max_size 由 totalMemory / 模板推导,值偏小),再追加 -initParams。
# 实测结果:sga_target=8G 追加成功,sga_max_size 仍是 DBCA 那个小值
# => sga_target > sga_max_size => ORA-00823 => 实例 NOMOUNT 起不来
# => ORA-01078 + ORA-01034,进度停在 4% Copying database files。
# 改法:rsp 里 AUTOMATICMEMORYMANAGEMENT="FALSE" + TOTALMEMORY=<SGA_MB>,
# 让 DBCA 一次写出自洽的 SGA(sga_target == sga_max_size),
# -initParams 只保留非内存参数;SGA/PGA 精确目标值由步骤 16.5 建库后落地。
# 2) 【撤回上一版误改】-redoLogFileSize 在本机 12.1 的容量校验里按「MB」解析。
# 传字节 1073741824 才导致日志里 Required space is 3221226902 MB 的荒谬需求;
# 回看第4轮日志 Required 4502MB = 3组 x 1024MB + 数据文件1430MB,反证单位是 MB。
# 因此改回传 REDO_SIZE_MB(1024),即每个 redo 成员 1GB。
# 3) kernel.shmmax 由固定 8GB 改为按 SGA+PGA 动态推导(必须严格大于 SGA)
# 4) 步骤 16.5 兜底校验扩展为同时校验 sga_target / sga_max_size /
# pga_aggregate_target / memory_target,任一不符即整组写 SPFILE 后重启,
# 并在结尾硬性确认 sga_max_size >= sga_target,不通过直接报错退出
# 5) 新增建库失败残留清理开关 CLEAN_FAILED_DB:上次失败留下的 DATA_DIR / admin
# 会让第3节路径冲突检查直接退出,重跑前必须先清掉
#
# 执行用户:root
# 执行方式:bash install_oracle12c.sh
#

set -Eeuo pipefail
umask 022

# ==================================================
# 1. 基础配置
# ==================================================

SID="cyhis"
DB_NAME="cyhis"

ORACLE_USER="oracle"
ORACLE_GROUP="oinstall"

ORACLE_BASE="/u01/app/oracle"
ORACLE_HOME="${ORACLE_BASE}/product/12.1.0/db_1"
INVENTORY="/u01/app/oraInventory"

DATA_DIR="/data"

SOFT_DIR="/opt/soft"
INSTALL_DIR="$SOFT_DIR/oracle_install"

ZIP1="linuxamd64_12102_database_1of2.zip"
ZIP2="linuxamd64_12102_database_2of2.zip"

# ==================================================
# Oracle内存配置(只需要修改这里)
# ==================================================

# 【新增】Oracle最大进程数
PROCESSES=1000

# SGA目标值,单位MB
SGA_TARGET_MB=8192

# SGA最大值,单位MB(静态参数,必须 >= SGA_TARGET)
SGA_MAX_SIZE_MB=8192

# PGA目标值,单位MB
PGA_AGGREGATE_TARGET_MB=2048

# 闪回恢复区(Fast Recovery Area)配置
# 路径遵循 Oracle 12c 规范:$ORACLE_BASE/fast_recovery_area
FRA_DEST="${ORACLE_BASE}/fast_recovery_area"
# FRA 大小,单位MB(建议 >= 数据文件总量 + 3天归档量;此处按 SGA+PGA 的 1.5 倍估算)
FRA_SIZE_MB=$(( (SGA_TARGET_MB + PGA_AGGREGATE_TARGET_MB) * 3 / 2 ))


# 说明:Oracle 根本不存在 MEMORY_MANAGEMENT 这个参数,AUTO_SGA 也不是任何取值。
# ASMM(自动共享内存管理)的真实开关是组合条件:
# memory_target=0 且 memory_max_target=0 且 sga_target>0
# 因此脚本不再维护 MEMORY_MANAGEMENT / TOTAL_MEMORY_MB 这类伪变量。

SGA_TARGET_BYTES=$((SGA_TARGET_MB * 1024 * 1024))
SGA_MAX_SIZE_BYTES=$((SGA_MAX_SIZE_MB * 1024 * 1024))
PGA_AGGREGATE_TARGET_BYTES=$((PGA_AGGREGATE_TARGET_MB * 1024 * 1024))

# ---- 建库阶段的内存入口只保留一个:DBCA 自己能算出「自洽」值的那个 ----
# automaticMemoryManagement=FALSE 时 totalMemory 被 DBCA 当作 SGA 大小,
# 它会同时写入 sga_target 与 sga_max_size(两者相等),
# 建库阶段不会出现 ORA-00823(target > max)。
DBCA_TOTALMEMORY_MB=${SGA_TARGET_MB}

# 传给 DBCA 的初始化参数:这里不能再出现 sga_* / pga_* / memory_*,
# 否则与上面 totalMemory 生成的值互相打架(v1 正是栽在这一条上)。
INIT_PARAMS="processes=${PROCESSES},open_cursors=600,audit_trail=DB"

# Redo日志配置
# 注意:dbca -redoLogFileSize 在本机 12.1 上按「MB」参与容量校验,
# 传字节 1073741824 会报 Required space is 1073745934 MB 这类不可能满足的需求。
# 单位是 MB:1024 => 每个 redo 日志成员 1GB。
REDO_SIZE_MB=1024
REDO_GROUPS=8

# 上一次建库失败时置为 "true":清理失败的实例残留(数据文件/admin/diag/口令文件),
# 否则第3节的路径冲突检查会让脚本直接退出。不会删除 ORACLE_HOME 与 oraInventory。
CLEAN_FAILED_DB="false"
LOG="/var/log/oracle12c_install.log"
PASSWORD_FILE="/root/oracle12c_credentials.txt"

LISTENER_PORT=1521


# ==================================================
# 内存参数检查(ASMM自动内存管理)
# ==================================================

# 检查内存参数是否大于0
if [ "$SGA_TARGET_MB" -le 0 ] ||
[ "$SGA_MAX_SIZE_MB" -le 0 ] ||
[ "$PGA_AGGREGATE_TARGET_MB" -le 0 ]; then

echo "错误:内存参数必须大于0"
exit 1
fi

# 检查SGA最大值(静态参数,必须 >= sga_target)
if [ "$SGA_MAX_SIZE_MB" -lt "$SGA_TARGET_MB" ]; then
echo "错误:SGA_MAX_SIZE_MB不能小于SGA_TARGET_MB"
exit 1
fi

# 检查建库阶段的内存入口是否唯一:
# -initParams 里一旦再出现 sga_* / pga_* / memory_*,就会与 DBCA 由 totalMemory
# 生成的 sga_max_size 打架,实例 NOMOUNT 直接 ORA-00823(本轮报错的根因)。
case "$INIT_PARAMS" in
*sga_target*|*sga_max_size*|*pga_aggregate_target*|*memory_target*)
echo "错误:INIT_PARAMS 中不得包含内存参数,建库阶段内存只由 DBCA_TOTALMEMORY_MB 控制"
exit 1 ;;
*) : ;;
esac

# 按物理内存做容量校验,并按 SGA 推导共享内存段参数

# 建库阶段的内存入口必须为正数(它决定 DBCA 写进 spfile 的 SGA 大小)
if [ "${DBCA_TOTALMEMORY_MB}" -le 0 ]; then
echo "错误:DBCA_TOTALMEMORY_MB 必须大于0"
exit 1
fi
PHYS_MEM_MB=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo)

# SGA 不得超过物理内存的 70%
if [ $((SGA_TARGET_MB * 100)) -gt $((PHYS_MEM_MB * 70)) ]; then
echo "错误:SGA_TARGET_MB=${SGA_TARGET_MB} 超过物理内存 ${PHYS_MEM_MB}MB 的70%"
exit 1
fi

# SGA + PGA 不得超过物理内存的 85%
if [ $(((SGA_TARGET_MB + PGA_AGGREGATE_TARGET_MB) * 100)) -gt $((PHYS_MEM_MB * 85)) ]; then
echo "错误:SGA+PGA=$((SGA_TARGET_MB + PGA_AGGREGATE_TARGET_MB))MB 超过物理内存 ${PHYS_MEM_MB}MB 的85%"
exit 1
fi

# kernel.shmmax 必须【严格大于】SGA:原脚本 shmmax 正好等于 8GB,
# 而 SGA 也是 8GB,实例一启动就会因共享内存段分配失败而 ORA-27102 / ORA-01034
SHMMAX_BYTES=$((SGA_TARGET_BYTES + PGA_AGGREGATE_TARGET_BYTES))
if [ "$SHMMAX_BYTES" -lt $((SGA_TARGET_BYTES * 11 / 10)) ]; then
SHMMAX_BYTES=$((SGA_TARGET_BYTES * 11 / 10))
fi
SHMALL_PAGES=$((SHMMAX_BYTES / 4096 + 1024))

# memlock 至少覆盖 SGA(防止后续启用 lock_sga 后启动失败)
MEMLOCK_KB=$((SHMMAX_BYTES / 1024))

echo "=========================================="
echo "Oracle内存参数检查通过"
echo "=========================================="

echo "内存管理模式 = ASMM;建库阶段 SGA=${DBCA_TOTALMEMORY_MB}MB(由 DBCA 生成自洽的 sga_target 等于 sga_max_size),建库后由 16.5 精调"
echo "SGA_TARGET_MB = ${SGA_TARGET_MB}MB (${SGA_TARGET_BYTES} bytes)"
echo "SGA_MAX_SIZE_MB = ${SGA_MAX_SIZE_MB}MB"
echo "PGA_AGGREGATE_TARGET_MB = ${PGA_AGGREGATE_TARGET_MB}MB"
echo "PHYS_MEM_MB = ${PHYS_MEM_MB}MB"
echo "kernel.shmmax = ${SHMMAX_BYTES}"
echo "kernel.shmall = ${SHMALL_PAGES} pages"

echo "=========================================="


# ==================================================
# 2. 日志
# ==================================================

touch "$LOG"
chmod 600 "$LOG"

exec > >(tee -a "$LOG") 2>&1

trap 'echo "[ERROR] 安装失败,行号:$LINENO,详情请查看:$LOG"' ERR

echo "=================================================="
echo "Oracle 12.1.0.2 单机自动安装"
echo "开始时间:$(date)"
echo "=================================================="

# ==================================================
# 3. 系统检查
# ==================================================

if [ "$(id -u)" -ne 0 ]; then
echo "错误:必须使用root用户执行"
exit 1
fi

#if [ ! -f /etc/centos-release ]; then
# echo "错误:未检测到CentOS系统"
# exit 1
#fi

#if ! grep -qE 'CentOS Linux release 7\.6' /etc/centos-release; then
# echo "错误:本脚本仅针对CentOS 7.6设计"
# cat /etc/centos-release
# exit 1
#fi

# 兼容 CentOS 7.x 及其主流衍生版
if ! grep -qiE '(centos|red hat|rocky|alma|anolis).*release.*7\.' /etc/os-release 2>/dev/null && \
! grep -qiE '(centos|red hat|rocky|alma|anolis).*release.*7\.' /etc/redhat-release 2>/dev/null; then
echo "错误:仅支持 RHEL/CentOS 7.x 及其兼容衍生版"
cat /etc/os-release 2>/dev/null | head -5
exit 1
fi

ARCH=$(uname -m)

if [ "$ARCH" != "x86_64" ]; then
echo "错误:仅支持x86_64"
exit 1
fi

MEM=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo)

if [ "$MEM" -lt 14000 ]; then
echo "错误:内存不足14GB,当前:${MEM}MB"
exit 1
fi

for f in "$SOFT_DIR/$ZIP1" "$SOFT_DIR/$ZIP2"; do
if [ ! -s "$f" ]; then
echo "错误:缺少Oracle安装包:$f"
exit 1
fi
done

# 检查磁盘空间
mkdir -p /u01 /data

U01_AVAIL=$(df -Pm /u01 | awk 'NR==2 {print $4}')
DATA_AVAIL=$(df -Pm /data | awk 'NR==2 {print $4}')

if [ "$U01_AVAIL" -lt 20000 ]; then
echo "错误:/u01 可用空间不足20GB"
exit 1
fi

if [ "$DATA_AVAIL" -lt 30000 ]; then
echo "错误:/data 可用空间不足30GB"
exit 1
fi

# 清理上一次建库失败的残留(只动实例文件,已装好的软件不碰)
if [ "$CLEAN_FAILED_DB" = "true" ]; then
echo "CLEAN_FAILED_DB=true,清理建库失败残留..."
[ -e "${ORACLE_BASE}/admin/${SID}" ] && rm -rf "${ORACLE_BASE}/admin/${SID}" || :
[ -e "${ORACLE_BASE}/diag/rdbms/${DB_NAME}/${SID}" ] && rm -rf "${ORACLE_BASE}/diag/rdbms/${DB_NAME}/${SID}" || :
rm -f "${ORACLE_HOME}/dbs/spfile${SID}.ora" "${ORACLE_HOME}/dbs/pw${SID}.ora" \
"${ORACLE_HOME}/dbs/orapw${SID}" "${INSTALL_DIR}/dbca_create.rsp"
if [ -e "$DATA_DIR" ]; then
BK="/data/oracle/.failed_${SID}_$(date +%Y%m%d%H%M%S)"
mkdir -p "$(dirname "${BK}")"
mv "$DATA_DIR" "$BK"
echo "旧的数据库文件目录已移到:${BK}(确认无用后再删除)"
fi
echo "残留清理完成,可以重跑建库"
fi
# 检查目标路径,防止覆盖已有数据库
FOUND_EXISTING=0

if [ -e "$ORACLE_HOME" ]; then
echo "错误:检测到已有Oracle安装目录: $ORACLE_HOME"
FOUND_EXISTING=1
fi

if [ -e "$INVENTORY" ]; then
echo "错误:检测到已有Inventory目录: $INVENTORY"
FOUND_EXISTING=1
fi

# DATA_DIR 可能尚未创建(第7节才 mkdir),不存在则无需检查
if [ -d "$DATA_DIR" ]; then
MATCHED_DIR=$(find "$DATA_DIR" -maxdepth 1 -type d -iname "$DB_NAME" -print -quit 2>/dev/null)
if [ -n "$MATCHED_DIR" ]; then
echo "错误:检测到已有数据库目录(大小写无关匹配): $MATCHED_DIR"
FOUND_EXISTING=1
fi
fi

if [ "$FOUND_EXISTING" -eq 1 ]; then
echo "请检查以上路径。"
echo "为防止覆盖已有数据,脚本停止执行。"
exit 1
fi

if id "$ORACLE_USER" >/dev/null 2>&1; then
echo "错误:oracle用户已经存在,请先检查现有环境"
exit 1
fi

if ss -lnt | grep -q ":${LISTENER_PORT} "; then
echo "错误:端口1521已经被占用"
exit 1
fi

echo "系统检查通过"
echo "内存:${MEM}MB"
echo "/u01可用空间:${U01_AVAIL}MB"
echo "/data可用空间:${DATA_AVAIL}MB"

# ==================================================
# 4. 生成随机数据库密码
# ==================================================

command -v openssl >/dev/null 2>&1 || {
echo "错误:缺少openssl"
exit 1
}

DB_PASSWORD="Cyhis#$(openssl rand -hex 12)"

if [ "${#DB_PASSWORD}" -lt 20 ]; then
echo "错误:密码生成失败"
exit 1
fi

cat > "$PASSWORD_FILE" <<EOF
========================================
Oracle Database 12c 登录信息
========================================
数据库名称:${DB_NAME}
实例SID:${SID}
SYS用户名:SYS
SYS密码:${DB_PASSWORD}
SYSTEM用户名:SYSTEM
SYSTEM密码:${DB_PASSWORD}
监听端口:${LISTENER_PORT}
========================================
EOF

chmod 600 "$PASSWORD_FILE"

# ==================================================
# 5. 安装依赖
# ==================================================

echo "安装Oracle依赖包..."

yum install -y \
binutils \
compat-libcap1 \
compat-libstdc++-33 \
gcc \
gcc-c++ \
glibc \
glibc-devel \
ksh \
libaio \
libaio-devel \
libgcc \
libstdc++ \
libstdc++-devel \
libXi \
libXtst \
make \
sysstat \
unixODBC \
unixODBC-devel \
unzip \
net-tools \
bc \
openssl \
which

# 检查依赖包
rpm -q binutils gcc gcc-c++ glibc glibc-devel \
libaio libaio-devel libstdc++ make unzip

# ==================================================
# 6. 创建用户和用户组
# ==================================================

echo "创建Oracle用户和用户组..."

groupadd oinstall
groupadd dba
groupadd oper
groupadd backupdba
groupadd dgdba
groupadd kmdba
groupadd racdba

useradd -g oinstall \
-G dba,oper,backupdba,dgdba,kmdba,racdba \
-d /home/oracle \
-s /bin/bash \
-m oracle

# ==================================================
# 7. 创建目录
# ==================================================

echo "创建Oracle目录..."

# 统一使用 DATA_DIR 作为数据库文件根目录
mkdir -p "$ORACLE_BASE"
mkdir -p "$ORACLE_HOME"
mkdir -p "$INVENTORY"
mkdir -p "$DATA_DIR"
mkdir -p "$INSTALL_DIR"

mkdir -p "${FRA_DEST}" # <--- 放在这里,与其他目录创建逻辑聚合


# ---- 权限设置 ----
chown -R oracle:oinstall "$ORACLE_BASE"
chown -R oracle:oinstall "$INVENTORY" # 【修复】原脚本遗漏了 INVENTORY 的 chown
chown -R oracle:oinstall "$DATA_DIR"
chown -R oracle:oinstall "$INSTALL_DIR"

chmod 775 "$ORACLE_BASE"
chmod 775 "$INVENTORY" # 【修复】原脚本遗漏了 INVENTORY 的 chmod
chmod 775 "$DATA_DIR"

chown -R oracle:oinstall "${FRA_DEST}"
chmod 775 "${FRA_DEST}"

# ---- 【关键修复】确保所有父目录对 oracle 用户可遍历 ----
# 即使 umask 已改为 022,如果 /u01 等目录在脚本运行前已存在且权限为 700,
# 仍需显式修正。逐级向上检查并修正,直到根目录或权限已足够的目录。
fix_parent_traverse() {
local target_dir="$1"
local parent
parent="$(dirname "$target_dir")"
while [ "$parent" != "/" ] && [ "$parent" != "." ]; do
# 确保 owner+group+others 都有 execute(穿越) 权限
# 最安全的方式:设为 755,不改变属主
local perms
perms=$(stat -c '%a' "$parent" 2>/dev/null || echo "000")
# 如果 others 位缺少 x(如 700、750、710 等),修正为 755
if [ $(( 0$perms & 001 )) -eq 0 ]; then
chmod 755 "$parent"
echo " 修正父目录权限: $parent ($perms -> 755)"
else
break # 当前目录已可穿越,上层也必然可穿越(否则到不了这里)
fi
parent="$(dirname "$parent")"
done
}

echo "检查并修正父目录穿越权限..."
fix_parent_traverse "$ORACLE_BASE"
fix_parent_traverse "$INVENTORY"
fix_parent_traverse "$DATA_DIR"
fix_parent_traverse "$INSTALL_DIR"

# 验证:以 oracle 用户测试是否能到达目标目录
if ! su - oracle -c "test -d '$ORACLE_BASE' && test -w '$ORACLE_BASE'"; then
echo "错误:oracle 用户无法访问 ORACLE_BASE ($ORACLE_BASE)"
ls -ld "$(dirname "$ORACLE_BASE")" "$ORACLE_BASE"
exit 1
fi

if ! su - oracle -c "test -d '$INVENTORY' && test -w '$INVENTORY'"; then
echo "错误:oracle 用户无法访问 INVENTORY ($INVENTORY)"
ls -ld "$(dirname "$INVENTORY")" "$INVENTORY"
exit 1
fi

echo "目录创建及权限设置完成"

# ==================================================
# 8. 配置内核参数
# ==================================================

echo "配置内核参数..."

cat > /etc/sysctl.d/99-oracle.conf <<EOF
fs.aio-max-nr = 1048576
fs.file-max = 6815744
kernel.shmall = ${SHMALL_PAGES}
kernel.shmmax = ${SHMMAX_BYTES}
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
EOF

sysctl --system

# ==================================================
# 9. 配置用户资源限制
# ==================================================

cat > /etc/security/limits.d/oracle.conf <<EOF
oracle soft nofile 1024
oracle hard nofile 65536
oracle soft nproc 16384
oracle hard nproc 16384
oracle soft stack 10240
oracle hard stack 32768
oracle soft memlock ${MEMLOCK_KB}
oracle hard memlock ${MEMLOCK_KB}
EOF

# ==================================================
# Oracle环境变量
# ==================================================

cat > /home/oracle/.bash_profile <<EOF
# Oracle Environment

export ORACLE_BASE=${ORACLE_BASE}
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}

export PATH=\$ORACLE_HOME/bin:\$PATH
export LD_LIBRARY_PATH=\$ORACLE_HOME/lib:/lib:/usr/lib

export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
export CV_ASSUME_DISTID=OEL7.1

# Oracle用户命令提示符
export PS1='[\u@\h \W]\$ '

EOF

chown oracle:oinstall /home/oracle/.bash_profile
chmod 644 /home/oracle/.bash_profile

# ==================================================
# 11. 解压Oracle安装介质
# ==================================================

echo "检查Oracle安装介质..."

INSTALL_SOURCE="${INSTALL_DIR}/database"

if [ -x "${INSTALL_SOURCE}/runInstaller" ]; then

echo "检测到Oracle安装包已经解压,跳过解压步骤。"

else

echo "开始解压Oracle安装包..."

mkdir -p "$INSTALL_DIR"
chown oracle:oinstall "$INSTALL_DIR"

su - oracle -c "
set -e
cd ${INSTALL_DIR}
unzip -oq ${SOFT_DIR}/${ZIP1}
unzip -oq ${SOFT_DIR}/${ZIP2}
"

fi

if [ ! -x "${INSTALL_SOURCE}/runInstaller" ]; then
echo "错误:Oracle安装介质解压失败,未找到runInstaller"
exit 1
fi

echo "Oracle安装介质检查通过。"

# ==================================================
# 12. 创建Oracle软件安装响应文件(修正版)
# ==================================================

echo "创建Oracle 12.1.0.2安装响应文件..."

cat > "${INSTALL_DIR}/db_install.rsp" <<EOF
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v12.1.0

oracle.install.option=INSTALL_DB_SWONLY

ORACLE_HOSTNAME=$(hostname -f)
UNIX_GROUP_NAME=oinstall
INVENTORY_LOCATION=${INVENTORY}
SELECTED_LANGUAGES=en

ORACLE_HOME=${ORACLE_HOME}
ORACLE_BASE=${ORACLE_BASE}

oracle.install.db.InstallEdition=EE

oracle.install.db.DBA_GROUP=dba
oracle.install.db.OPER_GROUP=oper
oracle.install.db.BACKUPDBA_GROUP=backupdba
oracle.install.db.DGDBA_GROUP=dgdba
oracle.install.db.KMDBA_GROUP=kmdba

SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
DECLINE_SECURITY_UPDATES=true
EOF

chown oracle:oinstall "${INSTALL_DIR}/db_install.rsp"
chmod 600 "${INSTALL_DIR}/db_install.rsp"

echo "Oracle安装响应文件创建完成"

echo "检查响应文件关键参数..."

grep -E \
'responseFileVersion|INSTALL_DB_SWONLY|InstallEdition|DBA_GROUP|OPER_GROUP|BACKUPDBA_GROUP|DGDBA_GROUP|KMDBA_GROUP' \
"${INSTALL_DIR}/db_install.rsp"

# ==================================================
# 13. 静默安装Oracle数据库软件
# ==================================================

echo "开始安装Oracle数据库软件..."

su - oracle -c "
export CV_ASSUME_DISTID=OEL7.1
cd ${INSTALL_SOURCE}

./runInstaller \
-silent \
-ignorePrereq \
-ignoreSysPrereqs \
-waitforcompletion \
-responseFile ${INSTALL_DIR}/db_install.rsp
"

# 检查软件安装结果
if [ ! -x "$ORACLE_HOME/bin/sqlplus" ]; then
echo "错误:Oracle软件安装失败"
exit 1
fi

# ==================================================
# 14. 执行Oracle root脚本
# ==================================================

echo "执行Oracle root脚本..."

if [ -f "$INVENTORY/orainstRoot.sh" ]; then
"$INVENTORY/orainstRoot.sh"
fi

if [ -f "$ORACLE_HOME/root.sh" ]; then
"$ORACLE_HOME/root.sh"
fi

# ==================================================
# 15. 配置监听器
# ==================================================

echo "配置Oracle监听器..."

su - oracle -c "
export ORACLE_BASE=${ORACLE_BASE}
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH
export CV_ASSUME_DISTID=OEL7.1

netca -silent \
-responseFile ${ORACLE_HOME}/assistants/netca/netca.rsp
"

# ==================================================
# 16. 创建数据库
# ==================================================

echo "开始创建Oracle数据库..."

DBCA_RSP="${INSTALL_DIR}/dbca_create.rsp"

cat > "$DBCA_RSP" <<EOF
[GENERAL]
RESPONSEFILE_VERSION = "12.1.0"
OPERATION_TYPE = "createDatabase"

[CREATEDATABASE]
GDBNAME = "${DB_NAME}"
SID = "${SID}"
TEMPLATENAME = "General_Purpose.dbc"

SYSPASSWORD = "${DB_PASSWORD}"
SYSTEMPASSWORD = "${DB_PASSWORD}"

DATAFILEDESTINATION = "${DATA_DIR}"
STORAGETYPE = "FS"

CHARACTERSET = "ZHS16GBK"
NATIONALCHARACTERSET = "AL16UTF16"

DATABASETYPE = "OLTP"
EMCONFIGURATION = "NONE"

CREATEASCONTAINERDATABASE = "false"

# 【新增】闪回恢复区配置
# RECOVERYAREASIZE 单位为 MB,对应 db_recovery_file_dest_size
# RECOVERYAREADESTINATION 对应 db_recovery_file_dest
RECOVERYAREASIZE = "${FRA_SIZE_MB}"
RECOVERYAREADESTINATION = "${FRA_DEST}"

# AMM 关闭,并由 DBCA 自己决定 SGA 大小:
# automaticMemoryManagement=FALSE 时 totalMemory 被 DBCA 当作 SGA,
# 它会同时写入 sga_target 与 sga_max_size(相等),建库阶段参数必定自洽,
# 不会出现 ORA-00823: Specified value of sga_target greater than sga_max_size。
# 精确目标值(SGA ${SGA_TARGET_MB}MB / PGA ${PGA_AGGREGATE_TARGET_MB}MB)由步骤 16.5 建库后落地。
AUTOMATICMEMORYMANAGEMENT = "FALSE"
TOTALMEMORY = "${DBCA_TOTALMEMORY_MB}"
EOF

chown oracle:oinstall "$DBCA_RSP"
chmod 600 "$DBCA_RSP"

echo "DBCA响应文件创建完成"

# 检查内存参数
echo "DBCA建库内存入口:rsp TOTALMEMORY=${DBCA_TOTALMEMORY_MB}MB(AMM=FALSE,由 DBCA 生成自洽的 sga_target/sga_max_size)"
echo "DBCA -initParams(仅非内存参数):${INIT_PARAMS}"
echo "建库后 16.5 精调为:SGA_TARGET=${SGA_TARGET_MB}MB / SGA_MAX_SIZE=${SGA_MAX_SIZE_MB}MB / PGA=${PGA_AGGREGATE_TARGET_MB}MB"
echo "REDO_SIZE_MB=${REDO_SIZE_MB}(-redoLogFileSize 按 MB 传,不再传字节)"

echo "开始执行DBCA..."

set +e

su - oracle -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
export LD_LIBRARY_PATH='${ORACLE_HOME}/lib'
export CV_ASSUME_DISTID=OEL7.1

dbca -silent \
-createDatabase \
-responseFile '${DBCA_RSP}' \
-sysPassword '${DB_PASSWORD}' \
-systemPassword '${DB_PASSWORD}' \
-redoLogFileSize '${REDO_SIZE_MB}' \
-initParams '${INIT_PARAMS}'
"

DBCA_RC=$?

set -e

if [ "$DBCA_RC" -ne 0 ]; then
echo "=========================================="
echo "错误:DBCA数据库创建失败"
echo "返回码:${DBCA_RC}"
echo "DBCA日志:"
echo "${ORACLE_BASE}/cfgtoollogs/dbca/${SID}/${SID}.log"
echo "=========================================="
exit 1
fi

echo "DBCA数据库创建成功"

# ==================================================

# ==================================================
# 16.5 建库后内存及进程参数校验与兜底修正
# ==================================================

echo "校验实例内存及进程参数..."

# 一次取回 5 个参数的当前值
MEM_NOW=$(su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba <<'SQL'
SET HEADING OFF FEEDBACK OFF VERIFY OFF PAGESIZE 0 LINESIZE 200
SELECT name||'='||TO_NUMBER(value) FROM V\$parameter
WHERE name IN ('sga_target','sga_max_size','pga_aggregate_target','memory_target','processes');
EXIT;
SQL
" | grep -E '^[a-z_]+=[0-9]+' | tr -d '[:space:]') || MEM_NOW=""

echo "当前参数:${MEM_NOW}"
echo "期望参数:sga_target=${SGA_TARGET_BYTES} sga_max_size=${SGA_MAX_SIZE_BYTES} pga=${PGA_AGGREGATE_TARGET_BYTES} mem_target=0 processes=${PROCESSES}"

NEED_FIX=0
for item in sga_target sga_max_size pga_aggregate_target memory_target processes; do
case "${item}" in
sga_target) want=${SGA_TARGET_BYTES} ;;
sga_max_size) want=${SGA_MAX_SIZE_BYTES} ;;
pga_aggregate_target) want=${PGA_AGGREGATE_TARGET_BYTES} ;;
memory_target) want=0 ;;
processes) want=${PROCESSES} ;;
esac
cur=$(echo "${MEM_NOW}" | grep -o "${item}=[0-9]*" | tail -1 | cut -d= -f2)
if [ "${cur:-NONE}" != "${want}" ]; then
echo " -> ${item} 当前=${cur:-未生效} 期望=${want},需要修正"
NEED_FIX=1
fi
done

if [ "${NEED_FIX}" -eq 1 ]; then
echo "参数未按预期落地,执行 ALTER SYSTEM ... SCOPE=SPFILE 并重启实例..."
su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE
ALTER SYSTEM SET sga_max_size=${SGA_MAX_SIZE_BYTES} SCOPE=SPFILE;
ALTER SYSTEM SET sga_target=${SGA_TARGET_BYTES} SCOPE=SPFILE;
ALTER SYSTEM SET pga_aggregate_target=${PGA_AGGREGATE_TARGET_BYTES} SCOPE=SPFILE;
ALTER SYSTEM SET memory_target=0 SCOPE=SPFILE;
ALTER SYSTEM SET memory_max_target=0 SCOPE=SPFILE;
ALTER SYSTEM SET processes=${PROCESSES} SCOPE=SPFILE;
SHUTDOWN IMMEDIATE;
STARTUP;
EXIT;
SQL
"
else
echo "参数校验通过,无需修正"
fi

# 硬性确认 sga_max_size >= sga_target
FINAL_MEM_CHK=$(su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH
sqlplus -s / as sysdba <<'SQL'
SET HEADING OFF FEEDBACK OFF VERIFY OFF PAGESIZE 0
SELECT CASE WHEN TO_NUMBER(a.value) >= TO_NUMBER(b.value)
THEN 'MEM_OK' ELSE 'MEM_BAD' END
FROM V\$parameter a, V\$parameter b
WHERE a.name='sga_max_size' AND b.name='sga_target';
EXIT;
SQL
")
echo "内存自洽检查结果:${FINAL_MEM_CHK}"
case "${FINAL_MEM_CHK}" in
*MEM_OK*) : ;;
*) echo "错误:sga_max_size 仍小于 sga_target,请人工检查 spfile"; exit 1 ;;
esac




# ==================================================
# 17. 等待数据库启动
# ==================================================

echo "等待数据库启动..."

for i in $(seq 1 60); do
if su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH
sqlplus -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT 1
SELECT STATUS FROM V\$INSTANCE;
EXIT;
SQL
" >/dev/null 2>&1; then
break
fi

sleep 10
done

if [ "$i" -eq 60 ]; then
echo "错误:等待数据库启动超时,请检查告警日志"
echo "${ORACLE_BASE}/diag/rdbms/*/${SID}/trace/alert_${SID}.log"
exit 1
fi


# ==================================================
# 18. 检查并补齐8组Redo日志(最终稳定版)
#
# 操作系统执行用户:root
# Oracle SQL执行用户:oracle
#
# Oracle版本:12.1.0.2
# SID:cyhis
# Redo日志组:8组
# 每组大小:1024MB
#
# 特点:
# 1. 不检查oracle用户及用户组
# 2. 不创建额外Redo目录
# 3. 使用数据库现有Redo目录
# 4. 不使用PL/SQL
# 5. 不删除已有Redo日志
# 6. 已有日志组不重复创建
# 7. 最终检查数量及大小
# ==================================================

echo
echo "=========================================="
echo "开始配置Oracle Redo日志"
echo "=========================================="

REDO_SQL="${INSTALL_DIR}/redo_config.sql"
REDO_QUERY_SQL="${INSTALL_DIR}/redo_query.sql"
REDO_ADD_SQL="${INSTALL_DIR}/redo_add.sql"
REDO_MEMBER_SQL="${INSTALL_DIR}/redo_member.sql"

REDO_GROUPS=8
REDO_SIZE_MB=1024
REDO_SIZE_BYTES=$((REDO_SIZE_MB * 1024 * 1024))

# --------------------------------------------------
# 18.1 SQLPlus执行函数
# --------------------------------------------------

run_oracle_sql() {

local SQL_FILE="$1"

if [ ! -f "${SQL_FILE}" ]; then
echo "错误:SQL文件不存在:${SQL_FILE}"
return 1
fi

su - oracle -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH
export LD_LIBRARY_PATH='${ORACLE_HOME}/lib'

sqlplus -L -s / as sysdba @'${SQL_FILE}'
"
}

# --------------------------------------------------
# 18.2 检查数据库状态
# --------------------------------------------------

echo
echo "检查Oracle数据库连接..."

DB_STATE_SQL="${INSTALL_DIR}/redo_db_state.sql"

cat > "${DB_STATE_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET PAGESIZE 0

SELECT STATUS FROM V$INSTANCE;

EXIT;
SQL

chown oracle:oinstall "${DB_STATE_SQL}"
chmod 600 "${DB_STATE_SQL}"

set +e

DB_STATE=$(run_oracle_sql "${DB_STATE_SQL}" 2>&1)
DB_STATE_RC=$?

set -e

if [ "${DB_STATE_RC}" -ne 0 ]; then

echo "错误:无法连接Oracle数据库"
echo "${DB_STATE}"

exit 1

fi

DB_STATE=$(echo "${DB_STATE}" | tr -d '[:space:]')

if [ "${DB_STATE}" != "OPEN" ]; then

echo "错误:数据库没有处于OPEN状态"
echo "当前状态:${DB_STATE}"

exit 1

fi

echo "数据库连接成功"
echo "数据库状态:${DB_STATE}"

# --------------------------------------------------
# 18.3 创建Redo查询SQL
# --------------------------------------------------

cat > "${REDO_QUERY_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET ECHO OFF
SET PAGESIZE 0
SET LINESIZE 200
SET TRIMSPOOL ON

SELECT
COUNT(*) || '|' || NVL(MAX(GROUP#), 0)
FROM V$LOG;

EXIT;
SQL

chown oracle:oinstall "${REDO_QUERY_SQL}"
chmod 600 "${REDO_QUERY_SQL}"

# --------------------------------------------------
# 18.4 查询当前Redo日志组数量
# --------------------------------------------------

echo
echo "查询当前Redo日志组数量..."

set +e

REDO_INFO=$(run_oracle_sql "${REDO_QUERY_SQL}" 2>&1)
REDO_QUERY_RC=$?

set -e

if [ "${REDO_QUERY_RC}" -ne 0 ]; then

echo "错误:查询Redo日志失败"
echo "${REDO_INFO}"

exit 1

fi

REDO_INFO=$(echo "${REDO_INFO}" | tr -d '[:space:]')

if [[ ! "${REDO_INFO}" =~ ^[0-9]+\|[0-9]+$ ]]; then

echo "错误:Redo日志查询结果无法解析"
echo "实际结果:${REDO_INFO}"

exit 1

fi

IFS='|' read -r CURRENT_GROUPS MAX_GROUP <<< "${REDO_INFO}"

echo "当前Redo日志组数量:${CURRENT_GROUPS}"
echo "当前最大日志组编号:${MAX_GROUP}"

# --------------------------------------------------
# 18.5 检查日志组数量
# --------------------------------------------------

if [ "${CURRENT_GROUPS}" -gt "${REDO_GROUPS}" ]; then

echo "错误:当前Redo日志组数量超过目标8组"
exit 1

fi

# --------------------------------------------------
# 18.6 查询现有Redo日志成员路径
# --------------------------------------------------

cat > "${REDO_MEMBER_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET PAGESIZE 0
SET LINESIZE 500

SELECT MEMBER
FROM V$LOGFILE
WHERE TYPE = 'ONLINE'
AND MEMBER IS NOT NULL
AND ROWNUM = 1;

EXIT;
SQL

chown oracle:oinstall "${REDO_MEMBER_SQL}"
chmod 600 "${REDO_MEMBER_SQL}"

# --------------------------------------------------
# 18.7 循环补齐Redo日志组
# --------------------------------------------------

while [ "${CURRENT_GROUPS}" -lt "${REDO_GROUPS}" ]; do

NEXT_GROUP=$((MAX_GROUP + 1))

echo
echo "=========================================="
echo "准备创建Redo日志组:${NEXT_GROUP}"
echo "=========================================="

# 查询现有Redo日志文件目录
set +e

REDO_MEMBER=$(run_oracle_sql "${REDO_MEMBER_SQL}" 2>&1)
MEMBER_RC=$?

set -e

if [ "${MEMBER_RC}" -ne 0 ]; then

echo "错误:无法查询现有Redo日志路径"
echo "${REDO_MEMBER}"

exit 1

fi

REDO_MEMBER=$(echo "${REDO_MEMBER}" | tr -d '[:space:]')

if [ -z "${REDO_MEMBER}" ]; then

echo "错误:没有找到现有Redo日志成员"
exit 1

fi

REDO_EXISTING_DIR=$(dirname "${REDO_MEMBER}")

REDO_FILE="${REDO_EXISTING_DIR}/redo${NEXT_GROUP}.log"

echo "现有Redo目录:${REDO_EXISTING_DIR}"
echo "新Redo文件:${REDO_FILE}"
echo "Redo大小:${REDO_SIZE_MB}MB"

# 防止覆盖已有文件
if [ -e "${REDO_FILE}" ]; then

echo "错误:Redo文件已经存在"
echo "${REDO_FILE}"
echo "停止执行,避免覆盖现有数据库文件"

exit 1

fi

# --------------------------------------------------
# 生成新增Redo日志SQL
# --------------------------------------------------

cat > "${REDO_ADD_SQL}" <<SQLEOF
WHENEVER OSERROR EXIT FAILURE
WHENEVER SQLERROR EXIT SQL.SQLCODE

ALTER DATABASE ADD LOGFILE GROUP ${NEXT_GROUP}
('${REDO_FILE}')
SIZE ${REDO_SIZE_MB}M;

EXIT;
SQLEOF

chown oracle:oinstall "${REDO_ADD_SQL}"
chmod 600 "${REDO_ADD_SQL}"

# --------------------------------------------------
# 执行新增Redo日志
# --------------------------------------------------

echo "执行Redo日志创建SQL..."

set +e

ADD_RESULT=$(run_oracle_sql "${REDO_ADD_SQL}" 2>&1)
ADD_RC=$?

set -e

if [ "${ADD_RC}" -ne 0 ]; then

echo "=========================================="
echo "错误:Redo日志创建失败"
echo "日志组:${NEXT_GROUP}"
echo "返回码:${ADD_RC}"
echo "${ADD_RESULT}"
echo "=========================================="

exit 1

fi

echo "${ADD_RESULT}"

# --------------------------------------------------
# 重新查询数据库实际日志组数量
# --------------------------------------------------

set +e

REDO_INFO=$(run_oracle_sql "${REDO_QUERY_SQL}" 2>&1)
REDO_QUERY_RC=$?

set -e

if [ "${REDO_QUERY_RC}" -ne 0 ]; then

echo "错误:新增后重新查询Redo日志失败"
echo "${REDO_INFO}"

exit 1

fi

REDO_INFO=$(echo "${REDO_INFO}" | tr -d '[:space:]')

if [[ ! "${REDO_INFO}" =~ ^[0-9]+\|[0-9]+$ ]]; then

echo "错误:Redo日志查询结果格式异常"
echo "${REDO_INFO}"

exit 1

fi

IFS='|' read -r CURRENT_GROUPS MAX_GROUP <<< "${REDO_INFO}"

echo "当前Redo日志组数量:${CURRENT_GROUPS}"

done

# --------------------------------------------------
# 18.8 创建最终Redo检查SQL
# --------------------------------------------------

cat > "${REDO_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING ON
SET FEEDBACK ON
SET VERIFY OFF
SET LINESIZE 200
SET PAGESIZE 100

PROMPT
PROMPT ===== CURRENT REDO LOG STATUS =====

SELECT
GROUP#,
ROUND(BYTES / 1024 / 1024) AS SIZE_MB,
MEMBERS,
STATUS,
ARCHIVED
FROM V$LOG
ORDER BY GROUP#;

PROMPT
PROMPT ===== REDO LOG FILE DETAILS =====

SELECT
GROUP#,
MEMBER,
TYPE,
STATUS
FROM V$LOGFILE
ORDER BY GROUP#, MEMBER;

PROMPT
PROMPT ===== REDO VALIDATION =====

SELECT
COUNT(*) AS TOTAL_GROUPS,
SUM(
CASE
WHEN BYTES = 1073741824 THEN 1
ELSE 0
END
) AS GROUPS_1024MB
FROM V$LOG;

PROMPT
PROMPT ===== FINAL RESULT =====

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0

SELECT
CASE
WHEN COUNT(*) = 8
AND SUM(
CASE
WHEN BYTES = 1073741824 THEN 1
ELSE 0
END
) = 8
THEN 'REDO_OK'
ELSE 'REDO_BAD'
END
FROM V$LOG;

EXIT;
SQL

chown oracle:oinstall "${REDO_SQL}"
chmod 600 "${REDO_SQL}"

# --------------------------------------------------
# 18.9 执行最终Redo校验
# --------------------------------------------------

echo
echo "=========================================="
echo "执行Redo日志最终校验"
echo "=========================================="

set +e

FINAL_REDO_RESULT=$(run_oracle_sql "${REDO_SQL}" 2>&1)
FINAL_REDO_RC=$?

set -e

echo "${FINAL_REDO_RESULT}"

if [ "${FINAL_REDO_RC}" -ne 0 ]; then

echo "错误:Redo最终SQL校验失败"
exit 1

fi

if ! echo "${FINAL_REDO_RESULT}" | grep -q 'REDO_OK'; then

echo "错误:Redo日志数量或大小校验未通过"
exit 1

fi

# --------------------------------------------------
# 18.10 完成
# --------------------------------------------------

echo
echo "=========================================="
echo "Oracle Redo日志配置成功"
echo "=========================================="

echo "数据库SID : ${SID}"
echo "Redo日志组数量 : ${REDO_GROUPS}"
echo "每组Redo大小 : ${REDO_SIZE_MB}MB"
echo "Redo总容量 : $((REDO_GROUPS * REDO_SIZE_MB))MB"

echo "=========================================="




# ==================================================
# 19. 检查数据库及字符集
# ==================================================

echo "执行数据库最终检查..."

CHECK_SQL="${INSTALL_DIR}/final_check.sql"

cat > "$CHECK_SQL" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200
SET PAGESIZE 100

PROMPT ===== INSTANCE =====

SELECT INSTANCE_NAME, STATUS
FROM V$INSTANCE;

PROMPT ===== DATABASE =====

SELECT NAME, OPEN_MODE, CDB
FROM V$DATABASE;

PROMPT ===== DB_UNIQUE_NAME =====

SHOW PARAMETER db_unique_name;

PROMPT ===== MEMORY =====

SHOW PARAMETER SGA_TARGET
SHOW PARAMETER PGA_AGGREGATE_TARGET

PROMPT ===== FAST RECOVERY AREA =====
SELECT NAME,
SPACE_LIMIT/1024/1024 AS LIMIT_MB,
SPACE_USED/1024/1024 AS USED_MB,
SPACE_RECLAIMABLE/1024/1024 AS RECLAIMABLE_MB
FROM V$RECOVERY_FILE_DEST;

SHOW PARAMETER db_recovery_file_dest;
SHOW PARAMETER db_recovery_file_dest_size;

PROMPT ===== CHARACTERSET =====

SELECT PARAMETER, VALUE
FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER IN (
'NLS_CHARACTERSET',
'NLS_NCHAR_CHARACTERSET'
);

PROMPT ===== REDO GROUPS =====

SELECT GROUP#,
BYTES/1024/1024 AS SIZE_MB,
STATUS
FROM V$LOG
ORDER BY GROUP#;

PROMPT ===== REDO COUNT =====

SELECT COUNT(*) AS REDO_GROUP_COUNT
FROM V$LOG;

PROMPT ===== DATAFILES =====

SELECT NAME
FROM V$DATAFILE;

EXIT;
SQL

chown oracle:oinstall "$CHECK_SQL"
chmod 600 "$CHECK_SQL"

su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba @${CHECK_SQL}
"

# ==================================================
# 20. 最终检查
# ==================================================

echo "检查数据库状态..."

DB_STATUS=$(su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba <<'SQL'
SET HEADING OFF FEEDBACK OFF
WHENEVER SQLERROR EXIT 1
SELECT STATUS FROM V\$INSTANCE;
EXIT;
SQL
" | tr -d '[:space:]')

if [ "$DB_STATUS" != "OPEN" ]; then
echo "错误:数据库没有处于OPEN状态"
exit 1
fi

REDO_COUNT=$(su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba <<'SQL'
SET HEADING OFF FEEDBACK OFF
SELECT COUNT(*) FROM V\$LOG;
EXIT;
SQL
" | grep -E '^[[:space:]]*[0-9]+' | tail -1 | tr -d '[:space:]')

if [ "$REDO_COUNT" != "8" ]; then
echo "错误:Redo日志组数量不是8"
exit 1
fi

# 检查所有Redo大小
BAD_REDO=$(su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export PATH=${ORACLE_HOME}/bin:\$PATH

sqlplus -s / as sysdba <<'SQL'
SET HEADING OFF FEEDBACK OFF
SELECT COUNT(*) FROM V\$LOG
WHERE BYTES <> 1073741824;
EXIT;
SQL
" | grep -E '^[[:space:]]*[0-9]+' | tail -1 | tr -d '[:space:]')

if [ "$BAD_REDO" != "0" ]; then
echo "错误:存在大小不为1GB的Redo日志组"
exit 1
fi

# 检查监听器
su - oracle -c "
export ORACLE_HOME=${ORACLE_HOME}
export PATH=${ORACLE_HOME}/bin:\$PATH
lsnrctl status
"

# ==================================================
# 21. 安装完成
# ==================================================

echo
echo "================================================="
echo "Oracle 12c 安装流程完成"
echo "================================================="

echo "SID : ${SID}"
echo "DB_NAME : ${DB_NAME}"
echo "Oracle Version:12.1.0.2 EE"
echo "DATABASE STATUS : ${DB_STATUS}"
echo "ORACLE_BASE : ${ORACLE_BASE}"
echo "ORACLE_HOME : ${ORACLE_HOME}"
echo "DATA DIRECTORY : ${DATA_DIR}"
echo "MEMORY MANAGEMENT : ASMM(memory_target=0 + sga_target>0)"
echo "SGA TARGET : ${SGA_TARGET_MB}MB"
echo "SGA MAX SIZE : ${SGA_MAX_SIZE_MB}MB"
echo "PGA AGGREGATE TARGET : ${PGA_AGGREGATE_TARGET_MB}MB"
echo "FRA SIZE : ${FRA_SIZE_MB}MB"
echo "kernel.shmmax : ${SHMMAX_BYTES}"
echo "SGA MANAGEMENT : AUTO"
echo "PGA MANAGEMENT : AUTO"
echo "SHARED POOL : ORACLE AUTO MANAGEMENT"
echo "BUFFER CACHE : ORACLE AUTO MANAGEMENT"
echo "CHARACTERSET : ZHS16GBK"
echo "NATIONAL CHARSET : AL16UTF16"
echo "REDO : 8组,每组1024MB"
echo
echo "密码文件:${PASSWORD_FILE}"
echo "安装日志:${LOG}"

echo "================================================="
echo "完成时间:$(date)"
echo "================================================="

🔔温馨提示:

​ 脚本之所以必须在建库后(步骤 16.5)二次修改内存参数,不是因为你开头没指定,而是因为 Oracle 12.1.0.2 DBCA 在静默安装模式下的机制缺陷。

📌 根据脚本注释中记录的实测经验及 Oracle 12c DBCA 的行为特性,原因如下:

核心冲突:DBCA 的“自洽”机制 vs** -initParams 追加机制

在 Oracle 12.1.0.2 中,当 AUTOMATICMEMORYMANAGEMENT="FALSE" 时,DBCA 的内存处理逻辑是分两步走的,且这两步互不感知:

  • 第一步(模板推导):DBCA 读取响应文件中的 TOTALMEMORY,结合 General_Purpose.dbc 模板,自行计算并写入一套它认为“自洽”的内存参数到初始 spfile/pfile 中。此时它会强制设置 sga_max_size = sga_target = TOTALMEMORY。
  • 第二步(参数追加):DBCA 处理 -initParams 或响应文件中的其他初始化参数。这些参数是以 APPEND(追加) 方式写入配置文件的。

致命问题在于:如果你在 -initParams 中同时指定了 sga_target=8G 和 sga_max_size=8G,DBCA 不会覆盖第一步生成的值,而是将新值追加到文件末尾。但由于 12.1 DBCA 的 Bug 或设计限制,**sga_max_size 的追加往往不生效或被忽略**,导致最终 spfile 中出现:

1
2
3
4
5
6
7
# DBCA 第一步生成(偏小值)
sga_max_size=4G
sga_target=4G

# -initParams 追加(仅 target 生效)
sga_target=8G
# sga_max_size=8G ← 这行可能丢失或未生效

结果就是 sga_target(8G) > sga_max_size(4G),实例启动直接报 ORA-00823,建库失败。

📌 为什么不在 RSP 里直接指定精确值?

你确实可以在 RSP 的 [CREATEDATABASE] 段或通过 -initParams 传递这些值,但如上所述,传了反而会导致建库失败。

你脚本采用的策略是当前 12.1.0.2 版本下最稳健的工程实践:

  • 建库阶段:只给 DBCA 一个它能完美处理的 TOTALMEMORY,让它生成一套自洽的参数,确保实例能成功 NOMOUNT/MOUNT/OPEN。此阶段的目标是“建库成功”,而非“参数精确”。
  • 建库后阶段(16.5):数据库已 OPEN,通过 ALTER SYSTEM SET ... SCOPE=SPFILE 精确设置目标值,然后重启实例。此阶段的目标是“参数落地”

📌 总结

阶段 做了什么 为什么这么做
开头变量定义 定义了 SGA_TARGET_MB 等精确值 作为最终目标和校验基准
DBCA RSP 只传 TOTALMEMORY=${SGA_TARGET_MB} 让 DBCA 生成自洽参数,避免 ORA-00823
步骤 16.5 ALTER SYSTEM 精确设置 + 重启 绕过 DBCA 缺陷,确保生产参数准确

结论:你的脚本逻辑是正确的。在 Oracle 12.1.0.2 上,试图通过 DBCA 一次性精确设定 SGA/PGA 参数是不可靠的;分两阶段处理是经过验证的最佳实践。如果未来升级到 19c 或更高版本,DBCA 的内存处理逻辑已有改进,届时可重新评估是否仍需此兜底步骤。

二、卸载Oracle数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
sqlplus -s / as sysdba <<EOF
shutdown immediate;
exit;
EOF

ps -ef|grep tnslsnr
pkill tnslsnr

userdel -r oracle
groupdel oinstall
groupdel dba
groupdel oper
groupdel dgdba
groupdel kmdba
groupdel racdba
groupdel backupdba

rm -rf /u01/
rm -rf /data/oracle

rm -f /etc/oraInst.loc
rm -f /etc/oratab
rm -f /usr/local/bin/oraenv
rm -f /usr/local/bin/coraenv
rm -f /usr/local/bin/dbhome
rm -rf /tmp/OraInstall*
rm -rf /tmp/deinstall*
rm -rf /var/tmp/.oracle
rm -rf /tmp/.oracle



参考:
# 删除 Oracle Base 及 Home 目录
rm -rf /u01/app/oracle # ORACLE_BASE,按实际修改
rm -rf /opt/oracle # 如存在其他安装路径

# 删除 Central Inventory(关键,否则重装会报错)
rm -rf /u01/app/oraInventory # 路径以 /etc/oraInst.loc 中记录为准

# 删除系统级配置文件
rm -f /etc/oraInst.loc
rm -f /etc/oratab

# 删除全局命令链接
rm -f /usr/local/bin/oraenv
rm -f /usr/local/bin/coraenv
rm -f /usr/local/bin/dbhome

# 删除临时安装文件
rm -rf /tmp/OraInstall*
rm -rf /tmp/deinstall*
rm -rf /var/tmp/.oracle
rm -rf /tmp/.oracle

# 查看并清理残留 IPC 资源
ipcs -m | grep oracle # 找到对应 shmid
ipcrm -m <shmid>

ipcs -s | grep oracle # 找到对应 semid
ipcrm -s <semid>

userdel -r oracle
groupdel oinstall
groupdel dba
groupdel oper
groupdel asmadmin # 如存在

#清理环境变量
编辑以下文件,移除 ORACLE_HOME、ORACLE_SID、ORACLE_BASE、TNS_ADMIN 及 PATH/LD_LIBRARY_PATH 中的 Oracle 相关条目:
/etc/profile
/etc/bashrc
~/.bash_profile(oracle 用户家目录,若未删除用户)
修改后执行 source 或重新登录使其生效。


# 确认无 Oracle 进程
ps -ef | grep -i oracle | grep -v grep

# 确认关键文件已删除
ls /etc/oratab /etc/oraInst.loc 2>&1 # 应报 No such file

# 确认环境变量已清除(新 shell 中执行)
echo $ORACLE_HOME # 应为空
which oraenv # 应无输出

三、修改数据库名

1.nid修改数据库名脚本(官方推荐)
  • 创建脚本(Oracle12.1.0.2),Oracle用户执行脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
######Oracle12.1.0.2######

#!/bin/bash
# ============================================================
# Oracle 12.1.0.2 单实例数据库改名
#
# OLD_SID = cyhis
# NEW_SID = histest
# OLD_DB_NAME = CYHIS
# NEW_DB_NAME = HISTEST
# DB_UNIQUE_NAME = histest
#
# 保留 DBID,不重建控制文件,不移动数据文件
#
# 执行用户:oracle
# 执行方式:bash rename_oracle_histest.sh
#
# 重要:
# 1. 必须先完成 RMAN 全库备份。
# 2. 执行期间禁止其他人连接数据库。
# 3. NID 操作不可视为普通配置修改。
# 4. 失败后不要直接重复运行 NID。
# ============================================================

set -Eeuo pipefail
umask 077

# ---------------- 基本配置 ----------------

OLD_SID="cyhis"
NEW_SID="histest"

NEW_DB_NAME="HISTEST"
NEW_DB_UNIQUE_NAME="histest"

ORACLE_HOME="${ORACLE_HOME:?ORACLE_HOME 未设置}"

export PATH="$ORACLE_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$ORACLE_HOME/lib"

DBS_DIR="$ORACLE_HOME/dbs"
NET_DIR="$ORACLE_HOME/network/admin"

TIMESTAMP=$(date +%Y%m%d_%H%M%S)

BACKUP_DIR="/tmp/oracle_rename_${TIMESTAMP}"
LOG="$BACKUP_DIR/rename.log"

mkdir -p "$BACKUP_DIR"

exec > >(tee -a "$LOG") 2>&1

trap 'echo "[ERROR] 第 ${LINENO} 行失败!日志:$LOG"; echo "保留现场,禁止自动删除或重复执行 NID。"' ERR

SQLPLUS="$ORACLE_HOME/bin/sqlplus"
NID="$ORACLE_HOME/bin/nid"

OLD_SPFILE="$DBS_DIR/spfile${OLD_SID}.ora"
NEW_SPFILE="$DBS_DIR/spfile${NEW_SID}.ora"

OLD_PW="$DBS_DIR/orapw${OLD_SID}"
NEW_PW="$DBS_DIR/orapw${NEW_SID}"

OLD_PFILE="$BACKUP_DIR/init_${OLD_SID}.ora"
NEW_PFILE="$BACKUP_DIR/init_${NEW_SID}.ora"

echo "================================================"
echo "Oracle 12.1.0.2 数据库改名"
echo "================================================"

# ---------------- 1. 环境检查 ----------------

[ "$(id -un)" = "oracle" ] || {
echo "错误:必须使用 oracle 用户"
exit 1
}

[ "${ORACLE_SID:-}" = "$OLD_SID" ] || {
echo "错误:当前 ORACLE_SID 必须是 $OLD_SID"
echo "当前值:${ORACLE_SID:-未设置}"
exit 1
}

[ -x "$SQLPLUS" ] || {
echo "错误:sqlplus 不存在"
exit 1
}

[ -x "$NID" ] || {
echo "错误:nid 不存在"
exit 1
}

[ -s "$OLD_SPFILE" ] || {
echo "错误:原 SPFILE 不存在:$OLD_SPFILE"
exit 1
}

echo "ORACLE_HOME=$ORACLE_HOME"
echo "ORACLE_SID=$ORACLE_SID"

# ---------------- 2. 检查数据库状态 ----------------

echo "=== 检查数据库状态 ==="

QUERY_RESULT=$(
sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET VERIFY OFF
SET ECHO OFF
SET TRIMSPOOL ON

SELECT
VERSION || '|' ||
(SELECT NAME FROM V$DATABASE) || '|' ||
(SELECT OPEN_MODE FROM V$DATABASE) || '|' ||
(SELECT DATABASE_ROLE FROM V$DATABASE) || '|' ||
(SELECT VALUE FROM V$PARAMETER WHERE NAME='cluster_database')
FROM V$INSTANCE;

EXIT;
SQL
) || {
echo "错误:无法连接数据库或读取数据库状态"
exit 1
}

QUERY_RESULT=$(echo "$QUERY_RESULT" | tr -d '\r' | sed '/^[[:space:]]*$/d' | tail -1)

IFS='|' read -r DB_VERSION CURRENT_DB_NAME OPEN_MODE DATABASE_ROLE CLUSTER_DATABASE <<< "$QUERY_RESULT"

DB_VERSION=$(echo "$DB_VERSION" | xargs)
CURRENT_DB_NAME=$(echo "$CURRENT_DB_NAME" | xargs)
OPEN_MODE=$(echo "$OPEN_MODE" | xargs)
DATABASE_ROLE=$(echo "$DATABASE_ROLE" | xargs)
CLUSTER_DATABASE=$(echo "$CLUSTER_DATABASE" | xargs)

echo "VERSION=$DB_VERSION"
echo "DB_NAME=$CURRENT_DB_NAME"
echo "OPEN_MODE=$OPEN_MODE"
echo "DATABASE_ROLE=$DATABASE_ROLE"
echo "CLUSTER_DATABASE=$CLUSTER_DATABASE"

# Oracle 版本检查
[[ "$DB_VERSION" == 12.1.0.2* ]] || {
echo "错误:当前不是 Oracle 12.1.0.2"
exit 1
}

# RAC 检查
[[ "${CLUSTER_DATABASE^^}" == "FALSE" ]] || {
echo "错误:检测到 RAC,停止执行"
exit 1
}

# 主库检查
[[ "${DATABASE_ROLE^^}" == "PRIMARY" ]] || {
echo "错误:当前不是 PRIMARY 数据库"
exit 1
}

# 数据库打开状态检查
OPEN_MODE_NORMALIZED=$(echo "$OPEN_MODE" | tr -d '[:space:]' | tr '[:lower:]' '[:upper:]')

[[ "$OPEN_MODE_NORMALIZED" == "READWRITE" ]] || {
echo "错误:数据库必须处于 READ WRITE 状态"
exit 1
}

echo "数据库状态检查全部通过"


# ---------------- 3. 检查控制文件 ----------------

echo "=== 检查控制文件 ==="

for CF in \
/data/cyhis/control01.ctl \
/u01/app/oracle/fast_recovery_area/cyhis/control02.ctl
do

if [ ! -s "$CF" ]; then
echo "错误:控制文件不存在:$CF"
exit 1
fi

echo "检查通过:$CF"

done

# ---------------- 4. 检查数据文件和日志 ----------------

echo "=== 检查数据库文件状态 ==="

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET LINESIZE 200
SET PAGESIZE 100

PROMPT ===== DATAFILES =====
SELECT FILE#, NAME, STATUS FROM V$DATAFILE;

PROMPT ===== REDO LOGS =====
SELECT GROUP#, MEMBER FROM V$LOGFILE;

PROMPT ===== CONTROL FILES =====
SELECT NAME FROM V$CONTROLFILE;

EXIT;
SQL

# ---------------- 5. 检查备份确认 ----------------

echo "================================================"
echo "重要:必须确认已有可恢复的 RMAN 全库备份"
echo "================================================"

read -r -p "已完成 RMAN 全库备份?输入 YES 继续:" CONFIRM

[ "$CONFIRM" = "YES" ] || {
echo "用户取消操作"
exit 1
}

read -r -p "确认将 CYHIS 改为 HISTEST?输入 HISTEST:" CONFIRM

[ "$CONFIRM" = "HISTEST" ] || {
echo "用户取消操作"
exit 1
}

# ---------------- 6. 备份配置文件 ----------------

echo "=== 备份原始配置 ==="

for FILE in \
"$OLD_SPFILE" \
"$OLD_PW" \
"$DBS_DIR/init${OLD_SID}.ora" \
"$NEW_SPFILE" \
"$NEW_PW" \
/etc/oratab \
"$HOME/.bash_profile" \
"$HOME/.bashrc" \
"$NET_DIR/listener.ora" \
"$NET_DIR/tnsnames.ora"
do

if [ -f "$FILE" ]; then

mkdir -p "$BACKUP_DIR$(dirname "$FILE")"

cp -p "$FILE" "$BACKUP_DIR$FILE"

echo "已备份:$FILE"

fi

done

# ---------------- 7. 导出原始 PFILE ----------------

echo "=== 导出原始 PFILE ==="

"$SQLPLUS" -L -s / as sysdba <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE
CREATE PFILE='$OLD_PFILE' FROM SPFILE='$OLD_SPFILE';
EXIT;
SQL

[ -s "$OLD_PFILE" ] || {
echo "错误:PFILE 导出失败"
exit 1
}

# ---------------- 8. 生成目标 PFILE ----------------

echo "=== 生成 histest 参数文件 ==="

awk '
{
line=tolower($0)

if (line ~ /^[[:space:]]*#/)
next

if (line ~ /(^|[.])(db_name|db_unique_name|instance_name|service_names)[[:space:]]*=/)
next

print
}
' "$OLD_PFILE" > "$NEW_PFILE"

cat >> "$NEW_PFILE" <<EOF

# ===== HISTEST DATABASE CONFIGURATION =====
*.db_name='${NEW_DB_NAME}'
*.db_unique_name='${NEW_DB_UNIQUE_NAME}'
*.instance_name='${NEW_SID}'
*.service_names='${NEW_SID}'
*.cluster_database=FALSE
EOF

chmod 600 "$NEW_PFILE"

echo "目标 PFILE:$NEW_PFILE"

grep -iE 'db_name|db_unique_name|instance_name|service_names|control_files' "$NEW_PFILE"

# ---------------- 9. 使用原 PFILE 启动 MOUNT ----------------

echo "================================================"
echo "阶段一:使用原 DB_NAME 启动 MOUNT"
echo "================================================"

"$SQLPLUS" -L -s / as sysdba <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE
SHUTDOWN IMMEDIATE;
STARTUP MOUNT PFILE='$OLD_PFILE';
EXIT;
SQL

echo "原数据库已经 MOUNT"

# ---------------- 10. 执行 NID ----------------

echo "================================================"
echo "阶段二:执行 NID 修改数据库名称"
echo "================================================"

echo "旧 DB_NAME:CYHIS"
echo "新 DB_NAME:HISTEST"
echo "DBID:保持不变"
echo
echo "注意:NID 可能要求交互确认,请按提示输入 Y。"

"$NID" target=/ \
dbname="$NEW_DB_NAME" \
setname=yes \
logfile="$BACKUP_DIR/nid.log"

echo "NID 命令正常返回"

# ---------------- 11. 检查 NID 后的实例 ----------------

echo "=== 检查 NID 执行后的实例状态 ==="

if ps -ef | grep -q "[o]ra_pmon_${OLD_SID}"; then

echo "发现旧实例仍在运行,先关闭实例"

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SHUTDOWN IMMEDIATE;
EXIT;
SQL

fi

# ---------------- 12. 切换 SID ----------------

echo "================================================"
echo "阶段三:切换至 histest"
echo "================================================"

export ORACLE_SID="$NEW_SID"
export ORACLE_UNQNAME="$NEW_DB_UNIQUE_NAME"

echo "ORACLE_SID=$ORACLE_SID"

# ---------------- 13. 使用新 PFILE 启动 MOUNT ----------------

echo "=== 使用新 DB_NAME 启动数据库 ==="

"$SQLPLUS" -L -s / as sysdba <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE
STARTUP MOUNT PFILE='$NEW_PFILE';
CREATE SPFILE='$NEW_SPFILE' FROM PFILE='$NEW_PFILE';
EXIT;
SQL

# ---------------- 14. 正常打开数据库 ----------------

echo "================================================"
echo "阶段四:打开数据库"
echo "================================================"

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
ALTER DATABASE OPEN;
EXIT;
SQL

# ---------------- 15. 使用新 SPFILE 重启 ----------------

echo "=== 使用新 SPFILE 正式启动 ==="

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SHUTDOWN IMMEDIATE;
STARTUP;
EXIT;
SQL

# ---------------- 16. 密码文件 ----------------

echo "=== 处理密码文件 ==="

if [ -s "$OLD_PW" ]; then

if [ -f "$NEW_PW" ]; then
echo "新密码文件已存在,原文件已在备份目录保存"
fi

cp -p "$OLD_PW" "$NEW_PW"
chmod 600 "$NEW_PW"

echo "密码文件已复制:$NEW_PW"

else

echo "警告:旧密码文件不存在"

fi

# ---------------- 17. 更新 oratab ----------------

echo "=== 更新 /etc/oratab ==="

if [ -f /etc/oratab ]; then

ORATAB_TMP="$BACKUP_DIR/oratab.tmp"

awk -F: -v old="$OLD_SID" -v new="$NEW_SID" '
BEGIN { OFS=":" }
$1 == old {
$1=new
found=1
}
{ print }
END {
if (!found)
print new, ENVIRON["ORACLE_HOME"], "N"
}
' /etc/oratab > "$ORATAB_TMP"

cp "$ORATAB_TMP" /etc/oratab

fi

# ---------------- 18. 更新环境变量 ----------------

echo "=== 更新 Oracle 环境变量 ==="

PROFILE="$HOME/.bash_profile"

[ -f "$PROFILE" ] || touch "$PROFILE"

sed -i \
-e '/^[[:space:]]*export ORACLE_SID=/d' \
-e '/^[[:space:]]*export ORACLE_UNQNAME=/d' \
"$PROFILE"

cat >> "$PROFILE" <<'EOF'

# ===== Oracle HISTEST Environment =====
export ORACLE_SID=histest
export ORACLE_UNQNAME=histest
EOF

# ---------------- 19. 检查监听器配置 ----------------

echo "=== 检查监听器配置 ==="

if [ -f "$NET_DIR/listener.ora" ]; then

grep -niE 'cyhis|histest' "$NET_DIR/listener.ora" || true

fi

if [ -f "$NET_DIR/tnsnames.ora" ]; then

grep -niE 'cyhis|histest' "$NET_DIR/tnsnames.ora" || true

fi

# ---------------- 20. 最终数据库验证 ----------------

echo "================================================"
echo "最终数据库验证"
echo "================================================"

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200
SET PAGESIZE 100

PROMPT ===== DATABASE =====

SELECT NAME, DBID, OPEN_MODE, DATABASE_ROLE
FROM V$DATABASE;

PROMPT ===== INSTANCE =====

SELECT INSTANCE_NAME, STATUS, VERSION
FROM V$INSTANCE;

PROMPT ===== PARAMETERS =====

SHOW PARAMETER DB_NAME
SHOW PARAMETER DB_UNIQUE_NAME
SHOW PARAMETER INSTANCE_NAME
SHOW PARAMETER SERVICE_NAMES
SHOW PARAMETER SPFILE
SHOW PARAMETER CONTROL_FILES

PROMPT ===== GLOBAL NAME =====

SELECT GLOBAL_NAME FROM GLOBAL_NAME;

EXIT;
SQL

# ---------------- 21. 自动检查核心结果 ----------------

echo "=== 自动检查核心结果 ==="

FINAL_RESULT=$(
"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET VERIFY OFF
SET ECHO OFF
SET TRIMSPOOL ON

SELECT
(SELECT NAME FROM V$DATABASE) || '|' ||
(SELECT INSTANCE_NAME FROM V$INSTANCE) || '|' ||
(SELECT VALUE FROM V$PARAMETER
WHERE NAME='db_unique_name') || '|' ||
REPLACE(
(SELECT OPEN_MODE FROM V$DATABASE),
' ',
''
) || '|' ||
(SELECT VALUE FROM V$PARAMETER
WHERE NAME='service_names')
FROM DUAL;

EXIT;
SQL
) || {
echo "ERROR:无法获取数据库最终状态"
exit 1
}

# 只删除回车和换行,不再删除字段中的空格
FINAL_RESULT=$(echo "$FINAL_RESULT" | tr -d '\r' | sed '/^[[:space:]]*$/d' | tail -1)

echo "FINAL_RESULT=$FINAL_RESULT"

IFS='|' read -r FINAL_DB FINAL_SID FINAL_UNIQUE FINAL_OPEN FINAL_SERVICE <<< "$FINAL_RESULT"

# 去除字段首尾空格
FINAL_DB=$(echo "$FINAL_DB" | xargs)
FINAL_SID=$(echo "$FINAL_SID" | xargs)
FINAL_UNIQUE=$(echo "$FINAL_UNIQUE" | xargs)
FINAL_OPEN=$(echo "$FINAL_OPEN" | xargs)
FINAL_SERVICE=$(echo "$FINAL_SERVICE" | xargs)

# 统一大小写
FINAL_DB=${FINAL_DB^^}
FINAL_SID=${FINAL_SID,,}
FINAL_UNIQUE=${FINAL_UNIQUE,,}
FINAL_OPEN=${FINAL_OPEN^^}
FINAL_SERVICE=${FINAL_SERVICE,,}

# 最终判断
if [[ "$FINAL_DB" != "HISTEST" ]] ||
[[ "$FINAL_SID" != "histest" ]] ||
[[ "$FINAL_UNIQUE" != "histest" ]] ||
[[ "$FINAL_OPEN" != "READWRITE" ]] ||
[[ "$FINAL_SERVICE" != "histest" ]]; then

echo "ERROR:最终数据库验证未全部通过"
echo "DB_NAME=$FINAL_DB"
echo "INSTANCE_NAME=$FINAL_SID"
echo "DB_UNIQUE_NAME=$FINAL_UNIQUE"
echo "OPEN_MODE=$FINAL_OPEN"
echo "SERVICE_NAMES=$FINAL_SERVICE"

exit 1
fi

echo "================================================"
echo "SUCCESS:数据库核心改名成功"
echo "================================================"

echo "DB_NAME = $FINAL_DB"
echo "INSTANCE_NAME = $FINAL_SID"
echo "DB_UNIQUE_NAME = $FINAL_UNIQUE"
echo "SERVICE_NAMES = $FINAL_SERVICE"
echo "OPEN_MODE = $FINAL_OPEN"
echo "ORACLE_SID = $ORACLE_SID"
echo "ORACLE_UNQNAME = $ORACLE_UNQNAME"

echo
echo "备份目录:$BACKUP_DIR"
echo "执行日志:$LOG"
echo
echo "旧 SPFILE 和密码文件暂时保留,不要立即删除。"
echo "请检查监听器、客户端 TNS 和 RMAN 配置。"
echo "建议立即执行一次 RMAN 全量备份。"
echo "================================================"
  • 执行脚本

Oracle用户执行脚本

1
2
3
4
5
6
7
8
9
# 1. 切换到 oracle 用户
su - oracle

# 2. 确认环境变量已加载
echo $ORACLE_HOME && echo $ORACLE_SID

# 3. 赋予脚本执行权限并运行
chmod +x change_dbname.sh
./change_dbname.sh NEWDBNAME YourSysPassword
3.验证检查脚本

此步不包括nid修改部分,即nid修改完成后,此脚本为了验证检查是否修成功,可单独运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
#!/bin/bash
# ============================================================
# Oracle 12.1.0.2 HISTEST 数据库改名后收尾脚本
#
# 目标:
# DB_NAME = HISTEST
# INSTANCE_NAME = histest
# DB_UNIQUE_NAME = histest
# SERVICE_NAMES = histest
# ORACLE_SID = histest
# ORACLE_UNQNAME = histest
#
# 原 SID:cyhis
#
# 特别说明:
# 1. 不执行 NID
# 2. 不修改 DBID
# 3. 不重建控制文件
# 4. 不移动数据文件和控制文件
# 5. 不关闭或重启数据库
# 6. 自动备份配置文件
# 7. 支持 yes/YES/y/Y 确认
# ============================================================

set -Eeuo pipefail
umask 077

# ---------------- 基本配置 ----------------

OLD_SID="cyhis"
NEW_SID="histest"

NEW_DB_NAME="HISTEST"
NEW_DB_UNIQUE_NAME="histest"

ORACLE_HOME="${ORACLE_HOME:-/u01/app/oracle/product/12.1.0/db_1}"

export ORACLE_HOME
export ORACLE_SID="$NEW_SID"
export ORACLE_UNQNAME="$NEW_DB_UNIQUE_NAME"

export PATH="$ORACLE_HOME/bin:$PATH"
export LD_LIBRARY_PATH="$ORACLE_HOME/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"

DBS_DIR="$ORACLE_HOME/dbs"
NET_DIR="$ORACLE_HOME/network/admin"

SQLPLUS="$ORACLE_HOME/bin/sqlplus"

TIMESTAMP=$(date +%Y%m%d_%H%M%S)

BACKUP_DIR="$HOME/histest_finish_backup_$TIMESTAMP"
LOG="$BACKUP_DIR/finished_histest.log"

mkdir -p "$BACKUP_DIR"

exec > >(tee -a "$LOG") 2>&1

trap '
echo
echo "[ERROR] 第 ${LINENO} 行执行失败"
echo "日志:$LOG"
echo "请保留现场,不要重复执行破坏性操作。"
exit 1
' ERR

# ---------------- 通用函数 ----------------

confirm() {
local answer

while true; do
read -r -p "$1 [yes/no]: " answer

case "$answer" in
yes|YES|Yes|y|Y)
return 0
;;
no|NO|No|n|N)
echo "用户取消操作"
return 1
;;
*)
echo "请输入 yes、YES、y 或 n、N"
;;
esac
done
}

backup_file() {
local file="$1"

if [ -f "$file" ]; then
local target="$BACKUP_DIR$(dirname "$file")"

mkdir -p "$target"
cp -p "$file" "$target/"

echo "已备份:$file"
fi
}

echo "================================================"
echo "Oracle HISTEST 改名后收尾"
echo "================================================"

# ---------------- 1. 环境检查 ----------------

echo "=== 环境检查 ==="

[ "$(id -un)" = "oracle" ] || {
echo "错误:必须使用 oracle 用户执行"
exit 1
}

[ -x "$SQLPLUS" ] || {
echo "错误:SQLPLUS 不存在:$SQLPLUS"
exit 1
}

[ -x "$ORACLE_HOME/bin/lsnrctl" ] || {
echo "错误:lsnrctl 不存在"
exit 1
}

echo "ORACLE_HOME=$ORACLE_HOME"
echo "ORACLE_SID=$ORACLE_SID"
echo "ORACLE_UNQNAME=$ORACLE_UNQNAME"

# ---------------- 2. 检查数据库状态 ----------------

echo
echo "=== 检查数据库状态 ==="

QUERY_RESULT=$(
"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET VERIFY OFF
SET ECHO OFF
SET LINESIZE 1000
SET TRIMSPOOL ON
SET TAB OFF

SELECT
TRIM(i.VERSION) || '|' ||
TRIM(d.NAME) || '|' ||
TRIM(i.INSTANCE_NAME) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='db_unique_name'
)) || '|' ||
REPLACE(TRIM(d.OPEN_MODE),' ','') || '|' ||
TRIM(d.DATABASE_ROLE) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='cluster_database'
)) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='spfile'
)) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='service_names'
))
FROM V$INSTANCE i, V$DATABASE d;

EXIT;
SQL
) || {
echo "错误:无法连接数据库"
exit 1
}

QUERY_RESULT=$(printf '%s\n' "$QUERY_RESULT" |
tr -d '\r' |
sed '/^[[:space:]]*$/d')

echo "数据库查询原始结果:"
echo "$QUERY_RESULT"

LINE_COUNT=$(printf '%s\n' "$QUERY_RESULT" | wc -l)

[ "$LINE_COUNT" -eq 1 ] || {
echo "错误:数据库查询结果数量异常"
exit 1
}

IFS='|' read -r \
DB_VERSION \
CURRENT_DB_NAME \
INSTANCE_NAME \
DB_UNIQUE_NAME \
OPEN_MODE \
DATABASE_ROLE \
CLUSTER_DATABASE \
CURRENT_SPFILE \
SERVICE_NAMES <<< "$QUERY_RESULT"

DB_VERSION=$(echo "$DB_VERSION" | xargs)
CURRENT_DB_NAME=$(echo "$CURRENT_DB_NAME" | xargs)
INSTANCE_NAME=$(echo "$INSTANCE_NAME" | xargs)
DB_UNIQUE_NAME=$(echo "$DB_UNIQUE_NAME" | xargs)
OPEN_MODE=$(echo "$OPEN_MODE" | xargs)
DATABASE_ROLE=$(echo "$DATABASE_ROLE" | xargs)
CLUSTER_DATABASE=$(echo "$CLUSTER_DATABASE" | xargs)
CURRENT_SPFILE=$(echo "$CURRENT_SPFILE" | xargs)
SERVICE_NAMES=$(echo "$SERVICE_NAMES" | xargs)

echo "VERSION=$DB_VERSION"
echo "DB_NAME=$CURRENT_DB_NAME"
echo "INSTANCE_NAME=$INSTANCE_NAME"
echo "DB_UNIQUE_NAME=$DB_UNIQUE_NAME"
echo "OPEN_MODE=$OPEN_MODE"
echo "DATABASE_ROLE=$DATABASE_ROLE"
echo "CLUSTER_DATABASE=$CLUSTER_DATABASE"
echo "SPFILE=$CURRENT_SPFILE"
echo "SERVICE_NAMES=$SERVICE_NAMES"

# ---------------- 3. 验证数据库状态 ----------------

echo
echo "=== 验证数据库核心参数 ==="

[[ "$DB_VERSION" == 12.1.0.2* ]] || {
echo "错误:Oracle 版本不符合预期"
exit 1
}

[[ "$CURRENT_DB_NAME" == "$NEW_DB_NAME" ]] || {
echo "错误:DB_NAME 不是 HISTEST"
exit 1
}

[[ "${INSTANCE_NAME,,}" == "$NEW_SID" ]] || {
echo "错误:INSTANCE_NAME 不是 histest"
exit 1
}

[[ "${DB_UNIQUE_NAME,,}" == "$NEW_DB_UNIQUE_NAME" ]] || {
echo "错误:DB_UNIQUE_NAME 不是 histest"
exit 1
}

[[ "$OPEN_MODE" == READWRITE ]] || {
echo "错误:数据库不是 READ WRITE 状态"
exit 1
}

[[ "$DATABASE_ROLE" == PRIMARY ]] || {
echo "错误:当前数据库不是 PRIMARY"
exit 1
}

[[ "${CLUSTER_DATABASE^^}" == FALSE ]] || {
echo "错误:当前数据库可能是 RAC"
exit 1
}

[[ "$CURRENT_SPFILE" == "$DBS_DIR/spfile${NEW_SID}.ora" ]] || {
echo "错误:当前 SPFILE 路径不正确"
echo "实际:$CURRENT_SPFILE"
exit 1
}

echo "数据库核心参数检查通过"

# ---------------- 4. 检查环境变量 ----------------

echo
echo "=== 检查 Oracle 环境变量 ==="

echo "ORACLE_HOME=$ORACLE_HOME"
echo "ORACLE_SID=$ORACLE_SID"
echo "ORACLE_UNQNAME=$ORACLE_UNQNAME"

# ---------------- 5. 检查 SPFILE ----------------

echo
echo "=== 检查 SPFILE ==="

NEW_SPFILE="$DBS_DIR/spfile${NEW_SID}.ora"

[ -s "$NEW_SPFILE" ] || {
echo "错误:SPFILE 不存在或为空"
exit 1
}

ls -l "$NEW_SPFILE"

# ---------------- 6. 检查密码文件 ----------------

echo
echo "=== 检查密码文件 ==="

NEW_PW="$DBS_DIR/orapw${NEW_SID}"

if [ -s "$NEW_PW" ]; then
ls -l "$NEW_PW"
echo "密码文件存在"
else
echo "警告:密码文件不存在"
fi

# ---------------- 7. 检查控制文件 ----------------

echo
echo "=== 检查控制文件 ==="

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200

SELECT NAME FROM V$CONTROLFILE;

EXIT;
SQL

# ---------------- 8. 检查数据文件和日志 ----------------

echo
echo "=== 检查数据文件 ==="

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200

SELECT FILE#, NAME, STATUS FROM V$DATAFILE;

PROMPT ===== REDO LOGS =====

SELECT GROUP#, MEMBER FROM V$LOGFILE;

PROMPT ===== ARCHIVE MODE =====

ARCHIVE LOG LIST;

EXIT;
SQL

# ---------------- 9. 备份配置文件 ----------------

echo
echo "=== 备份配置文件 ==="

backup_file "$NEW_SPFILE"
backup_file "$NEW_PW"
backup_file "$DBS_DIR/init${NEW_SID}.ora"
backup_file /etc/oratab
backup_file "$HOME/.bash_profile"
backup_file "$HOME/.bashrc"
backup_file "$NET_DIR/listener.ora"
backup_file "$NET_DIR/tnsnames.ora"

echo "备份目录:$BACKUP_DIR"

# ---------------- 10. 检查 oratab ----------------

echo
echo "=== 检查 /etc/oratab ==="

if [ -f /etc/oratab ]; then
grep -nE '^(cyhis|histest):' /etc/oratab || true
fi

# ---------------- 11. 检查环境配置文件 ----------------

echo
echo "=== 检查环境变量配置 ==="

PROFILE="$HOME/.bash_profile"

if [ -f "$PROFILE" ]; then
grep -nE 'ORACLE_HOME|ORACLE_SID|ORACLE_UNQNAME' "$PROFILE" || true
fi

# ---------------- 12. 检查监听器配置 ----------------

echo
echo "=== 检查 listener.ora ==="

if [ -f "$NET_DIR/listener.ora" ]; then
grep -niE 'cyhis|histest' "$NET_DIR/listener.ora" || true
fi

echo
echo "=== 检查 tnsnames.ora ==="

if [ -f "$NET_DIR/tnsnames.ora" ]; then
grep -niE 'cyhis|histest' "$NET_DIR/tnsnames.ora" || true
fi

# ---------------- 13. 检查监听器运行状态 ----------------

echo
echo "=== 监听器状态 ==="

"$ORACLE_HOME/bin/lsnrctl" status || {
echo "警告:监听器状态检查失败"
}

# ---------------- 14. 检查数据库服务注册 ----------------

echo
echo "=== 检查数据库服务注册 ==="

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

ALTER SYSTEM REGISTER;

SELECT NAME, NETWORK_NAME
FROM V$ACTIVE_SERVICES;

EXIT;
SQL

# ---------------- 15. 更新环境变量 ----------------

echo
echo "=== 更新环境变量配置 ==="

PROFILE="$HOME/.bash_profile"

if [ ! -f "$PROFILE" ]; then
touch "$PROFILE"
fi

if confirm "是否将 .bash_profile 中的 Oracle SID 和 UNQNAME 统一设置为 histest?"; then

TMP_PROFILE="$BACKUP_DIR/bash_profile.tmp"

sed \
-e '/^[[:space:]]*export[[:space:]]\+ORACLE_SID=/d' \
-e '/^[[:space:]]*export[[:space:]]\+ORACLE_UNQNAME=/d' \
"$PROFILE" > "$TMP_PROFILE"

cat >> "$TMP_PROFILE" <<'EOF'

# ===== Oracle HISTEST Environment =====
export ORACLE_SID=histest
export ORACLE_UNQNAME=histest
EOF

cp "$TMP_PROFILE" "$PROFILE"

echo "环境变量配置已更新"

else
echo "跳过环境变量文件修改"
fi

# ---------------- 16. 更新 oratab ----------------

echo
echo "=== 更新 oratab ==="

if [ -f /etc/oratab ]; then

if confirm "是否将 /etc/oratab 中的 cyhis 更新为 histest?"; then

ORATAB_TMP="$BACKUP_DIR/oratab.tmp"

awk -F: -v old="$OLD_SID" -v new="$NEW_SID" '
BEGIN { OFS=":"; found=0 }
$1 == old {
$1=new
found=1
}
$1 == new {
found=1
}
{ print }
END {
if (!found)
print new, ENVIRON["ORACLE_HOME"], "N"
}
' /etc/oratab > "$ORATAB_TMP"

if cmp -s "$ORATAB_TMP" /etc/oratab; then
echo "oratab 已经正确,无需修改"
else
cp "$ORATAB_TMP" /etc/oratab
echo "oratab 已更新"
fi

else
echo "跳过 oratab 修改"
fi

fi

# ---------------- 17. 更新全局名称 ----------------

echo
echo "=== 检查 GLOBAL_NAME ==="

GLOBAL_NAME=$(
"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT GLOBAL_NAME FROM GLOBAL_NAME;
EXIT;
SQL
)

GLOBAL_NAME=$(echo "$GLOBAL_NAME" | tr -d '[:space:]')

echo "GLOBAL_NAME=$GLOBAL_NAME"

if [[ "${GLOBAL_NAME^^}" != "$NEW_DB_NAME" ]]; then

echo "GLOBAL_NAME 与目标名称不同"

if confirm "是否执行 ALTER DATABASE RENAME GLOBAL_NAME?"; then

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
ALTER DATABASE RENAME GLOBAL_NAME TO HISTEST;
EXIT;
SQL

fi

else
echo "GLOBAL_NAME 已正确"
fi

# ---------------- 18. 最终验证 ----------------

echo
echo "================================================"
echo "最终数据库验证"
echo "================================================"

"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200
SET PAGESIZE 100

PROMPT ===== DATABASE =====

SELECT NAME, DBID, OPEN_MODE, DATABASE_ROLE
FROM V$DATABASE;

PROMPT ===== INSTANCE =====

SELECT INSTANCE_NAME, STATUS, VERSION
FROM V$INSTANCE;

PROMPT ===== PARAMETERS =====

SHOW PARAMETER DB_NAME
SHOW PARAMETER DB_UNIQUE_NAME
SHOW PARAMETER INSTANCE_NAME
SHOW PARAMETER SERVICE_NAMES
SHOW PARAMETER SPFILE
SHOW PARAMETER CONTROL_FILES

PROMPT ===== GLOBAL NAME =====

SELECT GLOBAL_NAME FROM GLOBAL_NAME;

PROMPT ===== ACTIVE SERVICES =====

SELECT NAME, NETWORK_NAME FROM V$ACTIVE_SERVICES;

EXIT;
SQL

# ---------------- 19. 最终自动验证 ----------------

echo
echo "=== 最终自动验证 ==="

FINAL_RESULT=$(
"$SQLPLUS" -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET VERIFY OFF
SET ECHO OFF
SET LINESIZE 1000
SET TRIMSPOOL ON

SELECT
TRIM(d.NAME) || '|' ||
TRIM(i.INSTANCE_NAME) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='db_unique_name'
)) || '|' ||
REPLACE(TRIM(d.OPEN_MODE),' ','') || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='service_names'
)) || '|' ||
TRIM((
SELECT VALUE FROM V$PARAMETER
WHERE NAME='spfile'
))
FROM V$DATABASE d, V$INSTANCE i;

EXIT;
SQL
)

FINAL_RESULT=$(printf '%s\n' "$FINAL_RESULT" |
tr -d '\r' |
sed '/^[[:space:]]*$/d')

echo "FINAL_RESULT=$FINAL_RESULT"

LINE_COUNT=$(printf '%s\n' "$FINAL_RESULT" | wc -l)

[ "$LINE_COUNT" -eq 1 ] || {
echo "错误:最终验证结果数量异常"
exit 1
}

IFS='|' read -r \
FINAL_DB \
FINAL_SID \
FINAL_UNIQUE \
FINAL_OPEN \
FINAL_SERVICE \
FINAL_SPFILE <<< "$FINAL_RESULT"

FINAL_DB=$(echo "$FINAL_DB" | xargs)
FINAL_SID=$(echo "$FINAL_SID" | xargs)
FINAL_UNIQUE=$(echo "$FINAL_UNIQUE" | xargs)
FINAL_OPEN=$(echo "$FINAL_OPEN" | xargs)
FINAL_SERVICE=$(echo "$FINAL_SERVICE" | xargs)
FINAL_SPFILE=$(echo "$FINAL_SPFILE" | xargs)

# ---------------- 20. 最终结果判断 ----------------

echo
echo "================================================"
echo "最终检查结果"
echo "================================================"

echo "DB_NAME = $FINAL_DB"
echo "INSTANCE_NAME = $FINAL_SID"
echo "DB_UNIQUE_NAME = $FINAL_UNIQUE"
echo "SERVICE_NAMES = $FINAL_SERVICE"
echo "SPFILE = $FINAL_SPFILE"
echo "ORACLE_SID = $ORACLE_SID"
echo "ORACLE_UNQNAME = $ORACLE_UNQNAME"

if [[ "$FINAL_DB" != "HISTEST" ]] ||
[[ "${FINAL_SID,,}" != "histest" ]] ||
[[ "${FINAL_UNIQUE,,}" != "histest" ]] ||
[[ "$FINAL_OPEN" != "READWRITE" ]] ||
[[ "${FINAL_SERVICE,,}" != "histest" ]] ||
[[ "$FINAL_SPFILE" != "$NEW_SPFILE" ]] ||
[[ "$ORACLE_SID" != "histest" ]] ||
[[ "$ORACLE_UNQNAME" != "histest" ]]; then

echo
echo "ERROR:最终数据库验证未全部通过"
echo "请查看日志:$LOG"
exit 1

fi

echo
echo "================================================"
echo "SUCCESS:HISTEST 数据库收尾检查完成"
echo "================================================"

echo "数据库名称:HISTEST"
echo "实例名称:histest"
echo "唯一名称:histest"
echo "环境变量:histest"
echo "SPFILE:$NEW_SPFILE"

echo
echo "备份目录:$BACKUP_DIR"
echo "日志文件:$LOG"

echo
echo "后续建议:"
echo "1. 检查客户端 TNS 配置"
echo "2. 检查监听器注册及远程连接"
echo "3. 检查 RMAN 备份配置"
echo "4. 执行一次新的 RMAN 全量备份"
echo "5. 验证数据库重启后的自动启动配置"

echo
echo "注意:旧配置暂时保留,不要立即删除。"
echo "================================================"

[oracle@hisdbtest ~]$

2.重建控制文件修改数据库名(备选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/bin/bash
# =====================================================================
# Oracle 11g 通过重建控制文件修改 DB_NAME 自动化脚本
# 使用方法: ./rename_db_controlfile.sh <NEW_DB_NAME> <SYS_PASSWORD>
# 执行用户: oracle (必须具备 SYSDBA 权限)
# =====================================================================
set -e

if [ "$#" -ne 2 ]; then
echo "用法: $0 <新数据库名> <SYS密码>"
exit 1
fi

NEW_DB_NAME=$(echo "$1" | tr '[:lower:]' '[:upper:]')
SYS_PASSWORD=$2
WORK_DIR="/tmp/ora_rename_$(date +%Y%m%d%H%M%S)"
mkdir -p "$WORK_DIR"
TRACE_SQL="$WORK_DIR/create_cf.sql"
TEMP_PFILE="$WORK_DIR/init_temp.ora"

if [ -z "$ORACLE_HOME" ] || [ -z "$ORACLE_SID" ]; then
echo "错误: 请先设置 ORACLE_HOME 和 ORACLE_SID"
exit 1
fi

echo "=== [1/7] 生成控制文件创建脚本 ==="
sqlplus -s / as sysdba <<EOF
ALTER DATABASE BACKUP CONTROLFILE TO TRACE AS '$WORK_DIR/cf_trace.trc' REUSE;
EXIT;
EOF

# 从 trace 文件中提取 RESETLOGS 版本的 CREATE CONTROLFILE 语句
# 并替换 REUSE DATABASE "OLD" 为 SET DATABASE "NEW"
awk '
/STARTUP NOMOUNT/ {capture=1}
capture && /CREATE CONTROLFILE/ {
gsub(/REUSE[[:space:]]+DATABASE[[:space:]]+"[^"]+"/, "SET DATABASE \"'"$NEW_DB_NAME"'\"")
# 确保使用 RESETLOGS
if ($0 !~ /RESETLOGS/) gsub(/NORESETLOGS/, "RESETLOGS")
}
capture {print}
capture && /^;/ {capture=0}
' "$WORK_DIR/cf_trace.trc" > "$TRACE_SQL"

# 移除可能存在的 RECOVER DATABASE USING BACKUP CONTROLFILE 行
sed -i '/RECOVER DATABASE USING BACKUP CONTROLFILE/d' "$TRACE_SQL"

if ! grep -q "SET DATABASE \"$NEW_DB_NAME\"" "$TRACE_SQL"; then
echo "错误: 未能正确生成 CREATE CONTROLFILE 脚本,请检查 $WORK_DIR/cf_trace.trc"
exit 1
fi
echo "已生成控制文件脚本: $TRACE_SQL"

echo "=== [2/7] 关闭数据库 ==="
sqlplus -s / as sysdba <<EOF
SHUTDOWN IMMEDIATE;
EXIT;
EOF

echo "=== [3/7] 更新参数文件中的 db_name ==="
sqlplus -s / as sysdba <<EOF
STARTUP NOMOUNT;
CREATE PFILE='$TEMP_PFILE' FROM SPFILE;
SHUTDOWN IMMEDIATE;
EXIT;
EOF

sed -i "s/^[*a-zA-Z0-9_]*\.db_name=.*/\*.db_name='$NEW_DB_NAME'/I" "$TEMP_PFILE"

echo "=== [4/7] 以新参数启动至 NOMOUNT 并重建控制文件 ==="
sqlplus -s / as sysdba <<EOF
STARTUP NOMOUNT PFILE='$TEMP_PFILE';
@$TRACE_SQL
EXIT;
EOF

echo "=== [5/7] 执行恢复并以 RESETLOGS 打开 ==="
sqlplus -s / as sysdba <<EOF
RECOVER DATABASE USING BACKUP CONTROLFILE UNTIL CANCEL;
CANCEL
ALTER DATABASE OPEN RESETLOGS;
EXIT;
EOF

echo "=== [6/7] 重建 SPFILE 和密码文件 ==="
sqlplus -s / as sysdba <<EOF
CREATE SPFILE FROM PFILE='$TEMP_PFILE';
SHUTDOWN IMMEDIATE;
STARTUP;
EXIT;
EOF

PWFILE="$ORACLE_HOME/dbs/orapw${ORACLE_SID}"
rm -f "$PWFILE"
orapwd file="$PWFILE" password="$SYS_PASSWORD" entries=10 force=y

echo "=== [7/7] 验证结果 ==="
RESULT=$(sqlplus -s / as sysdba <<EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT name FROM v\$database;
EXIT;
EOF
)

echo "========================================="
if [ "$(echo "$RESULT" | tr -d '[:space:]')" = "$NEW_DB_NAME" ]; then
echo "✅ 数据库名已成功修改为: $NEW_DB_NAME"
else
echo "❌ 验证失败!当前 DB_NAME: $RESULT (期望: $NEW_DB_NAME)"
echo " 请检查 $WORK_DIR 下的日志和 SQL 文件"
fi
echo "⚠️ 重要后续操作:"
echo " 1. 立即执行 RMAN 全量备份 (旧备份已全部失效)"
echo " 2. ALTER DATABASE RENAME GLOBAL_NAME TO $NEW_DB_NAME;"
echo " 3. 更新 listener.ora / tnsnames.ora 并 lsnrctl reload"
echo " 4. 通知应用方更新所有连接字符串"
echo "========================================="

脚本关键逻辑说明(基于检索证据)

步骤 关键操作 依据来源
生成控制文件脚本 ALTER DATABASE BACKUP CONTROLFILE TO TRACE + awk 提取 RESETLOGS 版本 Oracle 官方文档 、adp-gmbh.ch
语法替换 REUSE DATABASE "OLD" → SET DATABASE "NEW" + 强制 RESETLOGS Oracle 官方文档 、CSDN
移除恢复语句 删除 RECOVER DATABASE USING BACKUP CONTROLFILE Oracle 官方文档 明确要求移除此行
打开方式 RECOVER ... UNTIL CANCEL + OPEN RESETLOGS CSDN 、oracle-dba-online.com
参数文件更新 先建 PFILE 修改 db_name,再重建 SPFILE 阿里云开发者社区 、CSDN

⚠️ 脚本局限性与未覆盖场景

以下情况脚本无法自动处理,需 DBA 手动介入:

  1. 临时文件(TEMPFILES):BACKUP CONTROLFILE TO TRACE 生成的脚本通常不包含 TEMPFILE 的 ADD TEMPFILE 语句。数据库打开后需手动执行:

    1
    ALTER TABLESPACE TEMP ADD TEMPFILE '/path/to/temp01.dbf' SIZE xxx REUSE;
  2. RAC / Data Guard 环境:此脚本仅适用于单实例非 DG 环境。RAC 需逐节点处理,DG 需同步修改主备库配置。

  3. ASM 存储:若数据文件位于 ASM 磁盘组,trace 文件中的路径为 ASM 别名,脚本可直接使用;但若涉及磁盘组重命名则需额外处理。

  4. 全局数据库名:脚本不自动执行 ALTER DATABASE RENAME GLOBAL_NAME,需在打开后手动完成。

  5. 网络配置与环境变量:listener.ora、tnsnames.ora、ORACLE_SID 环境变量均需手动同步修改。

结合两个脚本–chatgpt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
#!/bin/bash
#
# Oracle Database 11g Release 2 (11.2.0.4) Enterprise Edition
# CentOS 7.x x86_64 单机自动安装脚本
#
# SID : yxdb
# DB_NAME : yxdb
# ORACLE_BASE : /u01/app/oracle
# ORACLE_HOME : /u01/app/oracle/product/11.2.0/db_1
# DATA_DIR : /data
# CHARACTERSET : ZHS16GBK
# NATIONALCHARSET : AL16UTF16
# MEMORY : ASMM
# SGA_TARGET : 16GB
# PGA_AGGREGATE_TARGET : 6GB
# REDO : 8 groups x 1024MB
#
# 执行用户:root
# 执行方式:bash install_oracle11g.sh
#
# 建库策略:
# 1. DBCA 使用 TOTALMEMORY 创建数据库。
# 2. 数据库成功启动后,再设置 SGA、PGA 和 ASMM。
# 3. 重启数据库,使 ASMM 参数生效。
#
# 注意:
# 1. 安装前确认 Oracle 安装包完整。
# 2. 安装前确认物理内存及磁盘空间充足。
# 3. 不会自动删除已有数据库文件。
# 4. Oracle 11.2.0.4 安装介质必须来自可信来源。
#

set -Eeuo pipefail
umask 022

# ============================================================
# 1. 基础配置
# ============================================================

SID="yxdb"
DB_NAME="yxdb"

ORACLE_USER="oracle"
ORACLE_GROUP="oinstall"
DBA_GROUP="dba"
OPER_GROUP="oper"

ORACLE_BASE="/u01/app/oracle"
ORACLE_HOME="${ORACLE_BASE}/product/11.2.0/db_1"
INVENTORY="/u01/app/oraInventory"

DATA_DIR="/data"

SOFT_DIR="/opt/soft"
INSTALL_DIR="${SOFT_DIR}/oracle_install"
INSTALL_SOURCE="${INSTALL_DIR}/database"

ZIP1="p13390677_112040_Linux-x86-64_1of7.zip"
ZIP2="p13390677_112040_Linux-x86-64_2of7.zip"

FRA_DEST="${ORACLE_BASE}/fast_recovery_area"

LISTENER_PORT=1521

CHARSET="ZHS16GBK"
NATIONAL_CHARSET="AL16UTF16"

# ============================================================
# 2. 数据库内存配置
# ============================================================

PROCESSES=1000

SGA_TARGET_MB=16384
SGA_MAX_SIZE_MB=16384
PGA_AGGREGATE_TARGET_MB=6144

SGA_TARGET_BYTES=$((SGA_TARGET_MB * 1024 * 1024))
SGA_MAX_SIZE_BYTES=$((SGA_MAX_SIZE_MB * 1024 * 1024))
PGA_AGGREGATE_TARGET_BYTES=$((PGA_AGGREGATE_TARGET_MB * 1024 * 1024))

TOTAL_TARGET_MB=$((SGA_TARGET_MB + PGA_AGGREGATE_TARGET_MB))

# DBCA 建库阶段使用的内存:
# 参考第二份脚本,使用物理内存的80%。
DBCA_MEMORY_PERCENT=80

# DBCA TOTALMEMORY,单位MB。
DBCA_TOTALMEMORY_MB=0

# FRA容量,单位MB。
FRA_SIZE_MB=$((TOTAL_TARGET_MB * 3 / 2))

# ============================================================
# 3. Redo配置
# ============================================================

REDO_SIZE_MB=1024
REDO_GROUPS=8

# ============================================================
# 4. 其他配置
# ============================================================

LOG="/var/log/oracle11g_install.log"

PASSWORD_FILE="/root/oracle11g_credentials.txt"

DB_PASSWORD=""

# 是否自动清理失败的数据库文件。
# 默认关闭,防止误删数据。
CLEAN_FAILED_DB="false"

# ============================================================
# 5. 基础函数
# ============================================================

log() {
echo "[$(date '+%F %T')] $*"
}

die() {
log "ERROR: $*"
exit 1
}

run_as_oracle() {
su - "${ORACLE_USER}" -c "$*"
}

# ============================================================
# 6. 日志配置
# ============================================================

touch "${LOG}"
chmod 600 "${LOG}"

exec > >(tee -a "${LOG}") 2>&1

trap 'log "安装失败,行号:${LINENO},请检查日志:${LOG}"' ERR

log "================================================"
log "Oracle 11.2.0.4 单机自动安装开始"
log "================================================"

# ============================================================
# 7. 系统检查
# ============================================================

[ "$(id -u)" -eq 0 ] || die "必须使用 root 用户执行"

[ "$(uname -m)" = "x86_64" ] || die "仅支持 x86_64"

if ! grep -qiE 'release 7\.' /etc/redhat-release 2>/dev/null; then
die "当前系统不是 CentOS/RHEL 7.x"
fi

MEM_MB=$(awk '/MemTotal/ {print int($2 / 1024)}' /proc/meminfo)

log "服务器物理内存:${MEM_MB}MB"

# SGA + PGA 必须能够在物理内存中合理分配。
if [ "${MEM_MB}" -lt 26000 ]; then
die "物理内存不足,建议至少26GB,当前${MEM_MB}MB"
fi

if [ "${TOTAL_TARGET_MB}" -gt $((MEM_MB * 85 / 100)) ]; then
die "SGA+PGA超过物理内存的85%,不允许继续安装"
fi

[ "${SGA_TARGET_MB}" -gt 0 ] || die "SGA_TARGET必须大于0"

[ "${SGA_MAX_SIZE_MB}" -ge "${SGA_TARGET_MB}" ] \
|| die "SGA_MAX_SIZE不能小于SGA_TARGET"

[ "${PGA_AGGREGATE_TARGET_MB}" -gt 0 ] \
|| die "PGA_AGGREGATE_TARGET必须大于0"

# DBCA 使用物理内存的80%。
DBCA_TOTALMEMORY_MB=$((MEM_MB * DBCA_MEMORY_PERCENT / 100))

log "DBCA TOTALMEMORY:${DBCA_TOTALMEMORY_MB}MB"
log "最终 SGA:${SGA_TARGET_MB}MB"
log "最终 PGA:${PGA_AGGREGATE_TARGET_MB}MB"

# ============================================================
# 8. 安装包检查
# ============================================================

[ -s "${SOFT_DIR}/${ZIP1}" ] \
|| die "安装包不存在:${SOFT_DIR}/${ZIP1}"

[ -s "${SOFT_DIR}/${ZIP2}" ] \
|| die "安装包不存在:${SOFT_DIR}/${ZIP2}"

command -v unzip >/dev/null 2>&1 \
|| die "缺少 unzip"

command -v openssl >/dev/null 2>&1 \
|| die "缺少 openssl"

command -v bc >/dev/null 2>&1 || {
log "安装 bc"
yum install -y bc
}

# ============================================================
# 9. 磁盘空间检查
# ============================================================

mkdir -p /u01 /data

U01_AVAIL=$(df -Pm /u01 | awk 'NR==2 {print $4}')
DATA_AVAIL=$(df -Pm /data | awk 'NR==2 {print $4}')

[ "${U01_AVAIL}" -ge 50000 ] \
|| die "/u01 可用空间不足50GB,当前${U01_AVAIL}MB"

[ "${DATA_AVAIL}" -ge 30000 ] \
|| die "/data 可用空间不足30GB,当前${DATA_AVAIL}MB"

log "/u01 可用空间:${U01_AVAIL}MB"
log "/data 可用空间:${DATA_AVAIL}MB"

# ============================================================
# 10. 冲突检查
# ============================================================

[ ! -e "${ORACLE_HOME}" ] \
|| die "ORACLE_HOME已经存在:${ORACLE_HOME}"

[ ! -e "${INVENTORY}" ] \
|| die "Inventory已经存在:${INVENTORY}"

[ ! -d "${DATA_DIR}/${DB_NAME}" ] \
|| die "数据库目录已经存在:${DATA_DIR}/${DB_NAME}"

if id "${ORACLE_USER}" >/dev/null 2>&1; then
die "Oracle用户已经存在,请检查原有安装环境"
fi

if ss -lnt | grep -qE ":${LISTENER_PORT}[[:space:]]"; then
die "监听端口${LISTENER_PORT}已经被占用"
fi

log "系统环境检查通过"

# ============================================================
# 11. 创建Oracle用户及用户组
# ============================================================

groupadd "${ORACLE_GROUP}"
groupadd "${DBA_GROUP}"
groupadd "${OPER_GROUP}"

useradd \
-g "${ORACLE_GROUP}" \
-G "${DBA_GROUP},${OPER_GROUP}" \
-d /home/oracle \
-s /bin/bash \
-m "${ORACLE_USER}"

# ============================================================
# 12. 创建安装目录
# ============================================================

mkdir -p \
"${ORACLE_BASE}" \
"${ORACLE_HOME}" \
"${INVENTORY}" \
"${DATA_DIR}" \
"${INSTALL_DIR}" \
"${FRA_DEST}"

chown -R "${ORACLE_USER}:${ORACLE_GROUP}" \
"${ORACLE_BASE}" \
"${INVENTORY}" \
"${INSTALL_DIR}"

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DATA_DIR}"

chmod 775 \
"${ORACLE_BASE}" \
"${INVENTORY}" \
"${DATA_DIR}" \
"${FRA_DEST}"

# ============================================================
# 13. 安装系统依赖
# ============================================================

log "安装Oracle依赖包"

yum install -y \
binutils \
compat-libcap1 \
compat-libstdc++-33 \
gcc \
gcc-c++ \
glibc \
glibc-devel \
ksh \
libaio \
libaio-devel \
libgcc \
libstdc++ \
libstdc++-devel \
libXi \
libXtst \
make \
sysstat \
unixODBC \
unixODBC-devel \
unzip \
net-tools \
bc \
openssl \
which

# ============================================================
# 14. 计算共享内存参数
# ============================================================

PAGE_SIZE=$(getconf PAGE_SIZE)

# shmmax按照物理内存的90%设置。
SHMMAX_BYTES=$((MEM_MB * 1024 * 1024 * 90 / 100))

# shmall按照物理内存的90%设置。
SHMALL_PAGES=$((SHMMAX_BYTES / PAGE_SIZE))

# memlock至少覆盖SGA。
MEMLOCK_KB=$((SGA_MAX_SIZE_BYTES / 1024))

log "共享内存参数计算完成"
log "PAGE_SIZE:${PAGE_SIZE}"
log "SHMMAX:${SHMMAX_BYTES}"
log "SHMALL:${SHMALL_PAGES}"
log "MEMLOCK:${MEMLOCK_KB}KB"

# ============================================================
# 15. 配置内核参数
# ============================================================

cat > /etc/sysctl.d/99-oracle.conf <<EOF
fs.aio-max-nr = 1048576
fs.file-max = 6815744

kernel.shmall = ${SHMALL_PAGES}
kernel.shmmax = ${SHMMAX_BYTES}
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
EOF

sysctl --system

# ============================================================
# 16. 配置Oracle用户资源限制
# ============================================================

cat > /etc/security/limits.d/oracle.conf <<EOF
oracle soft nofile 1024
oracle hard nofile 65536

oracle soft nproc 16384
oracle hard nproc 16384

oracle soft stack 10240
oracle hard stack 32768

oracle soft memlock ${MEMLOCK_KB}
oracle hard memlock ${MEMLOCK_KB}
EOF

# ============================================================
# 17. 检查/dev/shm
# ============================================================

SHM_REQUIRED_BYTES=$((DBCA_TOTALMEMORY_MB * 1024 * 1024 + 2147483648))

SHM_CURRENT_BYTES=$(df -B1 /dev/shm | awk 'NR==2 {print $2}')

if [ "${SHM_CURRENT_BYTES}" -lt "${SHM_REQUIRED_BYTES}" ]; then

log "/dev/shm容量不足,开始调整"

mount -o remount,size="${SHM_REQUIRED_BYTES}" /dev/shm \
|| die "/dev/shm扩容失败"

if grep -qE '^[^#]*[[:space:]]/dev/shm[[:space:]]' /etc/fstab; then

sed -i '/^[^#]*[[:space:]]\/dev\/shm[[:space:]]/d' /etc/fstab

fi

echo "tmpfs /dev/shm tmpfs defaults,size=${SHM_REQUIRED_BYTES} 0 0" \
>> /etc/fstab

fi

log "/dev/shm配置完成"

# ============================================================
# 18. 配置Oracle环境变量
# ============================================================

cat > /home/oracle/.bash_profile <<EOF
export TMP=/tmp
export TMPDIR=/tmp

export ORACLE_BASE=${ORACLE_BASE}
export ORACLE_HOME=${ORACLE_HOME}
export ORACLE_SID=${SID}
export ORACLE_UNQNAME=${DB_NAME}

export PATH=\$ORACLE_HOME/bin:\$PATH
export LD_LIBRARY_PATH=\$ORACLE_HOME/lib:/lib:/usr/lib

export CLASSPATH=\$ORACLE_HOME/jlib:\$ORACLE_HOME/rdbms/jlib

export NLS_LANG=AMERICAN_AMERICA.${CHARSET}

export PS1='[\\u@\\h \\W]\\$ '
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" /home/oracle/.bash_profile

chmod 644 /home/oracle/.bash_profile

# ============================================================
# 19. 生成数据库密码
# ============================================================

DB_PASSWORD="Yx$(openssl rand -hex 6)"

[ "${#DB_PASSWORD}" -ge 12 ] \
|| die "数据库密码长度不足"

[ "${#DB_PASSWORD}" -le 16 ] \
|| die "数据库密码超过16个字符"

cat > "${PASSWORD_FILE}" <<EOF
Oracle 11g Database Credentials

DB_NAME: ${DB_NAME}
SID: ${SID}

SYS_PASSWORD: ${DB_PASSWORD}
SYSTEM_PASSWORD: ${DB_PASSWORD}

LISTENER_PORT: ${LISTENER_PORT}
EOF

chmod 600 "${PASSWORD_FILE}"

log "数据库密码生成成功"
log "密码文件:${PASSWORD_FILE}"

# ============================================================
# 20. 解压Oracle安装介质
# ============================================================

if [ ! -x "${INSTALL_SOURCE}/runInstaller" ]; then

log "开始解压Oracle安装介质"

run_as_oracle "
cd '${INSTALL_DIR}'
unzip -oq '${SOFT_DIR}/${ZIP1}'
unzip -oq '${SOFT_DIR}/${ZIP2}'
"

fi

[ -x "${INSTALL_SOURCE}/runInstaller" ] \
|| die "runInstaller不存在"

chown -R "${ORACLE_USER}:${ORACLE_GROUP}" "${INSTALL_DIR}"

log "Oracle安装介质解压完成"

# ============================================================
# 21. Oracle软件安装响应文件
# ============================================================

DB_INSTALL_RSP="${INSTALL_DIR}/db_install.rsp"

cat > "${DB_INSTALL_RSP}" <<EOF
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v11_2_0
oracle.install.option=INSTALL_DB_SWONLY

ORACLE_HOSTNAME=$(hostname -f)

UNIX_GROUP_NAME=${ORACLE_GROUP}
INVENTORY_LOCATION=${INVENTORY}

SELECTED_LANGUAGES=en,zh_CN

ORACLE_HOME=${ORACLE_HOME}
ORACLE_BASE=${ORACLE_BASE}

oracle.install.db.InstallEdition=EE
oracle.install.db.EEOptionsSelection=false

oracle.install.db.optionalComponents=

oracle.install.db.DBA_GROUP=${DBA_GROUP}
oracle.install.db.OPER_GROUP=${OPER_GROUP}

oracle.install.db.CLUSTER_NODES=
oracle.install.db.isRACOneInstall=false
oracle.install.db.racOneServiceName=

oracle.install.db.config.starterdb.type=GENERAL_PURPOSE
oracle.install.db.config.starterdb.globalDBName=${DB_NAME}
oracle.install.db.config.starterdb.SID=${SID}

oracle.install.db.config.starterdb.characterSet=${CHARSET}

oracle.install.db.config.starterdb.memoryOption=false
oracle.install.db.config.starterdb.memoryLimit=

oracle.install.db.config.starterdb.installExampleSchemas=false
oracle.install.db.config.starterdb.enableSecuritySettings=true

oracle.install.db.config.starterdb.password.ALL=
oracle.install.db.config.starterdb.password.SYS=
oracle.install.db.config.starterdb.password.SYSTEM=
oracle.install.db.config.starterdb.password.DBSNMP=
oracle.install.db.config.starterdb.password.SYSMAN=

oracle.install.db.config.starterdb.control=DB_CONTROL

oracle.install.db.config.starterdb.gridcontrol.gridControlServiceURL=

oracle.install.db.config.starterdb.automatedBackup.enable=false

oracle.install.db.config.starterdb.storageType=FILE_SYSTEM_STORAGE

oracle.install.db.config.starterdb.fileSystemStorage.dataLocation=${DATA_DIR}
oracle.install.db.config.starterdb.fileSystemStorage.recoveryLocation=${FRA_DEST}

oracle.install.db.config.asm.diskGroup=

MYORACLESUPPORT_USERNAME=
MYORACLESUPPORT_PASSWORD=

SECURITY_UPDATES_VIA_MYORACLESUPPORT=false
DECLINE_SECURITY_UPDATES=true

oracle.installer.autoupdates.option=SKIP_UPDATES
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DB_INSTALL_RSP}"
chmod 600 "${DB_INSTALL_RSP}"

# ============================================================
# 22. 静默安装Oracle数据库软件
# ============================================================

log "开始安装Oracle 11.2.0.4数据库软件"

INSTALL_LOG="${INSTALL_DIR}/oracle_install.log"

if ! su - "${ORACLE_USER}" -c "
cd '${INSTALL_SOURCE}'

./runInstaller \
-silent \
-ignorePrereq \
-ignoreSysPrereqs \
-ignoreDiskWarning \
-waitforcompletion \
-responseFile '${DB_INSTALL_RSP}'
" > "${INSTALL_LOG}" 2>&1; then

log "Oracle软件安装失败"

tail -100 "${INSTALL_LOG}"

die "Oracle软件安装失败,请检查${INSTALL_LOG}"

fi

[ -x "${ORACLE_HOME}/bin/sqlplus" ] \
|| die "Oracle sqlplus不存在"

log "Oracle数据库软件安装成功"

# ============================================================
# 23. 执行Root脚本
# ============================================================

if [ -f "${INVENTORY}/orainstRoot.sh" ]; then

log "执行orainstRoot.sh"

"${INVENTORY}/orainstRoot.sh"

fi

if [ -f "${ORACLE_HOME}/root.sh" ]; then

log "执行root.sh"

"${ORACLE_HOME}/root.sh"

fi

log "Root脚本执行完成"

# ============================================================
# 24. 配置监听器
# ============================================================

log "开始配置Oracle监听器"

NETCA_RSP="${INSTALL_DIR}/netca.rsp"

cp "${INSTALL_SOURCE}/response/netca.rsp" "${NETCA_RSP}"

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${NETCA_RSP}"

chmod 600 "${NETCA_RSP}"

NETCA_LOG="${INSTALL_DIR}/netca.log"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'

export PATH='${ORACLE_HOME}/bin':\$PATH

'${ORACLE_HOME}/bin/netca' \
-silent \
-responseFile '${NETCA_RSP}'
" > "${NETCA_LOG}" 2>&1; then

tail -100 "${NETCA_LOG}"

die "NETCA监听器配置失败"

fi

log "监听器配置完成"

# ============================================================
# 25. 检查监听器
# ============================================================

su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

lsnrctl status
"

# ============================================================
# 26. DBCA建库响应文件
# ============================================================

DBCA_RSP="${INSTALL_DIR}/dbca_create.rsp"

cat > "${DBCA_RSP}" <<EOF
[GENERAL]

RESPONSEFILE_VERSION = "11.2.0"

OPERATION_TYPE = "createDatabase"

[CREATEDATABASE]

GDBNAME = "${DB_NAME}"

SID = "${SID}"

TEMPLATENAME = "General_Purpose.dbc"

SYSPASSWORD = "${DB_PASSWORD}"

SYSTEMPASSWORD = "${DB_PASSWORD}"

DATAFILEDESTINATION = "${DATA_DIR}"

STORAGETYPE = "FS"

CHARACTERSET = "${CHARSET}"

NATIONALCHARACTERSET = "${NATIONAL_CHARSET}"

DATABASETYPE = "OLTP"

EMCONFIGURATION = "NONE"

RECOVERYAREASIZE = "${FRA_SIZE_MB}"

RECOVERYAREADESTINATION = "${FRA_DEST}"

AUTOMATICMEMORYMANAGEMENT = "TRUE"

TOTALMEMORY = "${DBCA_TOTALMEMORY_MB}"

SAMPLESCHEMA = "FALSE"

LISTENERS = "LISTENER"
EOF

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DBCA_RSP}"

chmod 600 "${DBCA_RSP}"

[ -s "${DBCA_RSP}" ] || die "DBCA响应文件生成失败"

log "DBCA响应文件生成成功"

log "DBCA TOTALMEMORY=${DBCA_TOTALMEMORY_MB}MB"

# ============================================================
# 27. 检查DBCA程序及模板
# ============================================================

[ -x "${ORACLE_HOME}/bin/dbca" ] \
|| die "DBCA程序不存在"

[ -f "${ORACLE_HOME}/assistants/dbca/templates/General_Purpose.dbc" ] \
|| die "General_Purpose.dbc模板不存在"

mkdir -p "${DATA_DIR}" "${FRA_DEST}"

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DATA_DIR}" "${FRA_DEST}"

# ============================================================
# 28. DBCA创建数据库
# ============================================================

log "================================================"
log "开始执行DBCA创建数据库"
log "================================================"

DBCA_LOG="${INSTALL_DIR}/dbca_create.log"

touch "${DBCA_LOG}"

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${DBCA_LOG}"

chmod 600 "${DBCA_LOG}"

# 注意:
# 这里采用第二份脚本的TOTALMEMORY方式。
# 不在DBCA阶段指定16GB SGA和6GB PGA。
# 不通过-initParams提前设置SGA/PGA。
# 建库完成后再进行ASMM配置。

if ! su - "${ORACLE_USER}" -c "
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'

export PATH='${ORACLE_HOME}/bin':\$PATH

export LD_LIBRARY_PATH='${ORACLE_HOME}/lib:/lib:/usr/lib'

export NLS_LANG=AMERICAN_AMERICA.${CHARSET}

'${ORACLE_HOME}/bin/dbca' \
-silent \
-createDatabase \
-responseFile '${DBCA_RSP}' \
-redoLogFileSize '${REDO_SIZE_MB}'
" > "${DBCA_LOG}" 2>&1; then

log "DBCA建库失败"

log "========== DBCA错误日志 =========="

tail -200 "${DBCA_LOG}"

log "=================================="

die "DBCA建库失败,请检查${DBCA_LOG}"

fi

log "DBCA建库命令执行成功"

tail -50 "${DBCA_LOG}"

# ============================================================
# 29. 等待数据库启动
# ============================================================

log "等待数据库启动"

DB_READY=0

for i in $(seq 1 60); do

DB_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT STATUS FROM V\\$INSTANCE;
EXIT;
SQL
" 2>/dev/null | tr -d '[:space:]' || true)

if [ "${DB_STATUS}" = "OPEN" ]; then

DB_READY=1
break

fi

log "数据库启动中,等待10秒"

sleep 10

done

[ "${DB_READY}" -eq 1 ] || die "数据库启动超时"

log "数据库已经成功OPEN"

# ============================================================
# 30. 检查数据库字符集
# ============================================================

log "检查数据库字符集"

CHARSET_SQL="${INSTALL_DIR}/charset_check.sql"

cat > "${CHARSET_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0

SELECT PARAMETER || '=' || VALUE
FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER IN (
'NLS_CHARACTERSET',
'NLS_NCHAR_CHARACTERSET'
)
ORDER BY PARAMETER;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${CHARSET_SQL}"

CHARSET_RESULT=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${CHARSET_SQL}'
")

echo "${CHARSET_RESULT}"

echo "${CHARSET_RESULT}" | grep -q "${CHARSET}" \
|| die "数据库字符集不是${CHARSET}"

echo "${CHARSET_RESULT}" | grep -q "${NATIONAL_CHARSET}" \
|| die "国家字符集不是${NATIONAL_CHARSET}"

log "数据库字符集验证通过"

# ============================================================
# 31. 配置ASMM内存参数
# ============================================================

log "================================================"
log "开始配置ASMM内存参数"
log "================================================"

MEMORY_SQL="${INSTALL_DIR}/memory_config.sql"

cat > "${MEMORY_SQL}" <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE

ALTER SYSTEM SET sga_max_size=${SGA_MAX_SIZE_BYTES} SCOPE=SPFILE;

ALTER SYSTEM SET sga_target=${SGA_TARGET_BYTES} SCOPE=SPFILE;

ALTER SYSTEM SET pga_aggregate_target=${PGA_AGGREGATE_TARGET_BYTES} SCOPE=SPFILE;

ALTER SYSTEM SET memory_target=0 SCOPE=SPFILE;

ALTER SYSTEM SET memory_max_target=0 SCOPE=SPFILE;

ALTER SYSTEM SET processes=${PROCESSES} SCOPE=SPFILE;

ALTER SYSTEM SET open_cursors=600 SCOPE=BOTH;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${MEMORY_SQL}"

chmod 600 "${MEMORY_SQL}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${MEMORY_SQL}'
"; then

die "ASMM内存参数配置失败"

fi

log "ASMM内存参数配置完成"

# ============================================================
# 32. 重启数据库使内存参数生效
# ============================================================

log "重启数据库,使ASMM参数生效"

RESTART_SQL="${INSTALL_DIR}/database_restart.sql"

cat > "${RESTART_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SHUTDOWN IMMEDIATE;

STARTUP;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${RESTART_SQL}"

chmod 600 "${RESTART_SQL}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${RESTART_SQL}'
"; then

die "数据库重启失败"

fi

# ============================================================
# 33. 等待数据库重新启动
# ============================================================

log "等待数据库重新启动"

DB_READY=0

for i in $(seq 1 60); do

DB_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT STATUS FROM V\\$INSTANCE;
EXIT;
SQL
" 2>/dev/null | tr -d '[:space:]' || true)

if [ "${DB_STATUS}" = "OPEN" ]; then

DB_READY=1
break

fi

sleep 10

done

[ "${DB_READY}" -eq 1 ] || die "ASMM配置后数据库启动失败"

log "数据库重新启动成功"

# ============================================================
# 34. 验证ASMM内存参数
# ============================================================

log "检查ASMM内存参数"

MEMORY_CHECK_SQL="${INSTALL_DIR}/memory_check.sql"

cat > "${MEMORY_CHECK_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 200
SET PAGESIZE 100

COLUMN NAME FORMAT A30
COLUMN VALUE FORMAT A30

SELECT NAME, VALUE
FROM V$PARAMETER
WHERE NAME IN (
'sga_target',
'sga_max_size',
'pga_aggregate_target',
'memory_target',
'memory_max_target',
'processes'
)
ORDER BY NAME;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${MEMORY_CHECK_SQL}"

chmod 600 "${MEMORY_CHECK_SQL}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${MEMORY_CHECK_SQL}'
"; then

die "ASMM参数查询失败"

fi

log "ASMM参数查询完成"

# ============================================================
# 35. 数据库状态检查
# ============================================================

log "检查数据库OPEN状态"

DB_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT STATUS FROM V\\$INSTANCE;
EXIT;
SQL
" | tr -d '[:space:]')

[ "${DB_STATUS}" = "OPEN" ] \
|| die "数据库没有处于OPEN状态"

log "数据库状态检查通过"

# ============================================================
# 36. 查询当前Redo日志
# ============================================================

log "检查当前Redo日志"

REDO_QUERY_SQL="${INSTALL_DIR}/redo_query.sql"

cat > "${REDO_QUERY_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0

SELECT
COUNT(*) || '|' || NVL(MAX(GROUP#), 0)
FROM V$LOG;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${REDO_QUERY_SQL}"

chmod 600 "${REDO_QUERY_SQL}"

REDO_INFO=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${REDO_QUERY_SQL}'
" | tr -d '[:space:]')

IFS='|' read -r CURRENT_GROUPS MAX_GROUP <<< "${REDO_INFO}"

[[ "${CURRENT_GROUPS}" =~ ^[0-9]+$ ]] \
|| die "无法获取当前Redo组数"

[[ "${MAX_GROUP}" =~ ^[0-9]+$ ]] \
|| die "无法获取最大Redo Group"

log "当前Redo组数:${CURRENT_GROUPS}"
log "当前最大Group:${MAX_GROUP}"

[ "${CURRENT_GROUPS}" -le "${REDO_GROUPS}" ] \
|| die "当前Redo组数超过目标8组,请人工检查"

# ============================================================
# 37. 获取现有Redo文件目录
# ============================================================

REDO_DIR_SQL="${INSTALL_DIR}/redo_dir.sql"

cat > "${REDO_DIR_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0

SELECT MEMBER
FROM V$LOGFILE
WHERE TYPE = 'ONLINE'
AND MEMBER IS NOT NULL
AND ROWNUM = 1;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${REDO_DIR_SQL}"

chmod 600 "${REDO_DIR_SQL}"

REDO_MEMBER=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${REDO_DIR_SQL}'
" | sed '/^[[:space:]]*$/d' | tail -1 | xargs)

[ -n "${REDO_MEMBER}" ] \
|| die "无法获取Redo文件路径"

REDO_EXISTING_DIR=$(dirname "${REDO_MEMBER}")

log "Redo文件目录:${REDO_EXISTING_DIR}"

[ -d "${REDO_EXISTING_DIR}" ] \
|| die "Redo目录不存在"

# ============================================================
# 38. 补齐Redo日志
# ============================================================

log "开始检查并补齐Redo日志"

while [ "${CURRENT_GROUPS}" -lt "${REDO_GROUPS}" ]; do

NEXT_GROUP=$((MAX_GROUP + 1))

REDO_FILE="${REDO_EXISTING_DIR}/redo${NEXT_GROUP}.log"

[ ! -e "${REDO_FILE}" ] \
|| die "Redo文件已经存在:${REDO_FILE}"

REDO_ADD_SQL="${INSTALL_DIR}/redo_add.sql"

cat > "${REDO_ADD_SQL}" <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE

ALTER DATABASE ADD LOGFILE
GROUP ${NEXT_GROUP}
(
'${REDO_FILE}'
)
SIZE ${REDO_SIZE_MB}M;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${REDO_ADD_SQL}"

chmod 600 "${REDO_ADD_SQL}"

log "创建Redo Group ${NEXT_GROUP}"

if ! su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${REDO_ADD_SQL}'
"; then

die "Redo Group ${NEXT_GROUP}创建失败"

fi

CURRENT_GROUPS=$((CURRENT_GROUPS + 1))

MAX_GROUP="${NEXT_GROUP}"

log "Redo Group ${NEXT_GROUP}创建成功"

done

# ============================================================
# 39. Redo日志最终检查
# ============================================================

log "验证Redo日志"

REDO_CHECK_SQL="${INSTALL_DIR}/redo_check.sql"

cat > "${REDO_CHECK_SQL}" <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0

SELECT
CASE
WHEN COUNT(*) = ${REDO_GROUPS}
AND SUM(
CASE
WHEN BYTES = ${REDO_SIZE_MB} * 1024 * 1024
THEN 1
ELSE 0
END
) = ${REDO_GROUPS}
THEN 'REDO_OK'
ELSE 'REDO_BAD'
END
FROM V\\$LOG;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${REDO_CHECK_SQL}"

chmod 600 "${REDO_CHECK_SQL}"

FINAL_REDO=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${REDO_CHECK_SQL}'
")

echo "${FINAL_REDO}"

echo "${FINAL_REDO}" | grep -q "REDO_OK" \
|| die "Redo日志验证失败"

log "Redo验证成功:${REDO_GROUPS}组 x ${REDO_SIZE_MB}MB"

# ============================================================
# 40. 数据库最终检查
# ============================================================

log "执行数据库最终检查"

FINAL_CHECK_SQL="${INSTALL_DIR}/final_check.sql"

cat > "${FINAL_CHECK_SQL}" <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET LINESIZE 220
SET PAGESIZE 100
SET FEEDBACK ON

PROMPT ========================================
PROMPT INSTANCE
PROMPT ========================================

SELECT INSTANCE_NAME, STATUS, VERSION
FROM V$INSTANCE;

PROMPT ========================================
PROMPT DATABASE
PROMPT ========================================

SELECT NAME, OPEN_MODE, DATABASE_ROLE
FROM V$DATABASE;

PROMPT ========================================
PROMPT DATABASE PARAMETERS
PROMPT ========================================

SHOW PARAMETER DB_NAME
SHOW PARAMETER DB_UNIQUE_NAME
SHOW PARAMETER SERVICE_NAMES
SHOW PARAMETER SGA_TARGET
SHOW PARAMETER SGA_MAX_SIZE
SHOW PARAMETER PGA_AGGREGATE_TARGET
SHOW PARAMETER MEMORY_TARGET
SHOW PARAMETER PROCESSES
SHOW PARAMETER CONTROL_FILES
SHOW PARAMETER DB_RECOVERY_FILE_DEST

PROMPT ========================================
PROMPT CHARACTER SET
PROMPT ========================================

SELECT PARAMETER, VALUE
FROM NLS_DATABASE_PARAMETERS
WHERE PARAMETER IN (
'NLS_CHARACTERSET',
'NLS_NCHAR_CHARACTERSET'
);

PROMPT ========================================
PROMPT FRA
PROMPT ========================================

SELECT
NAME,
SPACE_LIMIT / 1024 / 1024 AS LIMIT_MB,
SPACE_USED / 1024 / 1024 AS USED_MB
FROM V$RECOVERY_FILE_DEST;

PROMPT ========================================
PROMPT REDO LOG
PROMPT ========================================

SELECT
GROUP#,
THREAD#,
BYTES / 1024 / 1024 AS SIZE_MB,
STATUS
FROM V$LOG
ORDER BY GROUP#;

PROMPT ========================================
PROMPT DATAFILES
PROMPT ========================================

SELECT FILE#, NAME, STATUS
FROM V$DATAFILE
ORDER BY FILE#;

PROMPT ========================================
PROMPT CONTROL FILES
PROMPT ========================================

SELECT NAME
FROM V$CONTROLFILE;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${FINAL_CHECK_SQL}"

chmod 600 "${FINAL_CHECK_SQL}"

su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${FINAL_CHECK_SQL}'
"

# ============================================================
# 41. 自动验证数据库核心配置
# ============================================================

log "检查数据库核心配置"

FINAL_SQL="${INSTALL_DIR}/final_verify.sql"

cat > "${FINAL_SQL}" <<SQL
WHENEVER SQLERROR EXIT SQL.SQLCODE

SET HEADING OFF
SET FEEDBACK OFF
SET PAGESIZE 0
SET VERIFY OFF

SELECT
(SELECT NAME FROM V\\$DATABASE)
|| '|'
|| (SELECT LOWER(INSTANCE_NAME) FROM V\\$INSTANCE)
|| '|'
|| (SELECT OPEN_MODE FROM V\\$DATABASE)
|| '|'
|| (SELECT VALUE FROM V\\$PARAMETER WHERE NAME = 'sga_target')
|| '|'
|| (SELECT VALUE FROM V\\$PARAMETER WHERE NAME = 'sga_max_size')
|| '|'
|| (SELECT VALUE FROM V\\$PARAMETER WHERE NAME = 'pga_aggregate_target')
|| '|'
|| (SELECT VALUE FROM V\\$PARAMETER WHERE NAME = 'memory_target')
FROM DUAL;

EXIT;
SQL

chown "${ORACLE_USER}:${ORACLE_GROUP}" "${FINAL_SQL}"

chmod 600 "${FINAL_SQL}"

FINAL_RESULT=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba @'${FINAL_SQL}'
" | tr -d '\r' | sed '/^[[:space:]]*$/d' | tail -1 | xargs)

IFS='|' read -r \
FINAL_DB \
FINAL_SID \
FINAL_OPEN \
FINAL_SGA \
FINAL_SGA_MAX \
FINAL_PGA \
FINAL_MEMORY <<< "${FINAL_RESULT}"

echo "FINAL_RESULT=${FINAL_RESULT}"

[ "${FINAL_DB}" = "${DB_NAME^^}" ] \
|| die "DB_NAME验证失败"

[ "${FINAL_SID}" = "${SID}" ] \
|| die "INSTANCE_NAME验证失败"

[ "${FINAL_OPEN}" = "READ WRITE" ] \
|| die "OPEN_MODE验证失败"

[ "${FINAL_SGA}" = "${SGA_TARGET_BYTES}" ] \
|| die "SGA_TARGET验证失败"

[ "${FINAL_SGA_MAX}" = "${SGA_MAX_SIZE_BYTES}" ] \
|| die "SGA_MAX_SIZE验证失败"

[ "${FINAL_PGA}" = "${PGA_AGGREGATE_TARGET_BYTES}" ] \
|| die "PGA_AGGREGATE_TARGET验证失败"

[ "${FINAL_MEMORY}" = "0" ] \
|| die "MEMORY_TARGET未关闭"

log "数据库核心配置验证通过"

# ============================================================
# 42. 检查监听器状态
# ============================================================

log "检查监听器状态"

su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_BASE='${ORACLE_BASE}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

lsnrctl status
"

# ============================================================
# 43. 强制注册数据库服务
# ============================================================

log "强制注册数据库服务"

su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

sqlplus -L -s / as sysdba <<'SQL'
WHENEVER SQLERROR EXIT SQL.SQLCODE

ALTER SYSTEM REGISTER;

EXIT;
SQL
"

sleep 5

# ============================================================
# 44. 再次检查监听器
# ============================================================

log "再次检查监听器服务"

LISTENER_STATUS=$(su - "${ORACLE_USER}" -c "
export ORACLE_HOME='${ORACLE_HOME}'
export ORACLE_SID='${SID}'
export PATH='${ORACLE_HOME}/bin':\$PATH

lsnrctl status
")

echo "${LISTENER_STATUS}"

echo "${LISTENER_STATUS}" | grep -qi "${DB_NAME}" \
|| die "监听器未发现数据库服务${DB_NAME}"

log "监听器数据库服务注册成功"

# ============================================================
# 45. 安装完成
# ============================================================

log "================================================"
log "Oracle 11.2.0.4 数据库安装成功"
log "================================================"

echo
echo "DB_NAME : ${DB_NAME}"
echo "ORACLE_SID : ${SID}"
echo "ORACLE_BASE : ${ORACLE_BASE}"
echo "ORACLE_HOME : ${ORACLE_HOME}"
echo "DATA_DIR : ${DATA_DIR}"
echo "CHARACTERSET : ${CHARSET}"
echo "NATIONAL_CHARSET : ${NATIONAL_CHARSET}"

echo
echo "MEMORY : ASMM"
echo "SGA_TARGET : ${SGA_TARGET_MB}MB"
echo "SGA_MAX_SIZE : ${SGA_MAX_SIZE_MB}MB"
echo "PGA_AGGREGATE_TARGET : ${PGA_AGGREGATE_TARGET_MB}MB"

echo
echo "REDO : ${REDO_GROUPS} x ${REDO_SIZE_MB}MB"

echo
echo "FRA : ${FRA_DEST}"
echo "FRA_SIZE : ${FRA_SIZE_MB}MB"

echo
echo "LISTENER_PORT : ${LISTENER_PORT}"

echo
echo "PASSWORD_FILE : ${PASSWORD_FILE}"
echo "INSTALL_LOG : ${LOG}"

echo
log "Oracle数据库安装全部完成"

log "请妥善保存数据库密码文件"

log "建议立即执行RMAN全库备份"

log "================================================"

exit 0
  • Title: 一键自动安装Oracle数据库脚本
  • Author: 𝓓𝓸𝓷
  • Created at : 2026-11-12 16:50:13
  • Updated at : 2026-11-13 11:42:47
  • Link: https://www.zhangdong.me/one-command-oracle12c-deployment-script.html
  • License: This work is licensed under CC BY-NC-SA 4.0.
评论