Oracle RAC


RACに関して
https://www.oracle.com/technetwork/jp/ondemand/database/db-technique/d-4-rac11gr2-1448379-ja.pdf


# 0.RAC構築方法
## 1.前提条件整備
```ruby:ユーザやディレクトリの整備(以下は例)
USER=oracle
GROUP1=oinstall
GROUP2=dba
GROUP3=asmadmin
export GRID_BASE=/opt/app/oracle/product
export GRID_HOME=/opt/app/oracle/grid/product/12.2.0
export ORACLE_BASE=/opt/app/oracle/product
export ORACLE_HOME=/opt/app/oracle/product/dbhome_1/12.2.0

groupadd -g 10001 ${GROUP1}
groupadd -g 10002 ${GROUP2}
groupadd -g 10003 ${GROUP3}
useradd -u 10001 -g ${GROUP1} -G ${GROUP1},${GROUP2},${GROUP3} ${USER}
mkdir -p ${GRID_BASE} ${GRID_HOME} ${ORACLE_BASE} ${ORACLE_HOME}
chown -R ${USER}:${GROUP1} /opt/app
echo "${USER}:P#ssw0rd" | chpasswd

MEMTOTAL=$(free -b | sed -n '2p' | awk '{print $2}')
SHMMAX=$(expr $MEMTOTAL / 2)
SHMMNI=4096
PAGESIZE=$(getconf PAGE_SIZE)
cat >> /etc/sysctl.conf << EOF
fs.aio-max-nr = 1048576
fs.file-max = 6815744
kernel.shmmax = $SHMMAX
kernel.shmall = `expr \( $SHMMAX / $PAGESIZE \) \* \( $SHMMNI / 16 \)`
kernel.shmmni = $SHMMNI
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 -p

cat >> /etc/security/limits.conf << EOF
oracle  soft  nproc   2047
oracle  hard  nproc   16384
oracle  soft  nofile  1024
oracle  hard  nofile  65536
oracle  soft  stack   10240
oracle  hard  stack   32768
EOF

#ntp無効化(chronydとntp両方有効だとGridインストール時にエラーになるため
systemctl disable ntpd && systemctl stop ntpd
mv /etc/ntp.conf /etc/ntp.conf_org

```
```ruby:RPM整備
マニュアル
https://docs.oracle.com/cd/E82638_01/ladbi/supported-red-hat-enterprise-linux-7-distributions-for-x86-64.html#GUID-2E11B561-6587-4789-A583-2E33D705E498

while read -r RPM;do
yum -y install ${RPM}
done << EOF
bc
binutils.x86_64
compat-libcap1.x86_64
compat-libstdc++.i686
compat-libstdc++-33.x86_64
glibc.i686
glibc.x86_64
glibc-devel.i686
glibc-devel.x86_64
ksh
libaio.i686
libaio.x86_64
libaio-devel.i686
libaio-devel.x86_64
libgcc.i686
libgcc.x86_64
libstdc++.i686
libstdc++.x86_64
libstdc++-devel.i686
libstdc++-devel.x86_64
libxcb.i686
libxcb.x86_64
libX11.i686
libX11.x86_64
libXau.i686
libXau.x86_64
libXi.i686
libXi.x86_64
libXtst.i686
libXtst.x86_64
libXrender.i686
libXrender.x86_64
libXrender-devel.i686
libXrender-devel.x86_64
make.x86_64
net-tools.x86_64
smartmontools.x86_64
sysstat.x86_64
EOF

加えて、GridInfrastructure用に以下追加する。
yum -y install gcc-c++ compat-libcap1 libstdc++-devel

メディア展開用
yum -y install unzip
```
```ruby:ASMディスクのudev登録
cat > /etc/udev/rules.d/99-oracle.rules << EOF
KERNEL=="sd??",ACTION=="add|change",OWNER="oracle",GROUP="asmadmin",MODE="0660"
EOF
/sbin/udevadm control --reload-rules
```
```ruby:cvuqdisk_rpmインストール
・RAC前提のrpmインストール
yum -y localinstall ${GRID_HOME}/cv/rpm/cvuqdisk*.rpm
```
## 2.Grid構成
```ruby:Grid初期構成
su - oracle
export GRID_HOME=/opt/app/oracle/grid/product/12.2.0
unzip -q -d ${GRID_HOME} /opt/app/tmp/linuxx64_12201_grid_home.zip

・サイレントモードの場合は以下で実行
${GRID_HOME}/gridSetup.sh -responseFile /opt/app/tmp/grid.rsp -silent -waitForCompletion -ignorePrereqFailure -skipPrereqs -applyOneOffs /opt/app/tmp/26247490/26247490 
※RHEL7.5の場合はACFSのBugがあるので26247490を導入時に同時適用
※SSH経由でモジュールは他ノードに転送するのでGRID_HOME以下にモジュールを置いておくのはgridSetup.shを実行する1ノードだけでOK
※RU同時適用の場合は、-applyPSU

※インストール失敗時の環境クリア用
#!/bin/bash

NODELIST="rac1 rac2"
LOC=rac1
USER=oracle
GROUP1=oinstall
GROUP2=dba
GROUP3=asmadmin
GRID_MEDIA=/opt/app/tmp/linuxx64_12201_grid_home.zip
export GRID_BASE=/opt/app/oracle/product
export GRID_HOME=/opt/app/oracle/grid/product/12.2.0
export ORACLE_BASE=/opt/app/oracle/product
export ORACLE_HOME=/opt/app/oracle/product/dbhome_1/12.2.0

for i in ${NODELIST};do
ssh ${i} "rm -rf ${GRID_BASE} ${GRID_HOME} ${ORACLE_BASE} ${ORACLE_HOME} /opt/app/oraInventory /etc/oracle"
ssh ${i} "rm -f /etc/oratab /etc/oraInst.loc /etc/systemd/system/oracle* /usr/local/bin/coraenv /usr/local/bin/dbhome /usr/local/bin/oraenv"
ssh ${i} "mkdir -p ${GRID_BASE} ${GRID_HOME} ${ORACLE_BASE} ${ORACLE_HOME} && chown -R ${USER}:${GROUP1} /opt/app"
done
ssh ${LOC} "su - ${USER} -c 'unzip -q -o ${GRID_MEDIA} -d ${GRID_HOME}'"

```

```ruby:FlexASDMローカルモード化（ASMNWを経由せず直接ローカルでIOする）
srvctl modify asm -count ALL
```
```ruby:RACノード追加
export GRID_BASE=/opt/app/oracle/product
export GRID_HOME=/opt/app/oracle/grid/product/12.2.0
export ORACLE_BASE=/opt/app/oracle/product
export ORACLE_HOME=/opt/app/oracle/product/dbhome_1/12.2.0

###Grid,RAC所有ユーザで全ノードでSSH鍵交換を実施済みの前提
# GRIDHOME追加(既存ノードでGrid所有ユーザで実行)
${GRID_HOME}/addnode/addnode.sh -silent -waitForCompletion -skipPrereqs 
"CLUSTER_NEW_NODES={rac3}" "CLUSTER_NEW_VIRTUAL_HOSTNAMES={rac3v}" "CLUSTER_NEW_NODE_ROLES={hub}"

#RACHOME追加(既存ノードでRAC所有ユーザで実行)
${ORACLE_HOME}/addnode/addnode.sh -silent -waitForCompletion -skipPrereqs "CLUSTER_NEW_NODES={rac3}" 
```

```ruby:ASMディスクグループ作成
※片ノードで実行
sqlplus / as sysasm

#REDO
CREATE DISKGROUP DG01 EXTERNAL REDUNDANCY DISK '/dev/sdd1'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

CREATE DISKGROUP DG02 EXTERNAL REDUNDANCY DISK '/dev/sdd2'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

#基盤表領域
CREATE DISKGROUP DG03 EXTERNAL REDUNDANCY DISK '/dev/sde1'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

CREATE DISKGROUP DG04 EXTERNAL REDUNDANCY DISK '/dev/sde2'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

#業務表領域
CREATE DISKGROUP DG05 NORMAL REDUNDANCY 
REGULAR FAILGROUP FG1 DISK '/dev/sde3' NAME disk1
REGULAR FAILGROUP FG2 DISK '/dev/sde4' NAME disk2
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

#アーカイブREDOログ
(ノード１用)
CREATE DISKGROUP DG06 EXTERNAL REDUNDANCY DISK '/dev/sdf1'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';
(ノード2用)
CREATE DISKGROUP DG07 EXTERNAL REDUNDANCY DISK '/dev/sdf2'
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

#ASMディスクグループ参照
set lines 10000 pages 500
col NAME for a20
col STATE for a20
col TYPE for a10
col COMPATIBILITY for a20
col DATABASE_COMPATIBILITY for a20
select * from V$ASM_DISKGROUP;

#ASMディスク参照
set lines 10000 pages 500
col NAME for a20
col HEADER_STATUS for a20
col PATH for a20
col FAILGROUP for a20
select GROUP_NUMBER, DISK_NUMBER, HEADER_STATUS, PATH, NAME, FAILGROUP, TOTAL_MB, FREE_MB from V$ASM_DISK order by NAME ;

#フェイルグループ再構成
##1.ddなどで特定のASMディスクを壊したあとは再起動時に対象DGをマウントできない。そのため強制マウント
alter diskgroup NOR mount force;

##2.壊したASMディスクを強制DROP
alter diskgroup NOR drop disks in failgroup FG3 force;

##3.壊れたASMディスクをフェイルグループへと復旧させる
alter diskgroup NOR add failgroup FG3 disk '/dev/sdd3' NAME DISK3 ;

```

```ruby:GIMR(MGMTDB再作成)

#1.リソース構成解除
srvctl remove mgmtdb

#2.GIMR再作成（管理データベースコンテナ)
export GI_HOME=/opt/app/oracle/grid/product/12.2.0
${GI_HOME}/bin/dbca -silent -createDatabase -createAsContainerDatabase true \
-templateName MGMTSeed_Database.dbc \
-sid -MGMTDB \
-gdbName _mgmtdb \
-storageType ASM \
-diskGroupName +DG00 \
-datafileJarLocation ${GI_HOME}/assistants/dbca/templates \
-characterset AL32UTF8 \
-autoGeneratePasswords \
-skipUserTemplateCheck

#3.GIMR再作成（プラガブルGIMRデータベース)
export GI_HOME=/opt/app/oracle/grid/product/12.2.0
${GI_HOME}/bin/mgmtca -local
```



```ruby:RACデータベース作成
sed -i 's/XXX/<インスタンス名>/g' createdb.sql init.ora

下記ORACLE_SID=XXX001で実施する前提
======[createdb.sql]===============================================
startup nomount pfile='init.ora';
CREATE DATABASE XXX
   USER SYS IDENTIFIED BY P#ssw0rd
   USER SYSTEM IDENTIFIED BY P#ssw0rd
   SET DEFAULT BIGFILE TABLESPACE
   LOGFILE GROUP 1 ('+DG01/XXX/ONLINELOG/redo_grp1','+DG02/XXX/ONLINELOG/redo_grp1') SIZE 200M BLOCKSIZE 512,
           GROUP 2 ('+DG01/XXX/ONLINELOG/redo_grp2','+DG02/XXX/ONLINELOG/redo_grp2') SIZE 200M BLOCKSIZE 512,
           GROUP 3 ('+DG01/XXX/ONLINELOG/redo_grp3','+DG02/XXX/ONLINELOG/redo_grp3') SIZE 200M BLOCKSIZE 512
   MAXLOGHISTORY 1
   MAXLOGFILES 16
   MAXLOGMEMBERS 3
   MAXDATAFILES 1024
   CHARACTER SET AL32UTF8
   NATIONAL CHARACTER SET AL16UTF16
   EXTENT MANAGEMENT LOCAL
   ARCHIVELOG
   DATAFILE '+DG03/XXX/DATAFILE/SYSTEM' SIZE 1024M
   SYSAUX DATAFILE '+DG03/XXX/DATAFILE/SYSAUX' SIZE 2048M
   DEFAULT TABLESPACE TBLSP01 DATAFILE '+DG03/XXX/DATAFILE/TBLSP01' SIZE 5120M
   BIGFILE DEFAULT TEMPORARY TABLESPACE TEMPTBLSP01 TEMPFILE '+DG03/XXX/TEMPFILE/TEMPTBLSP01' SIZE 512M
   BIGFILE UNDO TABLESPACE UNDOTBLSP01 DATAFILE '+DG03/XXX/DATAFILE/UNDOTBLSP01' SIZE 512M;
   ---(UNDOは複数ここで作れない)BIGFILE UNDO TABLESPACE UNDOTBLSP02 DATAFILE '+DG03/XXX/DATAFILE/UNDOTBLSP02' SIZE 512M;
alter database mount;
alter database open;
@?/rdbms/admin/catalog.sql
@?/rdbms/admin/catproc.sql
@?/rdbms/admin/utlrp.sql
conn system/P#ssw0rd
@?/sqlplus/admin/pupbld.sql
conn / as sysdba

--RAC固有
@?/rdbms/admin/catclust.sql  

create spfile='+DG03/XXX/spfileXXX.ora' from pfile='/home/oracle/createdb/init.ora';
host echo SPFILE='+DG03/XXX/spfileXXX.ora' > /opt/app/oracle/product/dbhome_1/12.2.0/dbs/initXXX001.ora
host /opt/app/oracle/product/dbhome_1/12.2.0/bin/srvctl add database -d XXX -o  /opt/app/oracle/product/dbhome_1/12.2.0 -p SPFILE='+DG03/XXX/spfileXXX.ora' -n XXX 
host /opt/app/oracle/product/dbhome_1/12.2.0/bin/srvctl add instance -d XXX -i XXX001 -n rac1
host /opt/app/oracle/product/dbhome_1/12.2.0/bin/srvctl add instance -d XXX -i XXX002 -n rac2
alter system set cluster_database=TRUE scope=spfile;
shutdown immediate;
startup mount;
alter database archivelog;
alter database open;
CREATE BIGFILE UNDO TABLESPACE UNDOTBLSP02 DATAFILE '+DG03/XXX/DATAFILE/UNDOTBLSP02' SIZE 512M;
shutdown immediate;
==================================
SPFILEは各ノードで/opt/app/oracle/product/dbhome_1/12.2.0/dbs/init<インスタンス名>.oraにリネームする
※CREATE DATABASE時は、*.cluster_database=FALSEにしないと以下エラーが出るので考慮する。RAC DBインスタンスが正常に作られた後にTRUEに変更。
ORA-12720: operation requires database is in EXCLUSIVE mode

その他SQLスクリプトは以下参照
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/refrn/sql-scripts.html#GUID-C308549C-A3DC-4CF2-9433-90650AD0F6DB

======[init.ora(Sample初期化パラメータファイル)]==========================
*._complex_view_merging=TRUE
*._fix_control='17760375:OFF'
*.audit_trail='XML','EXTENDED'
*.compatible='12.2.0.1.0'
*.control_file_record_keep_time=15
*.control_files='+DG01/XXX/CONTROLFILE/control01.ctl','+DG02/XXX/CONTROLFILE/control02.ctl'
*.db_block_checking='TRUE'
*.db_block_checksum='TRUE'
*.db_block_size=8192
*.db_cache_size=50331648
*.db_create_file_dest='+DG03'
*.db_create_online_log_dest_1='+DG01'
*.db_create_online_log_dest_2='+DG02'
*.db_domain=''
*.db_name='XXX'
*.db_writer_processes=4
*.ddl_lock_timeout=60
*.fast_start_mttr_target=1
*.log_archive_format='%T_%S_%R.dbf'
*.memory_target=0m
*.open_cursors=500
*.pga_aggregate_limit=0
*.pga_aggregate_target=10485760
*.processes=200
*.recyclebin='OFF'
*.remote_login_passwordfile='EXCLUSIVE'
*.sga_target=1073741824
*.statistics_level='TYPICAL'
*.streams_pool_size=16777216
*.undo_retention=900
*.log_archive_dest_1='LOCATION=+DG04'
*.log_archive_dest_2='LOCATION=+DG05'

# RAC
*.cluster_database=FALSE
XXX001.instance_name='XXX001'
XXX002.instance_name='XXX002'
XXX001.instance_number=1
XXX002.instance_number=2
XXX001.undo_tablespace='UNDOTBLSP01'
XXX002.undo_tablespace='UNDOTBLSP02'
============================================

```


# 1.Clusterwareアーキテクチャ
(1)Oracle Clusterwareスタック
Clusterwareは何かと言うと、アプリケーションの状態を定期的に関しし、障害発生時にアプリケーションを再起動したり、別のノードに再配置したりをする。(=高可用性)

Oracle Clusterwareスタック=OHASスタック+CRSスタック
最初にOHASが起動し、各OHASエージェントを起動する。
各OHASエージェントがCRS(Cluster Ready Service)、CSS(Cluster Synchronization Service)、EVM(Event Manager)を起動する。その後、CRSから起動されたエージェントが(orarootagent、oraagent)がデータベースやASMディスクグループ、リスナーといったOracle Clusterwareリソースを起動する。

```ruby:OracleClusterwareスタック
・OHAS(ohasd.bin)
Oracle Clusterwareスタックの起動、停止、監視を行う。

・CRS(crsd.bin,orarootagent(agentは,rootプロセスで起動するscan vip,ノードvip,GNSを管理)
Clusterwareリソースの起動、停止、監視を行う。リソースに障害が起きた時は再起動を仕掛ける主体。

・CSS(cssd.bin)
クラスタ環境のメンバーシップを制御する。CSSデーモンがノード間のハードビートを行い、通信が行えない場合(スプリットブレイン)の解決を行う。CSSデーモンが停止するとノードの再起動が行われる。

・EVM(evmd.bin,evmlogger)
クラスタ環境で発生したイベントを処理

・CTSS(octssd.bin)
NTPの設定がOSで実施されていない場合、時刻同期サービスを提供。
(OS側でNTPやCHRONYDが実行されていればプロセスとしては起動するが、何もしない)

OHAS以外のClusterwareスタック操作
crsctl {start|stop|check} cluster -all

OHAS含むClusterwareスタック操作(ローカルノードのみ操作)
crsctl {start|stop|check} crs

```

(2)Storage Components of Oracle Clusterware
Oracle Clusterware関連のストレージコンポーネントの説明
→OCRと投票ディスクは全ノードがアクセス可能な共有ディスク上にある必要がある。
→OCRは最大５多重、投票ディスクは最大3多重可能。多重化が推奨される。

1)OCR
クラスター構成を保存しておくコンポーネント。Clusterwareで管理するリソース情報を保有する。
例えば、RACデータベースやインスタンス、リスナーやVIPなど。

OCRのロケーションはOCR構成ファイル/etc/oracle/ocr.locに格納。
[root@rhel1 ~]# cat /etc/oracle/ocr.loc
ocrconfig_loc=+DG00/cluster-XXX/OCRFILE/registry.255.1003458313
local_only=false

OHASの起動に必要な情報はOLRファイルに格納されるがOHASは各ノードに存在するため、
OLRはローカルストレージに存在する。そしてRAC環境の構成に必要なリソース情報はOCRに格納されている。OCRはどのノードからもアクセス可能なように共有ストレージに配置され、各ノードはOCRの内容をキャッシュしたメモリが用意され、CRSDによってOCRキャッシュがリフレッシュされる。OCRファイルの変更はマスターノードが実施し、変更後に各ノードが内容をリフレッシュする。OCRファイルの配置変更は各ノードのCRSDによってOCR構成ファイル(/etc/ocr.loc)に保存されるため、基本的には全ノードのCRSD起動の前提で実施する。

```ruby:OCRファイルの配置構成
## OCRファイルの配置確認(OLR分は-localを付ける)
ocrcheck -config

## OCRファイルの整合性確認(OLR分は-localを付ける)
ocrcheck

##OCRファイルの追加(同一ASMDGには作成不可)
ocrconfig -add 'ASMDG名'

##OCRファイルの削除
ocrconfig -delete 'ASMDG名'

##OCRファイルの移動
ocrconfig -replace '移動元ASMDG名' -replacement '移動先ASMDG名'
```
```ruby:OCRファイルのバックアップ&リカバリ
OCRマスターノードによって実施される。OCRマスターノード=最初にCRSDが起動したノード。

# OCRファイルの自動バックアップ
1.4時間毎バックアップが最新3つ(backup00.ocr,backup01.ocr,backup02.ocr)
2.日次バックアップが最新２つ(day_.ocr,day.ocr)
3.週次バックアップが最新２つ(week_.ocr,week.ocr)

# OCRファイルの手動バックアップ
1.物理バックアップ(容量が大きい代わりに内容が確認できる)。個人環境実測2172KB。
ocrconfig -manualbackup
※当コマンドで最新5世代のバックアップが出力されるが、表示されなくなったものがは特に削除される訳ではなく残っているので個別に削除が必要。

2.論理バックアップ(容量が小さい代わりにリストアするまで内容が確認できない)。個人環境実測136KB。
ocrconfig -export '<バックアップ先のパス>'

##バックアップの確認
ocrconfig -showbackup

##バックアップ先の変更
ocrconfig -backuploc '<バックアップ先>'
※12.2だとASMDGにしか移動できない?
PROT-42: The specified backup location '/opt/app/oracle' is not an ASM disk group.

##リカバリ手順(OCRがASMにある場合)
1.全ノードでOracle Clusterwareスタック停止。正常停止不可能な場合があるので強制停止(-f)
crsctl stop cluster -all -f
crsctl stop crs -f

2.リストア実行対象ノードでCRSを排他モード起動。
-excl(排他モード)=ノード間通信をしない、-nocrs=CRSDが起動しないという意味。
crsctl start crs -excl -nocrs

3.OCR/投票ディスク用DG作成
CREATE DISKGROUP DG00 NORMAL REDUNDANCY 
REGULAR FAILGROUP FG1 DISK '/dev/sdb1' NAME disk1
REGULAR FAILGROUP FG2 DISK '/dev/sdb2' NAME disk2
REGULAR FAILGROUP FG3 DISK '/dev/sdb3' NAME disk3
ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1','COMPATIBLE.RDBMS'='12.2.0.1';

4.リストア実施。リストア場所は、ocr.locに記載されている箇所と識別される。
ocrconfig -restore ocrbk.ocr

5.リストア実行対象ノードでCRSを停止
crsctl stop crs -f

6.全ノードでOracle Clusterwareスタック起動
crsctl start crs -wait

7.OCRファイルの整合性チェック(root以外)
cluvfy comp ocr -n all

```

2)投票ディスク(Voting Disk)
どのノードがクラスターの中のメンバーなのかを判別するファイル。
CSSDはインターコネクトを介して他のノードと通信を行い、通信状況を投票ディスクに格納する。
インターコネクトに障害が発生した場合、共有ディスクに対するI/Oの同期が取れなくなるため、クラスタが分離された状態になる。クラスタが分離すると他のノードが使用可能かの判断ができなくなるため、同一データベースに非同期アクセスすることでデータの不整合が起こりうる（スプリットブレイン）。

上記の状況に陥ったきわにどのノードを再起動させるかを決定するのが投票ディスクである。
⑴通信できるノード数が多いノード群を残存ノードにし、逆に通信できるノード群が少ないノード群は削除（クラスタ再構成。削除されるノードはOS再起動される)
(2)(1)の算出結果として、ノード数が等しい場合は小さいノード番号のノードを残存ノードにする。ノード番号はolsnodesコマンドで確認可能

インターコネクトを利用したネットワークのハートビートと投票ディスクへのI/Oは、1秒間隔で実行される。
タイムアウトに抵触すると対象ノードの再起動が行われる。

下記タイムアウトのパラメータ。crsctl get css [タイムアウトパラメータ]で現在値を確認可能。
・MISSCOUNT デフォルト30秒
 インターコネクトでハートビートが識別できないと判断される最大許容時間

・DISKTIMEOUT デフォルト200秒
  投票ディスクへのI/Oが実行できないと判定される最大許容時間。マルチパスでディスクパスが冗長化されている場合は、切り替え時間より短いとNG

・REBOOTTIME
  CSSDがハング、またはインターコネクト及び投票ディスクへのI/O両方に問題が発生している判定に使われる。
  MISSCOUNT-REBOOTTIMEの時間経過後にノード再起動される。


投票ディスクは11gから変更時に自動的にバックアップが取得されるようになり、アクセスできない投票ディスクは自動的に復旧する。投票ディスクはASMインスタンスが起動していなくてもアクセスできるように、ASMファイルではなく個別のASMディスクに格納される。(実測130M程度/1ASMディスク)投票ディスクを多重化する場合、CSSDは3つ以上の投票ディスクを使用する。全ての投票ディスクから情報を読み込みCSSDは過半数から情報を読み込める時は動作し続ける。


```ruby:投票ディスク関連コマンド
#タイムアウトパラメータ現在値確認
crsctl get css misscount
crsctl get css disktimeout
crsctl get css reboottime

#タイムアウトパラメータ更新
crsctl set css misscount 30
crsctl set css disktimeout 200
crsctl set css reboottime 3

# 投票ディスクのロケーション確認
crsctl query css votedisk

# 投票ディスクのロケーション置換。ASMでは投票ディスクの多重化はASMDGの多重化で賄う。
crsctl replace votedisk '<ASMDG名>'

# 投票ディスクのロケーション追加(ASMでは無効)
crsctl add votedisk 'パス'

# 投票ディスクのロケーション削除(ASMでは無効)
crsctl delete votedisk 'パス'

```

3)ACFSファイルシステム
ACFSは、Oracle自動ストレージ管理クラスタ・ファイルシステム、つまりOracle製のクラスタファイルシステム。

・マルチプラットフォームでマウント可能なファイルシステムであること
・Oracleデータベースファイル以外のカスタマファイルをサポートすること
・Grid管理下にある複数ノードから同時に参照できること

便利な点としては、、、NFSを使わずとも各ノードでファイルと共有できるという点があるが具体的には、、、
・DataPumpのダンプファイルを配置すれば、CLUSTER=yで複数ノードでパラレルでのexport/importを実施可能
・$ORACLE_HOMEを配置することで、単一ORACLE_HOME構成を実現可能
・アプリケーション実行ファイルを配置　（リリースでノード間同期が不要）

```ruby:ACFSファイルシステム作成

##ACFS利用要件としては以下互換性パラメータが必須(v12.2)
alter diskgroup ACFS SET ATTRIBUTE 'COMPATIBLE.ASM'='12.2.0.1';
alter diskgroup ACFS SET ATTRIBUTE 'COMPATIBLE.ADVM'='12.2.0.1';

##通常のASMディスクグループ作成後に下記でASMDG内にボリューム作成
asmcmd volcreate -G ACFS -s 10240M acfstest;

##上記の作成したボリュームに紐づくOS上のデバイス名抽出
asmcmd volinfo -G ACFS -a
ASMCMD-9470: ASM proxy instance unavailable
ASMCMD-9473: volume STATE will show as REMOTE
Diskgroup Name: TEST

	 Volume Name: ACFSVOL1
	 Volume Device: /dev/asm/acfsvol1-12
	 State: REMOTE
	 Size (MB): 512
	 Resize Unit (MB): 64
	 Redundancy: UNPROT
	 Stripe Columns: 8
	 Stripe Width (K): 1024
	 Usage: 
	 Mountpath: 
 
##sqlplusでも確認可能
set lin 10000 pagesize 10000 colsep |
col VOLUME_NAME format a40;
col VOLUME_DEVICE format a40;
select VOLUME_NAME,VOLUME_DEVICE from v$asm_volume;

##ACFS用論理ボリューム上にクラスターファイルシステムACFSを作成
mkfs -t acfs /dev/asm/acfsvol1-12 

##マウント
mount -t acfs /dev/asm/acfsvol1-12 /acfs1

##ASMインスタンス起動時に自動マウントする場合
/sbin/acfsutil registry -a /dev/asm/acfsvol1-12 /acfs1

※##ACFSボリューム削除
asmcmd voldelete -G ACFS acfstest;

```

#2.RAC固有

1)RACの接続ロード・バランシング

①SCAN（単一クライアントアクセス名）
11GR2以降では、各ノードにOracle Grid Infrastructure（GI）が管理するローカル・リスナー以外に、クラスタを代表するSingle Client Access Name（SCAN）リスナーと、SCANリスナーに対応するSCAN Virtual IPアドレス（SCAN VIP）がセットでクラスタのいずれかのノードで起動する。複数あるSCAN VIPのIPアドレスはDNSで1つのホスト名に対応付けられるのでこの仕組みにより、Clientの接続識別子はRACノードが難題であろうと、SCAN名だけ定義しておけば良いことになる。DNSラウンドロビンで名前解決することは必須なのでDNSサーバが必要。AWSのRouteS3で実装するのも良いだろう。

```ruby:SCAN構成
###前提として、SCAN VIPになりうるIPアドレスをscan名で正引き出来るようにDNSにレコード登録を済ませておく。
下記例ではscan vipを192.168.1.200-202から192.168.1.201-204へ変更する。


#事前情報
[root@rhel1 ~]# srvctl config scan
SCAN name: cluster-XXX-scan, Network: 1
Subnet IPv4: 192.168.1.0/255.255.255.0/enp0s8, static
Subnet IPv6: 
SCAN 1 IPv4 VIP: 192.168.1.200
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 
SCAN 2 IPv4 VIP: 192.168.1.202
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 
SCAN 3 IPv4 VIP: 192.168.1.201
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 

[root@rhel1 ~]# srvctl config scan_listener
SCAN Listener LISTENER_SCAN1 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 
SCAN Listener LISTENER_SCAN2 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 
SCAN Listener LISTENER_SCAN3 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 

#構成
[root@rhel1 ~]# srvctl modify scan -scanname cluster-XXX-scan ; echo RC=$?
RC=0
[root@rhel1 ~]# srvctl modify scan_listener -update; echo RC=$?
RC=0

#事後情報
[root@rhel1 ~]# srvctl config scan
SCAN name: cluster-XXX-scan, Network: 1
Subnet IPv4: 192.168.1.0/255.255.255.0/enp0s8, static
Subnet IPv6: 
SCAN 1 IPv4 VIP: 192.168.1.204
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 
SCAN 2 IPv4 VIP: 192.168.1.202
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 
SCAN 3 IPv4 VIP: 192.168.1.201
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 
SCAN 4 IPv4 VIP: 192.168.1.203
SCAN VIP is enabled.
SCAN VIP is individually enabled on nodes: 
SCAN VIP is individually disabled on nodes: 

[root@rhel1 ~]# srvctl config scan_listener
SCAN Listener LISTENER_SCAN1 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 
SCAN Listener LISTENER_SCAN2 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 
SCAN Listener LISTENER_SCAN3 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 
SCAN Listener LISTENER_SCAN4 exists. Port: TCP:9999
Registration invited nodes: 
Registration invited subnets: 
SCAN Listener is enabled.
SCAN Listener is individually enabled on nodes: 
SCAN Listener is individually disabled on nodes: 

#この後scanとscan_listenerを再起動
srvctl stop scan_listener ; srvctl stop scan ; srvctl start scan ; srvctl start scan_listener

#tnsnamesについては上記変更(ノードの増減等)にかかわらず常に下記のように不変でOK
SCAN =
    (DESCRIPTION =
      (ADDRESS_LIST =
        (ADDRESS = (PROTOCOL = TCP)(HOST = cluster-XXX-scan)(PORT = 9999))
      )
      (CONNECT_DATA =
        (SERVER = DEDICATED)
        (SERVICE_NAME = XXX.world)
      )
    )

```

②TAF(透過的アプリケーションフェイルオーバー)
接続中に接続先インスタンスがダウンしたりした時などにRACの他ノードインスタンスにセッションをそのままフェイルオーバーする機能。参照系DML(SELECT文)については特にエラーなく、他インスタンスでリトライすることが可能。更新系DML(INSERT,UPDATE,DELETE文)はセッションがフェイルオーバーしても処理を引き継げないためエラーになる→一度ロールバックしてから、リランの必要があり。故に、アプリ側から見ると、更新系DML以外ならば特にDBインスタンスに障害が起きたことを意識する必要が無いという意味で透過的。

```ruby:TAF実装方法

#クライアントサイドでの実装方法(tnsnamesで設定。TAF設定自体はFAILOVER_MODE=部分)
SCAN =
    (DESCRIPTION =
      (ADDRESS_LIST =
        (ADDRESS = (PROTOCOL = TCP)(HOST = cluster-XXX-scan)(PORT = 9999))
      )
      (CONNECT_DATA =
        (SERVER = DEDICATED)
        (SERVICE_NAME = XXX.world)
        (FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRIES=5)(DELAY=2))
      )
    )

TYPE:
フェイルオーバーの種類
-SESSION: セッションをフェイルオーバー
-SELECT: SELECT中の切断でもリトライできる
-NONE: デフォルト、フェイルオーバー機能を使用しない

METHOD
接続先ノードからバックアップ・ノードにフェイルオーバーされる速さの設定。
-basic: フェイルオーバー時に接続を確立します。フェイルオーバー時まで、バックアップ・サーバーはほとんど何もする必要無
-preconnect: 接続を事前にバックアップインスタンスへと確立する。フェイルオーバーは高速に行われるが、バックアップ・インスタンスはサポートされた各インスタンスからの接続すべてをサポートできる必要があある。

RETRIES
フェイルオーバー後に接続を行う回数を指定。デフィルトでは5回

DELAY
次に接続を試みるまでの待機時間を秒数で指定。デフォルトは1秒

BACKUP
バックアップ接続として使用する別のネット・サービス名。preconnectを使用して事前に接続を確立する場合に指定。

```


■参考リンク
http://www.oracle.com/technetwork/jp/database/enterprise-edition/documentation/racdb12201-inst-linux-x64-3795525-ja.pdf
https://www.tank-sakurai.com/oracle-11gr2rac_5/
https://docs.oracle.com/cd/E82638_01/cwlin/multicast-requirements-for-networks-used-by-oracle-grid-infrastructure.html#GUID-09DA3502-5880-4E72-88C0-CBE8E4893DF7
https://www.oracle.com/technetwork/jp/ondemand/database/db-new/c-13-rac-1448417-ja.pdf
https://enterprisezine.jp/dbonline/detail/9709


```ruby:Oracle_JDBC_THIN接続確認スクリプト(JavaScript)
//oracleConn.js
var OracleDriver = Packages.oracle.jdbc.OracleDriver;
var OracleConnection = Packages.oracle.jdbc.OracleConnection;
var PreparedStatement = java.sql.PreparedStatement;
var Prop = new java.util.Properties();

Prop.setProperty('user','mario');
Prop.setProperty('password','P#ssw0rd');
Prop.setProperty('oracle.net.CONNECT_TIMEOUT','10000');
Prop.setProperty('oracle.jdbc.ReadTimeout','60000');
Prop.setProperty('oracle.net.encryption_client','REQUIRED');
Prop.setProperty('oracle.net.encryption_types_client','AES256');
Prop.setProperty('oracle.net.crypto_checksum_client','REQUIRED');
Prop.setProperty('oracle.net.crypto_checksum_types_client','(SHA512,SHA384,SHA256)');

var conn = new OracleDriver().connect('jdbc:oracle:thin:@\
(DESCRIPTION=(ENABLE=BROKEN)\
(ADDRESS_LIST=(FAILOVER=ON)(LOAD_BALANCE=ON)\
(ADDRESS=(PROTOCOL=TCP)(HOST=db1)(PORT=5001))\
(ADDRESS=(PROTOCOL=TCP)(HOST=db1)(PORT=5002)))\
(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XXX.world)))',Prop);

var ps = conn.createStatement();

var rs1 = ps.executeQuery("select sys_context('userenv','server_host'),sys_context('userenv','instance_name'),x.username,x.program,sys_context('userenv','host'),x.logon_time from dual,v$session x where x.sid = (select sys_context('userenv','sid') from dual)");

rs1.next();
print("DB_Host       :" + rs1.getString(1));
print("Instance_Name :" + rs1.getString(2));
print("Username      :" + rs1.getString(3));
print("Program       :" + rs1.getString(4));
print("Client_Host   :" + rs1.getString(5));
print("LoginTime     :" + rs1.getString(6));
rs1.close();
ps.close();
conn.close();

```
```ruby:Oracle_JDBC_THIN接続確認スクリプト実行方法
jss -cp <JDBCドライバパス> oracleConn.js
```

Oracle JDBC Java API Reference 12c Release 2 (12.2.0.1.0)
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jajdb/index.html
→よく使うのはoracle.jdbc→Interface OracleConnection