但當您使用的資料庫與 Web 服務器位於不同的電腦上時,快取資料庫結果集通常是一個不錯的方法。不過,根據您的情況確定最佳的緩衝策略卻是一個難題。例如,對於使用最新資料庫結果集比較重要的應用程式而言,時間觸發的緩衝方法(緩衝系統常用的方法,它假設每次到達失效時間戳記時就重建緩衝)可能並不是一個令人滿意的解決方案。這種情況下,您需要採用一種機制,每當應用程式需要緩衝的資料庫資料發生更改時,該機制將通知該應用程式,以便該應用程式將緩衝的到期資料與資料庫保持一致。這種情況下使用“資料庫更改通知”(一個新的 Oracle 資料庫 10g 第 2 版特性)將非常方便。
“資料庫更改通知”入門
“資料庫更改通知”特性的用法非常簡單:建立一個針對通知執行的通知處理常式 – 一個 PL/SQL 預存程序或用戶端 OCI 回呼函數。然後,針對要接收其更改通知的資料庫物件註冊一個查詢,以便每當事務更改其中的任何對象並提交時調用通知處理常式。通常情況下,通知處理常式將被修改的表的名稱、所做更改的類型以及所更改行的行 ID(可選)發送給用戶端監聽程式,以便用戶端應用程式可以在響應中執行相應的處理。
為了瞭解“資料庫更改通知”特性的作用方式,請考慮以下樣本。假設您的 PHP 應用程式訪問 OE.ORDERS 表中儲存的訂單以及 OE.ORDER_ITEMS 中儲存的訂單項。鑒於很少更改已下訂單的資訊,您可能希望應用程式同時緩衝針對 ORDERS 和 ORDER_ITEMS 表的查詢結果集。要避免訪問到期資料,您可以使用“資料庫更改通知”,它可讓您的應用程式方便地獲知以上兩個表中所儲存資料的更改。
您必須先將 CHANGE NOTIFICATION 系統許可權以及 EXECUTE ON DBMS_CHANGENOTIFICATION 許可權授予 OE 使用者,才能註冊對 ORDERS 和 ORDER_ITEMS 表的查詢,以便接收通知和響應對這兩個表所做的 DML 或 DDL 更改。為此,可以從 SQL 命令列工具(如 SQL*Plus)中執行下列命令。
CONNECT / AS SYSDBA;
GRANT CHANGE NOTIFICATION TO oe;
GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO oe;
確保將 init.ora 參數 job_queue_processes 設定為非零值,以便接收 PL/SQL 通知。或者,您也可以使用下面的 ALTER SYSTEM 命令:
ALTER SYSTEM SET "job_queue_processes"=2; 然後,在以 OE/OE 串連後,您可以建立一個通知處理常式。但首先,您必須建立將由通知處理常式使用的資料庫物件。例如,您可能需要建立一個或多個資料庫表,以便通知處理常式將註冊表的更改記錄到其中。在以下樣本中,您將建立 nfresults 表來記錄以下資訊:更改發生的日期和時間、被修改的表的名稱以及一個訊息(說明通知處理常式是否成功地將通知訊息發送給用戶端)。
CONNECT oe/oe;
CREATE TABLE nfresults (
operdate DATE,
tblname VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
在實際情況中,您可能需要建立更多表來記錄通知事件以及所更改行的行 ID 等資訊,但就本文而言,nfresults 表完全可以滿足需要。
使用 UTL_HTTP 向用戶端發送通知
您可能還要建立一個或多個 PL/SQL 預存程序,並從通知處理常式中調用這些預存程序,從而實現一個更具可維護性和靈活性的解決方案。例如,您可能要建立一個實現將通知訊息發送給用戶端的預存程序。“清單 1”是 PL/SQL 過程 sendNotification。該過程使用 UTL_HTTPPL 程式包向用戶端應用程式發送更改通知。
清單 1. 使用 UTL_HTTP 向用戶端發送通知
複製代碼 代碼如下:
CREATE OR REPLACE PROCEDURE sendNotification(url IN VARCHAR2,
tblname IN VARCHAR2, order_id IN VARCHAR2) IS
req UTL_HTTP.REQ;
resp UTL_HTTP.RESP;
err_msg VARCHAR2(100);
tbl VARCHAR(60);
BEGIN
tbl:=SUBSTR(tblname, INSTR(tblname, '.', 1, 1)+1, 60);
BEGIN
req := UTL_HTTP.BEGIN_REQUEST(url||order_id||'&'||'table='||tbl);
resp := UTL_HTTP.GET_RESPONSE(req);
INSERT INTO nfresults VALUES(SYSDATE, tblname, resp.reason_phrase);
UTL_HTTP.END_RESPONSE(resp);
EXCEPTION WHEN OTHERS THEN
err_msg := SUBSTR(SQLERRM, 1, 100);
INSERT INTO nfresults VALUES(SYSDATE, tblname, err_msg);
END;
COMMIT;
END;
/
如“清單 1”所示,sendNotification 以 UTL_HTTP.BEGIN_REQUEST 函數發出的 HTTP 要求的形式向用戶端發送通知訊息。此 URL 包含 ORDERS 表中已更改行的 order_id。然後,它使用 UTL_HTTP.GET_RESPONSE 擷取用戶端發出的響應資訊。實際上,sendNotification 並不需要處理用戶端返回的整個響應,而是只擷取一個在 RESP 記錄的 reason_phrase 欄位中儲存的簡短訊息(描述狀態碼)。
建立通知處理常式
現在,您可以建立一個通知處理常式,它將藉助於上面介紹的 sendNotification 過程向用戶端發送更改通知。來看一看“清單 2”中的 PL/SQL 過程 orders_nf_callback。
清單 2. 處理對 OE.ORDERS 表所做更改的通知的通知處理常式
複製代碼 代碼如下:
CREATE OR REPLACE PROCEDURE orders_nf_callback (ntfnds IN SYS.CHNF$_DESC) IS
tblname VARCHAR2(60);
numtables NUMBER;
event_type NUMBER;
row_id VARCHAR2(20);
numrows NUMBER;
ord_id VARCHAR2(12);
url VARCHAR2(256) := 'http://webserverhost/phpcache/dropResults.php?order_no=';
BEGIN
event_type := ntfnds.event_type;
numtables := ntfnds.numtables;
IF (event_type = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) THEN
FOR i IN 1..numtables LOOP
tblname := ntfnds.table_desc_array(i).table_name;
IF (bitand(ntfnds.table_desc_array(i).opflags,
DBMS_CHANGE_NOTIFICATION.ALL_ROWS) = 0) THEN
numrows := ntfnds.table_desc_array(i).numrows;
ELSE
numrows :=0;
END IF;
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id;
SELECT order_id INTO ord_id FROM orders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
END LOOP;
END IF;
END LOOP;
END IF;
COMMIT;
END;
/
如“清單 2”所示,此通知處理常式將 SYS.CHNF$_DESC 對象用作參數,然後使用它的屬性擷取該更改的詳細資料。在該樣本中,此通知處理常式將只處理資料庫為響應對註冊對象進行的 DML 或 DDL 更改(也就是說,僅當通知類型為 EVENT_OBJCHANGE 時)而發布的通知,並忽略有關其他資料庫事件(如執行個體啟動或執行個體關閉)的通知。從以上版本開始,處理常式可以處理針對 OE.ORDERS 表中每個受影響的行發出的更改通知。在本文後面的“將表添加到現有註冊”部分中,您將向處理常式中添加幾行代碼,以便它可以處理針對 OE.ORDER_ITEMS 表中被修改的行發出的通知。
為更改通知建立註冊
建立通知處理常式後,必須為其建立一個查詢註冊。對於本樣本而言,您必須在註冊過程中對 OE.ORDER 表執行查詢並將 orders_nf_callback 指定為通知處理常式。您還需要在 DBMS_CHANGE_NOTIFICATION 程式包中指定 QOS_ROWIDS 選項,以便在通知訊息中啟用 ROWID 層級的粒度。“清單 3”是一個 PL/SQL 塊,它為 orders_nf_callback 通知處理常式建立查詢註冊。
清單 3. 為通知處理常式建立查詢註冊
複製代碼 代碼如下:
DECLARE
REGDS SYS.CHNF$_REG_INFO;
regid NUMBER;
ord_id NUMBER;
qosflags NUMBER;
BEGIN
qosflags := DBMS_CHANGE_NOTIFICATION.QOS_RELIABLE +
DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS;
REGDS := SYS.CHNF$_REG_INFO ('orders_nf_callback', qosflags, 0,0,0);
regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START (REGDS);
SELECT order_id INTO ord_id FROM orders WHERE ROWNUM<2;
DBMS_CHANGE_NOTIFICATION.REG_END;
END;
/
本樣本針對 ORDERS 表建立了一個註冊,並將 orders_nf_callback 用作通知處理常式。現在,如果您使用 DML 或 DDL 語句修改 ORDERS 表並提交事務,則將自動調用 orders_nf_callback 函數。例如,您可能針對 ORDERS 表執行下列 UPDATE 語句並提交該事務:
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2421;
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2422;
COMMIT;
要確保資料庫發布了通知來響應以上事務,您可以檢查 nfresults 表:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
tblname, rslt_msg FROM nfresults;
結果應如下所示:
OPERDATE TBLNAME RSLT_MSG
--------------------- ----------- ---------
02-mar-06 04:31:28 OE.ORDERS Not Found
02-mar-06 04:31:29 OE.ORDERS Not Found
從以上結果中可以清楚地看到,orders_nf_callback 已經正常工作,但未找到用戶端指令碼。在該樣本中出現這種情況並不意外,這是因為您並未建立 URL 中指定的 dropResults.php 指令碼。
將表添加到現有註冊
前一部分介紹了如何使用更改通知服務使資料庫在註冊對象(在以上樣本中為 ORDERS 表)發生更改時發出通知。但從效能角度而言,用戶端應用程式可能更希望緩衝 ORDER_ITEMS 表而非 ORDERS 表本身的查詢結果集,這是因為它在每次訪問訂單時,不得不從 ORDERS 表中只檢索一行,但同時必須從 ORDER_ITEMS 表中檢索多個行。在實際情況中,訂單可能包含數十個甚至數百個訂單項。
由於您已經對 ORDERS 表註冊了查詢,因此不必再建立一個註冊來註冊對 ORDER_ITEMS 表的查詢了。相反,您可以使用現有註冊。為此,您首先需要檢索現有註冊的 ID。可以執行以下查詢來完成此工作:
SELECT regid, table_name FROM user_change_notification_regs; 結果可能如下所示:
REGID TABLE_NAME
----- --------------
241 OE.ORDERS
擷取註冊 ID 後,可以使用 DBMS_CHANGE_NOTIFICATION.ENABLE_REG 函數將一個新對象添加到該註冊,如下所示:
複製代碼 代碼如下:
DECLARE
ord_id NUMBER;
BEGIN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
SELECT order_id INTO ord_id FROM order_items WHERE ROWNUM < 2;
DBMS_CHANGE_NOTIFICATION.REG_END;
END;
完成了!從現在開始,資料庫將產生一個通知來響應對 ORDERS 和 ORDER_ITEMS 所做的任何更改,並調用 orders_nf_callback 過程來處理通知。因此,下一步就是編輯 orders_nf_callback,以便它可以處理因對 ORDER_ITEMS 表執行 DML 操作而產生的通知。但在重新建立 orders_nf_callback 過程之前,您需要建立以下將在更新過程中引用的表類型:
CREATE TYPE rdesc_tab AS TABLE OF SYS.CHNF$_RDESC; 然後,返回清單,在以下程式碼之後:
複製代碼 代碼如下:
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id;
SELECT order_id INTO ord_id FROM orders WHERE rowid = row_id;
sendNotification(url, tblname, ord_id);
END LOOP;
END IF;
插入以下代碼:
複製代碼 代碼如下:
IF (tblname = 'OE.ORDER_ITEMS') THEN
FOR rec IN (SELECT DISTINCT(o.order_id) o_id FROM
TABLE(CAST(ntfnds.table_desc_array(i).row_desc_array AS rdesc_tab)) t,
orders o, order_items d WHERE t.row_id = d.rowid AND d.order_id=o.order_id)
LOOP
sendNotification(url, tblname, rec.o_id);
END LOOP;
END IF;
重新建立 orders_nf_callback 後,您需要測試它能否正常工作。為此,您可以針對 ORDER_ITEMS 表執行下列 UPDATE 語句並提交該事務:
UPDATE ORDER_ITEMS SET quantity = 160 WHERE order_id=2421 AND line_item_id=1;
UPDATE ORDER_ITEMS SET quantity = 160 WHERE order_id=2421 AND line_item_id=2;
COMMIT;
然後,檢查 nfresults 表,如下所示:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
rslt_msg FROM nfresults WHERE tblname = 'OE.ORDER_ITEMS'; 輸出可能如下所示:
OPERDATE RSLT_MSG
------------------- --------------
03-mar-06 12:32:27 Not Found
您可能很奇怪為什麼只向 nfresults 表中插入了一行 – 畢竟您更新了 ORDER_ITEMS 表中的兩行。實際上,這兩個更新了的行具有相同的 order_id – 即它們屬於同一訂單。此處,我們假設用戶端應用程式將使用一個語句選擇訂單的所有訂單項,因此它並不需要確切知道已經更改了某個訂單的哪些訂單項。相反,用戶端需要知道其中至少修改、刪除或插入了一個訂單項的訂單 ID。
構建用戶端
現在,您已經針對 ORDERS 和 ORDER_ITEMS 表建立了註冊,下面我們將瞭解一下訪問這些表中儲存的訂單及其訂單項的用戶端應用程式如何使用更改通知。為此,您可以構建一個 PHP 應用程式,它將緩衝針對以上表的查詢結果,並採取相應的操作來響應有關對這些表所做更改的通知(從資料庫伺服器中收到這些通知)。一個簡單的方法是使用 PEAR::Cache_Lite 程式包,它為您提供了一個可靠的機制來使快取資料保持最新狀態。尤其是,您可以使用 Cache_Lite_Function 類(PEAR::Cache_Lite 程式包的一部分),通過該類您可以緩衝函數調用。
例如,您可以建立一個函數來執行下列任務:建立資料庫連接、針對該資料庫執行 select 語句、擷取檢索結果並最終以數組形式返回結果。然後,您可以通過 Cache_Lite_Function 執行個體的 call 方法緩衝由該函數返回的結果數組,以便可以從本機快取而不是從後端資料庫讀取這些數組,這樣可以顯著提高應用程式的效能。然後,在收到快取資料更改的通知時,您將使用 Cache_Lite_Function 執行個體的 drop 方法刪除緩衝中的到期資料。
回過頭來看看本文的樣本,您可能要建立兩個函數,用於應用程式與資料庫互動:第一個函數將查詢 ORDERS 表並返回具有指定 ID 的訂單,而另一個函數將查詢 ORDER_ITEMS 表並返回該訂單的訂單項。“清單 4”顯示了包含 getOrderFields 函數(該函數接受訂單 ID 並返回一個包含所檢索到訂單的某些欄位的關聯陣列)的 getOrderFields.php 指令碼。
清單 4. 擷取指定訂單的欄位
複製代碼 代碼如下:
<?php
//File:getOrderFields.php
require_once 'connect.php';
function getOrderFields($order_no) {
if (!$rsConnection = GetConnection()){
return false;
}
$strSQL = "SELECT TO_CHAR(ORDER_DATE) ORDER_DATE, CUSTOMER_ID,
ORDER_TOTAL FROM ORDERS WHERE order_id =:order_no";
$rsStatement = oci_parse($rsConnection,$strSQL);
oci_bind_by_name($rsStatement, ":order_no", $order_no, 12);
if (!oci_execute($rsStatement)) {
$err = oci_error();
print $err['message'];
trigger_error('Query failed:' . $err['message']);
return false;
}
$results = oci_fetch_assoc($rsStatement);
return $results;
}
?>
“清單 5”是 getOrderItems.php 指令碼。該指令碼包含 getOrderItems 函數,該函數接受訂單 ID 並返回一個二維數組,該數組包含表示訂單的訂單項的行。
清單 5. 擷取指定訂單的訂單項
複製代碼 代碼如下:
<?php
//File:getOrderItems.php
require_once 'connect.php';
function getOrderItems($order_no) {
if (!$rsConnection = GetConnection()){
return false;
}
$strSQL = "SELECT * FROM ORDER_ITEMS WHERE
order_id =:order_no ORDER BY line_item_id";
$rsStatement = oci_parse($rsConnection,$strSQL);
oci_bind_by_name($rsStatement, ":order_no", $order_no, 12);
if (!oci_execute($rsStatement)) {
$err = oci_error();
trigger_error('Query failed:' . $err['message']);
return false;
}
$nrows = oci_fetch_all($rsStatement, $results);
return array ($nrows, $results);
}
?>
注意,以上兩個函數都需要 connect.php 指令碼,該指令碼應包含返回資料庫連接的 GetConnection 函數。清單 6 就是 connect.php 指令碼:
清單 6. 擷取資料庫連接
複製代碼 代碼如下:
<?php
//File:connect.php
function GetConnection() {
$dbHost = "dbserverhost";
$dbHostPort="1521";
$dbServiceName = "orclR2";
$usr = "oe";
$pswd = "oe";
$dbConnStr = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$dbHost.")
(PORT=".$dbHostPort."))(CONNECT_DATA=(SERVICE_NAME=".$dbServiceName.")))";
if(!$dbConn = oci_connect($usr,$pswd,$dbConnStr)) {
$err = oci_error();
trigger_error('Failed to connect ' .$err['message']);
return false;
}
return $dbConn;
}
?>
現在,您已經建立了與資料庫通訊所需的所有函數,下面我們將瞭解一下 Cache_Lite_Function 類的工作方式。清單 7 是 testCache.php 指令碼,該指令碼使用 Cache_Lite_Function 類緩衝以上函數的結果。
清單 7. 使用 PEAR::Cache_Lite 緩衝
複製代碼 代碼如下:
<?php
//File:testCache.php
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 86400
);
if (!isset($_GET['order_no'])) {
die('The order_no parameter is required');
}
$order_no=$_GET['order_no'];
$cache = new Cache_Lite_Function($options);
if ($orderfields = $cache->call('getOrderFields', $order_no)){
print "<h3>ORDER #$order_no</h3>\n";
print "<table>";
print "<tr><td>DATE:</td><td>".$orderfields['ORDER_DATE']."</td></tr>";
print "<tr><td>CUST_ID:</td><td>".$orderfields['CUSTOMER_ID']."</td></tr>";
print "<tr><td>TOTAL:</td><td>".$orderfields['ORDER_TOTAL']."</td></tr>";
print "</table>";
} else {
print "Some problem occurred while getting order fields!\n";
$cache->drop('getOrderFields', $order_no);
}
if (list($nrows, $orderitems) = $cache->call('getOrderItems', $order_no)){
//print "<h3>LINE ITEMS IN ORDER #$order_no</h3>";
print "<table border=1>";
print "<tr>\n";
while (list($key, $value) = each($orderitems)) {
print "<th>$key</th>\n";
}
print "</tr>\n";
for ($i = 0; $i < $nrows; $i++) {
print "<tr>";
print "<td>".$orderitems['ORDER_ID'][$i]."</td>";
print "<td>".$orderitems['LINE_ITEM_ID'][$i]."</td>";
print "<td>".$orderitems['PRODUCT_ID'][$i]."</td>";
print "<td>".$orderitems['UNIT_PRICE'][$i]."</td>";
print "<td>".$orderitems['QUANTITY'][$i]."</td>";
print "</tr>";
}
print "</table>";
} else {
print "Some problem occurred while getting order line items";
$cache->drop('getOrderItems', $order_no);
}
?>
“清單 7”中的 testCache.php 指令碼應與 order_no URL 參數(代表 OE.ORDER 表中儲存的訂單 ID)一起被調用。例如,要檢索與 ID 為 2408 的訂單相關的資訊,需要在瀏覽器中輸入如下所示的 URL:
http://webserverhost/phpcache/testCache.php?order_no=2408 結果,瀏覽器將產生以下輸出:
ORDER #2408
DATE: 29-JUN-99 06.59.31.333617 AM
CUST_ID: 166
TOTAL: 309
ORDER_ID LINE_ITEM_ID PRODUCT_ID UNIT_PRICE QUANTITY
2408 1 2751 61 3
2408 2 2761 26 1
2408 3 2783 10 10
現在,如果您單擊瀏覽器中的 reload 按鈕,testCache.php 指令碼將不會再次調用 getOrderFields 和 getOrderItems 函數。相反,它將從本機快取中讀取它們的結果。因此,從現在起的 24 小時(因為 lifeTime 設定為 86400 秒)以內,本機快取即可滿足使用 order_no=2108 的每個 getOrderFields 或 getOrderItems 調用的需要。但請注意,Cache_Lite_Function 類未提供 API 來測試具有給定參數的給定函數是否存在可用緩衝。因此,要確定每次使用相同參數調用函數時應用程式是實際上讀取緩衝還是仍執行該函數可能有點棘手。例如,在以上樣本中,要確保緩衝機制正常工作,您可以臨時更改 connect.php 指令碼中指定的串連資訊,以便它無法建立資料庫連接;比如指定一個錯誤的資料庫伺服器主機名稱,然後再次使用 order_no=2108 運行 testCache.php 指令碼。如果緩衝正常工作,瀏覽器的輸出應與先前的一樣。
此外,您還可以檢查緩衝目錄,該目錄作為 cacheDir 選項的值(在該樣本中為 /tmp)傳遞給 Cache_Lite_Function 類的建構函式。在該目錄中,您將找到兩個剛建立的快取檔案,這些檔案的名稱類似於:cache_7b181b55b55aee36ad5e7bd9d5a091ec_3ad04d3024f4cd54296f75c92a359154。注意,如果您是一位 Windows 使用者,則可能要使用 %SystemDrive%\temp 目錄儲存快取檔案。如果是這樣,則必須將 cacheDir 選項設定為 /temp/。
驗證緩衝機制正常工作後,可以接著建立一個 PHP 來處理從資料庫伺服器收到的更改通知。“清單 8”是 dropResult.php 指令碼。資料庫伺服器將調用該指令碼來響應 ORDERS 和 ORDER_ITEMS 表的更改。
清單 8. 處理從資料庫伺服器收到的更改通知
複製代碼 代碼如下:
<?php
//File:dropResults.php
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/'
);
$cache = new Cache_Lite_Function($options);
if (isset($_GET['order_no'])&& isset($_GET['table'])) {
if($_GET['table']=='ORDER_ITEMS'){
$cache->drop('getOrderItems', $_GET['order_no']);
}
if ($_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no']);
}
}
?>
建立 dropResult.php 指令碼後,請確保在通知處理常式中指定的 URL(如清單 2 所示)正確。然後,在 SQL*Plus 或類似工具中以 OE/OE 串連,並執行 UPDATE 語句,這些語句將影響本部分先前通過 testCache.php 指令碼訪問的同一訂單(此處是 ID 為 2408 的訂單):
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2408;
UPDATE ORDER_ITEMS SET quantity = 3 WHERE order_id=2408 AND line_item_id=1;
UPDATE ORDER_ITEMS SET quantity = 1 WHERE order_id=2408 AND line_item_id=2;
COMMIT;
為響應以上更新,本文前面介紹的通知處理常式將逐個使用下列 URL 運行 dropResults.php 指令碼兩次:
http://webserverhost/phpcache/dropResults.php?order_no=2408&table=ORDERS
http://webserverhost/phpcache/dropresults.php?order_no=2408&table=ORDER_ITEMS
從“清單 8”中您可以清楚地看到,dropResult.php 指令碼在從資料庫伺服器收到更改通知後並未重新整理緩衝。它只是刪除了包含到期資料的快取檔案。因此,如果現在檢查緩衝目錄,則將看到在使用 order_no=2408 運行 testCache.php 指令碼時建立的快取檔案已經消失。這實際上意味著,testCache.php 在下次請求與 ID 為 2408 的訂單相關的資料時將從後端資料庫而非本機快取中擷取該資料。
您會發現,在應用程式請求的結果集很有可能在應用程式使用它之前更改的情況下該方法將很有用。就本文的樣本而言,這意味著與特定訂單相關的資料可能在 testCache.php 訪問該訂單之前多次更改。這樣,應用程式會因在從資料庫伺服器收到更改通知後立即重新整理它的緩衝而做了大量不必要的工作。
但如果您希望 dropResult.php 指令碼在收到更改通知後立即重新整理緩衝,則可以在調用 drop 方法後調用 Cache_Lite_Function 執行個體的 call 方法,並為這兩個調用指定相同的參數。在該情形下,還應確保包含 getOrderFields.php 和 getOrderItems.php 指令碼,以便 dropResults.php 可以調用 getOrderFields 和 getOrderItems 函數來重新整理緩衝。“清單 9”是修改後的 dropResult.php 指令碼。
清單 9. 在收到更改通知後立即重新整理緩衝
複製代碼 代碼如下:
<?php
//File:dropResults.php
require_once 'Cache/Lite/Function.php';
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
$options = array(
'cacheDir' => '/tmp/',
'lifeTime' => 86400
);
$cache = new Cache_Lite_Function($options);
if (isset($_GET['order_no'])&& isset($_GET['table'])) {
if($_GET['table']=='ORDER_ITEMS'){
$cache->drop('getOrderItems', $_GET['order_no']);
$cache->call('getOrderItems', $_GET['order_no']);
}
if ($_GET['table']=='ORDERS'){
$cache->drop('getOrderFields', $_GET['order_no']);
$cache->call('getOrderFields', $_GET['order_no']);
}
}
?>
如果儲存在 ORDERS 和 ORDER_ITEMS 表中的資料很少更改並且應用程式頻繁訪問它,則以上方法可能很有用。
總結
如果 PHP 應用程式與 Oracle 資料庫 10g 第 2 版互動,則可以利用“資料庫更改通知特性”,通過該特性應用程式可以接收通知來響應對與發出的請求關聯的對象進行的 DML 更改。使用該特性,您不必在特定時間段更新應用程式中的緩衝。相反,僅當註冊查詢的結果集已經更改時才執行該操作。