5.3 Ajustadores da Fila Executor.I/O.Disco
5.3.1 Estratégia de Ajuste do Shared Buffer
A estratégia para determinar qual o espaço de memória que deve ser alocado para o Shared Buffer de modo a melhorar o desempenho do PostgreSQL é baseada em um problema de otimização clássico: o Problema da Mochila (Knapsack Problem) [CLRS01]. O Problema da Mochila descreve uma situação em que se deve preencher uma mochila com vários objetos de diferentes pesos e valores. A mochila possui uma capacidade máxima de peso que ela consegue suportar. O objetivo é preencher a mochila com os referidos objetos de modo a maximizar o valor total carregado na mochila.
A aplicação do Problema da Mochila como estratégia de determinação do tamanho do Shared Buffer para o PostgreSQL é feita da seguinte maneira: a entidade que faz o papel da mochila, na aplicação, é o próprio Shared Buffer — local onde os objetos são colocados. Os objetos que terão que ser alocados na mochila são tabelas e índices de tabelas. Cada tabela e índice de tabela possui um peso e um valor. Para esta aplicação, o peso de um objeto (tabela ou índice) corresponde ao espaço ocupado por ele na memória (em Mb). Quanto mais espaço um objeto ocupar na memória, mais “pesado” será esse objeto. O valor de cada
5.3 Ajustadores da Fila Executor.I/O.Disco 87 objeto é atribuído com base na freqüência com que ele é utilizado.
A coleta de informações sobre os objetos inicia ainda na etapa do monitoramento, quando o framework está analisando o arquivo de log do PostgreSQL. Aproveitando que o compo- nente de monitoramento desenvolvido para analisar as estatísticas do log do PostgreSQL está lendo o arquivo de log do SGBD, o próprio componente de monitoramento se encarrega de salvar, em um arquivo, todos os comandos SQL que foram executados em um intervalo de tempo considerado.
O arquivo gerado com a coleção de comandos SQL é lido, durante o processo de ajuste, pelo Tuner desenvolvido. Nessa fase, um componente auxiliar do Tuner efetua uma análise dos comandos contidos na carga de comandos executada e coleta algumas estatísticas, tais como:
• freqüência de aparição de cada tabela nos comandos SQL; • tamanho, em Mb, de cada tabela contida nos comandos SQL; • índices definidos em cada tabela;
• tamanho dos índices definidos em cada tabela.
Essas informações são utilizadas na etapa de ajuste do Shared Buffer e servem para ali- mentar a entrada do algoritmo da mochila.
A etapa de ajuste do Shared Buffer inicia-se com a recuperação de algumas informações básicas entradas no processo. Tais informações incluem o tamanho da memória RAM do sistema onde o PostgreSQL está executando. Essa informação é importante para o Tuner calcular um tamanho máximo de alocação de memória para o Shared Buffer, que será de 15% do valor da memória RAM do sistema. Esse tamanho máximo será a capacidade máxima da mochila (L), no algoritmo da mochila. O percentual escolhido de 15% é sugerido por experts na administração do PostgreSQL [Mom01].
O algoritmo da mochila é executado duas vezes. Na primeira execução, o algoritmo aloca espaço para as tabelas envolvidas na carga de comando SQL. Para tanto, é preciso atribuir pesos e valores para as tabelas que serão alocadas no Shared Buffer. O peso das tabelas será o espaço ocupado por elas (em Mb) na memória. O valor de uma tabela é dado pela freqüência com que ela aparece na carga de comandos SQL, ou seja, quanto mais uma tabela
5.3 Ajustadores da Fila Executor.I/O.Disco 88 é consultada em comandos SQL, mais valiosa ela será. De posse dos objetos (tabelas), seus valores (freqüências), seus pesos (espaço ocupado em memória) e da capacidade máxima da mochila (15% do valor total da RAM do sistema), o algoritmo da mochila é executado. O retorno desse algoritmo é um array que informa a quantidade de cada objeto (tabela) alocado na mochila (shared buffer).
A versão do algoritmo utilizada é a Mochila Binária [CLRS01] e tem-se apenas 1 ele- mento de cada tipo de objeto. Isso significa que uma tabela só terá espaço alocado no Shared Buffer se ela couber completamente nele (não é possível alocar uma fração da tabela) e uma tabela só poderá ser alocada uma única vez no Shared Buffer. Esta restrição na estratégia de ajuste pode levar a crer que não será alocada memória suficiente caso as tabelas acessadas sejam muito grandes, no entanto, considerando as questões expostas na seção 2.6.3 do capí- tulo 2, o ajustador está ciente de que, caso não seja alocada memória ao Shared Buffer para as referidas tabelas, o próprio S.O. irá se encarregar de efetuar tal tarefa através de seu cache de disco.
Uma vez retornados os objetos (tabelas) com as respectivas quantidades que devem ser alocadas na mochila (Shared Buffer), o passo seguinte é determinar o espaço total ocupado pelos objetos que foram alocados, o que é feito somando-se os tamanhos das tabelas que foram alocadas ao Shared Buffer.
Terminado esse passo, ainda resta alocar os índices que utilizam as tabelas, pois, durante o processamento de uma consulta SQL, faz-se necessário ter em memória os dados de tabelas e os índices. Os índices das tabelas são alocados de maneira semelhante às tabelas, exceto por uma pequena mudança na determinação do valor de um índice. O valor de um índice é determinado com base em duas regras:
1. se a tabela na qual o índice é definido teve espaço alocado no Shared Buffer no passo anterior, o valor do respectivo índice será 1 (valor máximo que pode ser atribuído). Isso aumenta as chances de uma tabela e seus respectivos índices ficarem juntos no Shared Buffer.
2. caso contrário, o Tuner irá analisar as freqüências dos predicados nos quais a tabela onde o índice está definido aparece. Cada predicado, em que a tabela onde o índice está definido aparece, tem sua freqüência de aparição nas consultas calculada. Em
5.3 Ajustadores da Fila Executor.I/O.Disco 89 seguida, verifica-se, para cada índice definido, se ele possui algum atributo em comum com os atributos envolvidos nos predicados analisados. Através dessa verificação, a freqüência de um índice é calculada. Cada vez que um índice possuir um atributo em comum com um atributo envolvido em um predicado, a freqüência do índice será incrementada com a freqüência de aparição do respectivo predicado. Ao final, quando todos os índices de todas as tabelas forem analisados, os valores dos índices serão atribuídos com o valor dessas freqüências acumuladas.
Determinados os valores e tamanhos dos índices, o algoritmo da mochila é executado novamente. Desta vez, a capacidade máxima da mochila será o espaço que ainda pode ser alocado ao Shared Buffer quando retirado o espaço já alocado às tabelas durante a primeira execução do algoritmo. Sendo assim, a nova capacidade máxima da mochila (L1) na segunda
execução do algoritmo é dada por
L1 = (0, 15 × SystemRAM ) − EspacoAlocado `AsT abelas
Encerrada a execução do algoritmo, é determinado o tamanho da segunda mochila, agora com a alocação de índices, somando-se os tamanhos dos índices alocados para o Shared Buffer.
Por fim, o tamanho final do Shared Buffer será a soma dos espaços alocados para os índices e dos espaços alocados para as tabelas.
Dessa forma, o Tuner determina a quantidade de memória que deve ser alocada ao Shared Buffer de modo a comportar o conjunto de objetos mais valioso e que caiba em, no máximo, 15% da RAM total do Sistema.
Vale salientar que o algoritmo da mochila não aloca efetivamente as tabelas ao Shared Buffer. Quem decide qual tabela entra no Shared Buffer ou sai dele é o próprio PostgreSQL, utilizando uma política de alocação LRU [Gro06]. O que o Tuner desenvolvido faz é apenas determinar um tamanho para o Shared Buffer de modo que um conjunto de objetos valiosos caiba inteiramente, e exatamente, no espaço alocado para o Shared Buffer.