但當(dāng)您使用的數(shù)據(jù)庫與 Web 服務(wù)器位于不同的計(jì)算機(jī)上時(shí),緩存數(shù)據(jù)庫結(jié)果集通常是一個(gè)不錯(cuò)的方法。不過,根據(jù)您的情況確定最佳的緩存策略卻是一個(gè)難題。例如,對(duì)于使用最新數(shù)據(jù)庫結(jié)果集比較重要的應(yīng)用程序而言,時(shí)間觸發(fā)的緩存方法(緩存系統(tǒng)常用的方法,它假設(shè)每次到達(dá)失效時(shí)間戳記時(shí)就重新生成緩存)可能并不是一個(gè)令人滿意的解決方案。這種情況下,您需要采用一種機(jī)制,每當(dāng)應(yīng)用程序需要緩存的數(shù)據(jù)庫數(shù)據(jù)發(fā)生更改時(shí),該機(jī)制將通知該應(yīng)用程序,以便該應(yīng)用程序?qū)⒕彺娴倪^期數(shù)據(jù)與數(shù)據(jù)庫保持一致。這種情況下使用“數(shù)據(jù)庫更改通知”(一個(gè)新的 Oracle 數(shù)據(jù)庫 10g 第 2 版特性)將非常方便。
“數(shù)據(jù)庫更改通知”入門
“數(shù)據(jù)庫更改通知”特性的用法非常簡單:創(chuàng)建一個(gè)針對(duì)通知執(zhí)行的通知處理程序 – 一個(gè) PL/SQL 存儲(chǔ)過程或客戶端 OCI 回調(diào)函數(shù)。然后,針對(duì)要接收其更改通知的數(shù)據(jù)庫對(duì)象注冊(cè)一個(gè)查詢,以便每當(dāng)事務(wù)更改其中的任何對(duì)象并提交時(shí)調(diào)用通知處理程序。通常情況下,通知處理程序?qū)⒈恍薷牡谋淼拿Q、所做更改的類型以及所更改行的行 ID(可選)發(fā)送給客戶端監(jiān)聽程序,以便客戶端應(yīng)用程序可以在響應(yīng)中執(zhí)行相應(yīng)的處理。
為了了解“數(shù)據(jù)庫更改通知”特性的作用方式,請(qǐng)考慮以下示例。假設(shè)您的 PHP 應(yīng)用程序訪問 OE.ORDERS 表中存儲(chǔ)的訂單以及 OE.ORDER_ITEMS 中存儲(chǔ)的訂單項(xiàng)。鑒于很少更改已下訂單的信息,您可能希望應(yīng)用程序同時(shí)緩存針對(duì) ORDERS 和 ORDER_ITEMS 表的查詢結(jié)果集。要避免訪問過期數(shù)據(jù),您可以使用“數(shù)據(jù)庫更改通知”,它可讓您的應(yīng)用程序方便地獲知以上兩個(gè)表中所存儲(chǔ)數(shù)據(jù)的更改。
您必須先將 CHANGE NOTIFICATION 系統(tǒng)權(quán)限以及 EXECUTE ON DBMS_CHANGENOTIFICATION 權(quán)限授予 OE 用戶,才能注冊(cè)對(duì) ORDERS 和 ORDER_ITEMS 表的查詢,以便接收通知和響應(yīng)對(duì)這兩個(gè)表所做的 DML 或 DDL 更改。為此,可以從 SQL 命令行工具(如 SQL*Plus)中執(zhí)行下列命令。
CONNECT / AS SYSDBA;
GRANT CHANGE NOTIFICATION TO oe;
GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO oe;
確保將 init.ora 參數(shù) job_queue_processes 設(shè)置為非零值,以便接收 PL/SQL 通知?;蛘?,您也可以使用下面的 ALTER SYSTEM 命令:
ALTER SYSTEM SET "job_queue_processes"=2; 然后,在以 OE/OE 連接后,您可以創(chuàng)建一個(gè)通知處理程序。但首先,您必須創(chuàng)建將由通知處理程序使用的數(shù)據(jù)庫對(duì)象。例如,您可能需要?jiǎng)?chuàng)建一個(gè)或多個(gè)數(shù)據(jù)庫表,以便通知處理程序?qū)⒆?cè)表的更改記錄到其中。在以下示例中,您將創(chuàng)建 nfresults 表來記錄以下信息:更改發(fā)生的日期和時(shí)間、被修改的表的名稱以及一個(gè)消息(說明通知處理程序是否成功地將通知消息發(fā)送給客戶端)。
CONNECT oe/oe;
CREATE TABLE nfresults (
operdate DATE,
tblname VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
在實(shí)際情況中,您可能需要?jiǎng)?chuàng)建更多表來記錄通知事件以及所更改行的行 ID 等信息,但就本文而言,nfresults 表完全可以滿足需要。
使用 UTL_HTTP 向客戶端發(fā)送通知
您可能還要?jiǎng)?chuàng)建一個(gè)或多個(gè) PL/SQL 存儲(chǔ)過程,并從通知處理程序中調(diào)用這些存儲(chǔ)過程,從而實(shí)現(xiàn)一個(gè)更具可維護(hù)性和靈活性的解決方案。例如,您可能要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)將通知消息發(fā)送給客戶端的存儲(chǔ)過程?!扒鍐?1”是 PL/SQL 過程 sendNotification。該過程使用 UTL_HTTPPL 程序包向客戶端應(yīng)用程序發(fā)送更改通知。
清單 1. 使用 UTL_HTTP 向客戶端發(fā)送通知
復(fù)制代碼 代碼如下:
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;
/
復(fù)制代碼 代碼如下:
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;
/
復(fù)制代碼 代碼如下:
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 ROWNUMDBMS_CHANGE_NOTIFICATION.REG_END;
END;
/
復(fù)制代碼 代碼如下:
DECLARE
ord_id NUMBER;
BEGIN
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
SELECT order_id INTO ord_id FROM order_items WHERE ROWNUM DBMS_CHANGE_NOTIFICATION.REG_END;
END;
復(fù)制代碼 代碼如下:
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;
復(fù)制代碼 代碼如下:
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;
復(fù)制代碼 代碼如下:
//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;
}
?>
復(fù)制代碼 代碼如下:
//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);
}
?>
復(fù)制代碼 代碼如下:
//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;
}
?>
復(fù)制代碼 代碼如下:
//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 "
DATE: | ".$orderfields['ORDER_DATE']." |
CUST_ID: | ".$orderfields['CUSTOMER_ID']." |
TOTAL: | ".$orderfields['ORDER_TOTAL']." |
$key | \n";||||
---|---|---|---|---|
".$orderitems['ORDER_ID'][$i]." | ";".$orderitems['LINE_ITEM_ID'][$i]." | ";".$orderitems['PRODUCT_ID'][$i]." | ";".$orderitems['UNIT_PRICE'][$i]." | ";".$orderitems['QUANTITY'][$i]." | ";
復(fù)制代碼 代碼如下:
//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']);
}
}
?>
復(fù)制代碼 代碼如下:
//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']);
}
}
?>
該軟件包括了市面上所有手機(jī)CPU,手機(jī)跑分情況,電腦CPU,電腦產(chǎn)品信息等等,方便需要大家查閱數(shù)碼產(chǎn)品最新情況,了解產(chǎn)品特性,能夠進(jìn)行對(duì)比選擇最具性價(jià)比的商品。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)