Кэширование на уровне пакета

    Доброго времени суток. Хочу рассказать об одном из способов кэширования данных в моем любимом Oracle.
    Давайте рассмотрим кэширование редко меняющихся данных (например справочников) на уровне пакета. Этот метод не является масштабируемым, потому как основан он на состоянии пакета — кэшированные данные попадают в PGA, т.е. в «приватную» область памяти, недоступную другим пользователям. Т.о. если у вас 1000 пользователей и они вызывают функции пакета, то у вас будет 1000 экземпляров кэшированных данных. «Плохо» — скажете вы, да, плохо, но не обязательно всем пользователям давать права на выполнение данного пакета. Тем не менее, данный способ широко используется.
    Плох он или хорош можно решить только для конкретного случая.

    Итак начнем:
    Сначала табличка:

    CREATE TABLE testname
    (
    ID NUMBER,
    Name VARCHAR2(100)
    );


    Теперь наполним ее:

    INSERT INTO TestName(id, name) VALUES (1, 'Ivan');
    INSERT INTO TestName(id, name) VALUES (2, 'Stepan');
    INSERT INTO TestName(id, name) VALUES (3, 'Sidor');
    COMMIT;


    Теперь собственно пакет:

    CREATE OR REPLACE PACKAGE TestNamePck IS

    -- собственно получение имени по ID
    FUNCTION GetNameByID(pID NUMBER) RETURN VARCHAR2;

    END TestNamePck;

    /

    CREATE OR REPLACE PACKAGE BODY TestNamePck IS

    -- курсор со значениями справочника
    CURSOR TestCur IS
    SELECT * FROM TestName;

    SUBTYPE TTestName IS TestName%ROWTYPE;

    TYPE TTestName_ByID IS TABLE OF TTestName INDEX BY VARCHAR2(38);

    myTestName TTestName;

    TestName_ByID TTestName_ByID;

    -- собственно получение имени по ID
    FUNCTION GetNameByID(pID NUMBER) RETURN VARCHAR2 IS
    BEGIN
    IF TestName_ByID.EXISTS(TO_CHAR(pId)) THEN
    RETURN TestName_ByID(TO_CHAR(pId)) .Name;
    ELSE
    RETURN NULL;
    END IF;
    END;

    -- инициализация кэша
    BEGIN
    OPEN TestCur;
    FETCH TestCur INTO myTestName;
    WHILE TestCur%found LOOP
    TestName_ByID(TO_CHAR(myTestName.ID)) := myTestName;
    FETCH TestCur INTO myTestName;
    END LOOP;
    CLOSE TestCur;
    END TestNamePck;

    /


    После всего вышеперечисленного имеем:

    SQL> set serveroutput on;
    SQL> set feedback off;
    SQL>exec dbms_output.put_line(testnamepck.getnamebyid(pid => 1));

    Ivan

    SQL>exec dbms_output.put_line(testnamepck.getnamebyid(pid => 2));

    Stepan

    SQL>exec dbms_output.put_line(testnamepck.getnamebyid(pid => 3));

    Sidor

    SQL>


    Как видно из кода тела пакета, сначала происходит инициализация кэша справочника путем заполнения ассоциативного массива TestName_ByID. После чего функция получения имени по ID просто использует метод Exists ассоциативного массива для определения существования значения.
    Естественно, необходимо добавить обработчики ошибок, здесь приведен простой пример для объяснения принципа кэширования на основе состояния пакета.
    Поделиться публикацией

    Комментарии 12

      +2
      Добавьте хабракат.
        0
        если интересна тема кэширования могу по позже написать, как организовать кэширование в SGA
          0
          imho курсоры c rowtype`ами как-то уж очень массивно. можно обойтись ассоциативным массивом, заполняемом при инициализации пакета. для большинства задач этого хватит с головой. тем более, если мне не изменяет память для них можно использовать BULK COLLECT

          ну это я так, чтоб придраться )))
            0
            Можно. Здесь все так коротко и обобщенно только для того, чтобы донести идею. Данный способ можно точить под себя как угодно — без первичной инициализации с динамическим добавлением значений, флаги запрета кэширования, выборка только необходимых полей и т.д.
            0
            а про динамическое добавление значение можно подробнее.

            как между сессиями все это синхронизировать?
              0
              Что-то глючит, смотрите ниже
                0
                Между сессиями надо по другому — с использование хинтов CACHE и FULL.
                0
                Все просто — первичная инициализация — просто создание объекта. Далее при вызове функции сначала пробиваем его по кэшу, если есть — сразу возвращаем результат, если нет — выполняем select из справочника по конкретной ID. Результаты заносим в кэш. Если потом будет вызов функции с тем же параметром — он сразу же найдется в кэше и повторного select не будет.
                А синхронизации между сессиями никак (для версии ниже 11, для 11 новая фича есть) — это написано в самом начале. У меня данный принцип кэширования применяется для Job'a, который считает разнообразные аналитические показатели.
                  0
                  Спасибо, интересно. Я так понял что это кеширование подходит для малых объемов данных, так как временная таблица хранится в памяти.
                    0
                    Ну не совсем временная таблица — это на самом деле коллекция. У оракла есть понятие временной таблицы — а именно temporary table. Почитайте документацию и тома кайта «Oracle для профессионалов» много интересного найдете для себя.
                    Вообще если вам интересно могу еще много чего написать по ораклу — просто на сколько вы видите данная тема не пользуется популярностью на хабре, поэтому я тут и не пишу ничего. Если будет интересно хотя бы одному человеку могу начать активно писать про внутреннее устройство и работу сервера.
                      0
                      Как раз заказал книжки. Привезут — буду читать. Да, мне интересна эта тема. Если не сложно, можете написать что-то на тему оптимизаци SQL запросов при работе c dblink-ами. У меня постоянная проблема connection timeout при длительных запросах (15 минут) и подвисшие днями сессии, которые опять же возникают от проблем со связью. Хотелось бы узнать как с этим бороться. Если непонятно объяснил, расскажу подробнее.
                        0
                        Расскажите, вообще с плохой связью и dblink очень много проблем бывает, начиная от продвисших сессий заканчивая полным убийством базы.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое