Эффективный доступ к данным – индексы
Использование индексов в OpenEdge обеспечивает:
- Ускорение доступа к записи;
- Автоматическое упорядочение записей при выводе;
- Контроль уникальности значений;
Индексы могут быть составными – состоять из нескольких полей (компонент). Один из индексов таблицы определяется как первичный (Primary). Один или более индексов могут быть определены как уникальные (Unique). Можно определить индекс для поиска по словам внутри символьного поля (Word index). Кроме того, можно определить порядок сортировки по индексу – возрастающий (Ascending) или убывающий (Descending).
Если индекс содержит неопределенное значение (?), то при сортировке оно имеет вес больше, чем любое другое значение. Когда Вы определяете уникальный индекс, ABL гарантирует эту уникальность и не позволит Вам создать еще одну запись с таким же значением индексного поля. Однако ABL не мешает пользователям вводить любое количество записей с неопределенными значениями в индексных полях. Для предотвращения создания таких записей следует определить поля уникального индекса как обязательные (mandatory).
OpenEdge читает индексы при выполнении любых запросов на поиск записей в базе данных. Обновление индекса происходит немедленно в конце любого оператора, в котором изменяются значения для одного или более индексных полей (UPDATE, SET, ASSIGN, присвоение). Это означает, что сразу же можно найти записи по новым значениям индекса, в то время как данные в найденной записи еще неизменны. Данные в записи изменяются в конце транзакции, в конце области видимости записи или когда запись освобождается (Смотри Программа 58).
Программа 58. Пример обновленного индекса без обновления данных
В базе данных имеется запись в таблице test с индексным полем test_int1, значение которого равно 1. База данных запущена в многопользовательском режиме. Запущены две клиентские сессии, подсоединенные к этой базе данных. В первой сессии выполняется следующая программа: /* Test1.p – Index Field Change Procedure finds record with value 1, changes it to 2, and stops at the PAUSE statement. At this point Index value already has been changed, but the data in the record has not. */ FIND FIRST test WHERE test_int1 = 1 EXCLUSIVE-LOCK. ASSIGN test_int1 = 2. PAUSE MESSAGE "Индекс изменен". RELEASE TEST. Программа test1.p останавливается на операторе PAUSE, в этот момент во второй сессии запускается вторая программа: /* Test2.p – Reading the record after the Index change */ FIND FIRST test WHERE test_int1 = 2 NO-LOCK. MESSAGE test.test_int1 VIEW-AS ALERT-BOX. Программа выполняет поиск записи по значению 2 и выдает значение поля на экран. Результат: поле test_int1 в записи равно 1 (!) Повторный запуск программы во второй сессии после завершения первой программы выдаст значение 2.
Проанализировать использование индексов в программе ABL можно несколькими способами. Основным способом можно считать анализ листинга перекрестных ссылок (Xref). Получить такой листинг можно с помощью опции XREF оператора COMPILE. Следующая таблица содержит список тэгов, которые генерирует опция XREF при компиляции.
Таблица 13. Тэги доступа данных в листинге Xref.
Тэг |
Значение |
SEARCH | Указывает индексный брэкет, который будет использоваться. Перечисляются логическое имя базы данных, имя таблицы, и имена индексов. Когда будут использоваться множество брэкетов и индексов для одного и того же запроса, здесь Вы будете видеть одну строку поиска для каждого брэкета. |
SEARCH … WHOLE-INDEX | Указывает, что подходящий брэкет не мог быть создан, и будет выполнено индексное сканирование по всей таблице, используя указанный индекс. |
SORT-ACCESS | Указывает, что результат запроса группируется по конкретному столбцу, и никакого подходящего индекса не существует. |
ACCESS | Указывает, что таблица и значение поля используются в этой точке программы. |
CREATE | Указывает, что в этой точке программы создана запись. |
DELETE | Указывает, что в этой точке программы удалена запись. |
UPDATE | Указывает, что в этом месте указанное поле таблицы обновлено. |
Ниже приведен пример программы (Программа 59), выполняющей некоторые запросы к таблице Customer в базе данных Sports2000 и листинг перекрестных ссылок для данной программы (Рис. 23).
Программа 59. Использование индексов
/* TestCust.p Использование индексов. Пример различных запросов к базе данных. */ /* 1 - */ FOR EACH customer WHERE custnum > 2100 : END. /* 2 - */ FOR EACH customer WHERE NAME BEGINS 'Ath' AND Balance > 20000: END. /* 3 - */ FOR EACH customer WHERE contact BEGINS 'Al': END. /* 4 - */ FOR EACH customer WHERE Balance > 50000: END. /* 5 - */ FOR EACH customer WHERE NAME BEGINS 'A' OR salesrep BEGINS 'D': END. /* 6 - */ FOR EACH customer WHERE NAME BEGINS 'A' OR contact BEGINS 'B': END. /* 7 - */ FOR EACH customer WHERE (custnum <= 10) or (name = "Mary"): END. /* 8 - */ FOR EACH customer WHERE ((name > "Off The Wall") and (name < "Quick Toss Lacrosse")) : END.
В листинге перекрестных ссылок красным цветом выделены строки, относящиеся к выполнению запросов.
Альтернативным методом, для определения использования индексов может быть анализ индексной статистики времени выполнения (Runtime) в виртуальных системных таблицах (VST). Параметры запуска, которые активируют эти таблицы, описаны в OpenEdge Data Management: Database Administration.
База данных Sports2000 поставляется с уже активированными таблицами VST, для сбора статистики необходимо лишь указать параметры –baseindex и –indexrangesize (целые числа, определяющие номер индекса и диапазон). Получить информацию по номерам индексов можно с помощью следующей программы (Программа 60). Для таблицы Customer (можно поменять первый оператор FOR EACH на FOR EACH _file NO-LOCK WHERE _file-name = ‘customer’:) программа дает результаты, показанные на Рис. 24. То есть для мониторинга использования индексов таблицы Customer следует указать параметры запуска –baseindex 12 –indexrangesize 5.
.\testcust.p .\testcust.p 1 COMPILE testcust.p
.\testcust.p .\testcust.p 1 CPINTERNAL 1251
.\testcust.p .\testcust.p 1 CPSTREAM 1251
.\testcust.p .\testcust.p 2 STRING “Customer” 8 NONE UNTRANSLATABLE
.\testcust.p .\testcust.p 2 ACCESS sports2000.Customer CustNum
.\testcust.p .\testcust.p 2 SEARCH sports2000.Customer CustNum
.\testcust.p .\testcust.p 5 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 5 ACCESS sports2000.Customer Balance
.\testcust.p .\testcust.p 5 STRING “Ath” 3 NONE TRANSLATABLE
.\testcust.p .\testcust.p 5 SEARCH sports2000.Customer Name
.\testcust.p .\testcust.p 8 ACCESS sports2000.Customer Contact
.\testcust.p .\testcust.p 8 STRING “Al” 2 NONE TRANSLATABLE
.\testcust.p .\testcust.p 8 SEARCH sports2000.Customer CustNum WHOLE-INDEX
.\testcust.p .\testcust.p 11 ACCESS sports2000.Customer Balance
.\testcust.p .\testcust.p 11 SEARCH sports2000.Customer CustNum WHOLE-INDEX
.\testcust.p .\testcust.p 14 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 14 ACCESS sports2000.Customer SalesRep
.\testcust.p .\testcust.p 14 STRING “A” 1 NONE TRANSLATABLE
.\testcust.p .\testcust.p 14 STRING “D” 1 NONE TRANSLATABLE
.\testcust.p .\testcust.p 14 SEARCH sports2000.Customer Name
.\testcust.p .\testcust.p 14 SEARCH sports2000.Customer SalesRep
.\testcust.p .\testcust.p 17 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 17 ACCESS sports2000.Customer Contact
.\testcust.p .\testcust.p 17 STRING “A” 1 NONE TRANSLATABLE
.\testcust.p .\testcust.p 17 STRING “B” 1 NONE TRANSLATABLE
.\testcust.p .\testcust.p 17 SEARCH sports2000.Customer CustNum WHOLE-INDEX
.\testcust.p .\testcust.p 20 ACCESS sports2000.Customer CustNum
.\testcust.p .\testcust.p 20 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 20 STRING “Mary” 4 NONE TRANSLATABLE
.\testcust.p .\testcust.p 20 SEARCH sports2000.Customer CustNum
.\testcust.p .\testcust.p 20 SEARCH sports2000.Customer Name
.\testcust.p .\testcust.p 23 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 23 ACCESS sports2000.Customer Name
.\testcust.p .\testcust.p 23 STRING “Off The Wall” 12 NONE TRANSLATABLE
.\testcust.p .\testcust.p 23 STRING “Quick Toss Lacrosse” 19 NONE TRANSLATABLE
.\testcust.p .\testcust.p 23 SEARCH sports2000.Customer Name
Рис. 23. Листинг перекрестных ссылок
В таблице Таблица 14 приведена статистика по индексам после исполнения программы TestCust.p (Программа 59).
Таблица 14. Статистика использования индексов
Запрос |
Выбрано записей | Индексы |
Брэкет |
Чтений индексов | Примечание | ||
CustNum | Name | SalesRep | |||||
1 | 8 | CustNum | EQ | 9 | – | – | |
2 | 2(22) | Name | EQ | – | 22 | – | 21 запись выбрана по Name, 2 записи выбраны по второму критерию |
3 | 16 | CustNum | Whole | 1120 | – | – | Выбрано 16 записей из 1120 переданных клиенту |
4 | 1 | CustNum | Whole | 1120 | – | – | Выбрана 1 запись из 1120 переданных клиенту (!) |
5 | 387(415) | Name, SalesRep | EQ | – | 114 | 303 | Учтем, что у 28 записей выполняются оба условия |
6 | 147 | CustNum | Whole | 1120 | – | – | Выбрано 147 записей из 1120 переданных клиенту |
7 | 10 | CustNum, Name | EQ | 11 | 1 | – | Записей, удовлетворяющих второму условию, нет. Но индекс был проверен. |
8 | 88 | Name | Range | – | 89 | – |
Программа 60. Baseline.p – получение номеров индексов
/* Baseindex.p Use Base Index (-baseindex) with Index Range Size (-indexrangesize) startup parameters to specify the range of indexes for which you want to collect statistics. */ FOR EACH _file NO-LOCK: DISPLAY _file._file-name. FOR EACH _index NO-LOCK WHERE _index._file-recid = RECID(_file) BY _index._idx-num: DISPLAY _index._idx-num _index._index-name. END. END.

Таким образом, анализ листинга перекрестных ссылок позволяет сразу же выявить неправильно написанные медленные запросы. В дополнение к этому, анализ таблиц VST позволяет оценить, во первых, степень катастрофичности медленных запросов, и, во вторых, проанализировать эффективные с точки зрения компилятора запросы на реальную эффективность, как, например, запрос 2.
Смотри также (Программа 61) – пример очень простой программы анализа таблицы VST _indexstat.
Программа 61. Просмотр статистики по индексам
/* Просмотр статистики по индексам */ FOR EACH _indexstat NO-LOCK: DISPLAY _IndexStat-id /* Номер индекса */ _IndexStat-read /* Количество чтений */ _IndexStat-create /* Количество добавлений */ _IndexStat-delete /* Количество удалений */ _IndexStat-OsRead /* Количество чтений ОС */ _IndexStat-blockdelete /* Количество удалений блока */ _IndexStat-split /* Количество расщеплений индекса по блокам */ . END.