Известно, что кэширование результатов запросов к базе данных позволяет существенно сократить время выполнения скрипта и минимизировать нагрузку на сервер базы данных. Этот метод работает очень хорошо, если обрабатываемые данные по существу статичны. Это связано с тем, что многие запросы данных к удаленной базе данных в конечном итоге могут быть удовлетворены из локального кэша, что устраняет необходимость подключения к базе данных, выполнения запроса и получения результатов.
Но кэширование набора результатов базы данных часто является хорошей идеей, если используемая вами база данных находится на другом компьютере, чем веб-сервер. Однако определить лучшую стратегию кэширования для вашей ситуации может быть сложно. Например, для приложений, где важно использовать последний набор результатов базы данных, подход к кэшированию с синхронизацией по времени (обычно используемый системами кэширования, которые предполагают, что кэш обновляется каждый раз, когда достигается отметка времени истечения срока действия) может быть неудовлетворительным решением. . В этом случае вам нужен механизм, который будет уведомлять приложение при каждом изменении данных базы данных, которые приложение должно кэшировать, чтобы приложение могло поддерживать соответствие кэшированных данных с истекшим сроком действия с базой данных. В этом случае использование «Уведомления об изменении базы данных» будет очень удобным.
Начало работы с уведомлением об изменении базы данных
Использовать функцию уведомления об изменении базы данных очень просто: создайте обработчик уведомлений, который выполняется для уведомления – хранимую процедуру PL/SQL или функцию обратного вызова OCI клиента. Затем зарегистрируйте запрос к объектам базы данных, для которых вы хотите получать уведомления об изменениях, чтобы обработчик уведомлений вызывался всякий раз, когда транзакция изменяет какой-либо объект внутри нее и фиксируется. Обычно обработчик уведомлений отправляет имя измененной таблицы, тип внесенного изменения и, при необходимости, идентификатор измененной строки клиентскому прослушивателю, чтобы клиентское приложение могло выполнить соответствующие действия в ответной операции.
Чтобы понять, как работает функция уведомления об изменении базы данных, рассмотрим следующий пример. Предположим, что ваше PHP-приложение обращается к заказам, хранящимся в таблице OE.ORDERS, и элементам заказа, хранящимся в OE.ORDER_ITEMS. Учитывая, что информация о размещенных заказах редко меняется, вы можете захотеть, чтобы ваше приложение кэшировало наборы результатов запросов как для таблиц ORDERS, так и для таблиц ORDER_ITEMS. Чтобы избежать доступа к устаревшим данным, вы можете использовать уведомления об изменении базы данных, которые позволяют вашему приложению легко получать уведомления об изменениях данных, хранящихся в двух таблицах выше.
Вы должны предоставить системное разрешение CHANGE NOTIFICATION и разрешение EXECUTE ON DBMS_CHANGENOTIFICATION пользователю OE, прежде чем вы сможете регистрировать запросы к таблицам ORDERS и ORDER_ITEMS для получения уведомлений и ответа на изменения DML или DDL в этих таблицах. Для этого выполните следующую команду из инструмента командной строки SQL, такого как SQL*Plus.
ПОДКЛЮЧИТЬ/КАК SYSDBA;
ПРЕДОСТАВИТЬ УВЕДОМЛЕНИЕ ОБ ИЗМЕНЕНИИ oe;
РАЗРЕШИТЬ ВЫПОЛНЕНИЕ НА DBMS_CHANGE_NOTIFICATION TO oe;
Убедитесь, что для параметра init.ora job_queue_processes установлено ненулевое значение, чтобы получать уведомления PL/SQL. Альтернативно вы можете использовать следующую команду ALTER SYSTEM:
ALTER SYSTEM SET "job_queue_processes"=2 Затем, после подключения как OE/OE, вы можете создать обработчик уведомлений; Но сначала необходимо создать объект базы данных, который будет использоваться обработчиком уведомлений. Например, вам может потребоваться создать одну или несколько таблиц базы данных, в которые обработчик уведомлений записывает изменения реестра. В следующем примере вы создаете таблицу nfresults для записи даты и времени возникновения изменения, имени измененной таблицы и сообщения, указывающего, успешно ли обработчик уведомлений отправил уведомление клиенту.
ПОДКЛЮЧИТЬ oe/oe;
СОЗДАТЬ ТАБЛИЦУ nfresults (
ДАТА ДЕЙСТВИЯ,
имя таблицы VARCHAR2(60),
rslt_msg VARCHAR2(100)
);
В реальном сценарии вам может потребоваться создать дополнительные таблицы для записи такой информации, как события уведомлений и идентификаторы измененных строк, но для целей этой статьи таблицы nfresults будет достаточно.
Использование UTL_HTTP для отправки уведомлений клиентам.
Вы также можете создать одну или несколько хранимых процедур PL/SQL и вызывать эти хранимые процедуры из обработчика уведомлений, тем самым достигая более удобного в обслуживании и гибкого решения. Например, вы можете создать хранимую процедуру, реализующую отправку уведомлений клиентам. «Листинг 1» — это процедура PL/SQL sendNotification. Этот процесс использует пакет UTL_HTTPPL для отправки уведомлений об изменениях клиентским приложениям.
Листинг 1. Отправка уведомления клиенту с помощью UTL_HTTPCREATE
OR REPLACE PROCEDURE sendNotification(url IN VARCHAR2,
имя_таблицы В VARCHAR2, order_id В VARCHAR2) IS
требуется UTL_HTTP.REQ;
соответственно UTL_HTTP.RESP;
err_msg VARCHAR2 (100);
стол VARCHAR(60);
НАЧИНАТЬ
tbl:=SUBSTR(имя_таблицы, INSTR(имя_таблицы, '.', 1, 1)+1, 60);
НАЧИНАТЬ
req := UTL_HTTP.BEGIN_REQUEST(url||order_id||'&'||'table='||tbl);
соответственно: = UTL_HTTP.GET_RESPONSE(req);
INSERT INTO nfresults VALUES(SYSDATE, имя_таблицы, соотв.reason_phrase);
UTL_HTTP.END_RESPONSE(соответственно);
ИСКЛЮЧЕНИЕ, КОГДА ДРУГИЕ ТОГДА
err_msg:= SUBSTR(SQLERRM, 1, 100);
INSERT INTO nfresults VALUES(SYSDATE, имя_таблицы, err_msg);
КОНЕЦ;
СОВЕРШИТЬ;
КОНЕЦ;
/
Как показано в «Листинге 1», sendNotification отправляет клиенту сообщение с уведомлением в виде HTTP-запроса, выданного функцией UTL_HTTP.BEGIN_REQUEST. Этот URL-адрес содержит order_id измененной строки в таблице ORDERS. Затем он использует UTL_HTTP.GET_RESPONSE для получения информации ответа, отправленной клиентом. Фактически, sendNotification не требуется обрабатывать весь ответ, возвращаемый клиентом, а лишь получает короткое сообщение (описывающее код состояния), хранящееся в поле Reason_phrase записи RESP.
Создание обработчика уведомлений
Теперь вы можете создать обработчик уведомлений, который будет отправлять клиентам уведомления об изменениях с помощью описанной выше процедуры sendNotification. Давайте взглянем на процедуру PL/SQLorders_nf_callback в «Листинге 2».
Листинг 2. Обработчик уведомлений, обрабатывающий уведомления об изменениях в таблице OE.ORDERS.
CREATE OR REPLACE PROCEDUREorders_nf_callback (ntfnds IN SYS.CHNF$_DESC) IS
имя таблицы VARCHAR2(60);
числовые таблицы НОМЕР;
тип_события НОМЕР;
row_id VARCHAR2 (20);
числа НОМЕР;
ord_id VARCHAR2 (12);
url VARCHAR2(256) := 'http://webserverhost/phpcache/dropResults.php?order_no=';
НАЧИНАТЬ
event_type:= ntfnds.event_type;
numtables:= ntfnds.numtables;
ЕСЛИ (тип_события = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) ТО
FOR i IN 1..numtables LOOP
имя_таблицы:= ntfnds.table_desc_array(i).table_name;
ЕСЛИ (bitand(ntfnds.table_desc_array(i).opflags,
DBMS_CHANGE_NOTIFICATION.ALL_ROWS) = 0) ТОГДА
numrows:= ntfnds.table_desc_array(i).numrows;
ЕЩЕ
числа: = 0;
КОНЕЦ ЕСЛИ;
ЕСЛИ (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id:= ntfnds.table_desc_array(i).row_desc_array(j).row_id;
ВЫБЕРИТЕ order_id INTO ord_id ИЗ заказов WHERE rowid = row_id;
sendNotification (url, имя таблицы, ord_id);
КОНЕЦ ПЕТЛИ;
КОНЕЦ ЕСЛИ;
КОНЕЦ ПЕТЛИ;
КОНЕЦ ЕСЛИ;
СОВЕРШИТЬ;
КОНЕЦ;
/
Как показано в листинге 2, этот обработчик уведомлений принимает объект SYS.CHNF$_DESC в качестве параметра, а затем использует его свойства для получения подробной информации об изменении. В этом примере этот обработчик уведомлений будет обрабатывать только уведомления, отправленные базой данных в ответ на изменения DML или DDL зарегистрированных объектов (то есть только если тип уведомления — EVENT_OBJCHANGE), и игнорировать информацию о других событиях базы данных, таких как запуск экземпляра или завершение работы экземпляра). Начиная с указанной выше версии, обработчик может обрабатывать уведомления об изменениях, выдаваемые для каждой затронутой строки в таблице OE.ORDERS. Далее в этой статье, в разделе «Добавление таблицы в существующую регистрацию», вы добавите в обработчик несколько строк кода, чтобы он мог обрабатывать уведомления об измененных строках в таблице OE.ORDER_ITEMS.
Создание регистрации для уведомлений об изменениях
После создания обработчика уведомлений необходимо создать для него регистрацию запроса. В этом примере вы должны выполнить запрос к таблице OE.ORDER во время процесса регистрации и указать order_nf_callback в качестве обработчика уведомлений. Вам также необходимо указать параметр QOS_ROWIDS в пакете DBMS_CHANGE_NOTIFICATION, чтобы включить детализацию на уровне ROWID в сообщениях уведомлений. «Листинг 3» представляет собой блок PL/SQL, который создает регистрацию запроса для обработчика уведомленийorders_nf_callback.
Листинг 3. Создание запроса на регистрациюDECLARE
для обработчика уведомлений
РЕГДС SYS.CHNF$_REG_INFO;
РЕГИДНЫЙ НОМЕР;
ord_id НОМЕР;
qosflags НОМЕР;
НАЧИНАТЬ
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);
ВЫБЕРИТЕ order_id INTO ord_id ИЗ заказов WHERE ROWNUM<2;
DBMS_CHANGE_NOTIFICATION.REG_END;
КОНЕЦ;
/
В этом примере создается регистрация в таблице ORDERS и используется order_nf_callback в качестве обработчика уведомлений. Теперь, если вы используете оператор DML или DDL для изменения таблицы ORDERS и фиксации транзакции, функцияorders_nf_callback вызывается автоматически. Например, вы можете выполнить следующий оператор UPDATE для таблицы ORDERS и зафиксировать транзакцию:
UPDATE ORDERS SET order_mode = 'direct' WHERE order_id=2421;
ОБНОВЛЕНИЕ ЗАКАЗОВ SET order_mode = 'direct' ГДЕ order_id=2422;
СОВЕРШИТЬ;
Чтобы убедиться, что база данных отправила уведомления в ответ на вышеуказанную транзакцию, вы можете проверить таблицу nfresults:
SELECT TO_CHAR(operdate, 'dd-mon-yy hh:mi:ss') operdate,
имя_таблицы, rslt_msg ИЗ nfresults;
Результат должен выглядеть следующим образом:
OPERDATE TBLNAME RSLT_MSG.
--------------------- ---------- ---------
02 марта 06 04:31:28 OE.ORDERS не найден
02 марта 06 04:31:29 OE.ORDERS не найден
Из приведенных выше результатов видно, что order_nf_callback уже работает, но клиентский скрипт не найден. В этом примере это не является неожиданным, поскольку вы не создавали сценарий dropResults.php, указанный в URL-адресе. Инструкции по сценарию dropResults.php см. в разделе «Создание клиента» далее в этой статье.
Добавление таблицы к существующей регистрации
В предыдущем разделе было показано, как использовать службу уведомлений об изменениях, чтобы база данных уведомляла вас об изменении объекта регистрации (в приведенном выше примере — таблицы ORDERS). Но с точки зрения производительности клиентское приложение может предпочесть кэшировать набор результатов запроса таблицы ORDER_ITEMS, а не саму таблицу ORDERS, поскольку ему приходится извлекать только одну строку из таблицы ORDERS каждый раз, когда он обращается к заказу, но в в то же время из таблицы ORDER_ITEMS необходимо получить несколько строк. В действительности заказ может содержать десятки и даже сотни позиций.
Поскольку вы уже зарегистрировали запросы к таблице ORDERS, вам не нужно создавать регистрацию для регистрации запросов к таблице ORDER_ITEMS. Вместо этого вы можете использовать существующую регистрацию. Для этого сначала необходимо получить идентификатор существующей регистрации. Для этого можно выполнить следующий запрос:
SELECT regid, table_name FROM user_change_notification_regs. Результаты могут выглядеть следующим образом:
REGID TABLE_NAME;
----- --------------
241 ОЭ.ЗАКАЗЫ
После получения идентификатора регистрации вы можете добавить в регистрацию новый объект с помощью функции DBMS_CHANGE_NOTIFICATION.ENABLE_REG следующим образом:
DECLARE
ord_id НОМЕР;
НАЧИНАТЬ
DBMS_CHANGE_NOTIFICATION.ENABLE_REG(241);
ВЫБЕРИТЕ order_id INTO ord_id ИЗ order_items WHERE ROWNUM < 2;
DBMS_CHANGE_NOTIFICATION.REG_END;
КОНЕЦ;
/
Сделанный! С этого момента база данных будет генерировать уведомление в ответ на любые изменения, внесенные в ORDERS и ORDER_ITEMS, и вызывать процедуруorders_nf_callback для обработки уведомления. Поэтому следующим шагом будет изменение order_nf_callback, чтобы он мог обрабатывать уведомления, генерируемые операциями DML в таблице ORDER_ITEMS. Но прежде чем заново создавать процедуру order_nf_callback, вам необходимо создать следующий тип таблицы, на который будут ссылаться в процессе обновления:
CREATE TYPE rdesc_tab AS TABLE OF SYS.CHNF$_RDESC. Затем вернитесь к листингу 2 после следующей строки; код:
IF (tblname = 'OE.ORDERS') THEN
FOR j IN 1..numrows LOOP
row_id:= ntfnds.table_desc_array(i).row_desc_array(j).row_id;
ВЫБЕРИТЕ order_id INTO ord_id ИЗ заказов WHERE rowid = row_id;
sendNotification (url, имя таблицы, ord_id);
КОНЕЦ ПЕТЛИ;
КОНЕЦ ЕСЛИ;
Вставьте следующий код:
IF (tblname = 'OE.ORDER_ITEMS') THEN
ДЛЯ записи IN (SELECT DISTINCT(o.order_id) o_id FROM
TABLE(CAST(ntfnds.table_desc_array(i).row_desc_array AS rdesc_tab)) t,
заказы o, order_items d ГДЕ t.row_id = d.rowid И d.order_id=o.order_id)
ПЕТЛЯ
sendNotification (url, имя таблицы, Rec.o_id);
КОНЕЦ ПЕТЛИ;
КОНЕЦ ЕСЛИ;
После повторного создания order_nf_callback нужно проверить корректность работы. Для этого вы можете выполнить следующий оператор UPDATE для таблицы ORDER_ITEMS и зафиксировать транзакцию:
UPDATE ORDER_ITEMS SET количество = 160 WHERE order_id=2421 AND line_item_id=1;
UPDATE ORDER_ITEMS SET количество = 160, ГДЕ order_id=2421 И line_item_id=2;
СОВЕРШИТЬ;
Затем проверьте таблицу 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.03.06 12:32:27 Не найдено
Вы можете задаться вопросом, почему в таблицу nfresults была вставлена только одна строка – ведь вы обновили две строки в таблице ORDER_ITEMS. Фактически две обновленные строки имеют один и тот же order_id, т. е. они принадлежат одному и тому же порядку. Здесь мы предполагаем, что клиентское приложение будет использовать один оператор для выбора всех позиций заказа, поэтому ему не нужно точно знать, какие позиции заказа были изменены. Вместо этого клиенту необходимо знать идентификатор заказа, в котором хотя бы одна позиция была изменена, удалена или вставлена.
Создание клиента
Теперь, когда вы создали регистрации для таблиц ORDERS и ORDER_ITEMS, давайте посмотрим, как уведомления об изменениях используются клиентскими приложениями, которые получают доступ к заказам и их позициям, хранящимся в этих таблицах. Для этого можно создать PHP-приложение, которое будет кэшировать результаты запросов к указанным выше таблицам и предпринимать соответствующие действия в ответ на уведомления об изменениях в этих таблицах (которые поступают с сервера базы данных). Самый простой способ — использовать пакет PEAR::Cache_Lite, который предоставляет вам надежный механизм обновления данных кэша. В частности, вы можете использовать класс Cache_Lite_Function (часть пакета PEAR::Cache_Lite), который позволяет кэшировать вызовы функций.
Например, вы можете создать функцию, которая выполняет следующие задачи: устанавливает соединение с базой данных, выполняет оператор выбора для базы данных, получает результаты поиска и, наконец, возвращает результаты в виде массива. Затем вы можете кэшировать массивы результатов, возвращаемые функцией, с помощью метода вызова экземпляра Cache_Lite_Function, чтобы их можно было читать из локального кэша, а не из внутренней базы данных, что может значительно повысить производительность вашего приложения. Затем, когда вы будете уведомлены об изменениях в кэшированных данных, вы будете использовать метод drop экземпляра Cache_Lite_Function, чтобы удалить просроченные данные в кэше.
Возвращаясь к примеру в этой статье, вы можете создать две функции для взаимодействия вашего приложения с базой данных: первая функция будет запрашивать таблицу ORDERS и возвращать заказы с указанным идентификатором, а другая функция будет запрашивать таблицу ORDER_ITEMS. table и return Возвращает позиции для этого заказа. В «Листинге 4» показан сценарий getOrderFields.php, содержащий функцию getOrderFields, которая принимает идентификатор заказа и возвращает ассоциативный массив, содержащий некоторые поля полученного заказа.
Листинг 4. Получение полей указанного порядка
<?php
//Файл:getOrderFields.php
require_once 'connect.php';
функция getOrderFields($order_no) {
если (!$rsConnection = GetConnection()){
вернуть ложь;
}
$strSQL = "ВЫБРАТЬ 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);
если (!oci_execute($rsStatement)) {
$err = oci_error();
напечатайте $err['сообщение'];
триггер_error('Ошибка запроса:' . $err['message']);
вернуть ложь;
}
$results = oci_fetch_assoc($rsStatement);
вернуть $результаты;
}
?>
«Листинг 5» — это сценарий getOrderItems.php. Скрипт содержит функцию getOrderItems, которая принимает идентификатор заказа и возвращает двумерный массив, содержащий строки, представляющие позиции заказа.
Листинг 5. Получение позиций указанного порядка
<?php
//Файл:getOrderItems.php
require_once 'connect.php';
функция getOrderItems($order_no) {
если (!$rsConnection = GetConnection()){
вернуть ложь;
}
$strSQL = "ВЫБРАТЬ * ИЗ ORDER_ITEMS ГДЕ
order_id =:order_no ORDER BY line_item_id";
$rsStatement = oci_parse($rsConnection,$strSQL);
oci_bind_by_name($rsStatement, ":order_no", $order_no, 12);
если (!oci_execute($rsStatement)) {
$err = oci_error();
триггер_error('Ошибка запроса:' . $err['message']);
вернуть ложь;
}
$nrows = oci_fetch_all($rsStatement, $results);
возвращаемый массив ($nrows, $results);
}
?>
Обратите внимание, что для обеих вышеуказанных функций требуется сценарий Connect.php, который должен содержать функцию GetConnection, возвращающую соединение с базой данных. Листинг 6 представляет собой сценарий Connect.php:
Листинг 6. Получение соединения с базой данных
<?php
//Файл:connect.php
функция GetConnection() {
$dbHost = "dbserverhost";
$dbHostPort="1521";
$dbServiceName = "orclR2";
$usr = "ое";
$pswd = "ое";
$dbConnStr = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$dbHost.")
(PORT=".$dbHostPort."))(CONNECT_DATA=(SERVICE_NAME=".$dbServiceName.")))";
if(!$dbConn = oci_connect($usr,$pswd,$dbConnStr)) {
$err = oci_error();
триггер_error('Не удалось подключиться' .$err['сообщение']);
вернуть ложь;
}
вернуть $dbConn;
}
?>
Теперь, когда вы создали все функции, необходимые для взаимодействия с базой данных, давайте посмотрим, как работает класс Cache_Lite_Function. В листинге 7 показан сценарий testCache.php, который использует класс Cache_Lite_Function для кэширования результатов вышеуказанной функции.
<?php
с помощью PEAR::Cache_Lite
//Файл:testCache.php
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/',
'время жизни' => 86400
);
if (!isset($_GET['order_no'])) {
die('Обязательный параметр order_no');
}
$order_no=$_GET['order_no'];
$cache = новая Cache_Lite_Function($options);
if ($orderfields = $cache->call('getOrderFields', $order_no)){
напечатайте "<h3>ORDER #$order_no</h3>n";
напечатайте "<таблица>";
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>";
напечатайте "</table>";
} еще {
print "Произошла проблема при получении полей заказа!n";
$cache->drop('getOrderFields', $order_no);
}
if (list($nrows, $orderitems) = $cache->call('getOrderItems', $order_no)){
//print "<h3>СТРОКИ В ЗАКАЗЕ #$order_no</h3>";
напечатайте "<граница таблицы=1>";
напечатайте "<tr>n";
while (список($ключ, $значение) = каждый($orderitems)) {
напечатайте "<th>$key</th>n";
}
напечатайте "</tr>n";
for ($i = 0; $i < $nrows; $i++) {
напечатайте "<tr>";
напечатайте "<td>".$orderitems['ORDER_ID'][$i]."</td>";
напечатайте "<td>".$orderitems['LINE_ITEM_ID'][$i]."</td>";
напечатайте "<td>".$orderitems['PRODUCT_ID'][$i]."</td>";
напечатайте "<td>".$orderitems['UNIT_PRICE'][$i]."</td>";
напечатайте "<td>".$orderitems['QUANTITY'][$i]."</td>";
напечатайте "</tr>";
}
напечатайте "</table>";
} еще {
print «Произошла проблема при получении позиций заказа»;
$cache->drop('getOrderItems', $order_no);
}
?>
Сценарий testCache.php в листинге 7 следует вызывать с URL-параметром order_no (представляющим идентификатор заказа, хранящийся в таблице OE.ORDER). Например, чтобы получить информацию, относящуюся к заказу с идентификатором 2408, вы должны ввести в браузер следующий URL-адрес:
http://webserverhost/phpcache/testCache.php?order_no=2408. В результате браузер сгенерирует следующий вывод. :
ЗАКАЗ № 2408
ДАТА: 29 ИЮНЯ 99 ГОДА 59.06.31.333617
CUST_ID: 166
ИТОГО: 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Теперь
, если вы нажмете кнопку перезагрузки в браузере, скрипт testCache.php не будет снова вызывать функции getOrderFields и getOrderItems. Вместо этого он будет читать результаты из локального кэша. Таким образом, каждый вызов getOrderFields или getOrderItems с order_no=2108 будет удовлетворен локальным кешем в течение 24 часов (поскольку время жизни установлено на 86 400 секунд). Однако обратите внимание, что класс Cache_Lite_Function не предоставляет API для проверки доступности кэша для данной функции с заданными параметрами. Поэтому может быть немного сложно определить, действительно ли приложение читает кэш или по-прежнему выполняет функцию каждый раз, когда она вызывается с теми же параметрами. Например, в приведенном выше примере, чтобы обеспечить правильную работу механизма кэширования, вы можете временно изменить информацию о соединении, указанную в скрипте Connect.php, чтобы он не мог установить соединение с базой данных, например, указать неправильное имя хоста сервера базы данных; , а затем снова используйте order_no= 2108 Запустите скрипт testCache.php. Если кеширование работает правильно, вывод браузера должен быть таким же, как и раньше.
Кроме того, вы можете проверить каталог кэша, который передается конструктору класса Cache_Lite_Function как значение параметра кэшDir (/tmp в этом примере). В этом каталоге вы найдете два только что созданных вами файла кэша с именами, похожими на: кэш_7b181b55b55aee36ad5e7bd9d5a091ec_3ad04d3024f4cd54296f75c92a359154. Обратите внимание: если вы являетесь пользователем Windows, вы можете использовать каталог %SystemDrive%temp для сохранения файлов кэша. В этом случае для параметра cacheDir должно быть установлено значение /temp/.
Убедившись, что механизм кэширования работает правильно, вы можете создать PHP для обработки уведомлений об изменениях, полученных с сервера базы данных. «Листинг 8» — это скрипт dropResult.php. Сервер базы данных вызовет этот сценарий в ответ на изменения в таблицах ORDERS и ORDER_ITEMS.
Листинг 8. Обработка уведомлений об изменениях, полученных от сервера базы данных
<?php
//Файл:dropResults.php
require_once 'Cache/Lite/Function.php';
$options = array(
'cacheDir' => '/tmp/'
);
$cache = новая 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), верен. Затем подключитесь как OE/OE в SQL*Plus или аналогичном инструменте и выполните инструкции UPDATE, которые повлияют на те же заказы, которые были доступны ранее в этом разделе через скрипт testCache.php (здесь заказ с идентификатором 2408):
UPDATE ORDERS SET order_mode = ' прямой' ГДЕ order_id = 2408;
UPDATE ORDER_ITEMS SET количество = 3 WHERE order_id=2408 И line_item_id=1;
UPDATE ORDER_ITEMS SET количество = 1 ГДЕ order_id=2408 И line_item_id=2;
СОВЕРШИТЬ;
В ответ на вышеуказанное обновление обработчик уведомлений, описанный ранее в этой статье, дважды запустит сценарий dropResults.php, используя следующие URL-адреса: http://webserverhost/phpcache/dropResults.php?order_no=2408&table=ORDERS.
http://webserverhost/phpcache/dropresults.php?order_no=2408&table=ORDER_ITEMS
Из «Листинга 8» ясно видно, что сценарий dropResult.php не очищает кеш после получения уведомления об изменении от сервера базы данных. Он просто удаляет файлы кэша, содержащие просроченные данные. Итак, если вы сейчас проверите каталог кеша, вы увидите, что файл кеша, созданный при запуске скрипта testCache.php с order_no=2408, исчез. По сути, это означает, что в следующий раз, когда testCache.php запросит данные, относящиеся к заказу с идентификатором 2408, он получит эти данные из внутренней базы данных, а не из локального кеша.
Этот метод может оказаться полезным в ситуациях, когда набор результатов, запрошенный приложением, вероятно, изменится до того, как приложение его использует. Для примера этой статьи это означает, что данные, относящиеся к определенному заказу, могут измениться несколько раз, прежде чем testCache.php получит доступ к этому заказу. Таким образом, приложение выполняет много ненужной работы, очищая свой кеш сразу после получения уведомлений об изменениях от сервера базы данных.
Но если вы хотите, чтобы скрипт dropResult.php очистил кеш, как только он получит уведомление об изменении, вы можете вызвать метод вызова экземпляра Cache_Lite_Function после вызова метода drop, указав одни и те же параметры для обоих вызовов. В этом случае вам также следует обязательно включить сценарии getOrderFields.php и getOrderItems.php, чтобы dropResults.php мог вызывать функции getOrderFields и getOrderItems для обновления кеша. «Листинг 9» представляет собой модифицированный скрипт dropResult.php.
Листинг 9. Очистка кэша сразу после получения уведомления об изменении
<?php
//Файл:dropResults.php
require_once 'Cache/Lite/Function.php';
require_once 'getOrderItems.php';
require_once 'getOrderFields.php';
$options = array(
'cacheDir' => '/tmp/',
'время жизни' => 86400
);
$cache = новая 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 Database 10g Release 2, вы можете воспользоваться функцией уведомления об изменении базы данных, которая позволяет вашему приложению получать уведомления в ответ на изменения DML в объекте, связанном с сделанным запросом. Используя эту функцию, вам не придется обновлять кэш вашего приложения в течение определенного периода времени. Вместо этого операция выполняется только в том случае, если набор результатов зарегистрированного запроса изменился.