银河麒麟V10SP3-arm版本安装oracle19C数据库

项目上经常有在银河麒麟arm上安装oracle的需求,网上搜的资料过于零散,整理一下自己测试的过程。

1.安装依赖

yum install binutils glibc glibc-devel ksh libaio libaio-devel libX11 libXau libXi libXtst libgcc libstdc++ libstdc++-devel libxcb make nfs-utils net-tools smartmontools sysstat unixODBC unixODBC-devel libnsl -y

# 无网的话下载依赖包手动导入
rpm -ivh *.rpm --nodeps --force --replacefiles

2.初始化环境

# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 关闭selinux
vim /etc/selinux/config
SELINUX=disabled

# 这个是oracle官方提供的初始化脚本可以自动创建oralce用户,修改参数等
rpm -ivh oracle-database-preinstall-19c-1.0-3.el8.aarch64.rpm --nodeps

# 环境初始化之后最好重启一下服务器

3.软连接编译文件

mkdir -p /opt/rh/devtoolset-8/root/usr/bin/

cd /opt/rh/devtoolset-8/root/usr/bin/

ln -s /usr/bin/ar ar

ln -s /usr/bin/as as

ln -s /lib64/libnsl.so.1 /lib64/libnsl.so.2

4.环境变量配置

oracle用户登录 vim .bash_profile

export CV_ASSUME_DISTID=OEL8.8
 
# Oracle Settings
export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=$ORACLE_BASE/product/19.0.0/dbhome_1
export ORA_INVENTORY=/u01/app/oraInventory
export ORACLE_SID=orcl
export PATH=.:$PATH:$HOME/bin:$ORACLE_HOME/bin
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
export CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib

5.建好oracle目录 ,root用户执行

mkdir -p /u01/software/Oracle19c

unzip  /u01/software/Oracle19c/LINUX.ARM64_1919000_db_home.zip -d /u01/app/oracle/product/19.0.0/dbhome_1

mkdir -p /u01/app/oracle/product/19.0.0/dbhome_1

mkdir /u01/app/oraInventory 

chown -R oracle:oinstall /u01

chmod -R 777 /u01

6.安装数据库软件

su - oracle

#静默方式
修改响应文件(压缩包解压完成后得到名为database的文件夹,其中包含安装文件。一般在根目录install/response里)

[oracle@creating ~]$ cd /u01/app/oracle/product/19.0.0/dbhome_1/install/response
[oracle@creating response]$ vim db_install.rsp

oracle.install.option=INSTALL_DB_SWONLY
UNIX_GROUP_NAME=oinstall
INVENTORY_LOCATION=/u01/app/oraInventory 
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
ORACLE_BASE=/u01/app/oracle
oracle.install.db.InstallEdition=EE
oracle.install.db.OSDBA_GROUP=dba
oracle.install.db.OSOPER_GROUP=dba
oracle.install.db.OSBACKUPDBA_GROUP=dba
oracle.install.db.OSDGDBA_GROUP=dba
oracle.install.db.OSKMDBA_GROUP=dba
oracle.install.db.OSRACDBA_GROUP=dba

cd $ORACLE_HOME
./runInstaller -silent -waitForCompletion -ignorePrereqFailure -responseFile /u01/app/oracle/product/19.0.0/dbhome_1/install/response/db_install.rsp

7.手动建库,arm架构目前在银河麒麟V10SP3上dbca无法使用

# 1. 建库脚本中指定的数据文件目录(必须与你CREATE DATABASE语句中的路径一致)
mkdir -p /oracledata/ORCL/datafile
mkdir -p /oracledata/ORCL/onlinelog
mkdir -p /oracledata/ORCL/tempfile

# 2. Oracle系统必需目录(基于你的ORACLE_BASE)
mkdir -p /u01/app/oracle/admin/orcl/adump
mkdir -p /u01/app/oracle/cfgtoollogs

# 3. 授予oracle用户完全权限(关键!否则会报权限不足)
chown -R oracle:oinstall /oracledata
chmod -R 775 /oracledata
chown -R oracle:oinstall /u01/app/oracle
chmod -R 775 /u01/app/oracle

# 切换oracle用户
# 创建初始化参数文件
vi $ORACLE_HOME/dbs/initorcl.ora

db_name=orcl
db_unique_name=orcl
instance_name=orcl
service_names=orcl
memory_target=8G
memory_max_target=16G
processes=300
sessions=335
control_files=(/oracledata/ORCL/control01.ctl, /oracledata/ORCL/control02.ctl)
audit_file_dest=/u01/app/oracle/admin/orcl/adump
diagnostic_dest=/u01/app/oracle
compatible=19.0.0
remote_login_passwordfile=EXCLUSIVE
undo_management=AUTO
undo_tablespace=UNDOTBS1
db_block_size=8192
open_cursors=300

# 切换到root用户重新给下属组
chown -R oracle:oinstall /u01/app/oracle/product/19.0.0/dbhome_1/dbs/initorcl.ora

# 创建oracle密码文件
orapwd file=$ORACLE_HOME/dbs/orapworcl password=Oracle_2026 entries=10 force=y

# sqlplus启动
sqlplus / as sysdba

-- 使用我们创建的参数文件启动
startup nomount pfile='$ORACLE_HOME/dbs/initorcl.ora';

-- 验证实例状态(应该显示STARTED)
select status from v$instance;

建库命令

CREATE DATABASE orcl USER SYS IDENTIFIED BY Oracle_2026 USER SYSTEM IDENTIFIED BY Oracle_2026 LOGFILE GROUP 1 ('/oracledata/ORCL/onlinelog/redo01a.log') SIZE 200M BLOCKSIZE 512, GROUP 2 ('/oracledata/ORCL/onlinelog/redo02a.log') SIZE 200M BLOCKSIZE 512, GROUP 3 ('/oracledata/ORCL/onlinelog/redo03a.log') SIZE 200M BLOCKSIZE 512 MAXLOGHISTORY 1 MAXLOGFILES 16 MAXLOGMEMBERS 3 MAXDATAFILES 8192 CHARACTER SET AL32UTF8 NATIONAL CHARACTER SET AL16UTF16 EXTENT MANAGEMENT LOCAL DATAFILE '/oracledata/ORCL/datafile/system01.dbf' SIZE 700M REUSE AUTOEXTEND ON NEXT 10240K MAXSIZE UNLIMITED SYSAUX DATAFILE '/oracledata/ORCL/datafile/sysaux01.dbf' SIZE 550M REUSE AUTOEXTEND ON NEXT 10240K MAXSIZE UNLIMITED DEFAULT TABLESPACE users DATAFILE '/oracledata/ORCL/datafile/users01.dbf' SIZE 5M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED DEFAULT TEMPORARY TABLESPACE temp TEMPFILE '/oracledata/ORCL/tempfile/temp01.dbf' SIZE 20M REUSE AUTOEXTEND ON NEXT 640K MAXSIZE UNLIMITED UNDO TABLESPACE UNDOTBS1 DATAFILE '/oracledata/ORCL/datafile/undotbs01.dbf' SIZE 200M REUSE AUTOEXTEND ON NEXT 5120K MAXSIZE UNLIMITED;

执行脚本

@?/rdbms/admin/catalog.sql

@?/rdbms/admin/catproc.sql

@?/rdbms/admin/utlrp.sql

-- 必须切换到SYSTEM用户执行,不能用SYS用户
connect system/Oracle_2026
@?/sqlplus/admin/pupbld.sql

-- 1. 检查数据库状态(应该显示OPEN)
select name, open_mode from v$database;

-- 2. 检查所有表空间状态
select tablespace_name, status, contents from dba_tablespaces;

-- 3. 检查所有数据文件状态
select name, status, bytes/1024/1024 as size_mb from v$datafile order by name;

-- 4. 检查无效对象数量(应该为0或个位数)
select count(*) from dba_objects where status='INVALID';

create spfile from pfile='$ORACLE_HOME/dbs/initorcl.ora';

-- 重启数据库验证SPFILE生效
shutdown immediate
startup

-- 确认使用的是SPFILE
show parameter spfile;

8.创建监听文件

vim $ORACLE_HOME/network/admin/listener.ora

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 你的服务器IP地址)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = orcl)
      (ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1)
      (SID_NAME = orcl)
    )
  )
  
  
 vim $ORACLE_HOME/network/admin/tnsnames.ora
 
 ORCL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 你的服务器IP地址)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
  
  
 # 启动监听
lsnrctl start

# 查看监听状态
lsnrctl status

# 测试本地网络连接
sqlplus system/Creating2026@orcl

9.设置数据库自启动

sudo vim /etc/oratab

orcl:/u01/app/oracle/product/19.0.0/dbhome_1:Y

# 创建系统服务
sudo vim /etc/systemd/system/oracle.service

[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
ExecStart=/u01/app/oracle/product/19.0.0/dbhome_1/bin/dbstart $ORACLE_HOME
ExecStop=/u01/app/oracle/product/19.0.0/dbhome_1/bin/dbshut $ORACLE_HOME
User=oracle
Group=oinstall
Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target

# 启动服务
sudo systemctl daemon-reload
sudo systemctl enable oracle
sudo systemctl start oracle

10.依赖包和一键安装脚本整理中,后续补充

在这里插入图片描述
在这里插入图片描述

11. 一键安装脚本

#!/bin/bash
# =====================================================================
# Oracle 19c ARM64 一键安装脚本
# 适配:银河麒麟 V10 SP3 aarch64
# =====================================================================

# 【第一步】日志初始化(必须在所有操作之前)
LOG_DIR="/var/log"
LOG_FILE="${LOG_DIR}/oracle19c_arm_install_$(date +%Y%m%d_%H%M%S).log"

if [ ! -w "${LOG_DIR}" ]; then
    LOG_DIR="/tmp"
    LOG_FILE="${LOG_DIR}/oracle19c_arm_install_$(date +%Y%m%d_%H%M%S).log"
fi

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

# 【第二步】错误捕获钩子
trap 'echo -e "\n❌ 脚本在第 $LINENO 行失败,错误代码: $?"; exit 1' ERR
set -o pipefail

export LANG=C.UTF-8

# =====================================================================
# 配置变量
# =====================================================================
ORACLE_SID="orcl"
ORACLE_BASE="/u01/app/oracle"
ORACLE_HOME="${ORACLE_BASE}/product/19.0.0/dbhome_1"
ORA_INVENTORY="/u01/app/oraInventory"
DATA_PATH="/oracledata/${ORACLE_SID}"
ZIP_FILE="LINUX.ARM64_1919000_db_home.zip"
PREINSTALL_RPM="oracle-database-preinstall-19c-1.0-3.el8.aarch64.rpm"
LISTENER_PORT=1521
STAGE1_FLAG="/root/.oracle_stage1_done"
INSTALL_CONF="/root/.oracle_install.conf"
ORACLE_OS_PASSWORD="Oracle@2026,"
RESPONSE_FILE="${ORACLE_HOME}/install/response/db_install.rsp"
RESPONSE_FILE_BAK="${ORACLE_HOME}/install/response/db_install.rsp.bak"

# =====================================================================
# 工具函数
# =====================================================================
error_exit() {
    echo -e "\n❌ 错误:$1"
    echo -e "📄 排查日志:${LOG_FILE}\n"
    exit 1
}

success_msg() {
    echo -e "✅ $1"
}

warning_msg() {
    echo -e "⚠️  $1"
}

info_msg() {
    echo -e "ℹ️  $1"
}

get_local_ip() {
    ip a | grep "inet " | grep -v 127.0.0.1 | awk '{print $2}' | cut -d/ -f1 | head -1
}

check_root() {
    [ $(id -u) -ne 0 ] && error_exit "必须使用 root 用户执行脚本!"
}

# =====================================================================
# SQL 执行核心函数
# =====================================================================
exec_sql() {
    local desc="$1"
    local sql_content="$2"
    local connect_str="${3:-/ as sysdba}"

    local tmpfile
    tmpfile=$(mktemp /tmp/oracle_sql_XXXXXX.sql)

    cat > "${tmpfile}" << SQLFILE
WHENEVER SQLERROR EXIT SQL.SQLCODE
SET ECHO ON
SET FEEDBACK ON
SET PAGESIZE 50
SET LINESIZE 200
${sql_content}
EXIT;
SQLFILE

    chown oracle:oinstall "${tmpfile}" 2>/dev/null || true

    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "⏳ ${desc}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

    su - oracle -c "NLS_LANG=AMERICAN_AMERICA.AL32UTF8 sqlplus ${connect_str} @${tmpfile}" 2>&1 | tee -a "${LOG_FILE}"
    local rc=${PIPESTATUS[0]}

    rm -f "${tmpfile}"

    if [ "${rc}" -ne 0 ]; then
        error_exit "${desc} 执行失败 (sqlplus 退出码=${rc})"
    fi

    success_msg "${desc} 完成"
    echo ""
}

# =====================================================================
# 运行内置 SQL 脚本文件
# =====================================================================
exec_sql_script() {
    local desc="$1"
    local script_path="$2"
    local connect_str="${3:-/ as sysdba}"

    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "⏳ ${desc}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

    su - oracle -c "NLS_LANG=AMERICAN_AMERICA.AL32UTF8 sqlplus ${connect_str} << 'ENDSQL'
SET ECHO ON
SET FEEDBACK ON
SET PAGESIZE 50
SET LINESIZE 200
@${script_path}
EXIT;
ENDSQL" 2>&1 | tee -a "${LOG_FILE}"

    local rc=${PIPESTATUS[0]}
    if [ "${rc}" -ne 0 ]; then
        error_exit "${desc} 执行失败 (sqlplus 退出码=${rc})"
    fi
    success_msg "${desc} 完成"
    echo ""
}

# =====================================================================
# 环境检查
# =====================================================================
check_env() {
    echo -e "🔍 系统环境检查...\n"

    [ $(uname -m) != "aarch64" ] && error_exit "仅支持 ARM64(aarch64) 架构!"
    echo "✓ 架构: $(uname -m)"

    HOST_NAME=$(hostname)
    if [[ "${HOST_NAME}" == "localhost" || "${HOST_NAME}" == "localhost.localdomain" ]]; then
        warning_msg "检测到主机名为 localhost,自动修改为 oracle19c..."
        hostnamectl set-hostname oracle19c
        sed -i '/oracle19c/d' /etc/hosts
        echo "127.0.0.1   oracle19c localhost" >> /etc/hosts
        success_msg "主机名已修改为 oracle19c"
    fi

    mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    mem_gb=$((mem_total / 1024 / 1024))
    echo "✓ 内存: ${mem_gb} GB"
    [ ${mem_total} -lt 8388608 ] && warning_msg "建议内存 ≥8G(当前 ${mem_gb}G 可能影响稳定性)"

    swap_total=$(grep SwapTotal /proc/meminfo | awk '{print $2}')
    swap_gb=$((swap_total / 1024 / 1024))
    echo "✓ Swap: ${swap_gb} GB"
    [ ${swap_total} -lt 16777216 ] && warning_msg "建议 Swap ≥16G(ARM 必备,当前 ${swap_gb}G 较小)"

    mkdir -p /u01
    disk_free=$(df -m /u01 | awk 'NR==2{print $4}')
    echo "✓ /u01 可用空间: ${disk_free} MB"
    [ ${disk_free} -lt 30720 ] && error_exit "/u01 目录剩余空间不足 30G!"

    echo ""
    success_msg "环境检查通过"
    echo ""
}

# =====================================================================
# 依赖安装
# =====================================================================
install_deps() {
    echo -e "📦 安装系统依赖..."
    DEPS="binutils glibc glibc-devel ksh libaio libaio-devel libX11 libXau libXi libXtst libgcc libstdc++ libstdc++-devel libxcb make nfs-utils net-tools smartmontools sysstat unixODBC unixODBC-devel libnsl"

    ping -c 2 -W 2 www.google.com >/dev/null 2>&1
    if [ $? -eq 0 ]; then
        echo -e "🌐 网络正常,在线安装依赖..."
        dnf install -y ${DEPS} || yum install -y ${DEPS}
    else
        echo -e "📶 无网络,本地RPM离线安装..."
        shopt -s nullglob
        RPM_FILES=()
        for file in *.rpm; do
            if [[ "${file}" == oracle-database-preinstall-19c* ]]; then
                continue
            fi
            RPM_FILES+=("${file}")
        done
        shopt -u nullglob

        if [ ${#RPM_FILES[@]} -eq 0 ]; then
            error_exit "当前目录无可用离线依赖RPM包!"
        fi

        rpm -ivh "${RPM_FILES[@]}" --nodeps --force --replacefiles 2>/dev/null || true
    fi

    echo -e "🔧 安装Oracle官方前置包:${PREINSTALL_RPM}"
    [ -f "${PREINSTALL_RPM}" ] || error_exit "缺少官方前置包:${PREINSTALL_RPM}"
    rpm -ivh "${PREINSTALL_RPM}" --nodeps --force 2>/dev/null || echo "✅ 前置包已安装"
    

    echo -e "🔐 设置oracle系统用户密码"
    echo "oracle:${ORACLE_OS_PASSWORD}" | chpasswd
    echo -e "✅ oracle系统用户密码已设置为:${ORACLE_OS_PASSWORD}"
    
    echo -e "✅ 依赖安装完成\n"
}

# =====================================================================
# 系统初始化
# =====================================================================
system_init() {
    echo -e "⚙️  系统基础配置...\n"

    systemctl stop firewalld 2>/dev/null && systemctl disable firewalld 2>/dev/null || true
    sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
    setenforce 0 2>/dev/null || true

    mkdir -p /opt/rh/devtoolset-8/root/usr/bin/
    [ ! -L "/opt/rh/devtoolset-8/root/usr/bin/ar" ] && ln -sf /usr/bin/ar /opt/rh/devtoolset-8/root/usr/bin/ar 2>/dev/null || true
    [ ! -L "/opt/rh/devtoolset-8/root/usr/bin/as" ] && ln -sf /usr/bin/as /opt/rh/devtoolset-8/root/usr/bin/as 2>/dev/null || true
    [ ! -L "/lib64/libnsl.so.2" ] && ln -sf /lib64/libnsl.so.1 /lib64/libnsl.so.2 2>/dev/null || true

    HOST_IP=$(get_local_ip)
    HOST_NAME=$(hostname)
    sed -i "/${HOST_NAME}/d" /etc/hosts 2>/dev/null || true
    echo "${HOST_IP}   ${HOST_NAME}" >> /etc/hosts

    success_msg "系统初始化完成"
    echo ""
}

# =====================================================================
# 创建目录
# =====================================================================
create_dirs() {
    echo -e "📂 创建 Oracle 目录结构...\n"

    mkdir -p "${ORACLE_HOME}" \
             "${ORA_INVENTORY}" \
             "${DATA_PATH}/datafile" \
             "${DATA_PATH}/onlinelog" \
             "${DATA_PATH}/tempfile" \
             "${ORACLE_BASE}/admin/${ORACLE_SID}/adump" \
             "${ORACLE_BASE}/cfgtoollogs" \
             /u01/software

    chown -R oracle:oinstall /u01 /oracledata 2>/dev/null || true
    chmod -R 775 /u01 /oracledata 2>/dev/null || true

    success_msg "目录创建和权限设置完成"
    echo ""
}

# =====================================================================
# 配置 Oracle 环境变量
# =====================================================================
config_oracle_env() {
    echo -e "🔧 配置 oracle 用户环境变量...\n"

    cat > /home/oracle/.bash_profile << 'EOF'
export CV_ASSUME_DISTID=OEL8.8
export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export ORACLE_SID=orcl
export ORA_INVENTORY=/u01/app/oraInventory
export PATH=.:$PATH:$HOME/bin:$ORACLE_HOME/bin
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
export CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
EOF

    chown oracle:oinstall /home/oracle/.bash_profile
    success_msg "环境变量配置完成"
    echo ""
}

# =====================================================================
# 第一阶段
# =====================================================================
stage1() {
    check_root
    check_env

    echo ""
    DEFAULT_IP=$(get_local_ip)
    read -p "请输入服务器 IP [默认: ${DEFAULT_IP}]: " SERVER_IP
    SERVER_IP=${SERVER_IP:-${DEFAULT_IP}}

    read -s -p "请设置 SYS/SYSTEM 数据库密码 [默认: Oracle_2026]: " DB_PWD
    echo ""
    DB_PWD=${DB_PWD:-Oracle_2026}

    cat > "${INSTALL_CONF}" << EOF
SERVER_IP="${SERVER_IP}"
DB_PWD="${DB_PWD}"
ORACLE_OS_PASSWORD="${ORACLE_OS_PASSWORD}"
EOF
    chmod 600 "${INSTALL_CONF}"

    install_deps
	echo -e "✅ 第一阶段安装完成!"
    system_init
    create_dirs
    config_oracle_env

    touch "${STAGE1_FLAG}"

    echo -e "============================================"
    echo -e "✅ 第一阶段安装完成!"
    echo -e "⚠️  【重要】必须执行重启命令:reboot"
    echo -e "🔄 重启后重新执行本脚本,进入第二阶段"
    echo -e "============================================"
    exit 0
}

# =====================================================================
# 检查是否已解压
# =====================================================================
is_already_extracted() {
    [ -f "${ORACLE_HOME}/runInstaller" ] && [ -d "${ORACLE_HOME}/install/response" ]
}

# =====================================================================
# 【核心修复】生成响应文件 → 仅改手动必填项,其余全部原生留空
# 完全对齐你成功服务器的rsp文件,无任何多余修改
# =====================================================================
generate_response_file() {
    echo -e "📝 生成响应文件(复刻手动vim编辑规则)...\n"

    # 备份原生响应文件
    if [ -f "${ORACLE_HOME}/install/response/db_install.rsp" ]; then
        cp "${ORACLE_HOME}/install/response/db_install.rsp" "${RESPONSE_FILE_BAK}"
        cp "${RESPONSE_FILE_BAK}" "${RESPONSE_FILE}"
    else
        error_exit "未找到原生响应文件,请先解压Oracle安装包!"
    fi

    # ========== 只修改你手动配置的字段,仅此12行,其余一概不动 ==========
    sed -i "s|^oracle.install.option=.*|oracle.install.option=INSTALL_DB_SWONLY|" "${RESPONSE_FILE}"
    sed -i "s|^UNIX_GROUP_NAME=.*|UNIX_GROUP_NAME=oinstall|" "${RESPONSE_FILE}"
    sed -i "s|^INVENTORY_LOCATION=.*|INVENTORY_LOCATION=${ORA_INVENTORY}|" "${RESPONSE_FILE}"
    sed -i "s|^ORACLE_HOME=.*|ORACLE_HOME=${ORACLE_HOME}|" "${RESPONSE_FILE}"
    sed -i "s|^ORACLE_BASE=.*|ORACLE_BASE=${ORACLE_BASE}|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.InstallEdition=.*|oracle.install.db.InstallEdition=EE|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSDBA_GROUP=.*|oracle.install.db.OSDBA_GROUP=dba|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSOPER_GROUP=.*|oracle.install.db.OSOPER_GROUP=dba|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSBACKUPDBA_GROUP=.*|oracle.install.db.OSBACKUPDBA_GROUP=dba|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSDGDBA_GROUP=.*|oracle.install.db.OSDGDBA_GROUP=dba|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSKMDBA_GROUP=.*|oracle.install.db.OSKMDBA_GROUP=dba|" "${RESPONSE_FILE}"
    sed -i "s|^oracle.install.db.OSRACDBA_GROUP=.*|oracle.install.db.OSRACDBA_GROUP=dba|" "${RESPONSE_FILE}"
    # =================================================================

    # 彻底放弃修改:内存、数据库类型、SID等所有字段,保持原生空白
    success_msg "响应文件生成完成,与手动成功配置完全一致"
    echo ""
}

# =====================================================================
# 验证 Oracle 软件安装
# =====================================================================
verify_oracle_install() {
    echo -e "🔍 验证 Oracle 软件安装...\n"

    for f in "${ORACLE_HOME}/bin/sqlplus" "${ORACLE_HOME}/bin/oracle" "${ORACLE_HOME}/lib/libclntsh.so.19.1"; do
        if [ -f "${f}" ]; then
            echo "✓ 文件存在: ${f}"
        else
            error_exit "缺少关键文件:${f}"
        fi
    done

    echo ""
    echo "🧪 测试 sqlplus 命令可用性..."
    local sqlplus_ver
    sqlplus_ver=$(su - oracle -c "sqlplus -v" 2>&1)
    if echo "${sqlplus_ver}" | grep -qi "SQL\*Plus"; then
        success_msg "sqlplus 可用:${sqlplus_ver}"
    else
        error_exit "sqlplus 命令调用失败,输出:${sqlplus_ver}"
    fi

    echo ""
}

# =====================================================================
# 确保 Oracle 用户和组存在
# =====================================================================
setup_oracle_user() {
    echo -e "👤 检查并修复 Oracle 用户/组...\n"

    for grp in oinstall dba oper backupdba dgdba kmdba racdba; do
        if ! getent group "${grp}" >/dev/null 2>&1; then
            groupadd "${grp}"
            echo "  ✓ 创建组: ${grp}"
        else
            echo "  ✓ 组已存在: ${grp}"
        fi
    done

    if ! id oracle >/dev/null 2>&1; then
        useradd -m -g oinstall -G dba,oper,backupdba,dgdba,kmdba,racdba oracle
        echo "  ✓ 创建用户: oracle"
    else
        usermod -g oinstall -G dba,oper,backupdba,dgdba,kmdba,racdba oracle
        echo "  ✓ oracle 用户组已修正"
    fi

    echo "oracle:${ORACLE_OS_PASSWORD}" | chpasswd
    echo "  ✓ oracle 密码已确认"

    chown oracle:oinstall /home/oracle 2>/dev/null || true
    success_msg "Oracle 用户/组检查完成"
    echo ""
}

# =====================================================================
# 第二阶段 - 安装 Oracle 软件
# =====================================================================
stage2_install_software() {
    echo -e "\n🚀 Oracle 19c ARM64 一键安装 - 第二阶段"
    echo "IP: ${SERVER_IP} | SID: ${ORACLE_SID}"
    echo "============================================"
    echo ""

    setup_oracle_user

    if is_already_extracted; then
        success_msg "检测到 Oracle 软件已解压,跳过解压步骤"
    else
        echo "🗂️  解压 Oracle 安装包(耗时较长)..."
        [ -f "${ZIP_FILE}" ] || error_exit "缺少安装包:${ZIP_FILE}"
        unzip -q "${ZIP_FILE}" -d "${ORACLE_HOME}/" 2>/dev/null || error_exit "解压失败"
    fi

    chown -R oracle:oinstall "${ORACLE_HOME}" 2>/dev/null || true

    generate_response_file

    echo "📝 执行静默安装(耗时 10-20 分钟)..."
    su - oracle -c "cd ${ORACLE_HOME} && ./runInstaller -silent -waitForCompletion -ignorePrereqFailure -responseFile ${RESPONSE_FILE}" 2>&1 | tee -a "${LOG_FILE}"
    local rc=${PIPESTATUS[0]}
    if [ "${rc}" -ne 0 ] && [ "${rc}" -ne 6 ]; then
        error_exit "runInstaller 失败(退出码=${rc})"
    fi

    sleep 3
    verify_oracle_install
    echo ""
}

# =====================================================================
# 第二阶段 - 执行 Root 脚本
# =====================================================================
stage2_root_scripts() {
    echo -e "🔑 执行 Root 脚本...\n"

    if [ -f "${ORA_INVENTORY}/orainstRoot.sh" ]; then
        echo "执行 orainstRoot.sh..."
        "${ORA_INVENTORY}/orainstRoot.sh" 2>&1 | tee -a "${LOG_FILE}"
        success_msg "orainstRoot.sh 执行完成"
    fi

    if [ -f "${ORACLE_HOME}/root.sh" ]; then
        echo ""
        echo "执行 root.sh..."
        "${ORACLE_HOME}/root.sh" 2>&1 | tee -a "${LOG_FILE}"
        success_msg "root.sh 执行完成"
    fi

    echo ""
}

# =====================================================================
# 创建初始化参数文件
# =====================================================================
create_init_file() {
    echo -e "📄 生成初始化参数文件...\n"

    cat > "${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora" << EOF
db_name=${ORACLE_SID}
db_unique_name=${ORACLE_SID}
instance_name=${ORACLE_SID}
service_names=${ORACLE_SID}
memory_target=8G
memory_max_target=16G
processes=300
sessions=335
control_files=(${DATA_PATH}/control01.ctl,${DATA_PATH}/control02.ctl,${DATA_PATH}/control03.ctl)
audit_file_dest=${ORACLE_BASE}/admin/${ORACLE_SID}/adump
diagnostic_dest=${ORACLE_BASE}
compatible=19.0.0
remote_login_passwordfile=EXCLUSIVE
undo_management=AUTO
undo_tablespace=UNDOTBS1
db_block_size=8192
open_cursors=300
EOF

    chown oracle:oinstall "${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora"
    success_msg "初始化参数文件创建完成"
    echo ""
}

# =====================================================================
# 创建数据库密码文件
# =====================================================================
create_password_file() {
    echo -e "🔐 创建数据库密码文件...\n"
    su - oracle -c "orapwd file=${ORACLE_HOME}/dbs/orapw${ORACLE_SID} password=${DB_PWD} entries=10 force=y" 2>&1 | tee -a "${LOG_FILE}"
    success_msg "密码文件创建完成"
    echo ""
}

# =====================================================================
# 创建数据库
# =====================================================================
create_database() {
    echo -e "🏗️  创建数据库(耗时 30-60 分钟,请勿中断)...\n"
    exec_sql "启动实例到 NOMOUNT 状态(使用 pfile)" \
        "STARTUP NOMOUNT PFILE='${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora';"

    sleep 3
    exec_sql "创建数据库 ${ORACLE_SID}" \
"CREATE DATABASE ${ORACLE_SID}
  USER SYS    IDENTIFIED BY ${DB_PWD}
  USER SYSTEM IDENTIFIED BY ${DB_PWD}
  LOGFILE
    GROUP 1 ('${DATA_PATH}/onlinelog/redo01a.log','${DATA_PATH}/onlinelog/redo01b.log') SIZE 200M,
    GROUP 2 ('${DATA_PATH}/onlinelog/redo02a.log','${DATA_PATH}/onlinelog/redo02b.log') SIZE 200M,
    GROUP 3 ('${DATA_PATH}/onlinelog/redo03a.log','${DATA_PATH}/onlinelog/redo03b.log') SIZE 200M
  MAXLOGHISTORY 1
  MAXLOGFILES  16
  MAXLOGMEMBERS 3
  MAXDATAFILES 8192
  CHARACTER SET AL32UTF8
  NATIONAL CHARACTER SET AL16UTF16
  DATAFILE        '${DATA_PATH}/datafile/system01.dbf'  SIZE 1000M REUSE AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
  SYSAUX DATAFILE '${DATA_PATH}/datafile/sysaux01.dbf'  SIZE  800M REUSE AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED
  DEFAULT TABLESPACE users
    DATAFILE        '${DATA_PATH}/datafile/users01.dbf'  SIZE  500M REUSE AUTOEXTEND ON MAXSIZE UNLIMITED
  DEFAULT TEMPORARY TABLESPACE temp
    TEMPFILE        '${DATA_PATH}/tempfile/temp01.dbf'   SIZE  500M REUSE AUTOEXTEND ON NEXT 640K MAXSIZE UNLIMITED
  UNDO TABLESPACE UNDOTBS1
    DATAFILE        '${DATA_PATH}/datafile/undotbs01.dbf' SIZE  300M REUSE AUTOEXTEND ON NEXT 5M MAXSIZE UNLIMITED;"
    sleep 5
    echo ""
}

# =====================================================================
# 执行数据字典脚本
# =====================================================================
run_dict_scripts() {
    echo -e "📜 执行数据字典脚本(每步均需等待完成)...\n"
    exec_sql_script "执行 catalog.sql(约 15-30 分钟)" "?/rdbms/admin/catalog.sql"
    sleep 3
    exec_sql_script "执行 catproc.sql(约 30-60 分钟)" "?/rdbms/admin/catproc.sql"
    sleep 3
    exec_sql_script "执行 utlrp.sql(编译无效对象)" "?/rdbms/admin/utlrp.sql"
    sleep 3
    exec_sql_script "执行 pupbld.sql(SYSTEM 用户)" "?/sqlplus/admin/pupbld.sql" "system/${DB_PWD}"
    sleep 3
}

# =====================================================================
# 创建 SPFILE 并重启
# =====================================================================
create_spfile_restart() {
    echo -e "🔧 创建 SPFILE 并重启数据库...\n"
    exec_sql "从 pfile 创建 SPFILE" "CREATE SPFILE FROM PFILE='${ORACLE_HOME}/dbs/init${ORACLE_SID}.ora';"
    exec_sql "关闭数据库" "SHUTDOWN IMMEDIATE;"
    sleep 3
    exec_sql "以 SPFILE 启动数据库" "STARTUP;"
    sleep 5
    success_msg "SPFILE 创建完成,数据库已重启"
    echo ""
}

# =====================================================================
# 配置监听器
# =====================================================================
config_listener() {
    echo -e "📡 配置监听器...\n"
    mkdir -p "${ORACLE_HOME}/network/admin"
    cat > "${ORACLE_HOME}/network/admin/listener.ora" << EOF
LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = ${SERVER_IP})(PORT = ${LISTENER_PORT}))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )
SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = ${ORACLE_SID})
      (ORACLE_HOME   = ${ORACLE_HOME})
      (SID_NAME      = ${ORACLE_SID})
    )
  )
EOF

    cat > "${ORACLE_HOME}/network/admin/tnsnames.ora" << EOF
${ORACLE_SID} =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = ${SERVER_IP})(PORT = ${LISTENER_PORT}))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ${ORACLE_SID})
    )
  )
EOF

    chown oracle:oinstall "${ORACLE_HOME}/network/admin/listener.ora" "${ORACLE_HOME}/network/admin/tnsnames.ora"
    su - oracle -c "lsnrctl stop" 2>/dev/null || true
    sleep 2
    su - oracle -c "lsnrctl start"  2>&1 | tee -a "${LOG_FILE}"
    su - oracle -c "lsnrctl status" 2>&1 | tee -a "${LOG_FILE}"
    success_msg "监听器配置完成"
    echo ""
}

# =====================================================================
# 配置开机自启
# =====================================================================
config_autostart() {
    echo -e "🔄 配置开机自启...\n"
    if grep -q "^${ORACLE_SID}:" /etc/oratab 2>/dev/null; then
        sed -i "s|^${ORACLE_SID}:.*|${ORACLE_SID}:${ORACLE_HOME}:Y|" /etc/oratab
    else
        echo "${ORACLE_SID}:${ORACLE_HOME}:Y" >> /etc/oratab
    fi

    cat > /etc/systemd/system/oracle.service << EOF
[Unit]
Description=Oracle 19c Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=oinstall
ExecStart=${ORACLE_HOME}/bin/dbstart ${ORACLE_HOME}
ExecStop=${ORACLE_HOME}/bin/dbshut ${ORACLE_HOME}
Restart=always

[Install]
WantedBy=multi-user.target
EOF

    systemctl daemon-reload
    systemctl enable oracle
    systemctl start oracle
    success_msg "开机自启配置完成"
    echo ""
}

# =====================================================================
# 最终验证
# =====================================================================
verify_installation() {
    echo -e "🔍 最终验证 Oracle 安装\n"
    echo "【1/3 检查关键文件】"
    for f in "${ORACLE_HOME}/bin/sqlplus" "${ORACLE_HOME}/bin/oracle" "${ORACLE_HOME}/lib/libclntsh.so.19.1"; do
        [ -f "${f}" ] && echo "✓ ${f}" || error_exit "缺失文件:${f}"
    done

    echo ""
    echo "【2/3 验证 sqlplus 可用性】"
    su - oracle -c "sqlplus -v" 2>&1 | tee -a "${LOG_FILE}"

    echo ""
    echo "【3/3 测试数据库连接】"
    exec_sql "查询数据库与实例状态" \
"SELECT 'Database : ' || name || '  Status: ' || open_mode FROM v\$database;
SELECT 'Instance : ' || instance_name || '  Status: ' || status FROM v\$instance;"

    echo ""
}

# =====================================================================
# 第二阶段完整流程
# =====================================================================
stage2() {
    check_root
    [ ! -f "${STAGE1_FLAG}"  ] && error_exit "未完成第一阶段,请先执行脚本并重启!"
    [ ! -f "${INSTALL_CONF}" ] && error_exit "配置文件丢失:${INSTALL_CONF}"
    source "${INSTALL_CONF}"

    # 修复旧环境错误NLS_LANG
    if grep -q "NLS_LANG=en_US" /home/oracle/.bash_profile 2>/dev/null; then
        sed -i 's|NLS_LANG=en_US.*|NLS_LANG=AMERICAN_AMERICA.AL32UTF8|' /home/oracle/.bash_profile
        info_msg "已修复 .bash_profile 中的 NLS_LANG 格式"
    fi

    stage2_install_software
    stage2_root_scripts
    create_init_file
    create_password_file
    create_database
    run_dict_scripts
    create_spfile_restart
    config_listener
    config_autostart
    verify_installation

    rm -f "${STAGE1_FLAG}"

    echo -e "\n============================================"
    echo -e "🎉 Oracle 19c ARM64 安装全部完成!"
    echo -e "============================================"
    echo -e "🔑 系统用户  :oracle / ${ORACLE_OS_PASSWORD}"
    echo -e "🔑 数据库用户:system / ${DB_PWD}"
    echo -e "📶 连接串    ://${SERVER_IP}:${LISTENER_PORT}/${ORACLE_SID}"
    echo -e "📄 安装日志  :${LOG_FILE}"
    echo -e "============================================"
    exit 0
}

# =====================================================================
# 脚本入口
# =====================================================================
if [ -f "${STAGE1_FLAG}" ]; then
    stage2
else
    stage1
fi

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐