傳統快取依賴 TTL (時間到期) 或手動清除,無法即時反映資料庫變更。Oracle Database Change Notification (DCN) 允許應用程式註冊查詢,資料表異動並 Commit 時自動推送通知,PHP 收到通知即可精準失效對應快取。
[Oracle DB] --(DCN 通知)--> [PHP Listener Daemon] --> [Memcached/Redis] 失效快取
│ ▲
└------------<-- 應用查詢 (快取未命中時回查) ---------------┘
-- 授權
GRANT CHANGE NOTIFICATION TO app_user;
GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO app_user;
-- 確保 Job Queue 執行程序啟用
ALTER SYSTEM SET job_queue_processes = 4 SCOPE=BOTH;
-- 建立通知記錄表 (選用)
CREATE TABLE dcn_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY,
op_date TIMESTAMP DEFAULT SYSTIMESTAMP,
table_name VARCHAR2(60),
msg VARCHAR2(200)
);
CREATE OR REPLACE PROCEDURE send_dcn_callback(
p_url IN VARCHAR2,
p_table IN VARCHAR2,
p_rowid IN VARCHAR2
) IS
l_req UTL_HTTP.REQ;
l_resp UTL_HTTP.RESP;
l_err VARCHAR2(200);
BEGIN
l_req := UTL_HTTP.BEGIN_REQUEST(p_url || '?table=' || p_table || '&rowid=' || p_rowid);
UTL_HTTP.SET_HEADER(l_req, 'User-Agent', 'Oracle-DCN');
l_resp := UTL_HTTP.GET_RESPONSE(l_req);
INSERT INTO dcn_log (table_name, msg) VALUES (p_table, l_resp.reason_phrase);
UTL_HTTP.END_RESPONSE(l_resp);
EXCEPTION
WHEN OTHERS THEN
l_err := SUBSTR(SQLERRM, 1, 200);
INSERT INTO dcn_log (table_name, msg) VALUES (p_table, l_err);
END;
/
-- 註冊查詢 (範例:監聽 ORDERS 表)
DECLARE
l_reg_id NUMBER;
BEGIN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(
reg_id => l_reg_id,
query_text => 'SELECT * FROM orders',
callback => 'send_dcn_callback',
callback_ctxt => 'https://php-app.example.com/dcn_callback.php',
qosflags => DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS + DBMS_CHANGE_NOTIFICATION.QOS_QUERY
);
DBMS_OUTPUT.PUT_LINE('Registered: ' || l_reg_id);
END;
/
addServer('127.0.0.1', 11211);
// 依資料表對應快取鍵前綴
$prefix_map = [
'ORDERS' => 'order:',
'ORDER_ITEMS' => 'order_item:',
'CUSTOMERS' => 'customer:',
];
$prefix = $prefix_map[$table] ?? $table . ':';
// 失效相關快取 (簡單策略:刪除前綴所有鍵需掃描,生產建議維護反向索引或用 Tag)
$keys_to_delete = [];
// 這裡需配合應用層快取鍵命名規範,例如 order:123, order:list:user:456
// 簡易示範:刪除單筆與常見列表
$mem->delete($prefix . $rowid);
$mem->delete($prefix . 'list:all');
$mem->delete($prefix . 'list:recent');
http_response_code(200);
echo 'OK';
OCI8 擴充支援 oci_register_taf_callback、oci_set_notify_callback (需 Oracle Instant Client),可在 PHP CLI Daemon 中直接接收通知,避免 HTTP 開銷與防火牆問題。架構更簡潔,但需自行管理進程存活。