Использование частных буферов (Private Buffers) в Progress
Чтобы понять когда и как необходимо использовать частные буфера в Progress, давайте сначала поймем, каким образом Progress работает с буферами. База данных Progress состоит из последовательности блоков. Все эти блоки имеют одинаковый размер (1Кб, 2Кб, 4Кб или 8Кб), но каждый их них, в зависимости от назначения, имеет различную структуру, т.е. одни для хранения записей или индексов, а другие для хранения различной управляющей информации. Блоки, которые содержат записи, называют RM – блоками. Каждый из таких блоков содержит некоторое количество слотов или пространство для записей, которое определено параметром “record per block” при создании базы данных.
Когда вызывается Progress процедура, которая должна считать запись, Progress считывает блок (или блоки, так как запись может быть распределена между различными блоками) в область разделяемой памяти, которая называется Буферный пул (Buffer Pool). Таким образом при последующих обращениях к записи, обеспечивается возможность быстрого доступа к блокам, которые её содержат, так как этот буферный пул общедоступен, и исключается необходимость повторного их чтения в память с дисков.
Размер буферного пула определяется параметром запуска брокера –B (Blocks in Database Buffers), умноженным на размер блока (blocksize) базы данных. Если размер буферного пула очень большой, значит в нем может разместиться большое количество блоков, поэтому вероятность наличия в нем искомой записи очень велика. Но есть точка, уменьшения количества возвратов когда буферный пул слишком большой. Вы можете не получить улучшения производительности, поскольку улучшение вероятности нахождения записи в памяти будет компенсироваться увеличением времени затраченном на поиск буферов, и в конечном счете вызовет резкое падение производительности, когда система начнет подкачку (swapping).
Для правильной установки размера буферного пула, Progress имеет определенный инструмент. Этот инструмент называется Progress Monitor (promon). Используя один из его экранов, а именно Activity, необходимо наблюдать за полем “Buffer Hits”, значение которого должно стремиться к 95% или пока система не начнет подкачку. Наилучшим же показателем является опция promon “3 Block Access”, где можно определить отношение “DB Request” к “DB Reads”. Количество DB Reads равное нулю (0) при некотором количестве DB Requests это оптимальное состояние, которое вряд ли может возникнуть. Отношение 1 DB Reads к 20 DB Requests считается хорошим показателем.
Теперь перейдем к рассмотрению частных буферов. Представьте ситуацию, когда много пользователей заняты формированием заказов. В этом приложении каждая введенная запись строки заказа использует основную таблицу Item чтобы получить текущую цену. После того, как будут введены первые несколько заказов, блоки которые содержат записи наиболее часто продающихся товаров будут записаны в общий буферный пул, и таким образом, каждый последующий запрос к этим записям не будет считывать их с диска, тем самым улучшая производительность. Теперь предположим, что кто-то захотел сформировать некий отчет по клиентам, при этом, каждая запись о клиенте, точнее каждый блок содержащий эту запись, должна быть сначала считана в общий буферный пул, и в некоторый момент времени, буферный пул полностью заполнится. Обычно это не проблема, так как Progress для того чтобы выбирать блоки, которые должны быть записаны на диск, для освобождения места новым блокам, использует алгоритм Least Recently Used (LRU). В случае, где должно считываться много блоков, многие блоки, которые находятся в настоящий момент в общем буферном пуле будут сброшены на диск, тем самым снижая производительность других пользователей, обращающихся к базе данных. Это снижение вызвано тем, что записи которые были сброшены на диск, могут понадобится повторно, и должны быть считаны с диска вновь.
Исправить эту ситуацию мы можем используя частные буфера для пользователей, активно выполняющих операции чтения из базы данных. Для этого используется параметр Private Buffers (-Bp). Каждый сеанс, который использует частные буфера, кстати, находящиеся в режиме read-only, сокращает количество общедоступных буферов и создает изолированный участок частных буферов, выделенный из общего буферного пула управляемого брокером базы данных. Частные буфера используются Progress только для операций чтения, как в нашем примере с формированием отчета по клиентам. Если пользователь будет считывать записи, которые не обнаружены в общем буферном пуле, то они будут записаны в частные буфера. Когда частный буферный пул окажется заполнен, вместо того, чтобы считывать необходимые блоки в общий буферный пул, смещая все блоки, буфер, который был последним использованным в частном пуле, будет выселен тем же пользователем, который разметил его в память. Таким образом воздействие формирования отчета на общий буферный пул будет сильно сокращено.
Так как частный буферный пул (-Bp) это часть общего буферного пула (-B), он имеет ограничение. Общий размер –Bp всех клиентских сессий не должен превышать 25% от –B. В версии 9.1С был введен параметр –Bpmax, который ограничивает максимально возможное количество частных буферов (-Bp) которое может запросить отдельное клиентское подключение. По умолчанию это значение равно 64, которое в предыдущих версиях было жестко закодировано. Соответственно, сейчас –Bpmax не может превышать 25% от значения –B.
Проблемы производительности начинаются, когда вы понимаете, что использование частных буферов эффективно только для одного пользователя работающего в текущий момент времени. Если два и более пользователя используют параметр –Bp, это приведет к использованию большого количества памяти, так как они не могут использовать в своих интересах частные буфера друг друга, тем самым вызывая перегрузку системы, а так же порождая большое количество операций ввода/вывода. Обратно этому, пользователи, использующие общий буферный пул, могут извлечь выгоду из содержимого частного буферного пула. Если такой пользователь нуждается в буфере, который в настоящее время находится в частном буфере другого пользователя, то этот буфер будет передан общему буферному пулу, путем добавления его к общему списку буферов и удаления из списка частных буферов.
Самый большой недостаток в использовании частных буферных пулов, это то, что интерактивные пользователи не имеют тенденции только формировать отчеты, они так же вносят изменения в базу данных. Когда они что-то хотят изменить, то блок, который был считан в частный буфер, должен сначала быть скопирован в общий буферный пул. Если это изменение понадобилось спустя некоторое время после первых чтений в частный буфер, то вероятнее всего, этот блок придется считать с диска вновь, тем самым выполняя лишнее чтение и замедляя производительность.
Как мы видим, частные буфера могут быть полезны только при осторожном использовании. Т.е. они должны использоваться только в качестве опции запуска для отдельных пользователей, которые выполняют множество операций чтения, т.е. различные отчеты или запросы. Как только формирование отчета закончится, пользователь должен завершить сессию Progress, и для продолжения нормальной работы, открыть другую сессию без использования параметра –Bp. Таким образом, он будет освобождать память для общего буферного пула. Так же, частный буферный пул может быть инициализирован через ABL во время выполнения программы. Для этого, прежде чем выполнить отчет, используйте поле VST таблицы _MyConnection._MyConn-NumSeqBuffer, а после завершения его формирования сбрасывайте установленное значение. Если вы будете использовать параметр –Bp для всех пользователей, то вы получите значительное ухудшение производительности. Это из-за того, что когда общий буферный пул окажется заполнен, его буферы ни когда не будут заменяться часто читаемыми данными, потому что все пользователи используют собственные частные буфера и LRU цепочки. Таким образом, они будут намного больше данных считывать с диска, вместо того, чтобы иметь потенциальную возможность совместного их использования в памяти.