標籤

4GL (1) 人才發展 (10) 人物 (3) 太陽能 (4) 心理 (3) 心靈 (10) 文學 (31) 生活常識 (14) 光學 (1) 名句 (10) 即時通訊軟體 (2) 奇狐 (2) 爬蟲 (1) 音樂 (2) 產業 (5) 郭語錄 (3) 無聊 (3) 統計 (4) 新聞 (1) 經濟學 (1) 經營管理 (42) 解析度 (1) 遊戲 (5) 電學 (1) 網管 (10) 廣告 (1) 數學 (1) 機率 (1) 雜趣 (1) 證券 (4) 證券期貨 (1) ABAP (15) AD (1) agentflow (4) AJAX (1) Android (1) AnyChart (1) Apache (14) BASIS (4) BDL (1) C# (1) Church (1) CIE (1) CO (38) Converter (1) cron (1) CSS (23) DMS (1) DVD (1) Eclipse (1) English (1) excel (5) Exchange (4) Failover (1) Fedora (1) FI (57) File Transfer (1) Firefox (3) FM (2) fourjs (1) Genero (1) gladiatus (1) google (1) Google Maps API (2) grep (1) Grub (1) HR (2) html (23) HTS (8) IE (1) IE 8 (1) IIS (1) IMAP (3) Internet Explorer (1) java (4) JavaScript (22) jQuery (6) JSON (1) K3b (1) ldd (1) LED (3) Linux (120) Linux Mint (4) Load Balance (1) Microsoft (2) MIS (2) MM (51) MSSQL (1) MySQL (27) Network (1) NFS (1) Office (1) OpenSSL (1) Oracle (131) Outlook (3) PDF (6) Perl (60) PHP (33) PL/SQL (1) PL/SQL Developer (1) PM (3) Postfix (2) postfwd (1) PostgreSQL (1) PP (50) python (5) QM (1) Red Hat (4) Reporting Service (28) ruby (11) SAP (234) scp (1) SD (16) sed (1) Selenium (3) Selenium-WebDriver (5) shell (5) SQL (4) SQL server (8) sqlplus (1) SQuirreL SQL Client (1) SSH (3) SWOT (3) Symantec (2) T-SQL (7) Tera Term (2) tip (1) tiptop (24) Tomcat (6) Trouble Shooting (1) Tuning (5) Ubuntu (37) ufw (1) utf-8 (1) VIM (11) Virtual Machine (2) VirtualBox (1) vnc (3) Web Service (2) wget (1) Windows (19) Windows (1) WM (6) Xvfb (2) youtube (1) yum (2)

2026年8月3日 星期一

Oracle如何在SQL不變情況下,替換目前SQL execution plan

 在 Oracle 資料庫中,如果無法或不想修改應用程式發送的原始 SQL 程式碼(例如 SQL 固化在第三方套件或舊系統中),想要強制改變其執行計畫(Execution Plan),主要有以下幾種正統做法

方法一:使用 SQL Plan Baseline(DBMS_SPM)— 官方最推薦

這是 Oracle 11g 以上最標準、穩定的做法。原理是將「目標 SQL」與「帶有期望 Hint(如使用特定 Index)的 SQL」的執行計畫進行綁定

操作步驟:

  1. 取得原始 SQL 與好 SQL 的 SQL_ID 與 PLAN_HASH_VALUE

    • 原始 SQL(壞計畫):sql_id_a

    • 手動加上 Hint 改寫後的 SQL(好計畫):sql_id_b,其 Plan Hash 為 plan_hash_b

  2. 將好 SQL 的 Plan 載入為原始 SQL 的 Baseline

    執行以下 PL/SQL:

    SQL
    DECLARE
      l_plans_loaded PLS_INTEGER;
    BEGIN
      l_plans_loaded := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
        sql_id          => 'sql_id_b',       -- 好 SQL 的 ID
        plan_hash_value => plan_hash_b,      -- 好 SQL 的 Plan Hash
        sql_id          => 'sql_id_a'        -- 綁定給原始 SQL
      );
    END;
    /
    

    此時,當系統再次執行原始 SQL(sql_id_a)時,Oracle 優化器(CBO)就會強制採用指定的 Plan。

方法二:使用 SQL Profile 搭配 force_match(DBMS_SQLTUNE)

如果原始 SQL 中的字面值(Literal values)或變數名稱會動態變化(未統一使用 Bind Variable),使用 SQL Baseline 可能無法匹配 ,此時可以建立 SQL Profile 並開啟 force_match => TRUE

操作步驟:

使用 DBMS_SQLTUNE.IMPORT_SQL_PROF 將包含良好 Hint(例如 /*+ INDEX(...) */)的 Hint 集合直接注入到指定的 sql_id 中:

SQL
BEGIN
  DBMS_SQLTUNE.IMPORT_SQL_PROF(
    sql_text    => 'SELECT * FROM my_table WHERE col = :1', -- 原始 SQL 文字
    profile_xml => '...',                                   -- 或利用 Coe_xfr_sql_profile 指令碼產生
    name        => 'PROFILE_FIX_PLAN',
    force_match => TRUE                                     -- 忽略字面值差異強制套用
  );
END;
/

提示: 實務上 DBA 常配合 Oracle 官方提供的 coe_xfr_sql_profile.sql 腳本(來自 Oracle Support Note 271166.1),只要輸入壞 SQL 的 sql_id 與好 Plan 的 plan_hash_value,腳本會自動生成可替換 Plan 的 SQL Profile 檔案。

方法三:使用 SQL Patch(DBMS_SQLDIAG)

如果只想簡單地幫某條固定的 SQL「加上特定的 Hint」(例如強制走特定索引 /*+ INDEX(t my_idx) */ 或平行處理 /*+ PARALLEL(4) */),可以使用 SQL Patch,不需要手動建立新的 SQL。

操作步驟:

SQL
DECLARE
  l_patch_name VARCHAR2(30);
BEGIN
  l_patch_name := DBMS_SQLDIAG.CREATE_SQL_PATCH(
    sql_id    => 'sql_id_a',                         -- 目標 SQL_ID
    hint_text => 'INDEX(a my_index_name)',           -- 強制加入的 Hint
    name      => 'patch_force_index'
  );
END;
/

當原始 SQL 再次執行時,優化器會自動把 hint_text 注入進去並重新編譯出新的 Execution Plan。

方法四:使用 SQL Translation Framework(12c 以上)

如果需要進行真正的 SQL 語法替換(例如原 SQL 寫法極度不佳 ,需要將整個 WHERE 條件結構或 JOIN 方式改寫),可以使用 Oracle 12c 引入的 DBMS_SQL_TRANSLATOR

操作步驟:

  1. 建立 Translation Profile

    SQL
    BEGIN
      DBMS_SQL_TRANSLATOR.CREATE_PROFILE('MY_SQL_TRANSLATOR');
    END;
    /
    
  2. 註冊「原始 SQL」與「替換後 SQL」的對映關係

    SQL
    BEGIN
      DBMS_SQL_TRANSLATOR.REGISTER_SQL_TRANSLATION(
        profile_name => 'MY_SQL_TRANSLATOR',
        sql_text     => 'SELECT * FROM orders WHERE UPPER(status) = ''NEW''', -- 原始 Bad SQL
        translated_text => 'SELECT * FROM orders WHERE status = ''NEW'''      -- 改寫後的 Good SQL
      );
    END;
    /
    
  3. 在 Session 或系統層級啟用

    SQL
    ALTER SESSION SET SQL_TRANSLATION_PROFILE = MY_SQL_TRANSLATOR;
    

各方法適用場景總結

解決方案適用情境優點限制 / 注意事項

SQL Plan Baseline

SQL 語法固定(使用 Bind Variable)

官方標準機制,不會隨統計資訊變動而跑位

必須使用 Exact Match(字元與變數完全一致)

SQL Profile

SQL 帶有動態字面值(Literals)

支援 force_match => TRUE

需維護 Profile,極少數情況下 Optimizer 仍可能忽視
SQL Patch僅需要為指定 SQL 追加 Hint簡單快速,直接注入 Hint只適用於加 Hint,無法改動 SQL 本身結構
SQL Translation

需要修改 SQL 本身語法結構

可做真正的 SQL 重寫替換

需設定 Session/System Profile,適用 12c 

2026年7月21日 星期二

嚴重 screwed column,強制指定建立 254 階直方圖,讓oracle 選擇正確excution plan

REPORT_PROCESS_MAIL 嚴重 screwed 

BEGIN
    DBMS_STATS.GATHER_TABLE_STATS(
        ownname          => 'REVIEWREPORT',
        tabname          => 'REPORT_PROCESS_MAIL',
        method_opt       => 'FOR COLUMNS SIZE 254 SEND_MAIL_FLAG',
        estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
        -- estimate_percent => 100,
        
cascade          => TRUE
    );
END;
/



收集後驗證 Histogram 是否成功建立

SELECT column_name, num_distinct, num_buckets, histogram
  FROM all_tab_col_statistics
WHERE owner = 'REVIEWREPORT' 
     AND table_name = 'REPORT_PROCESS_MAIL'
     AND column_name = 'SEND_MAIL_FLAG';


如果 HISTOGRAM 顯示 FREQUENCYHYBRID(且 NUM_BUCKETS > 1),才代表直方圖建立成功

以下SQL 可以觀察 直方圖分布
SELECT 
    column_name,
    endpoint_number,     -- 累積筆數/端點編號 (Cumulative row count or bucket number)
    endpoint_value,      -- 數值型態的值 (若是文字型態會被轉成 Double)
    endpoint_actual_value-- Oracle 12c+ 用於顯示文字型態的真實字串 (若是文字欄位)
FROM all_tab_histograms
WHERE owner = 'REVIEWREPORT'
  AND table_name = 'REPORT_BATCH_MAIL'
  AND column_name = 'ERROR_FLAG' -- 指定要查看直方圖的欄位
ORDER BY endpoint_number;

2026年7月20日 星期一

將shared pool 裡面的SQL execution plan 拿掉

1.
SELECT address, hash_value, sql_text
   FROM v$sql
WHERE sql_id = 'gy4mvy9rkpmfu';


2.
BEGIN DBMS_SHARED_POOL.PURGE('00007FFBC18DC578, 1865076186', 'C'); END;

抓 Oracle full table scan

SELECT * FROM (

    SELECT 

        --s.parsing_schema_name as "執行Schema",

        p.OBJECT_OWNER,p.OBJECT_NAME,

        s.sql_id,

        

        -- 執行週期核心指標

        TO_CHAR(TO_DATE(s.first_load_time, 'YYYY-MM-DD/HH24:MI:SS'), 'YYYY-MM-DD HH24:MI') as "SQL首次載入時間",

        TO_CHAR(TO_DATE(s.last_load_time, 'YYYY-MM-DD/HH24:MI:SS'), 'YYYY-MM-DD HH24:MI') as "SQL最後載入時間",

        s.executions as "總執行次數",

        ROUND(s.executions / DECODE(ROUND((SYSDATE - TO_DATE(s.first_load_time, 'YYYY-MM-DD/HH24:MI:SS')) * 24, 2), 0, 1, ROUND((SYSDATE - TO_DATE(s.first_load_time, 'YYYY-MM-DD/HH24:MI:SS')) * 24, 2)), 2) as "平均每小時執行次數",

        --ROUND((((SYSDATE - TO_DATE(s.first_load_time, 'YYYY-MM-DD/HH24:MI:SS')) * 86400) / DECODE(s.executions, 0, 1, s.executions)), 1) as "平均每隔幾秒執行一次",

        --ROUND((((SYSDATE - TO_DATE(s.first_load_time, 'YYYY-MM-DD/HH24:MI:SS')) * 86400) / DECODE(s.executions, 0, 1, s.executions)) / 60, 1) as "平均每隔幾分執行/次",

        

        -- I/O 與時間指標

        s.disk_reads as "累積硬碟讀取(Blocks)",

        ROUND(s.disk_reads / DECODE(s.executions, 0, 1, s.executions), 2) as "單次平均硬碟讀取",

        ROUND(s.elapsed_time / 1000000, 2) as "累積消耗時間(秒)",

        ROUND(s.elapsed_time / 1000000 / DECODE(s.executions, 0, 1, s.executions), 2) "平均消耗時間(秒)",

        ROUND(s.cpu_time / 1000000 / DECODE(s.executions, 0, 1, s.executions), 2) "平均CPU時間(秒)",

        

        s.sql_text as "SQL縮略文本"

    FROM v$sql_plan p

    JOIN v$sql s ON p.sql_id = s.sql_id AND p.child_number = s.child_number

    WHERE 1 = 1

      AND p.operation = 'TABLE ACCESS' 

      AND p.options = 'FULL'

      AND s.parsing_schema_name NOT IN ('SYS','SYSTEM','SYSMAN','DBA','EXFSYS')

      AND s.executions > 0  

      --AND s.SQL_TEXT LIKE '%select att.value  || %'

    ORDER BY "單次平均硬碟讀取" DESC

) WHERE ROWNUM <= 100;


Oracle SQL 的 exution plan and statistics

 
1. 需要知道exution plan and statistics 的SQL
select /*+ GATHER_PLAN_STATISTICS */ max(verson) from ESP_NDE_NEW.SYS_LOG t where t.verson !='singleton ver.';

2.
SELECT sql_id, child_number, sql_text, last_active_time
   FROM v$sql
 WHERE UPPER(sql_text) LIKE '%GATHER_PLAN_STATISTICS%' -- 抓有GATHER_PLAN_STATISTICS字眼的
      AND sql_text NOT LIKE '%v$sql%'
 ORDER BY last_active_time DESC;

 3. 查詢 結果
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('71d3ck9v8vtb4', NULL, 'ALLSTATS LAST'));

2025年11月26日 星期三

postfix + procmail + spamassassin

 CentOS 6 已經EOL,所以要增加yum repository
1. cd /etc/yum.repos.d/
2. sed -i 's|#baseurl=http://mirror.centos.org/centos/$releasever|baseurl=http://vault.centos.org/6.10|g' CentOS-Base.repo

安裝spamassassin相關package
3.yum install spamassassin spamassassin-tools procmail perl-Mail-DKIM
4.service spamassassin restart
5.chkconfig spamassassin on

設定spamassassin
6.vim /etc/mail/spamassassin/local.cf
# These values can be overridden by editing ~/.spamassassin/user_prefs.cf
# (see spamassassin(1) for details)
# These should be safe assumptions and allow for simple visual sifting
# without risking lost emails.
required_hits 5
report_safe 0
rewrite_header Subject [SPAM]
# -----------------------------
# 測試設定:標記 X-Spam-Status
# -----------------------------
report_safe 0      # 直接顯示原始信件分析結果,方便測試
required_score 5.0 # 分數達到 5 才算垃圾信
# -----------------------------
# 自訂規則:Display Name + domain
# -----------------------------
# 標記寄件人 domain
header LOCAL_NOT_COMPANY From !~ /\@zzz\.com$/
# 標記寄件人 Display Name
header LOCAL_BAD_NAME From =~ /xxx/
# 綜合規則:同時符合以上兩個條件
meta LOCAL_1 (LOCAL_BAD_NAME && LOCAL_NOT_COMPANY)
# 分數設定
priority LOCAL_1 10
score LOCAL_1 5.0
# 標記寄件人 Display Name
header LOCAL_BAD_NAME2 From =~ /yyy/
# 綜合規則:同時符合以上兩個條件
meta LOCAL_2 (LOCAL_BAD_NAME2 && LOCAL_NOT_COMPANY)
# 分數設定
priority LOCAL_2 10
score LOCAL_2 5.0

安裝 procmail

Procmail 是一款用於過濾和處理電子郵件的工具,其主要功能是在郵件伺服器(如 Postfix)接收到新郵件後,根據使用者設定的規則自動進行分類、轉寄、刪除或存檔等動作

7.yum install spamassassin procmail -y
8.vi /etc/procmailrc
MAILDIR=/var/mail
LOGFILE=/var/log/procmail.log
SPAMFOLDER=$MAILDIR/spam

# ---- 過濾垃圾信件 (經由 SpamAssassin 標記的 X-Spam-Flag: YES) ----
:0 fw
| /usr/bin/spamc

:0:
* ^X-Spam-Flag: YES
$SPAMFOLDER

create spam信件存放 file
9.sudo touch /var/mail/spam
10.sudo chown postfix:mail /var/mail/spam
11.sudo chmod 666 /var/mail/spam

create spam信件存放 file
12.sudo touch /var/log/procmail.log
13.sudo chown postfix:mail /var/log/procmail.log
14.sudo chmod 666 /var/log/procmail.log

設定postfix
15.vim /etc/postfix/main.cf
## 設定postfix使用procmail處理分類、轉寄、刪除或存檔
mailbox_command = /usr/bin/procmail -a "$EXTENSION"
# 使用系統預設的本地傳送方式
local_transport = local:$myhostname
relay_transport = smtp
virtual_transport = virtual
content_filter =
receive_override_options =
16.vim /etc/postfix/master.cf (搞錯,不用設定)
#每個參數要換行縮排兩個空格(或一個 tab)
#不要在 argv 內拆成多行,整個命令必須在同一行
#將以下加在最後
#spamassassin unix - n n - - pipe
#  user=nobody                                                                # 使用运行 spamd 的用户 (通常是 spamd 或 nobody)
#  argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient} # SpamAssassin 客户端

重啟service
17.service spamassassin restart
18.service postfix reload

cron job 加上清空spam & log
19. crontab -l > cronfile ; vim cronfile ; crontab cronfile
1 23 * * */3 cat /dev/null > /var/mail/spam
2 23 * * */3 cat /dev/null > /var/log/procmail.log

2025年6月4日 星期三

Oracle RAC installation using Vmware Sphere ESXi

環境:
A - cluster
N1 - 實體機
N2 - 實體機

1.N1 / N2 進入 Vmware client設定node1 / node2 網路
1-1 virtual switch 就是虛擬node之間的switch ,做高速網路流量用
1-2 連接埠擇像是網段,意思是node之間要用virtual switch的這個網段互通



 


 

2 設定HD,注意紅框設定