Sistemas de Base de Dados 2010/11 GRUPO 10 ANDRÉ MOURÃO Nº 35008 EDUARDO COSTA Nº 355049 RICARDO MARQUES Nº 35048



Documentos relacionados
Comandos de Manipulação

PostgreSQL Performance

Tarefa Orientada 19 Triggers

Bases de Dados 2005/2006. Aula 5

SQL TGD/JMB 1. Projecto de Bases de Dados. Linguagem SQL

Sistemas de Informação

BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar

SQL Linguagem de Definição de Dados. Banco de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

Administração de Banco de Dados

1. Domínio dos Atributos

Tarefa Orientada 16 Vistas

Comandos DDL. id_modulo = id_m odulo

Tarefa Orientada 12 Junção Externa, Auto-Junção e União

Programação SQL. Introdução

BASES DE DADOS I LTSI/2. Universidade da Beira Interior, Departamento de Informática Hugo Pedro Proença, 2010/2011

Banco de Dados I. Aula 12 - Prof. Bruno Moreno 04/10/2011

SQL - Criação de Tabelas

Triggers. um trigger permite que uma determinada sequência de comandos SQL seja accionada quando um determinado evento ocorre.

Faculdade Pitágoras 16/08/2011. Curso Superior de Tecnologia: Banco de Dados Sistemas para Internet

Banco de Dados. Prof. Antonio

SQL (Structured Query Language)

Linguagem SQL Sub-linguagem DDL

Linguagem de Consulta Estruturada SQL- DML

Triggers em PostgreSQL. Linguagem de Programação de Banco de Dados. Triggers em PostgreSQL. Triggers em PostgreSQL

SQL Linguagem de Definição de Dados. Laboratório de Bases de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

Consistem num conjunto de apontadores para instâncias especificas de cada relação.

Faculdade Pitágoras. Curso Superior de Tecnologia: Banco de Dados. Disciplina: Banco de Dados Prof.: Fernando Hadad Zaidan SQL

Junções e Índices em Tabelas

Triggers e Regras. Fernando Lobo. Base de Dados, Universidade do Algarve

Introdução à Banco de Dados. Nathalia Sautchuk Patrício

SQL Linguagem de Manipulação de Dados. Banco de Dados Profa. Dra. Cristina Dutra de Aguiar Ciferri

Tarefa Orientada 15 Manipulação de dados

Usando PostgreSQL na Regra de Negócio de um ERP. Fabiano Machado Dias Eduardo Wolak

Structured Query Language (SQL)

A linguagem SQL

Administração e Optimização de BDs

SQL comando SELECT. SELECT [DISTINCT] <campos> FROM <tabela> [condição] [ ; ] Paulo Damico - MDK Informática Ltda.

Regras de Integridade. Profa. Késsia Marchi

Programação SQL. Manipulação de Dados. DML Data Manipulation Language

Structured Query Language (SQL) Ambiente Simplificado de um SGBD

Modelo de Dados Relacional Restrições de um Banco de Dados Relacional

EXERCÍCIOS PRÁTICOS. Banco de Dados

Banco de Dados I Módulo V: Indexação em Banco de Dados. (Aulas 4) Clodis Boscarioli

Tarefa Orientada 11 Junção Interna

APOSTILA BANCO DE DADOS INTRODUÇÃO A LINGUAGEM SQL

SQL Structured Query Language

BD Oracle. Licenciatura em Engenharia Informática e Computação. Bases de Dados 2003/04

Bases de Dados. Lab 1: Introdução ao ambiente

SQL DML. Frederico D. Bortoloti

EXEMPLOS DE COMANDOS NO SQL SERVER

Tarefa Orientada 14 Subconsultas

SQL. SQL (Structured Query Language) Comando CREATE TABLE. SQL é uma linguagem de consulta que possibilita:

Roteiro 9 - SQL Básico: chave estrangeira, operadores de comparação e operadores booleanos

Ex.: INSERT INTO tmpautor (CDAUTOR, NMAUTOR) VALUES (1, Renato Araújo )

Linguagem SQL. Comandos Básicos

Operação de União JOIN

Banco de Dados. StructuredQuery Language- SQL. Prof. Walteno Martins Parreira Jr

SQL: Definição de tabelas, Modificações à Base de Dados

AULA 2 INTERAÇÃO COM O BANCO DE DADOS

O dono de uma livraria cuja base de dados é administrada por si pediu-lhe para efectuar as seguintes alterações ao preço dos livros:

O que são Bancos de Dados?

Banco de Dados. Marcio de Carvalho Victorino Exercícios SQL

Estudo do Sistema de Gestão de Bases de Dados PostgreSQL. André Ricardo Carlos Nobre Cristiano Lopes Nº Nº Nº 17662

SQL. Autor: Renata Viegas

SQL (Structured Querie Language) Escola Secundária de Emídio Navarro 2001/2002 Estruturas, Tratamento e Organização de Dados

Bases de Dados 2007/2008. Aula 1. Referências

IF685 Gerenciamento de Dados e Informação - Prof. Robson Fidalgo 1

BANCO DE DADOS. info 3º ano. Prof. Diemesleno Souza Carvalho

PHP INTEGRAÇÃO COM MYSQL PARTE 1

PROGRAMA. Objectivos Gerais :

Hugo Pedro Proença, 2007

Básico da Linguagem SQL. Definição de Esquemas em SQL. SQL(Structured Query Language)

Uma expressão básica em SQL consiste em três cláusulas: select, from e where.

Tarefa Orientada 10 Obter informação a partir de uma tabela

Banco de Dados. Um momento crucial na organização dos dados é a forma com que cadastramos estes dados, a estrutura de armazenamento que criamos.

AULA 2 INTERAÇÃO COM O BANCO DE DADOS

Exercícios de Lógica Exercícios de Fixação 08


Os dados no MySQL são armazenado em tabelas. Uma tabela é uma colecção de informação relacionada e consiste em colunas e linhas.

Bases de Dados. O ficheiro create-bank.sql contém um conjunto de instruções SQL para criar a base de dados de exemplo ilustrada na figura 1.

Ex.: INSERT INTO tmpautor (CDAUTOR, NMAUTOR) VALUES (1, Renato Araújo )


UNIVERSIDADE FEDERAL DO RIO GRANDE DO NORTE ESCOLA AGRÍCOLA DE JUNDIAÍ EAJ - PRONATEC / REDE etec MÓDULO III DESENVOLVIMENTO PROFESSOR ADDSON COSTA

UNIVERSIDADE FEDERAL DE SANTA MARIA - UFSM COLÉGIO AGRÍCOLA DE FREDERICO WESTPHALEN BANCO DE DADOS II

Structured Query Language (SQL) Aula Prática

Introdução à Engenharia da Computação. Banco de Dados Professor Machado

Iniciar o Data Adapter Configuration Wizard. Toolbox Data Duplo clique em OleDbDataAdapter. Botão next na caixa de diálogo

Integridade dos Dados

A VISTA BACKSTAGE PRINCIPAIS OPÇÕES NO ECRÃ DE ACESSO

SQL é uma linguagem de consulta que implementa as operações da álgebra relacional de forma bem amigável.

Programação WEB II. PHP e Banco de Dados. progweb2@thiagomiranda.net. Thiago Miranda dos Santos Souza

Microsoft Access Para conhecermos o Access, vamos construir uma BD e apresentar os conceitos necessários a cada momento

Banco de dados. Linguagens de Banco de Dados II. Wedson Quintanilha da Silva -

Prof.: Clayton Maciel Costa

BDII SQL Junção Revisão 8

Tarefa Orientada 13 Agrupamento e sumário de dados

Transações Seguras em Bancos de Dados (MySQL)

SQL DDL. Frederico D. Bortoloti

SQL. Prof. Márcio Bueno.

Transcrição:

DEPARTAMENTO DE INFORMÁTICA TRABALHO FINAL POSTGRESQL Sistemas de Base de Dados 2010/11 Mestrado em Engenharia Informática GRUPO 10 ANDRÉ MOURÃO Nº 35008 EDUARDO COSTA Nº 355049 RICARDO MARQUES Nº 35048

Índice Índice...- 1-1. Introdução...- 3-1.1 Introdução histórica...- 3-2. Cobertura do SQL... - 4-2.1. SQL:...- 4-2.2. DDL:...- 4-2.3. DML...- 9-3. Armazenamento e Estrutura de Ficheiros...- 12-3.1. Buffer Management...- 12-3.2. Sistema de Ficheiros...- 12-3.3. Páginas...- 13-3.4. Registos de Tamanho Variável...- 13-3.5. Partições...- 14-4. Indexação e Hashing...- 18-4.1. Estruturas de Indexação...- 18-4.2. Índices multi-coluna...- 21-4.3. Índices para organização de ficheiros...- 21-4.4. Índices e a cláusula ORDER BY...- 22-4.5. Múltiplos ficheiros de índices para os mesmos atributos...- 23-4.6. Estruturas temporariamente inconsistentes...- 23-4.7. Combinar múltiplos índices...- 24-5. Processamento e Optimização de Perguntas...- 25-5.1. Implementação de Operações...- 25-5.2. Materialização...- 30-5.3. Pipelining...- 31-5.4. Paralelismo...- 31-5.5. Processamento duma query...- 31-5.6. Estimativas e visualização de planos...- 32 - - 1 -

6. Gestão de transacções e controlo de concorrência...- 38-6.1. Transacções...- 38-6.2. Consistência dos dados...- 39-6.3. Isolamento entre Transacções...- 39-6.4. Locks...- 42-6.5. Adiamento das verificações de consistência...- 45-6.6. Atomicidade e Durabilidade...- 46-7. Bases de Dados Distribuídas...- 47-8. Outras Características...- 48-8.1. XML...- 48-8.2. Objectos...- 48-8.3. Segurança...- 49 - - 2 -

1. Introdução Os Sistemas de Base de Dados são uma grande parte do mundo da Informática hoje em dia. Poucas serão as aplicações que não usam uma base de dados, desde as mais pequenas com poucas dezenas de tabelas até a enormes base de dados distribuídas armazenando vários Petabytes de dados. E para gerir toda essa informação, é preciso conhecer bem o Sistema de Base de Dados que se usa, como executa as consultas pretendidas e como melhorar a sua perfomance. Neste trabalho vamos analisar o funcionamento do PostgreSQL. É um sistema particularmente importante e interessante por ser open source e ter variadas características avançadas que merecem um estudo aprofundado. Ao longo deste relatório, iremos usar, para os vários exemplos, duas base de dados usadas nas aulas práticas da cadeira durante o semestre. A base de dados Mondial e a tabela uscensus. Os scripts destas duas base de dados encontram-se na página da cadeira. Na próxima secção iremos fazer uma breve introdução histórica sobre este Sistema. No capítulos 2 iremos abordar a cobertura de SQL por parte do PostgreSQL. No capítulo 3 descrevemos o armazenamento de dados e estrutura de ficheiros, e no capítulo 4 indicamos quais os índices sobre tabelas suportados. No capítulo 5, descrevemos o processamento de perguntas e como são optimizadas. Por fim, no capítulo 6 abordamos o funcionamento da gestão de transacções e do controlo de concorrência. 1.1 Introdução histórica O PostgreSQL é uma evolução do Ingres, um projecto desenvolvido na Universidade da Califórnia em Berkeley. Começou a ser desenvolvido em 1985, com a primeira versão a ser lançada em Junho de 1989, para poucos utilizadores. Em 1991 é lançada uma 3ª versão, com um sistema de regras re-escrito e um motor de consultas melhorado. Em 1994 o projecto, oficialmente, terminou. Devido à natureza open source do sistema, o projecto continou a ser desenvolvido independentemente por vários programadores. Ainda em 1994, estudantes de Berkeley substituíram a linguagem de consultas baseada no Ingres por um interpretador de SQL, criando assim o Postgres95. Em 1996 o sistema foi renomeado para PostgreSQL, reflectindo assim o suporte da linguagem SQL. Desde então, o sistema continua a ser mantido por voluntários de todo o mundo, que se coordenam através da Internet. O PostgreSQL é neste momento, a par do MySQL, um dos sistemas open source mais usados no Mundo. O PostgreSQL é distribuído com uma licença MIT, podendo o seu código ser alterado livremente, e podendo ser usado para qualquer fim, incluindo académicos e comerciais. Muitas aplicações conhecidas usam o PostgreSQL, incluindo o MySpace na sua base de dados para data warehousing, a Sony para os seus jogos multiplayer online, o hi5 e o Skype. - 3 -

2. Cobertura do SQL 2.1. SQL: SQL (Structured Query Language) é uma linguagem de programação utilizada para gerir dados em sistemas administradores de base de dados relacionais. Foi criada na IBM nos anos 1970 para manipular os dados na base de dados da IBM, System R. Hoje em dia é utilizada em todos os grandes sistemas de bases de dados. Muitos destes sistemas desenvolveram extensões próprias, tal como o PL/pgSQL do Postgres e o PL/SQL da Oracle. O seu standard mais recente é o SQL-2008, mas a sua documentação é paga. A última versão cujo o standard está disponível gratuitamente é o SQL-92. 2.2. DDL: Linguagem de Definição de Dados (Data Definition Language) consiste numa linguagem de programação utilizada por aplicações ou utilizadores que permite definir estruturas de dados numa base de dados. No caso da linguagem SQL, as principais querys desse tipo são CREATE TABLE, DROP TABLE, ALTER TABLE. 2.2.1. CREATE TABLE O comando CREATE TABLE é o comando usado para criar tabelas. Por ser um comando que permite muitos modificadores, vamos apenas mostrar aqui apenas uma pequena parte. CREATE [[ GLOBAL LOCAL] {TEMPORARY TEMP}] TABLE table_name ([{column_name data_type [DEFAULT default_expr] [column_constraint [...]] table_constraint LIKE parent_table [like_option... ] } ] ) [,... ] [INHERITS (parent_table [,... ])] [ WITH ( storage_parameter [= value] [,... ] ) WITH OIDS WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS DELETE ROWS DROP } ] [ TABLESPACE tablespace ] A tabela country que vai ser usada nos exemplos está definida da seguinte forma: CREATE TABLE Country (Name VARCHAR(32) NOT NULL UNIQUE, Code VARCHAR(4) CONSTRAINT CountryKey PRIMARY KEY, Capital VARCHAR(35), Province VARCHAR(32), Area NUMERIC CONSTRAINT CountryArea CHECK (Area >= 0), Population NUMERIC CONSTRAINT CountryPop CHECK (Population >= 0)); - 4 -

2.2.1.1. Tipos de dados As colunas podem ser de diferentes tipos de dados. Alguns dos mais utilizados são os seguintes: Nome Aliases Descrição boolean bool Booleano (true/false) character varying [ (n) ] varchar [ (n) ] String de tamanho variável (n) character [ (n) ] char [ (n) ] Caracter date double precision float8 Número em virgula flutuante (8 bytes) integer int, int4 Inteiro de 4 bytes com sinal numeric [ (p, s) ] decimal [ (p, s) ] Número de precisão definida real float4 Número em virgula flutuante (4 bytes) smallint int2 Inteiro de 2 bytes com sinal text time [ (p) ] [ without time zone ] timestamp [ (p) ] [ without time zone ] uuid xml Data Texto de tamanho variável Tempo do dia Data e hora Identificador único universal Dados XML 2.2.1.2. Constraints As constraints são restrições que podem ser aplicadas às colunas de uma tabela, permitindo um grande controlo sobre os dados lá presentes. Os tipos de constraints suportados são: Check Constraints tipo mais genérico, a expressão avaliada tem de ser verdadeira; Not-Null Constraints especifica que o campo não pode conter o valor null; Unique Constraints o valor do campo tem de ser único na tabela; Primary Keys combinação de Unique Constraint e Not-Null Constraint. É utilizado para definir qual o campo que vai definir inequivocamente o tuplo na tabela; Foreign Keys chaves que fazem referência a valores de oturas tabelas. Por exemplo, na tabela acima está presente a seguinte Check Constraint: Area NUMERIC CONSTRAINT CountryArea CHECK (Area >= 0) O objectivo é não permitir que a área de um país seja menor que zero, propriedade essa verificada em (Area >= 0). Outras funções mais complexas podem ser implementadas, para garantir que apenas dados esperados são guardados na DB. - 5 -

2.2.1.3. Foreign Keys O Postgres tem suporte para chaves estrangeiras. Caso desejemos que o campo capital faça referência ao campo name da tabela city, a linha referente à criação do campo capital, seria alterado para o seguinte: Name VARCHAR(32) NOT NULL UNIQUE REFERENCES City (name), O Postgres também tem suporte para a indicação de qual é o comportamento a tomar quando se tenta eliminar um tuplo com uma chave estrangeira. Acrescentado ON DELETE RESTRICT no fim da linha, ao eliminar o tuplo, o tuplo da tabela referenciada correspondente não é eliminado. Se fosse adicionado ON DELETE CASCADE, todos os tuplos referenciados são eliminados. Existem outras duas opções ( ON DELETE SET NULL, ON DELETE SET DEFAULT), que vão colocar o valor NULL ou DEFAULT respectivamente, nos tuplos referenciados. Compatibilidade com o SQL padrão: Na generalidade, este comando está de acordo com o SQL padrão, embora com algumas excepções específicas apresentadas no manual. 2.2.2. ALTER TABLE O comando ALTER TABLE é o comando usado para alterar tabelas existentes na DB. A sua sintaxe pode ser representada da seguinte forma, ALTER TABLE [ ONLY ] name [ * ] action [,... ] em que action é a acção a efectuar. A lista de acções possíveis é imensa. É possível adicionar ou remover colunas, alterar os atributos das já existentes, adicionar ou remover constraints, activar ou desactivar triggers, e muito mais acções. Caso desejássemos adicionar uma nova coluna com o Primeiro Ministro do País à tabela country, poderíamos utilizar a seguinte query: ALTER TABLE country ADD COLUMN PrimeMinister varchar(30); Compatibilidade com o SQL padrão: Está de acordo com o SQL padrão e ainda apresenta bastantes extensões ao mesmo. 2.2.3. DROP TABLE O comando DROP TABLE é o comando usado para eliminar tabelas existentes na DB. A sua sintaxe pode ser representada da seguinte forma, DROP TABLE [ IF EXISTS ] name [,...] [CASCADE RESTRICT] em que name é o nome da tabela a apagar. Para remover a tabela country, o comando é o seguinte: DROP TABLE country; Compatibilidade com o SQL padrão: Está de acordo com o SQL padrão e ainda apresenta algumas extensões ao mesmo, como permitir eliminar mais do que uma tabela por query (separando os nomes das tabelas por vírgulas) e o modificador IF EXISTS, que, como o nome indica, elimina a tabela apenas caso ela exista. - 6 -

2.2.4. ASSERTIONS e TRIGGERS Um trigger consiste num pedaço de SQL (no caso do Postgres, um procedure) que é executado antes ou depois de uma inserção, actualização ou remoção na base de dados. A sua criação é feita da seguinte forma: CREATE TRIGGER name { BEFORE AFTER } { event [ OR... ] } ON table [ FOR [ EACH ] { ROW STATEMENT } ] [ WHEN ( condition ) ] EXECUTE PROCEDURE function_name ( arguments ) Uma assertion é um pedaço de código SQL que garante que uma condição é satisfeita. Caso não o seja, a acção em progresso é terminada. O PostgreSQL não tem suporte para assertions. Em alternativa, sugere-se a utilização de triggers, que podem funcionar da mesma maneira. 2.2.5. VIEWS Uma view consiste numa tabela virtual da base de dados, criada a partir de uma query SELECT. Em geral, uma vista não tem os seus dados guardados, executando a sua query cada vez que é consultada. A sua criação é feita da seguinte forma: CREATE [ OR REPLACE ] [ TEMP TEMPORARY ] VIEW name [( column_name [,...] )] AS query Um exemplo de uma view na base de dados Mondial seria o seguinte: CREATE VIEW cidades_portuguesas AS SELECT * FROM City WHERE country = 'P'; Esta vista vai devolver todas as cidades portuguesas. As views são acedidas como se fossem tabelas: SELECT * FROM cidades_portuguesas; 2.2.6. RULES As regras no PostgreSQL são mecanismos que permitem fazer alterações nas queries executadas. Podem ser definidas como modificadores de queries, que caso seja disparado o evento definido na regra, a query é reescrita para estar de acordo com a regra e passada ao processador de queries. A sua criação é feita da seguinte forma: CREATE [ OR REPLACE ] RULE name AS ON event TO table [ WHERE condition ] DO [ ALSO INSTEAD ] { NOTHING command ( command ; command... ) } - 7 -

Uma view em PostgreSQL é nada mais que um conjunto de regras. Quando se executa: CREATE VIEW cidades_portuguesas AS SELECT * FROM city WHERE country = 'P'; internamente o PostgreSQL cria uma regra select: CREATE RULE "_RETURN" AS ON SELECT TO cidades_portuguesas DO INSTEAD SELECT * FROM city WHERE country = 'P'; Devido a isto, para efeitos de planeamento de queries, o PostgreSQL considera uma vista como uma relação. Por defeito, não se pode executar comandos insert ou delete em vistas. Para tal, é preciso definir regras para lidar com esses comandos. - 8 -

2.3. DML Uma Linguagem de Manipulação de Dados (Data Manipulation Language) consiste numa linguagem de programação utilizada por aplicações ou utilizadores que permite fazer consultas e alterar informação numa base de dados. No caso da linguagem SQL, consiste nas perguntas que criam ou manipulam dados (INSERT, UPDATE e DELETE) ou que apenas fazem consultas (SELECT). Em seguida vamos apresentar em mais detalhe estas operações e a sua inclusão (ou não) no PostgreSQL. 2.3.1. SELECT O comando SELECT é o comando utilizado para fazer consultas aos dados da Base de Dados. Devolve tuplos de tabelas ou de vistas. A sua sintaxe pode ser apresentada da seguinte forma: [WITH [RECURSIVE] with_query [,...]] SELECT [ALL DISTINCT [ON ( expression [,...] )]] * expression [[AS] output_name] [,...] [FROM from_item [,...]] [WHERE condition] [GROUP BY expression [,...]] [HAVING condition [,...]] [WINDOW window_name AS ( window_definition ) [,...]] [{ UNION INTERSECT EXCEPT } [ALL] select] [ORDER BY expression [ASC DESC USING operator] [NULLS { FIRST LAST }] [,...]] [LIMIT { count ALL }] [OFFSET start [ROW ROWS]] [FETCH { FIRST NEXT } [count] { ROW ROWS } ONLY] [FOR { UPDATE SHARE } [OF table_name [,...]] [NOWAIT] [...]] onde from_item pode ser um dos seguintes: [ONLY] table_name[*] [[AS] alias[(column_alias[,...])]] ( select ) [AS] alias [( column_alias [,...] )] with_query_name [[AS] alias [( column_alias [,...] )]] function_name ( [argument [,...]] ) [AS] alias [(column_alias [,...] column_definition [,...] )] function_name ( [argument [,...]] ) AS (column_definition [,...] ) from_item [NATURAL] join_type from_item [ON join_condition USING ( join_column [,...] )] - 9 -

onde with_query é: with_query_name [( column_name [,...] )] AS ( select ) TABLE { [ONLY] table_name [*] with_query_name } onde join_type é um dos seguintes: [INNER] JOIN LEFT [OUTER] JOIN RIGHT [OUTER] JOIN FULL [OUTER] JOIN CROSS JOIN Para seleccionar todos os tuplos da tabela country, era necessário executar: SELECT * FROM country Compatibilidade com o SQL padrão: O comando é compatível com o SQL padrão, apesar de existirem algumas omissões e extensões. 2.3.2. INSERT O comando INSERT é o comando usado para inserir dados em tabelas da BD. A sua sintaxe e modificadores possíveis podem ser apresentados da seguinte forma: INSERT INTO table [( column [,...] )] { DEFAULT VALUES VALUES ( { expression DEFAULT } [,...] ) [,...] query } [RETURNING * output_expression [[AS] output_name] [, ]] Por exemplo, a inserção de um país na tabela country da base de dados Mondial é feita da seguinte forma, INSERT INTO country VALUES ('Greece','GR','Athens','Greece',131940,10538594); Uma funcionalidade presente é a possibilidade de adição de default values, que consistem em valores que são colocados no tuplo, caso o valor dessa coluna seja omitido no comando INSERT. Substituindo, por Capital VARCHAR(35), Capital VARCHAR(35) DEFAULT 'X', o comando INSERT podia ser executado assim: INSERT INTO country VALUES ('Greece','GR',DEFAULT,'Greece',131940,10538594); Assim o valor 'X' é assumido para capital e o resultado de um select é o seguinte: name code capital province area population ------+----+-------+--------+------+---------- Greece GR X Greece 131940 10538594-10 -

Compatibilidade com o SQL padrão: O comando INSERT está de acordo com o SQL padrão, exceptuando a cláusula RETURNING que é disponibilizada como uma extensão do PostgreSQL. 2.3.3. UPDATE O comando UPDATE é o comando usado para alterar dados existentes nas tabelas da BD. A sua sintaxe e modificadores possíveis podem ser apresentados da seguinte forma: UPDATE [ ONLY ] table [ [ AS ] alias ] SET { column = { expression DEFAULT } ( column [,...] ) = ( { expression DEFAULT } [,...] ) } [,...] [ FROM from_list ] [ WHERE condition WHERE CURRENT OF cursor_name ] [ RETURNING * output_expression [ [ AS ] output_name ] [,...] ] Por exemplo, caso queiramos alterar a capital da Grécia definida anteriormente, a query é a seguinte: UPDATE country SET Capital = 'Athens' WHERE Name = 'Greece'; Compatibilidade com o SQL padrão: Este comando está de acordo com o SQL padrão, exceptuando as cláusulas FROM e RETURNING que estão disponíveis como extensões. 2.3.4. DELETE O comando DELETE é o comando usado para apagar dados das tabelas da BD. A sua sintaxe e modificadores possíveis pode ser apresentada da seguinte forma: DELETE FROM [ ONLY ] table [ [ AS ] alias ] [ USING using_list ] [ WHERE condition WHERE CURRENT OF cursor_name ] [ RETURNING * output_expression [ [ AS ] output_name] [,...] ] Por exemplo, caso queiramos apagar a Grécia da tabela, a query é a seguinte: DELETE FROM country WHERE Name = 'Greece'; Dica: caso o objectivo seja apagar todos os tuplos de uma tabela, existe um comando (padrão no SQL 2008) chamado TRUNCATE, que funciona da seguinte forma: TRUNCATE table Compatibilidade com o SQL padrão: Este comando está de acordo com o SQL padrão, exceptuando as cláusulas FROM e RETURNING, que estão disponíveis como extensões. - 11 -

3. Armazenamento e Estrutura de Ficheiros O armazenamento de dados e a estrutura de ficheiros é uma parte fundamental de qualquer Sistema de Base de Dados. Todavia, estes sistemas possuem mecanismos e funcionalidades muito diferentes dos sistemas operativos sobre os quais operam, logo, nem sempre as estratégias usadas pelo sistema operativo para gerir a memória e o sistema de ficheiros é a ideal para uma Base de Dados. Consequentemente, as Bases de Dados possuem módulos que se sobrepõem às funcionalidades base do sistema operativo, optimizando assim o seu desempenho na altura de executar as operações. Seguidamente, serão apresentadas algumas características do PostgreSQL relativos ao armazenamento de dados e estrutura de ficheiros. Nenhum tópico sobre multitable clustering será abordado pelo simples facto do PostgreSQL não o suportar. 3.1. Buffer Management Os sistemas operativos guardam em memória os dados que em alguma altura foram obtidos dos respectivos discos rígidos. Logicamente, estes dados não ficam eternamente em memória, são removidos seguindo uma certa estratégia. Mas esta estratégia pode não ser a mais indicada para uma Base de Dados, logo, existe a necessidade de criar um mecanismo que altere as politicas de gestão de memória do sistema operativo para algo que melhore o desempenho da Base de Dados. É aí que o buffer management entra em acção. O PostgreSQL implementa o seu próprio buffer management system. Uma implementação inicial usava a estratégia least-recently-used (LRU) como forma de gerir a cache das páginas solicitadas. No entanto, isto não tinha em consideração o número de vezes que uma dada página era acedida, logo um scan de uma tabela de tamanho considerável podia remover de memória páginas úteis. Tendo isto em consideração, o actual algoritmo de cache usa quatro listas distintas por forma a saber quais as páginas usadas mais frequentemente, dinamicamente optimizando assim a sua substituição, consoante a carga de trabalho. 3.2. Sistema de Ficheiros O PostgreSQL usa o sistema de ficheiros base do sistema operativo, gerindo apenas os clusters de Bases de Dado. Ora, todos os dados referentes a um cluster estão presentes na directoria PGDATA, que por sua vez está na directoria /var/lib/pgsql/data. A PGDATA contém várias subdirectorias e ficheiros de controlo, sendo que cada base de dados tem os seus dados na directoria PGDATA/base/b, sendo b a respectiva base de dados. Cada tabela ou índice é guardado(a) num ficheiro separado sendo este nomeado pelo respectivo número de filenode, que por sua vez está presente no ficheiro pg_class.relfilenode. Todavia, tabelas ou índices cujo tamanho ultrapasse 1 GB são divididas em segmentos de 1 GB. Estes segmentos são nomeados com base no filenode do objecto, sendo que o primeiro segmento tem o nome do filenode e os consequentes segmentos correspondem a filenode.1, filenode.2 e assim sucessivamente. O valor base de 1 GB por segmento pode ser alterado, naturalmente, por sistemas que desejem controlar o tamanho dos respectivos ficheiros, que assim têm a sua vida facilitada com o PostgreSQL. - 12 -

3.3. Páginas O PostgreSQL guarda cada tabela ou índice num vector de páginas de tamanho físico(heap), usualmente 8 KB. Em uma tabela, todas as páginas são logicamente equivalentes, portanto, um tuplo pode ser guardado em qualquer página. Por sua vez, em um índice, a primeira página está reservada para guardar informação de controlo. Uma página está dividida em cinco partes, como exemplificado abaixo. Item Descrição PageHeaderData ItemIdData Free Space Items Special space Tamanho 24 bytes. Contém informação geral sobre a página, incluindo apontadores para espaço livre. Vector de pares ( offset, tamanho) que apontam para os itens. 4 bytes por item. O espaço por alocar. Apontadores para novos itens são alocados do principio deste espaço e o espaço para os respectivos itens é alocado do fim. Os itens em questão. Espaço para informação de acesso aos índices. Vazio no caso de tabelas comuns. Os itens em si são guardados em espaço alocado do fim do espaço livre. Por sua vez, tabelas e sequências usam uma estrutura chamada HeapTupleHeaderData. É preciso frisar que no caso dos índices, a ultima página tem diferentes tipos de utilização. Por exemplo, no caso de árvores b+, são armazenados apontadores para irmãos da página. 3.4. Registos de Tamanho Variável Como já foi referido o tamanho usual de uma página em PostgreSQL é de 8 KB, não permitindo que os tuplos criem múltiplas páginas. Logo, é impossível guardar valores com tamanhos muito grandes com páginas, directamente. Porém, o PostgreSQL permite a utilização de registos com tamanho variável, recorrendo a uma ferramenta chamada TOAST (The Oversized-Attribute Storage Technique), ou se preferirmos, "the best thing since sliced bread", gerindo assim, eficientemente, a organização no disco destes valores. O TOAST tem por base a associação de uma tabela TOAST a colunas de registos com tamanho variável, ou TOAST-able. Assim, valores out-of-line são guardados na respectiva tabela TOAST, sendo estes divididos e até comprimidos para caberem em páginas com cerca de 2000 bytes. Por fim, cada pedaço é guardado em uma linha da tabela TOAST, associada à tabela original, logicamente obtendo um índice único(chunk_id e chunk_seq), permitindo a rápida obtenção dos respectivos valores. Este esquema apresenta várias vantagens em relação a abordagens mais directas, como por exemplo, permitir que um atributo crie varias páginas. A maior vantagem revela-se quando, as comparações são feitas com valores chave pequenos presentes na linha principal, pois desta forma, os valores grandes e de tamanho variável presentes na tabela TOAST, só são carregados do disco na altura de apresentar os resultados finais ao utilizador. Logo, a tabela principal é muito mais pequena e no buffer de páginas cabem um maior número de tuplos da mesma tabela. - 13 -

3.5. Partições A partição de tabelas consiste em dividir fisicamente uma tabela lógica grande em pedaços mais pequenos. É um mecanismo que permite melhorar bastante o tempo de acesso a tabelas muito grandes, bem como a actualização de uma porção da tabela presente em uma partição, evitando o uso de índices ou o acesso aleatório a pedaços de toda a tabela. O PostgreSQL implementa partições via herança de tabelas. Cada partição deve ser criada como filha da tabela original. Normalmente a tabela mãe, original, está vazia, existe apenas para representar logicamente o conjunto total dos dados nela contidos. Existem dois tipos de partições no PostgreSQL : 1. Range Partitioning - A tabela é particionada por extensões definidas por uma ou mais colunas da tabela. Por exemplo, poder-se-á particionar uma tabela por extensões de datas, isto é, podemos particionar a tabela em 4 definindo que cada intervalo corresponde a um trimestre do ano. 2. List Partitioning - A tabela é particionada dizendo explicitamente que valores correspondem a cada partição, na coluna chave da tabela. Por exemplo, pode-se definir 3 partições para uma coluna cidades. Uma partição guarda valores que correspondam à cidade x e w, outra partição às cidades y e z, e assim sucessivamente. Seguidamente daremos um exemplo de como criar e gerir uma partição em PostgreSQL. 3.5.1. Implementar particionamento O utilizador deverá executar os passos por ordem (este exemplo foi retirado do manual do PostgreSQL ). 1. Criar uma tabela mestra, da qual as partições irão herdar. Esta tabela estará vazia, logo não fará sentido definir nela índices ou constraints, a não ser para aplicar igualmente por todas as partições. CREATE TABLE measurement ( ); city_id int not null, logdate date not null, peaktemp int, unitsales int - 14 -

2. Criar várias tabelas filhas que vão herdar da tabela mãe. Normalmente estas tabelas não acrescentam campos aos herdados da mãe. Para além disto, devem ser definidas constraints (normalmente CHECKs) que permitam decidir que tuplo pertence a cada partição. CREATE TABLE measurement_y2006m02 ( CHECK ( logdate >= DATE 2006-02-01 AND logdate < DATE 2006-03-01 ) ) INHERITS (measurement); CREATE TABLE measurement_y2006m03 ( CHECK ( logdate >= DATE 2006-03-01 AND logdate < DATE 2006-04-01 ) ) INHERITS (measurement);... CREATE TABLE measurement_y2007m11 ( CHECK ( logdate >= DATE 2007-11-01 AND logdate < DATE 2007-12-01 ) ) INHERITS (measurement); CREATE TABLE measurement_y2007m12 ( CHECK ( logdate >= DATE 2007-12-01 AND logdate < DATE 2008-01-01 ) ) INHERITS (measurement); CREATE TABLE measurement_y2008m01 ( CHECK ( logdate >= DATE 2008-01-01 AND logdate < DATE 2008-02-01 ) ) INHERITS (measurement); 3. Para cada partição, criar um índice sobre o valor chave da partição e sobre outros valores que sejam desejáveis. Nota: o índice sobre o valor chave da partição não é fundamental, mas, na maioria dos cenários é desejável. CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate); CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);... CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate); CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate); CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate); - 15 -

4. Opcionalmente, criar um trigger que facilite a inserção de dados nas partições. Este trigger activar-se-á quando o utilizador tentar inserir na tabela original, desviando-os para a respectiva partição. CREATE OR REPLACE FUNCTION measurement_insert_trigger() RETURNS TRIGGER AS $$ BEGIN... END;$$ IF ( NEW.logdate >= DATE 2006-02-01 AND NEW.logdate < DATE 2006-03-01 ) THEN INSERT INTO measurement_y2006m02 VALUES (NEW.*); ELSIF ( NEW.logdate >= DATE 2006-03-01 AND NEW.logdate < DATE 2006-04-01 ) THEN INSERT INTO measurement_y2006m03 VALUES (NEW.*); ELSIF ( NEW.logdate >= DATE 2008-01-01 AND ELSE END IF; NEW.logdate < DATE 2008-02-01 ) THEN INSERT INTO measurement_y2008m01 VALUES (NEW.*); RAISE EXCEPTION Date out of range. Fix the measurement_insert_trigger() function! ; RETURN NULL; LANGUAGE plpgsql; CREATE TRIGGER insert_measurement_trigger BEFORE INSERT ON measurement FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger(); - 16 -

3.5.2. Gerir partições Depois de criada a partição desejada, convém saber como alterá-la ou até removê-la. Esta secção mostra alguns exemplos de como gerir as partições em PostgreSQL. Uma das grandes vantagens das partições é que a manipulação da sua estrutura é muito mais limpa e amiga do utilizador, do que mover pedaços grandes de informação. Logo é muito mais fácil apagar dados indesejados, ou adicionar uma nova partição onde serão guardados os novos dados. Remover os dados desnecessários é tão simples quanto remover a partição desnecessária. DROP TABLE measurement_y2006m02; Por outro lado, se apenas desejarmos remover a partição da estrutura de partições, de modo a mais tarde podermos ter acesso aos seus dados individualmente, desligamos a partição da sua respectiva tabela mãe. ALTER TABLE measurement_y2006m02 NO INHERIT measurement; - 17 -

4. Indexação e Hashing Um índice é um excelente forma de aumentar a performance da Base de Dados porque um índice permite a obtenção de tuplos de uma forma muito mais rápida do que o usual. Isto é particularmente interessante quando são feitos joins em atributos não chave. Depois de criado um índice, o sistema irá manter o índice actualizado sempre que a tabela é alterada, não havendo necessidade de acções externas por forma a manter a coerência do índice. O sistema usará o índice sempre que achar que a sua utilização trará benefícios em termos de desempenho dos comandos a executar. Logicamente, devido a esta autonomia do sistema, um índice geralmente causa um maior overhead à Base de Dados. Logo, índices associados a colunas pouco requisitadas em termos de comparação nas queries, trará computações desnecessárias. Por tudo isto, o uso de indexação deve ser cuidadosamente ponderado, pesando as vantagens em relação aos custos de manutenção, por forma a garantir um maior aumento de performance possível, sem prejudicar em demasia a manutenção da Base de Dados. 4.1. Estruturas de Indexação O PostgreSQL suporta quatro tipos de estruturas de indexação: 1. B-tree 2. Hash 3. GiST 4. GIN Cada um usa um algoritmo diferente, mais apropriado para um certo de tipo de queries. Por defeito, o comando CREATE INDEX, abordado mais a frente, um índice B-tree dado ser o que mais se adequa à maioria das situações. No PostgreSQL o comando para a criação de índices é o CREATE INDEX, tendo a seguinte sintaxe: CREATE[ UNIQUE ] INDEX [ CONCURRENTLY ] name ON table [ USING method ] ({ column ( expression ) } [opclass] [ASC DESC ] [ NULLS { FIRST LAST } ] [,...] ) [ WITH ( storage_parameter = value [,... ] ) ] [ TABLESPACE tablespace ] [ WHERE predicate ] Este comando irá criar um índice de nome name sobre a tabela especificada pelo parâmetro table, implementado na estrutura de dados method. A chave do índice corresponde ao conjunto de colunas da tabela identificadas em column, ou ainda através duma expressão identificada entre parênteses, expression, correspondente a uma função de um ou mais atributos da tabela. - 18 -

Por outro lado, a utilização destas estruturas de indexação está dependente da análise de estatísticas de acesso guardadas pelo sistema. Logo, é aconselhavel correr o comando ANALYZE, por forma às estatisticas serem actualizadas. A sintaxe deste comando é: ANALYZE [ VERBOSE ] [ table [ ( column [,...] ) ] ] Se ao invés disto, for necessário remover o índice, a sintaxe é a seguinte: DROP INDEX [ IF EXISTS ] name [,...] [ CASCADE RESTRICT] 4.1.1. B-tree Um índice do tipo B-tree lida particularmente bem com queries que usem igualdades ou ranges sobre dados que possam ter algum tipo de ordenação lógica. No caso particular do PostgreSQL, o optimizador de queries tenderá a escolher índices B-tree quando a respectiva coluna indexada estiver envolvida em uma comparação que use os respectivos operadores: < <= = >= > Por outro lado, construções equivalentes a combinações destes operadores, como por exemplo BETWEEN e IN, também poderão ser implementadas à custa de uma pesquisa de índice B-tree. Por fim, por forma a criar e gerir o uso de B-tree, o PostgreSQL usa o algoritmo de Lehman e Yao que, devido à sua implementação, faz com que a indexação de valores com este tipo de estrutura de dados tenham uma maior eficiência em queries que usem comparação por igualdade. Aqui temos um exemplo de uma árvore B-tree A titulo de exemplo vamos assumir que existe a tabela T com as colunas a e b. Como por defeito, se não especificarmos o tipo de índice a usar, basta correr o seguinte comando para criar uma B-tree sobre b da tabela T: CREATE INDEX my_index ON T(b); - 19 -

4.1.2. Hash Índices baseados em estruturas e funções de hashes apenas conseguem lidar com comparações de igualdade, o que limita bastante o uso deste tipo de índices. O PostgreSQL usa o algoritmo de hashing desenvolvido por W. Litwin. Este hashing é dinâmico, não possuindo qualquer rehashing que permita a eliminação dos buckets de overflow. O algoritmo de W.Litwin efectua a divisão de buckets por ordem crescente em vez de dividir o bucket que excedeu a capacidade. Todavia, essa divisão é efectuada quando um dado bucket excede a sua capacidade. No caso de o bucket cuja capacidade foi excedida, não ser o bucket que foi dividido, então poder-se-á aplicar técnicas de overflow. Porém, este tipo de indexação é relativamente pior que a indexação por B-tree, que serve também para comparações de igualdade. Para além de um maior uso de recursos, este indexação por hash não suporta índices de múltiplas colunas. Por tudo isto, o uso deste tipo de indexação é bastante desencorajado. Criar um índice de hash é tão simples como criar uma B-tree. Vamos partir do exemplo anterior, da tabela T, e criar um índice hash sobre a coluna a. CREATE INDEX my_index ON T USING hash (a); 4.1.3. GiST Os índices GiST (Generalized Search Tree) não representam um tipo específico de estruturas de dados, mas sim uma abstracção/interface que pode ser utilizada para implementar vários tipos de árvores tais como B+ trees, R-trees, hb-trees, RD-trees. Os operadores particulares de cada índice GiST podem variar consoante a estratégia de indexação (a operator class ). A título de exemplo, a distribuição standard do PostgreSQL inclui classes de operadores GiST para diversos tipos de dados geométricos, bi-dimensionais. Por forma a criar um índice GiST será necessário definir as classes de operadores, incluindo operadores de comparação e estratégias de pesquisa. Seguidamente, os seus respectivos nomes têm de ser incluídos na função CREATE INDEX. 4.1.4. GIN Índices GIN são índices invertidos que são capazes de lidar com valores que contém mais do que uma chave, por exemplo arrays. Do mesmo modo que os índices GiST, os índices GIN suportam diversas estratégias de indexação, definidas pelo utilizador, bem como os operadores a usar nessas mesmas estratégias. A distribuição standard do PostgreSQL inclui classes de operadores para arrays unidimensionais. Estas duas ultimas estruturas de indexação não serão mais aprofundadas com exemplos, devido à sua complexidade e pouca relevância para este trabalho. - 20 -

4.2. Índices multi-coluna Em certos casos de pesquisas sobre tabelas que envolvam mais do que um atributo da mesma, pode possivelmente ser benéfico usar um índice sobre mais do que um atributo da tabela. No PostgreSQL isto é possível, apesar de só puder ser usado com estruturas de indexação do tipo, B-tree, GiST e GIN. Existe um limite de colunas para um índice, sendo esse limite 32 colunas. No entanto, este parâmetro não é fixo, podendo ser alterado na altura da compilação do sistema. O optimizador pode ainda optar usar um índice multi-coluna mesma quando a pesquisa apenas referir um subconjunto das colunas indexadas pelo índice. É aconselhável ponderar fortemente sobre o uso destes índices pois, em diversas ocasiões, o uso de um índice sobre uma coluna é suficiente. Ou até, n índices diferentes sobre as n colunas a indexar, dado que o optimizador consegue combinar múltiplos índices, por forma a obter uma melhor performance da query. No índice B-tree, a eficiência de pesquisas sobre instâncias multi-coluna do mesmo é maior quando as condições das pesquisas são sobre as colunas indexadas mais à esquerda no índice. Nos índices GiST, a sua eficiência é tão maior quanto o número de valores distintos associados à primeira coluna indexada pelo índice. Por outro lado, nos índices GIN a eficiência das pesquisas é a mesma independentemente das colunas, indexadas pelo índice, usadas nas condições das mesmas. A sintaxe para criar um índice multi-coluna é bastante similar à de criação de um índice simples. No exemplo apresentado a baixo, será criada uma B-tree sobre ambas as colunas de T. CREATE INDEX my_multi_index ON T (a,b); 4.3. Índices para organização de ficheiros Por defeito, a organização das tabelas em ficheiro é feita em heap. Todavia, às vezes é preferível, do ponto de vista de maximizar a performance na obtenção das páginas do disco, organizar as tabelas segundo uma estrutura de indexação. O PostgreSQL permite efectuar isto mesmo através do comando CLUSTER. Usado da seguinte forma: CLUSTER [VERBOSE] tablename [ USING indexname ] Aplicando este exemplo ao recorrente caso, da tabela T, logicamente o comando seria do género: CREATE INDEX my_index ON T (b); CLUSTER T USING my_index; Por forma a reestruturar o ficheiro da tabela segundo o índice é necessário usar a clausula USING indexname. Depois de reorganizada a tabela, o sistema sabe qual é o índice a usar para organizar a tabela em ficheiro daqui por diante, bastando apenas usar a clausula: CLUSTER table_name Podendo ainda ser atribuído um novo índice de organização através da clausula ALTER TABLE. A cláusula VERBOSE permite obter informação sobre o clustering de uma dada tabela. Dado este comando cria uma cópia temporária dos dados (quer da tabela quer do índice) para fazer a operação, é necessário ter espaço de disco livre equivalente ou superior ao espaço ocupado por ambos. Por outro, importa realçar que este comando é apenas executado uma vez. Qualquer alteração futura na tabela será organizado em ficheiro novamente seguindo a estrutura por defeito, heap. Consequentemente, caso seja importante manter a estrutura do - 21 -

ficheiro segundo um certo tipo de índice, deve ser executado o comando de forma periódica. Ainda, alterar o parâmetro FILLFACTOR da respectiva tabela, para valores menores que 100% pode ajudar a prevenir a ordenação do cluster durante actualizações, dado que os tuplos actualizados são mantidos na mesma página desde que exista espaço livre suficiente. Por fim, quando uma tabela é reorganizada segundo um índice, um LOCK de acesso exclusivo é obtido. Isto previne que outras operações possam ser executadas sobre a tabela, enquanto a reorganização está em curso. 4.4. Índices e a cláusula ORDER BY Adicionalmente a simplesmente encontrar os tuplos desejados, um índice pode também entregá-los por uma ordem específica. Isto simplifica a execução das pesquisas que definam uma cláusula ORDER BY, pois ao invés de um passo extra para a ordenação dos tuplos, estes já são obtido pela ordem certa. De todos os tipos de estruturas de indexação suportadas pelo PostgreSQL, apenas a B-tree consegue retornar valores de forma ordenada. O optimizador consegue satisfazer uma cláusula ORDER BY de duas formas distintas. Por um lado pode percorrer um índice ordenado retornando os respectivos tuplos ordenadamente, ou percorrendo a tabela de forma sequencial e executando uma ordenação no fim. Para uma pesquisa onde seja necessário consultar grande parte da tabela, será mais eficiente consultar os valores na própria tabela e acrescentar um passo de ordenação, ao invés de usar um índice, isto porque a consulta no índice envolve acessos sequenciais ao disco logo será mais pesado. Por outro lado, se somente desejar obter poucas entradas então o índice será tendencialmente mais eficiente. Um caso especial, bastante eficiente, é a combinação da clausula ORDER BY com a LIMIT n. Isto porque em uma ordenação explicita, teriam que ser processados todos os valores por forma a identificar os primeiros n valores. Porém, se existir um índice que coincide com a clausula ORDER BY, os primeiros n valores podem ser obtidos directamente sem pesquisar todos os restantes. Por defeito a B-tree ordena as suas entradas crescentemente com nulls por ultimo. Isto faz com que o índice produza um resultado esperado por uma clausula ORDER BY. É possível reajustar a ordenação por defeito desta estrutura de indexação, incluindo as opções ASC, DESC, NULLS FIRST, e NULLS LAST, a quando da criação do índice. Tomando o exemplo da tabela T com as colunas a e b. Pretende-se que a coluna a seja ordenada com os nulls primeiro, e que a coluna b seja ordenada de forma decrescente com nulls no fim. A sintaxe para criar índices corresponderes a estes casos seria: CREATE INDEX my_index ON T (a NULLS FIRST); CREATE INDEX my_index_2 ON T (b DESC NULLS LAST); - 22 -

4.5. Múltiplos ficheiros de índices para os mesmos atributos Uma possível necessidade de um qualquer database tuner é a de criar mais do que um tipo de índice, com a mesma estrutura de indexação ou não, para um mesmo conjunto de atributos. Isto mostra-se importante quando, por exemplo, uma determinada estrutura de indexação não cobre todos os operadores de comparação que, do ponto de vista do database tuner, sejam importantes para melhorar as queries feitas sobre a Base de Dados. Sobre este assunto o PostgreSQL facilita bastante a vida do database tuner pois não impõe qualquer restrição quanto ao número de índices por conjunto de atributos. Consequentemente também não impõe qualquer restrição quanto ao número de índices por tabela. Por forma a criar mais do que um índice para um mesmo atributo podemos tomar como exemplo a sintaxe presente na imagem a baixo. Como se pode ver pelo exemplo, no PostgreSQL não há qualquer problema em criar um índice b-tree e um índice hash sobre um mesmo atributo da mesma tabela. Também, se as estruturas dos índices fossem as mesmas, duas b-tree's por exemplo, continuava a ser perfeitamente aceitável é exequível no PostgreSQL. 4.6. Estruturas temporariamente inconsistentes Num sistema onde diferentes processos executem operações de actualização dos índices, concorrentemente, existe a necessidade de controlar os acessos às estruturas de índices, por forma a evitar inconsistências nas estruturas dos índices. Tendo em conta este facto, o PostgreSQL implementa mecanismos de LOCKs, para controlar os acessos de diferentes processos à mesma estrutura de indexação. O sistema obtém um AccessShareLock sobre o índice na altura de executar uma pesquisa no mesmo e um RowExclusiveLock quando é necessário actualizar o mesmo. Porém, este mecanismo apenas está implementado em índices do tipo b-tree e hash, consequentemente a criação de um tipo de índice que suporte concorrência não é nada trivial, sendo necessário uma análise sobre o comportamento necessário. Contudo, isto não resolve todos os problemas. Actualizações concorrentes podem criar inconsistências entre a tabela mãe (heap) e o índice. Isto deve-se ao facto do PostgreSQL separar os acessos e as respectivas actualizações na tabela, da actualização nos respectivos índices. Isto faz com que exista uma pequena janela temporal, onde o índice possa estar inconsistente em relação ao heap. O PostgreSQL lida com o problema, através das três seguintes regras. - 23 -

1. Uma nova entrada no heap é criada antes de criar a respectiva entrada no índice. Isto não causa problemas de concorrência pois o leitor do índice não está interessado em obter uma linha uncommitted. 2. Quando uma entrada no heap é removida, todas as suas respectivas entradas têm que ser removidas em primeiro lugar. 3. Uma pesquisa no índice deve assegurar uma marca na página do índice que aponta para a última entrada retornada pela função amgettuple. Por outro lado, a função ambulkdelete não pode remover entradas de páginas que estejam marcadas por outros processos. Esta ultima solução tem um custo baixo em run time, porém introduz um overhead no bloqueio de remoções que introduzam problemas de concorrência, isto nos raros casos onde tal bloqueio acontece. Por fim, para um melhor fluxo de execução nestes casos, deve ser usado uma aproximação síncrona de snapshots non-mvcc-compliant. 4.7. Combinar múltiplos índices Uma pesquisa em um índice está limitada a usar cláusulas de condição que usem as colunas indexadas com a respectiva classe de operadores, e unidos por uma operação AND. Por outras palavras, uma condição de uma query sobre um índice sobre as colunas (a,b) da tabela do género, WHERE a = 5 AND b = 6 permite a utilização do índice por parte do optimizador. Todavia a mesma operação WHERE a = 5 OR b = 6, não permite a utilização do índice, directamente. O PostgreSQL têm um mecanismo que permite lidar com esta situação, sendo este a combinação de múltiplos índices. Uma possível solução para o problema de cima era partir a cláusula em duas pesquisas diferentes sobre o índice, seguidas de uma unificação. Ainda, outra solução é ter dois índices separados, um sobre a coluna a e outro sobre a coluna b, fazendo um scan em cada índice para obter os resultados correctos, seguidos mais uma vez da operação de unificação apropriada. Para combinar múltiplos índices, o sistema pesquisa cada índice por forma a saber as localizações de cada valor que respeite as condições, preparando em memória um bitmap, para cada índice. Consequentemente os bitmaps são unificados através de operações lógicas, AND ou OR, por forma a obter o resultado desejado para a pesquisa original. Finalmente, a tabela é fisicamente visitada, obtendo os respectivos valores desejados. Estes valores são visitados pela ordem que estão guardados em disco, dado que é nessa ordem que os bitmaps estão ordenados. Este mecanismo torna as cláusulas que combinam operações lógicas, com operadores de comparação, mais eficientes. Todavia, cai sobre o database tuner decidir qual o melhor caminho a seguir, sendo que vai depender muito do layout da Base de Dados, bem como do tipo de pesquisas efectuadas sobres os diferentes atributos da tabela. - 24 -

5. Processamento e Optimização de Perguntas A optimização de perguntas é uma das parte mais importantes de se conhecer num Sistema de Base de Dados. Para bases de dados de tamanho considerável, é útil planear o desenho das tabelas e das queries de forma a ter a melhor perfomance possível. Também é importante conhecer os mecanismos que possibilitam forçar o planeador a seguir o plano pretendido, quando se sabe de antemão que este é mais eficiente. 5.1. Implementação de Operações 5.1.1 Selecção Para operações de selecção, o planeador começa sempre por considerar um sequential scan, ou seja, percorrer toda a tabela para selecionar todos os tuplos de acordo a condição requerida. No entanto, se houverem índices sobre a colunas ou as colunas sobre as quais a operação de selecção de incide, o planeador considera uma outra operação: index scan. Nesta operação, a pesquisa dos tuplos é efectuada usando o índice. O planeador considera o uso de índices dependendo do tipo de índice usado e do operador usado na selecção. Os índices B-tree que são os índices por defeito em Postgres são usados com os operadores <, <=, >, >=, =, not null, is null e ainda no operador like se a string for constante e a variação só ocorrer no final. Por exemplo: like foo%. Mas o índice B-tree não será usado em like %foo. Os índices de hash só são usados com o operador de igualdade. Os índices GiST e GIN podem ter várias estratégias de indexação, pelo que a escolha do planeador em usar um index scan nestes índices depende do operador usado assim como da estratégia de indexação desse índice. Os índices multi-coluna, para além das considerações antes referidas dependendo do tipo de índice, podem ser usadas se na operação de selecção estiver qualquer subconjunto das colunadas referenciadas em tal índice. De referir também que os index scans são também considerados pelo planeador se a ordenação do índice estiver de acordo com a cláusula ORDER BY da query ou se for necessário uma ordenação para um merge join. Para forçar o planeador a fazer sempre um sequential scan é necessario alterar a variável enable_indexscan fazendo: set enable_indexscan = off; Pode-se também forçar o planeador a usar um index scan sempre que for possível. Ou seja, utilizar um sequential scan só quando não houver outra possibilidade para a operação de selecção. Para isso é necessário executar: set enable_seqscan = off; Pode-se também impedir o planeador de considerar a utilização de bitmaps ao fazer: set enable_bitmapscan = off; - 25 -

Exemplos: O uso de um bitmap apenas é vantajoso quando usados em tabelas com muito tuplos. Podemos usar como exemplo uma query na tabela uscensus, que contém 32561 tuplos: create index on uscensus(age); create index on uscensus(educationnum); explain select * from uscensus where age = 37 and educationnum = 13; que origina o seguinte plano, mostrado graficamente: Devido ao elevado número de tuplos, é usado um bitmap com os resultados do varrimento dos índices. É através desse bitmap que depois são obtidos os tuplos que respeitam as condições da query. create index on city(country); explain select * from city order by country; que origina o seguinte plano, mostrado textualmente: index scan using city_country_idx on city 5.1.2. Junções Se uma query envolver mais de duas junções, o planeador tenta construir uma árvore de junções, sendo que cada uma tem duas entradas. O PostgreSQL tem dois algoritmos para construir os possíveis plano de junções. Quando o número de tabelas é muito elevado (definido pela variável geqo_threshold), é usado um algoritmo genético. Quando o número de tabelas é inferior a tal variável, o planeador faz uma pesquisa quase exaustiva por todas as combinações de junções possíveis. O planeador considera primeiro junções entre tabelas que tenham clausulas de ligação tal como tabela1.att = tabela2.att) e só depois considera junções entre tabelas não relacionadas entre si. O PostgreSQL considera três algoritmos possíveis para as junções: nested-loop join, merge join e o hash join. A única maneira de controlar os algoritmos usados é forçar o planeador a não considerar o uso dum determinado algoritmo. Isso pode ser feito executando: set enable_hashjoin = off; set enable_mergejoin = off; set enable_nestloop = off; - 26 -