通過(guò)緩存數(shù)據(jù)庫(kù)結(jié)果提高PHP性能的原理介紹
Jun 13, 2016 am 11:58 AM
但當(dāng)您使用的數(shù)據(jù)庫(kù)與 Web 服務(wù)器位于不同的計(jì)算機(jī)上時(shí),緩存數(shù)據(jù)庫(kù)結(jié)果集通常是一個(gè)不錯(cuò)的方法。不過(guò),根據(jù)您的情況確定最佳的緩存策略卻是一個(gè)難題。例如,對(duì)于使用最新數(shù)據(jù)庫(kù)結(jié)果集比較重要的應(yīng)用程序而言,時(shí)間觸發(fā)的緩存方法(緩存系統(tǒng)常用的方法,它假設(shè)每次到達(dá)失效時(shí)間戳記時(shí)就重新生成緩存)可能并不是一個(gè)令人滿意的解決方案。這種情況下,您需要采用一種機(jī)制,每當(dāng)應(yīng)用程序需要緩存的數(shù)據(jù)庫(kù)數(shù)據(jù)發(fā)生更改時(shí),該機(jī)制將通知該應(yīng)用程序,以便該應(yīng)用程序?qū)⒕彺娴倪^(guò)期數(shù)據(jù)與數(shù)據(jù)庫(kù)保持一致。這種情況下使用“數(shù)據(jù)庫(kù)更改通知”(一個(gè)新的 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版特性)將非常方便。
“數(shù)據(jù)庫(kù)更改通知”入門(mén)
“數(shù)據(jù)庫(kù)更改通知”特性的用法非常簡(jiǎn)單:創(chuàng)建一個(gè)針對(duì)通知執(zhí)行的通知處理程序 – 一個(gè) PL/SQL 存儲(chǔ)過(guò)程或客戶端 OCI 回調(diào)函數(shù)。然后,針對(duì)要接收其更改通知的數(shù)據(jù)庫(kù)對(duì)象注冊(cè)一個(gè)查詢,以便每當(dāng)事務(wù)更改其中的任何對(duì)象并提交時(shí)調(diào)用通知處理程序。通常情況下,通知處理程序?qū)⒈恍薷牡谋淼拿Q、所做更改的類型以及所更改行的行 ID(可選)發(fā)送給客戶端監(jiān)聽(tīng)程序,以便客戶端應(yīng)用程序可以在響應(yīng)中執(zhí)行相應(yīng)的處理。
為了了解“數(shù)據(jù)庫(kù)更改通知”特性的作用方式,請(qǐng)考慮以下示例。假設(shè)您的 PHP 應(yīng)用程序訪問(wèn) OE.ORDERS 表中存儲(chǔ)的訂單以及 OE.ORDER_ITEMS 中存儲(chǔ)的訂單項(xiàng)。鑒于很少更改已下訂單的信息,您可能希望應(yīng)用程序同時(shí)緩存針對(duì) ORDERS 和 ORDER_ITEMS 表的查詢結(jié)果集。要避免訪問(wèn)過(guò)期數(shù)據(jù),您可以使用“數(shù)據(jù)庫(kù)更改通知”,它可讓您的應(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ù)庫(kù)對(duì)象。例如,您可能需要?jiǎng)?chuàng)建一個(gè)或多個(gè)數(shù)據(jù)庫(kù)表,以便通知處理程序?qū)⒆?cè)表的更改記錄到其中。在以下示例中,您將創(chuàng)建 nfresults 表來(lái)記錄以下信息:更改發(fā)生的日期和時(shí)間、被修改的表的名稱以及一個(gè)消息(說(shuō)明通知處理程序是否成功地將通知消息發(fā)送給客戶端)。
CONNECT oe/oe;
CREATE TABLE nfresults (
operdate DATE,
tblname VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
在實(shí)際情況中,您可能需要?jiǎng)?chuàng)建更多表來(lái)記錄通知事件以及所更改行的行 ID 等信息,但就本文而言,nfresults 表完全可以滿足需要。
使用 UTL_HTTP 向客戶端發(fā)送通知
您可能還要?jiǎng)?chuàng)建一個(gè)或多個(gè) PL/SQL 存儲(chǔ)過(guò)程,并從通知處理程序中調(diào)用這些存儲(chǔ)過(guò)程,從而實(shí)現(xiàn)一個(gè)更具可維護(hù)性和靈活性的解決方案。例如,您可能要?jiǎng)?chuàng)建一個(gè)實(shí)現(xiàn)將通知消息發(fā)送給客戶端的存儲(chǔ)過(guò)程?!扒鍐?1”是 PL/SQL 過(guò)程 sendNotification。該過(guò)程使用 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;
/
如“清單 1”所示,sendNotification 以 UTL_HTTP.BEGIN_REQUEST 函數(shù)發(fā)出的 HTTP 請(qǐng)求的形式向客戶端發(fā)送通知消息。此 URL 包含 ORDERS 表中已更改行的 order_id。然后,它使用 UTL_HTTP.GET_RESPONSE 獲取客戶端發(fā)出的響應(yīng)信息。實(shí)際上,sendNotification 并不需要處理客戶端返回的整個(gè)響應(yīng),而是只獲取一個(gè)在 RESP 記錄的 reason_phrase 字段中存儲(chǔ)的簡(jiǎn)短消息(描述狀態(tài)代碼)。
創(chuàng)建通知處理程序
現(xiàn)在,您可以創(chuàng)建一個(gè)通知處理程序,它將借助于上面介紹的 sendNotification 過(guò)程向客戶端發(fā)送更改通知。來(lái)看一看“清單 2”中的 PL/SQL 過(guò)程 orders_nf_callback。
清單 2. 處理對(duì) OE.ORDERS 表所做更改的通知的通知處理程序
復(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;
/
如“清單 2”所示,此通知處理程序?qū)?SYS.CHNF$_DESC 對(duì)象用作參數(shù),然后使用它的屬性獲取該更改的詳細(xì)信息。在該示例中,此通知處理程序?qū)⒅惶幚頂?shù)據(jù)庫(kù)為響應(yīng)對(duì)注冊(cè)對(duì)象進(jìn)行的 DML 或 DDL 更改(也就是說(shuō),僅當(dāng)通知類型為 EVENT_OBJCHANGE 時(shí))而發(fā)布的通知,并忽略有關(guān)其他數(shù)據(jù)庫(kù)事件(如實(shí)例啟動(dòng)或?qū)嵗P(guān)閉)的通知。從以上版本開(kāi)始,處理程序可以處理針對(duì) OE.ORDERS 表中每個(gè)受影響的行發(fā)出的更改通知。在本文后面的“將表添加到現(xiàn)有注冊(cè)”部分中,您將向處理程序中添加幾行代碼,以便它可以處理針對(duì) OE.ORDER_ITEMS 表中被修改的行發(fā)出的通知。
為更改通知?jiǎng)?chuàng)建注冊(cè)
創(chuàng)建通知處理程序后,必須為其創(chuàng)建一個(gè)查詢注冊(cè)。對(duì)于本示例而言,您必須在注冊(cè)過(guò)程中對(duì) OE.ORDER 表執(zhí)行查詢并將 orders_nf_callback 指定為通知處理程序。您還需要在 DBMS_CHANGE_NOTIFICATION 程序包中指定 QOS_ROWIDS 選項(xiàng),以便在通知消息中啟用 ROWID 級(jí)別的粒度?!扒鍐?3”是一個(gè) PL/SQL 塊,它為 orders_nf_callback 通知處理程序創(chuàng)建查詢注冊(cè)。
清單 3. 為通知處理程序創(chuàng)建查詢注冊(cè)
復(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;
/
本示例針對(duì) ORDERS 表創(chuàng)建了一個(gè)注冊(cè),并將 orders_nf_callback 用作通知處理程序?,F(xiàn)在,如果您使用 DML 或 DDL 語(yǔ)句修改 ORDERS 表并提交事務(wù),則將自動(dòng)調(diào)用 orders_nf_callback 函數(shù)。例如,您可能針對(duì) ORDERS 表執(zhí)行下列 UPDATE 語(yǔ)句并提交該事務(wù):
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2421;
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2422;
COMMIT;
要確保數(shù)據(jù)庫(kù)發(fā)布了通知來(lái)響應(yīng)以上事務(wù),您可以檢查 nfresults 表:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
tblname, rslt_msg FROM nfresults;
結(jié)果應(yīng)如下所示:
OPERDATE TBLNAME RSLT_MSG
--------------------- ----------- ---------
02-mar-06 04:31:28 OE.ORDERS Not Found
02-mar-06 04:31:29 OE.ORDERS Not Found
從以上結(jié)果中可以清楚地看到,orders_nf_callback 已經(jīng)正常工作,但未找到客戶端腳本。在該示例中出現(xiàn)這種情況并不意外,這是因?yàn)槟⑽磩?chuàng)建 URL 中指定的 dropResults.php 腳本。
將表添加到現(xiàn)有注冊(cè)
前一部分介紹了如何使用更改通知服務(wù)使數(shù)據(jù)庫(kù)在注冊(cè)對(duì)象(在以上示例中為 ORDERS 表)發(fā)生更改時(shí)發(fā)出通知。但從性能角度而言,客戶端應(yīng)用程序可能更希望緩存 ORDER_ITEMS 表而非 ORDERS 表本身的查詢結(jié)果集,這是因?yàn)樗诿看卧L問(wèn)訂單時(shí),不得不從 ORDERS 表中只檢索一行,但同時(shí)必須從 ORDER_ITEMS 表中檢索多個(gè)行。在實(shí)際情況中,訂單可能包含數(shù)十個(gè)甚至數(shù)百個(gè)訂單項(xiàng)。
由于您已經(jīng)對(duì) ORDERS 表注冊(cè)了查詢,因此不必再創(chuàng)建一個(gè)注冊(cè)來(lái)注冊(cè)對(duì) ORDER_ITEMS 表的查詢了。相反,您可以使用現(xiàn)有注冊(cè)。為此,您首先需要檢索現(xiàn)有注冊(cè)的 ID??梢詧?zhí)行以下查詢來(lái)完成此工作:
SELECT regid, table_name FROM user_change_notification_regs; 結(jié)果可能如下所示:
REGID TABLE_NAME
----- --------------
241 OE.ORDERS
獲取注冊(cè) ID 后,可以使用 DBMS_CHANGE_NOTIFICATION.ENABLE_REG 函數(shù)將一個(gè)新對(duì)象添加到該注冊(cè),如下所示:
復(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;
完成了!從現(xiàn)在開(kāi)始,數(shù)據(jù)庫(kù)將生成一個(gè)通知來(lái)響應(yīng)對(duì) ORDERS 和 ORDER_ITEMS 所做的任何更改,并調(diào)用 orders_nf_callback 過(guò)程來(lái)處理通知。因此,下一步就是編輯 orders_nf_callback,以便它可以處理因?qū)?ORDER_ITEMS 表執(zhí)行 DML 操作而生成的通知。但在重新創(chuàng)建 orders_nf_callback 過(guò)程之前,您需要?jiǎng)?chuàng)建以下將在更新過(guò)程中引用的表類型:
CREATE TYPE rdesc_tab AS TABLE OF SYS.CHNF$_RDESC; 然后,返回清單,在以下代碼行之后:
復(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;
重新創(chuàng)建 orders_nf_callback 后,您需要測(cè)試它能否正常工作。為此,您可以針對(duì) ORDER_ITEMS 表執(zhí)行下列 UPDATE 語(yǔ)句并提交該事務(wù):
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 表中的兩行。實(shí)際上,這兩個(gè)更新了的行具有相同的 order_id – 即它們屬于同一訂單。此處,我們假設(shè)客戶端應(yīng)用程序?qū)⑹褂靡粋€(gè)語(yǔ)句選擇訂單的所有訂單項(xiàng),因此它并不需要確切知道已經(jīng)更改了某個(gè)訂單的哪些訂單項(xiàng)。相反,客戶端需要知道其中至少修改、刪除或插入了一個(gè)訂單項(xiàng)的訂單 ID。
構(gòu)建客戶端
現(xiàn)在,您已經(jīng)針對(duì) ORDERS 和 ORDER_ITEMS 表創(chuàng)建了注冊(cè),下面我們將了解一下訪問(wèn)這些表中存儲(chǔ)的訂單及其訂單項(xiàng)的客戶端應(yīng)用程序如何使用更改通知。為此,您可以構(gòu)建一個(gè) PHP 應(yīng)用程序,它將緩存針對(duì)以上表的查詢結(jié)果,并采取相應(yīng)的操作來(lái)響應(yīng)有關(guān)對(duì)這些表所做更改的通知(從數(shù)據(jù)庫(kù)服務(wù)器中收到這些通知)。一個(gè)簡(jiǎn)單的方法是使用 PEAR::Cache_Lite 程序包,它為您提供了一個(gè)可靠的機(jī)制來(lái)使緩存數(shù)據(jù)保持最新?tīng)顟B(tài)。尤其是,您可以使用 Cache_Lite_Function 類(PEAR::Cache_Lite 程序包的一部分),通過(guò)該類您可以緩存函數(shù)調(diào)用。
例如,您可以創(chuàng)建一個(gè)函數(shù)來(lái)執(zhí)行下列任務(wù):建立數(shù)據(jù)庫(kù)連接、針對(duì)該數(shù)據(jù)庫(kù)執(zhí)行 select 語(yǔ)句、獲取檢索結(jié)果并最終以數(shù)組形式返回結(jié)果。然后,您可以通過(guò) Cache_Lite_Function 實(shí)例的 call 方法緩存由該函數(shù)返回的結(jié)果數(shù)組,以便可以從本地緩存而不是從后端數(shù)據(jù)庫(kù)讀取這些數(shù)組,這樣可以顯著提高應(yīng)用程序的性能。然后,在收到緩存數(shù)據(jù)更改的通知時(shí),您將使用 Cache_Lite_Function 實(shí)例的 drop 方法刪除緩存中的過(guò)期數(shù)據(jù)。
回過(guò)頭來(lái)看看本文的示例,您可能要?jiǎng)?chuàng)建兩個(gè)函數(shù),用于應(yīng)用程序與數(shù)據(jù)庫(kù)交互:第一個(gè)函數(shù)將查詢 ORDERS 表并返回具有指定 ID 的訂單,而另一個(gè)函數(shù)將查詢 ORDER_ITEMS 表并返回該訂單的訂單項(xiàng)?!扒鍐?4”顯示了包含 getOrderFields 函數(shù)(該函數(shù)接受訂單 ID 并返回一個(gè)包含所檢索到訂單的某些字段的關(guān)聯(lián)數(shù)組)的 getOrderFields.php 腳本。
清單 4. 獲取指定訂單的字段
復(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;
}
?>
“清單 5”是 getOrderItems.php 腳本。該腳本包含 getOrderItems 函數(shù),該函數(shù)接受訂單 ID 并返回一個(gè)二維數(shù)組,該數(shù)組包含表示訂單的訂單項(xiàng)的行。
清單 5. 獲取指定訂單的訂單項(xiàng)
復(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);
}
?>
注意,以上兩個(gè)函數(shù)都需要 connect.php 腳本,該腳本應(yīng)包含返回?cái)?shù)據(jù)庫(kù)連接的 GetConnection 函數(shù)。清單 6 就是 connect.php 腳本:
清單 6. 獲取數(shù)據(jù)庫(kù)連接
復(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;
}
?>
現(xiàn)在,您已經(jīng)創(chuàng)建了與數(shù)據(jù)庫(kù)通信所需的所有函數(shù),下面我們將了解一下 Cache_Lite_Function 類的工作方式。清單 7 是 testCache.php 腳本,該腳本使用 Cache_Lite_Function 類緩存以上函數(shù)的結(jié)果。
清單 7. 使用 PEAR::Cache_Lite 緩存
復(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 "
ORDER #$order_no
\n";print "
DATE: | ".$orderfields['ORDER_DATE']." |
CUST_ID: | ".$orderfields['CUSTOMER_ID']." |
TOTAL: | ".$orderfields['ORDER_TOTAL']." |
} 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 "
LINE ITEMS IN ORDER #$order_no
";print "
$key | \n";||||
---|---|---|---|---|
".$orderitems['ORDER_ID'][$i]." | ";".$orderitems['LINE_ITEM_ID'][$i]." | ";".$orderitems['PRODUCT_ID'][$i]." | ";".$orderitems['UNIT_PRICE'][$i]." | ";".$orderitems['QUANTITY'][$i]." | ";
} else {
print "Some problem occurred while getting order line items";
$cache->drop('getOrderItems', $order_no);
}
?>
“清單 7”中的 testCache.php 腳本應(yīng)與 order_no URL 參數(shù)(代表 OE.ORDER 表中存儲(chǔ)的訂單 ID)一起被調(diào)用。例如,要檢索與 ID 為 2408 的訂單相關(guān)的信息,需要在瀏覽器中輸入如下所示的 URL:
http://webserverhost/phpcache/testCache.php?order_no=2408 結(jié)果,瀏覽器將生成以下輸出:
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
現(xiàn)在,如果您單擊瀏覽器中的 reload 按鈕,testCache.php 腳本將不會(huì)再次調(diào)用 getOrderFields 和 getOrderItems 函數(shù)。相反,它將從本地緩存中讀取它們的結(jié)果。因此,從現(xiàn)在起的 24 小時(shí)(因?yàn)?lifeTime 設(shè)置為 86400 秒)以內(nèi),本地緩存即可滿足使用 order_no=2108 的每個(gè) getOrderFields 或 getOrderItems 調(diào)用的需要。但請(qǐng)注意,Cache_Lite_Function 類未提供 API 來(lái)測(cè)試具有給定參數(shù)的給定函數(shù)是否存在可用緩存。因此,要確定每次使用相同參數(shù)調(diào)用函數(shù)時(shí)應(yīng)用程序是實(shí)際上讀取緩存還是仍執(zhí)行該函數(shù)可能有點(diǎn)棘手。例如,在以上示例中,要確保緩存機(jī)制正常工作,您可以臨時(shí)更改 connect.php 腳本中指定的連接信息,以便它無(wú)法建立數(shù)據(jù)庫(kù)連接;比如指定一個(gè)錯(cuò)誤的數(shù)據(jù)庫(kù)服務(wù)器主機(jī)名稱,然后再次使用 order_no=2108 運(yùn)行 testCache.php 腳本。如果緩存正常工作,瀏覽器的輸出應(yīng)與先前的一樣。
此外,您還可以檢查緩存目錄,該目錄作為 cacheDir 選項(xiàng)的值(在該示例中為 /tmp)傳遞給 Cache_Lite_Function 類的構(gòu)造函數(shù)。在該目錄中,您將找到兩個(gè)剛創(chuàng)建的緩存文件,這些文件的名稱類似于:cache_7b181b55b55aee36ad5e7bd9d5a091ec_3ad04d3024f4cd54296f75c92a359154。注意,如果您是一位 Windows 用戶,則可能要使用 %SystemDrive%\temp 目錄保存緩存文件。如果是這樣,則必須將 cacheDir 選項(xiàng)設(shè)置為 /temp/。
驗(yàn)證緩存機(jī)制正常工作后,可以接著創(chuàng)建一個(gè) PHP 來(lái)處理從數(shù)據(jù)庫(kù)服務(wù)器收到的更改通知?!扒鍐?8”是 dropResult.php 腳本。數(shù)據(jù)庫(kù)服務(wù)器將調(diào)用該腳本來(lái)響應(yīng) ORDERS 和 ORDER_ITEMS 表的更改。
清單 8. 處理從數(shù)據(jù)庫(kù)服務(wù)器收到的更改通知
復(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']);
}
}
?>
創(chuàng)建 dropResult.php 腳本后,請(qǐng)確保在通知處理程序中指定的 URL(如清單 2 所示)正確。然后,在 SQL*Plus 或類似工具中以 OE/OE 連接,并執(zhí)行 UPDATE 語(yǔ)句,這些語(yǔ)句將影響本部分先前通過(guò) testCache.php 腳本訪問(wèn)的同一訂單(此處是 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;
為響應(yīng)以上更新,本文前面介紹的通知處理程序?qū)⒅饌€(gè)使用下列 URL 運(yùn)行 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 腳本在從數(shù)據(jù)庫(kù)服務(wù)器收到更改通知后并未刷新緩存。它只是刪除了包含過(guò)期數(shù)據(jù)的緩存文件。因此,如果現(xiàn)在檢查緩存目錄,則將看到在使用 order_no=2408 運(yùn)行 testCache.php 腳本時(shí)創(chuàng)建的緩存文件已經(jīng)消失。這實(shí)際上意味著,testCache.php 在下次請(qǐng)求與 ID 為 2408 的訂單相關(guān)的數(shù)據(jù)時(shí)將從后端數(shù)據(jù)庫(kù)而非本地緩存中獲取該數(shù)據(jù)。
您會(huì)發(fā)現(xiàn),在應(yīng)用程序請(qǐng)求的結(jié)果集很有可能在應(yīng)用程序使用它之前更改的情況下該方法將很有用。就本文的示例而言,這意味著與特定訂單相關(guān)的數(shù)據(jù)可能在 testCache.php 訪問(wèn)該訂單之前多次更改。這樣,應(yīng)用程序會(huì)因在從數(shù)據(jù)庫(kù)服務(wù)器收到更改通知后立即刷新它的緩存而做了大量不必要的工作。
但如果您希望 dropResult.php 腳本在收到更改通知后立即刷新緩存,則可以在調(diào)用 drop 方法后調(diào)用 Cache_Lite_Function 實(shí)例的 call 方法,并為這兩個(gè)調(diào)用指定相同的參數(shù)。在該情形下,還應(yīng)確保包含 getOrderFields.php 和 getOrderItems.php 腳本,以便 dropResults.php 可以調(diào)用 getOrderFields 和 getOrderItems 函數(shù)來(lái)刷新緩存?!扒鍐?9”是修改后的 dropResult.php 腳本。
清單 9. 在收到更改通知后立即刷新緩存
復(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']);
}
}
?>
如果存儲(chǔ)在 ORDERS 和 ORDER_ITEMS 表中的數(shù)據(jù)很少更改并且應(yīng)用程序頻繁訪問(wèn)它,則以上方法可能很有用。
總結(jié)
如果 PHP 應(yīng)用程序與 Oracle 數(shù)據(jù)庫(kù) 10g 第 2 版交互,則可以利用“數(shù)據(jù)庫(kù)更改通知特性”,通過(guò)該特性應(yīng)用程序可以接收通知來(lái)響應(yīng)對(duì)與發(fā)出的請(qǐng)求關(guān)聯(lián)的對(duì)象進(jìn)行的 DML 更改。使用該特性,您不必在特定時(shí)間段更新應(yīng)用程序中的緩存。相反,僅當(dāng)注冊(cè)查詢的結(jié)果集已經(jīng)更改時(shí)才執(zhí)行該操作。

Outils d'IA chauds

Undress AI Tool
Images de déshabillage gratuites

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1
éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Pour réaliser la correction d'erreur de texte et l'optimisation de la syntaxe avec l'IA, vous devez suivre les étapes suivantes: 1. Sélectionnez un modèle ou une API d'IA appropriée, tels que Baidu, Tencent API ou bibliothèque NLP open source; 2. Appelez l'API via Curl ou Guzzle de PHP et traitez les résultats de retour; 3. Afficher les informations de correction d'erreur dans l'application et permettre aux utilisateurs de choisir d'adopter l'adoption; 4. Utilisez PHP-L et PHP_CODESNIFFER pour la détection de syntaxe et l'optimisation du code; 5. Collectez en continu les commentaires et mettez à jour le modèle ou les règles pour améliorer l'effet. Lorsque vous choisissez AIAPI, concentrez-vous sur l'évaluation de la précision, de la vitesse de réponse, du prix et du support pour PHP. L'optimisation du code doit suivre les spécifications du PSR, utiliser le cache raisonnablement, éviter les requêtes circulaires, revoir le code régulièrement et utiliser x

PhpisstillRelevantinmodernerterpriseenvironments.1.modernPhp (7.xand8.x) offre des performances, des stricts, un jitcompilation, et modernsyntax, rendant la main

évitez N 1 Problèmes de requête, réduisez le nombre de requêtes de base de données en chargeant à l'avance des données associées; 2. Sélectionnez uniquement les champs requis pour éviter de charger des entités complètes pour enregistrer la mémoire et la bande passante; 3. Utilisez raisonnablement les stratégies de cache, telles que le cache secondaire de la doctrine ou les résultats de requête à haute fréquence de cache de Doctrine; 4. Optimisez le cycle de vie de l'entité et appelez régulièrement () pour libérer la mémoire pour empêcher le débordement de la mémoire; 5. Assurez-vous que l'indice de base de données existe et analysez les instructions SQL générées pour éviter les requêtes inefficaces; 6. Désactiver le suivi automatique des changements dans les scénarios où les modifications ne sont pas nécessaires et utilisez des tableaux ou des modes légers pour améliorer les performances. L'utilisation correcte de l'ORM nécessite de combiner la surveillance SQL, la mise en cache, le traitement par lots et l'optimisation appropriée pour garantir les performances de l'application tout en maintenant l'efficacité du développement.

Pour construire un microservice PHP flexible, vous devez utiliser RabbitMQ pour obtenir une communication asynchrone, 1. Découplez le service via des files d'attente de messages pour éviter les défaillances en cascade; 2. Configurer des files d'attente persistantes, des messages persistants, une confirmation de libération et un ACK manuel pour assurer la fiabilité; 3. Utilisez des échecs de traitement de la sécurité de la file d'attente de la file d'attente de la file d'attente de la file d'attente de la file d'attente de la file d'attente de la file d'attente de la file d'attente de la file d'attente; 4. Utilisez des outils tels que SuperVisord pour protéger les processus de consommation et permettre des mécanismes de battements cardiaques pour assurer la santé des services; et finalement réaliser la capacité du système à opérer en continu en échecs.

Utilisez Sub-Process.run () pour exécuter en toute sécurité les commandes de shell et la sortie de capture. Il est recommandé de transmettre des paramètres dans les listes pour éviter les risques d'injection; 2. Lorsque les caractéristiques du shell sont nécessaires, vous pouvez définir Shell = True, mais méfiez-vous de l'injection de commande; 3. Utilisez un sous-processus.popen pour réaliser le traitement de sortie en temps réel; 4. SET CHECK = TRUE pour lancer des exceptions lorsque la commande échoue; 5. Vous pouvez appeler directement des cha?nes pour obtenir la sortie dans un scénario simple; Vous devez donner la priorité à Sub-Process.run () dans la vie quotidienne pour éviter d'utiliser OS.System () ou les modules obsolètes. Les méthodes ci-dessus remplacent l'utilisation du noyau de l'exécution des commandes shell dans Python.

L'utilisation de l'image de base PHP correcte et la configuration d'un environnement Docker sécurisé et optimisé sont la clé pour obtenir la production prête. 1. Sélectionnez PHP: 8.3-FPM-Alpine comme image de base pour réduire la surface d'attaque et améliorer les performances; 2. Désactiver les fonctions dangereuses via PHP.ini personnalisé, désactiver l'affichage des erreurs et activer Opcache et Jit pour améliorer la sécurité et les performances; 3. Utilisez Nginx comme proxy inverse pour restreindre l'accès aux fichiers sensibles et transférer correctement les demandes PHP à PHP-FPM; 4. Utilisez des images d'optimisation en plusieurs étapes pour supprimer les dépendances de développement et configurez les utilisateurs non racinaires pour exécuter des conteneurs; 5. Supervisord facultatif pour gérer plusieurs processus tels que Cron; 6. Vérifiez qu'aucune fuite d'informations sensibles avant le déploiement

Le fichier SetfitS.JSON est situé dans le chemin de niveau utilisateur ou au niveau de l'espace de travail et est utilisé pour personnaliser les paramètres VScode. 1. Chemin de niveau utilisateur: Windows est C: \ Users \\ AppData \ Roaming \ Code \ User \ Settings.json, macOS est /users//library/applicationsupport/code/user/settings.json, Linux est /home//.config/code/user/settings.json; 2. Chemin au niveau de l'espace de travail: .vscode / Paramètres dans le répertoire racine du projet

Le mécanisme de collecte des ordures de PHP est basé sur le comptage de référence, mais les références circulaires doivent être traitées par un collecteur de déchets circulaires périodique; 1. Le nombre de références libère la mémoire immédiatement lorsqu'il n'y a pas de référence à la variable; 2. Référence La référence fait que la mémoire ne peut pas être automatiquement libérée, et cela dépend de GC pour le détecter et le nettoyer; 3. GC est déclenché lorsque la "racine possible" Zval atteint le seuil ou appelle manuellement gc_collect_cycles (); 4. Les applications PHP à long terme devraient surveiller GC_Status () et appeler GC_COLLECT_CYCLES () à temps pour éviter la fuite de mémoire; 5. Les meilleures pratiques incluent d'éviter les références circulaires, en utilisant gc_disable () pour optimiser les zones clés de performance et les objets de déréférence via la méthode Clear () d'Orm.
