Escolar Documentos
Profissional Documentos
Cultura Documentos
1 A Apostila 11
1.1 Notações utilizadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.1.1 Comandos e saída em tela . . . . . . . . . . . . . . . . . . . . . . . . 12
1.1.2 Aviso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.3 Observação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.4 Conteúdo extra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Introdução ao PostgreSQL 14
2.1 Sobre o PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.1 O que é o PostgreSQL? . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.2 Extensibilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.3 Licença . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.1.4 Como se Fala e Como se Escreve? . . . . . . . . . . . . . . . . . . . . 15
2.1.5 PGDG - PostgreSQL Global Development Group . . . . . . . . . . . . 15
2.2 Suporte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.1 Sobre Software Livre em Geral . . . . . . . . . . . . . . . . . . . . . . 16
2.2.2 Suporte da comunidade . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.3 Suporte comercial . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.3 Versionamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.1 Política de Versionamento . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.2 Política de ciclo de vida das versões do PostgreSQL . . . . . . . . . . 18
2.4 Revisão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3 Instalação do PostgreSQL 20
3.1 Sobre instalação do PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Instalação via pacotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.1 Pacotes PGDG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.2.2 Instalação via pacotes - distribuições Linux derivadas de Red Hat . . . 24
3.3 Instalação via código-fonte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.3.1 Preparativos Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.3.2 Preparativos RedHat . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2
3 Sumário
4 Noções básicas 39
4.1 Conectando à uma base de dados com o psql . . . . . . . . . . . . . . . . . . 40
4.1.1 Variáveis de ambiente de conexão . . . . . . . . . . . . . . . . . . . . 46
4.2 Bases de dados no PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.2.1 Bancos de dados padrão e templates . . . . . . . . . . . . . . . . . . 47
4.2.2 Templates de bancos de dados . . . . . . . . . . . . . . . . . . . . . 47
4.2.3 Propriedades de bancos de dados . . . . . . . . . . . . . . . . . . . . 57
4.3 Tablespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3.1 Movendo tablespaces . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.4 Schemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.4.1 search_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.5 Revisao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5 Autenticação e autorização 70
5.1 Roles: Gerenciamento de usuários no Postgres . . . . . . . . . . . . . . . . . 71
5.1.1 Prompt psql e roles . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.2 Permissões de papéis . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.1.3 Gerenciando papéis e grupos de usuários . . . . . . . . . . . . . . . . 72
5.1.4 Transferindo propriedades: REASSIGN OWNED . . . . . . . . . . . . 83
5.2 Gerenciamento de permissões . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2.1 Privilégios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2.2 Tipos de privilégios . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
5.2.3 Gerenciando privilégios . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.2.4 Permissões em tabelas . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.2.5 Permissões em colunas . . . . . . . . . . . . . . . . . . . . . . . . . . 96
5.2.6 Permissões em bancos de dados . . . . . . . . . . . . . . . . . . . . . 97
5.2.7 Permissões em schemas . . . . . . . . . . . . . . . . . . . . . . . . . 101
5.2.8 Privilégios padrão . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.3 Autenticação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.3.1 pg_hba.conf - host-based authentication (autenticação baseada em
máquina) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.3.2 Campos do pg_hba.conf . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.3.3 O arquivo .pgpass . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
5.3.4 Arquivo de serviço de conexão . . . . . . . . . . . . . . . . . . . . . . 116
5.4 RLS: Row Level Security - Segurança em nível de linha . . . . . . . . . . . . . 117
7 Configuração 146
7.1 postgresql.conf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
7.1.1 Tipos de valores do postgresql.conf . . . . . . . . . . . . . . . . . . . 147
7.1.2 Directivas include . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
7.1.3 Contextos de parâmetros de configuração . . . . . . . . . . . . . . . . 148
7.1.4 Exibir / alterar parâmetros . . . . . . . . . . . . . . . . . . . . . . . . 149
7.2 Formas alternativas de configurar parâmetros . . . . . . . . . . . . . . . . . . 150
7.3 A view pg_settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
7.4 O comando ALTER SYSTEM . . . . . . . . . . . . . . . . . . . . . . . . . . 154
11 Performance 212
11.1 Performance / Tuning do PostgreSQL . . . . . . . . . . . . . . . . . . . . . . 213
11.2 Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
11.2.1 Disco / armazenamento . . . . . . . . . . . . . . . . . . . . . . . . . 215
11.2.2 Memória RAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
11.2.3 CPU . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
11.2.4 Rede . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
11.3 Sistema Operacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
11.3.1 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
11.3.2 Comunicação inter-processos . . . . . . . . . . . . . . . . . . . . . . 225
11.3.3 Swapiness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
11.3.4 Memory overcommit . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
11.3.5 Limpeza do page cache . . . . . . . . . . . . . . . . . . . . . . . . . 229
11.3.6 Sistema de arquivos . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
11.3.7 UUID no fstab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
6 Sumário
12 Observabilidade 283
12.1 Observabilidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.1.1 Métricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.1.2 Rastreamento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.1.3 Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.1.4 Monitoramento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
12.2 Logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
12.2.1 Configurações de logs no PostgreSQL . . . . . . . . . . . . . . . . . . 285
12.3 Análise de logs com pgbadger . . . . . . . . . . . . . . . . . . . . . . . . . . 290
12.4 Monitoramento pontual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
12.5 Monitoramento contínuo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
12.5.1 SAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
12.6 Catálogos e views de sistema . . . . . . . . . . . . . . . . . . . . . . . . . . 305
12.6.1 Catálogos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
12.6.2 Views de sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
12.6.3 Information Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
12.7 Colunas de sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
12.8 O Módulo pg_stat_statements . . . . . . . . . . . . . . . . . . . . . . . . . 312
12.8.1 Configurações do pg_stat_statements (nome - tipo - contexto) . . . . 312
15 Replicação 370
15.1 O que é replicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
15.1.1 Conceitos de replicação . . . . . . . . . . . . . . . . . . . . . . . . . 371
15.1.2 Slots de replicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
15.2 Replicação física via streaming . . . . . . . . . . . . . . . . . . . . . . . . . . 374
15.2.1 Preparação do laboratório de replicação streaming . . . . . . . . . . . 375
15.2.2 Configurações relativas à replicação streaming . . . . . . . . . . . . . 375
15.2.3 Funções relativas à replicação streaming . . . . . . . . . . . . . . . . 377
15.2.4 Procedimentos para replicação via streaming assíncrona . . . . . . . . 379
15.2.5 Monitorando a replicação via streaming . . . . . . . . . . . . . . . . . 383
15.2.6 Mudando o ambiente para replicação síncrona . . . . . . . . . . . . . 387
15.2.7 Failover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
15.2.8 Failback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
15.3 Replicação lógica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
15.3.1 Publicação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
15.3.2 Subscrição e gerenciamento de slots de replicação . . . . . . . . . . . 393
15.3.3 Conflitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
15.3.4 Restrições . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
15.3.5 Arquitetura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.3.6 Monitoramento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.3.7 Configuração do publicador . . . . . . . . . . . . . . . . . . . . . . . 396
15.4 Configuração do assinante . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.4.1 Preparação do laboratório . . . . . . . . . . . . . . . . . . . . . . . . 397
15.4.2 Procedimentos e testes para replicação lógica . . . . . . . . . . . . . . 397
15.5 Soluções de terceiros para alta disponibilidade no PostgreSQL . . . . . . . . . 401
15.5.1 repmgr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
15.5.2 Patroni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
15.5.3 PAF - PostgreSQL Automatic Failover . . . . . . . . . . . . . . . . . 401
15.5.4 CitusDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
15.5.5 Greenplum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
9 Sumário
11
12 1. A Apostila
Notações utilizadas
Comandos e saída em tela
Simbologia Descrição
[#] Shell no sistema operacional como usuário root
[$] Shell no sistema operacional como usuário não-root
[>] Shell interativo (banco de dados, programação)
SELECT
col1,
col2
FROM tabela
WHERE col1 != 1
AND col2 = 0
ORDER BY col1;
col1 | col2
------+------
5 | 0
apt update
13 1. A Apostila
Aviso
Aviso
Observação
Observação
O comando anterior não necessita especificar a porta se for usada a padrão (5432).
Conteúdo extra
Devido à questão do tempo, infelizmente não é possível passar todo conteúdo da apostila.
Dentro do conteúdo normal, a seguinte demarcação é utilizada:
14
15 2. Introdução ao PostgreSQL
Sobre o PostgreSQL
O que é o PostgreSQL?
https://www.postgresql.org/docs/current/history.html
Extensibilidade
O PostgreSQL pode ter suas funcionalidades ampliadas instalando extensões que podem adi-
cionar tipos de dados, funções, linguagens procedurais, foreign data wrappers etc.
Licença
Devido a sua licença [1] liberal, baseada na licença BSD [2], o PostgreSQL pode ser usado,
modificado e distribuído por qualquer um gratuitamente sob qualquer pretexto, seja privado,
comercial ou acadêmico.
[1] https://opensource.org/licenses/postgresql
[2] https://opensource.org/licenses/BSD-3-Clause
Seu nome é uma referência ao Ingres, que foi a base de seu código-fonte no início.
É uma entidade, ou seja, um grupo de desenvolvedores que trabalha nos códigos e mantém o
PostgreSQL.
16 2. Introdução ao PostgreSQL
Suporte
Informações gerais podem ser encontradas no link:
https://www.postgresql.org/support/
Suporte da comunidade
• Sites oficiais
– Global: https://www.postgresql.org/
– Wiki: https://wiki.postgresql.org/
• Documentação
A documentação do PostgreSQL é muito rica e tem as opções:
– Arquivo PDF: https://www.postgresql.org/docs/manuals/
– On-line: https://www.postgresql.org/docs/.
• Listas de discussão
Uma lista de discussão é um meio rápido de resolução de problemas. Contando com
uma comunidade forte e vibrante, temos as listas:
– Internacional: https://lists.postgresql.org
17 2. Introdução ao PostgreSQL
• Blogs
São vários os blogs ao redor do planeta sobre PostgreSQL, mas, para facilitar, temos
blogs oficiais da comunidade que aglutinam posts de outros blogs, de forma a termos
um conhecimento diversificado por meio de tutoriais e artigos divulgados nesses.
– Planet PostgreSQL: http://planet.postgresql.org/
Suporte comercial
Há várias empresas que provêm suporte ao PostgreSQL no mundo inteiro. Elas podem ser
encontradas por região [1] ou especificamente por hosting [2]:
• [1] https://www.postgresql.org/support/professional_support/
• [2] https://www.postgresql.org/support/professional_hosting/
18 2. Introdução ao PostgreSQL
Versionamento
Política de Versionamento
Até a versão 9.6, o PostgreSQL adotava o modelo de versão X.Y.Z, sendo que a parte X.Y
era a versão majoritária e a Z a versão minoritária. A partir da versão 10, adotou-se o modelo
X.Y, sendo X a versão majoritária e Y a versão minoritária. É fortemente recomendado a
atualização para a última versão minoritária (minor version: X.Y) para qualquer que seja sua
sua versão majoritária (major release: X) em uso.
As versões majoritárias do PostgreSQL incluem novas funcionalidades e ocorrem uma vez por
ano. Estas Major releases normalmente mudam o formato interno do sistema de tabelas e
arquivos de dados, de forma que o dump ou o uso do módulo pg_upgrade são necessários para
a atualização. Versões minoritárias (minor releases) são numeradas incrementando a segunda
parte do número da versão, e.g. 10.0 para 10.1. Nestas versões, apenas correções de bugs
são aplicadas. É recomendado que todos usuários atualizem para a versão de lançamento
minoritária (X.Y) assim que possível. Versões minoritárias corrigem bugs, sejam eles de segu-
rança ou até mesmo risco de perda de dados. É importante verificar periodicamente no site
oficial do PostgreSQL a respeito dessas correções para evitar danos e prejuízos.
A comunidade considera que não atualizar é mais arriscado do que atualizar.
Atualizando para um minor release não requer um dump e restore; simplesmente pare o banco
de dados, instale os binários atualizados e reinicie o servidor.
Para alguns lançamentos, mudanças manuais podem ser necessárias para completar a atual-
ização, então sempre leia as notas de lançamento antes de atualizar.
O projeto PostgreSQL tem como política suportar uma versão majoritária por 5 anos.
Bugs e/ou falhas de segurança encontradas após este período não serão mais corrigidos.
Para saber sobre versões suportadas no momento, há uma tabela no site oficial do PostgreSQL:
http://www.postgresql.org/support/versioning/
No site oficial do PostgreSQL há uma parte que tem uma matriz de funcionalidades que foram
adicionadas ao longo das versões:
http://www.postgresql.org/about/featurematrix/
19 2. Introdução ao PostgreSQL
Revisão
1. P: Com que frequência são lançadas novas versões majoritárias (major) do PostgreSQL?
R:
2. P: Com que frequência são lançadas novas versões minoritárias (minor) do PostgreSQL?
R:
3. P: O suporte da comunidade se estende a quantas versões do PostgreSQL?
R:
3
Instalação do PostgreSQL
• Sobre a instalação do PostgreSQL
• Instalação via pacotes
• Instalação via código-fonte
• SSH sem senha
20
21 3. Instalação do PostgreSQL
https://www.postgresql.org/download
Muitas vezes, os pacotes disponíveis para instalação em uma distribuição Linux não incluem a
versão mais recente do PostgreSQL, então configurar o repositório oficial pode ser muito útil.
No entanto, há casos também que se opta por instalar o PostgreSQL via compilação de seu
código-fonte, que oferece mais flexibilidade, porém é muito mais demorada e complexa.
22 3. Instalação do PostgreSQL
Pacotes PGDG
São pacotes oficiais disponibilizados pelo próprio PGDG, que torna possível instalar a última
versão do PostgreSQL. Para isso, é preciso configurar o repositório de acordo com a distribuição
ou família de distribuição Linux:
- RedHat - Debian - Ubuntu - SuSE
Arquitetura de diretórios
Tipo Localização
Instalação /usr/lib/postgresql/<VERSÃO MAJORITÁRIA>
Configuração /etc/postgresql/<VERSÃO MAJORITÁRIA>/main
Dados /var/lib/postgresql/<VERSÃO MAJORITÁRIA>/main
Binários /usr/lib/postgresql/<VERSÃO MAJORITÁRIA>/bin
23 3. Instalação do PostgreSQL
echo \
"deb http://apt.postgresql.org/pub/repos/apt `lsb_release -cs`-pgdg main" \
> /etc/apt/sources.list.d/pgdg.list
apt update
su - postgres
[$] Testando:
PostgreSQL 13.1
24 3. Instalação do PostgreSQL
Arquitetura de diretórios
Tipo Localização
Instalação /usr/pgsql-<VERSÃO MAJORITÁRIA>
Configuração /var/lib/pgsql/<VERSÃO MAJORITÁRIA>/data
Dados /var/lib/pgsql/<VERSÃO MAJORITÁRIA>/data
Binários /usr/pgsql-<VERSÃO MAJORITÁRIA>/bin
[#] Crie uma variável de ambiente que captura a versão majoritária da distro:
URL="https://download.postgresql.org/pub/repos/yum/reporpms/EL-\
${DISTRO_VERSION}-x86_64/pgdg-redhat-repo-latest.noarch.rpm"
dnf check-update -y
/usr/pgsql-${PGMAJOR}/bin/postgresql-${PGMAJOR}-setup initdb
Tipo Localização
Instalação /usr/local/pgsql/<VERSÃO MAJORITÁRIA>
Configuração /var/local/pgsql/<VERSÃO MAJORITÁRIA>/data
Dados (PGDATA) /var/local/pgsql/<VERSÃO MAJORITÁRIA>/data
Binários /usr/local/pgsql/<VERSÃO MAJORITÁRIA>/bin
Preparativos Debian
Preparativos RedHat
# Diretório de binários
PGBIN="${PGHOME}/bin"
# Diretório de logs
PGLOG="/var/log/pgsql/${PGMAJOR}"
# Diretório de headers C
PGINCLUDEDIR="/usr/local/include/pgsql/${PGMAJOR}"
30 3. Instalação do PostgreSQL
# Opções do configure
CONFIGURE_OPTS="
--prefix=${PGHOME} \
--with-python \
--with-libxml \
--with-openssl \
--with-ldap \
--with-uuid=e2fs \
--includedir=${PGINCLUDEDIR}
"
# Opções do make
MAKEOPTS="-j${NJOBS}"
# Tipo de hardware
CHOST="x86_64-unknown-linux-gnu"
useradd \
-c 'PostgreSQL system user' \
-s /bin/bash \
-k /etc/skel \
-d ${PGUSERHOME} \
-g postgres \
-m -r postgres &> /dev/null
[#] Criar o arquivo .pgvars com seu respectivo conteúdo no diretório do usuário home postgres:
# Binary directory
PGBIN="${PGHOME}/bin"
# Library directories
export LD_LIBRARY_PATH="\${PGHOME}/lib:\${LD_LIBRARY_PATH}"
# Manuals directories
export MANPATH="\${PGHOME}/man:\${MANPATH}"
# Path to binaries
export PATH="\${PGBIN}:\${PATH}"
# Unset variables
unset PGMAJOR PGHOME PGBIN
EOF
32 3. Instalação do PostgreSQL
[#] Adiciona linha no arquivo de perfil do usuário postgres para ler o arquivo ~/.pgvars e
aplicá-las:
if [ -f ~postgres/.bash_profile ]; then
echo -e "\nsource ~/.pgvars" >> ~postgres/.bash_profile
else
echo -e "\nsource ~/.pgvars" >> ~postgres/.profile
fi
wget -c \
https://ftp.postgresql.org/pub/source/v${PGVERSION}/postgresql-\
${PGVERSION}.tar.bz2 -P /tmp/
cd postgresql-${PGVERSION}
33 3. Instalação do PostgreSQL
./configure ${CONFIGURE_OPTS}
make world
[#] Instalação:
make install-world
su - postgres -c "\
initdb \
-D ${PGDATA} \
-E utf8 \
-U postgres \
-k \
--locale=pt_BR.utf8 \
--lc-collate=pt_BR.utf8 \
--lc-monetary=pt_BR.utf8 \
--lc-messages=en_US.utf8 \
-T portuguese \
-X ${PGWAL}"
# listen_addresses = '*'
sed "s:\(^#listen_addresses.*\):\1\nlisten_addresses = '*':g" \
-i ${PGDATA}/postgresql.conf
# log_destination = 'stderr'
sed "s:\(^#log_destination.*\):\1\nlog_destination = 'stderr':g" \
-i ${PGDATA}/postgresql.conf
# logging_collector = on
sed "s:\(^#logging_collector.*\):\1\nlogging_collector = on:g" \
-i ${PGDATA}/postgresql.conf
# log_directory = '${PGLOG}'
sed "s:\(^#log_directory.*\):\1\nlog_directory = '${PGLOG}':g" \
-i ${PGDATA}/postgresql.conf
# stats_temp_directory = '${PG_STAT_TEMP}'
sed \
"s:\(^#stats_temp_directory.*\):\1\nstats_temp_directory = '${PG_STAT_TEMP}':g" \
-i ${PGDATA}/postgresql.conf
35 3. Instalação do PostgreSQL
echo -e \
"\ntmpfs ${PG_STAT_TEMP} tmpfs size=32M,uid=postgres,gid=postgres 0 0"\
>> /etc/fstab
mount -a
Observação
Se a versão do SystemD instalada no servidor for muito antiga, será necessário fazer as
operações de enable e start separadamente.
• Debian
• Red Hat
[$] Caso não exista a chave na máquina local, ela será criada:
if [ ! -e ~/.ssh/id_rsa ]; then
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa;
fi
[$] Enviar a chave pública do usuário local para o usuário root no servidor de destino:
ssh-copy-id root@${PGSERVER}
38 3. Instalação do PostgreSQL
Revisão
1. P: Quais as vantagens de usar pacotes provenientes do PGDG?
R:
2. P: Quais os nomes dos diretórios de WAL, bancos de dados e tablespaces, dentro do
diretório de dados?
R:
3. P: Quais os nomes dos arquivos de configuração e autenticação?
R:
4. P: É possível ter mais de uma versão majoritária do PostgreSQL na mesma máquina?
R:
5. P: É possível ter mais de uma instância da mesma versão majoritária do PostgreSQL
em uma máquina?
R:
4
Noções básicas
• Conectando à uma base de dados com o psql
• Bases de dados no PostgreSQL
• Tablespaces
• Schemas
39
40 4. Noções básicas
<Ctlr> + D
psql -U postgres
\q
[$] Agora sim podemos nos conectar de qualquer usuário do sistema operacional por meio de
uma conexão TCP:
SELECT version();
version
----------------------------------------------------------------------------------------
PostgreSQL 13.2 on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
\?
. . .
\l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
42 4. Noções básicas
\dt
List of relations
Schema | Name | Type | Owner
-----------+----------+-------+----------
pg_temp_3 | tb_teste | table | postgres
\d tb_teste
Table "pg_temp_3.tb_teste"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------------------------------------
id_ | integer | | not null | nextval('tb_teste_id__seq'::regclass)
campo | integer | | |
Indexes:
"tb_teste_pkey" PRIMARY KEY, btree (id_)
\h CREATE INDEX
URL: https://www.postgresql.org/docs/13/sql-createindex.html
44 4. Noções básicas
\c template1
\q
[$] Quando o nome da base de dados é omitido, o psql utiliza o nome do usuário:
postgres
45 4. Noções básicas
[$] Quando o nome da base de dados e do usuário são omitidos, utiliza-se o nome do usuário
do sistema operacional:
postgres
postgres
foo
bar
baz
# Sinal de menor
psql -Atq < /tmp/teste.sql
# Parâmetro -f
psql -Atqf /tmp/teste.sql
As seguintes variáveis de ambiente podem ser usadas para definir valores padrões de conexão
que são utilizadas pelas funções da libpq PQconnectdb, PQsetdbLogin e PQsetdb se não houver
nenhum outro valor especificado pelo código de chamada.
São úteis para evitar embutir informações de conexão de banco de dados em aplicações de
clientes simples.
https://www.postgresql.org/docs/current/static/libpq-envars.htm
47 4. Noções básicas
Cada instância (cluster ) do PostgreSQL tem por padrão três bancos de dados:
• template0: imutável e não conectável. É a base de dados mais pura de uma instância
Postgres.
\c template1
\c db_foo
\dt
List of relations
Schema | Name | Type | Owner
--------+--------+-------+----------
public | tb_foo | table | postgres
Não foi criada explicitamente qualquer tabela na base, mas implicitamente essa nova base foi
criada a partir de template1.
\c template1
\c db_foo
\dt
\dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | tb_1 | table | postgres
\c db_bar
51 4. Noções básicas
\dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | tb_1 | table | postgres
Tabela tb_1 foi copiada da base db_foo, que foi usada como template.
\c postgres user_test1
\c db1 user_test1
53 4. Noções básicas
SELECT
u.usename AS proprietario,
d.datistemplate AS base_de_dados_e_template
FROM pg_database AS d
INNER JOIN pg_user AS u
ON (d.datdba = u.usesysid)
WHERE datname = 'db1';
proprietario | base_de_dados_e_template
--------------+--------------------------
user_test1 | f
\c db_one user_test1
\dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+------------
public | tb1 | table | user_test1
Mesmo a base não tendo a propriedade istemplate, como true, foi possível fazê-la de template
porque a clonagem foi feita por seu proprietário.
54 4. Noções básicas
\c postgres user_test2
\c postgres user_test1
\c postgres user_test2
Dessa vez não houve erro, pois a propriedade istemplate era verdadeira.
\c postgres user_test1
56 4. Noções básicas
[>] Após alterar a propriedade istemplate para true é possível apagar a base de dados:
Nem todas opções estão disponíveis para ALTER DATABASE, tendo apenas dispoíveis:
TABLESPACE, ALLOW_CONNECTIONS, CONNECTION LIMIT e IS_TEMPLATE.
• OWNER
Define um proprietário (user_name) para o banco de dados;
• TEMPLATE
Especifica um banco de dados (template) como template;
• ENCODING
Codificação de caracteres para o novo banco de dados;
• LOCALE
É um atalho para definir LC_COLLATE e LC_CTYPE de uma só vez;
• LC_COLLATE
Ordem de agrupamento de caracteres (collate). Afeta a ordem de classificação em
consultas com ORDER BY, assim como a ordem usada em índices de colunas de texto. O
padrão é pegar o valor do banco de dados template;
• LC_CTYPE
Classificação de caracteres, afeta a categorização de caracteres, sejam eles letras maiús-
culas, letras minúsculas ou dígito;
• TABLESPACE
Tablespace onde o banco de dados estará fisicamente;
58 4. Noções básicas
• ALLOW_CONNECTIONS
Permite ou não conexões ao banco.
É muito útil quando não se quer novas conexões ao banco, mantendo as que já estavam;
• CONNECTION LIMIT
Quantas conexões podem ser feitas à base para usuários que não tenham o atributo
SUPERUSER;
• IS_TEMPLATE
Se seu valor for verdadeiro (true) a base de dados poderá ser clonada como template
por qualquer usuário com o atributo CREATEDB, caso contrário, somente superusuários e
seu dono poderão tomar a base de dados como template.
59 4. Noções básicas
Tablespaces
Tablespace é a localização, no sistema de arquivos, onde objetos do banco de dados são
criados.
Esse recurso permite que administradores de banco de dados criem e/ou alterem objetos em
tablespaces diferentes do padrão.
O tablespace padrão reside no diretório $PGDATA.
Uma vez criado, o tablespace pode ser referido pelo seu nome.
Tablespaces são muito úteis para gerenciamento de armazenamento e performance de discos.
Por exemplo, uma determinada tabela que é muito mais acessada em um sistema do que as
demais talvez possa ser alocada em um tablespace, cujo diretório seja um ponto de montagem
(Unix like) em um outro disco. A ideia é evitar concorrência de I/O, ou seja, é um recurso
extremamente útil na obtenção de maior desempenho.
Dois tablespaces são criados automaticamente quando uma instância é inicializada.
O tablespace pg_global é usado para catálogos de sistema compartilhados, enquanto que o
pg_default é o tablespace padrão das bases de dados template1 e template0 (e, portanto, será
o tablespace padrão para outras bases de dados também, a não ser que seja especificada a
cláusula TABLESPACE em CREATE DATABASE).
[#] Criação de diretório, onde serão criados os tablespaces com o modo de permissão 0700:
su - postgres
60 4. Noções básicas
mkdir /var/db_storage/ts_{alpha,beta,gamma}
[$] Verificando o diretório pg_tblspc, que está dentro do diretório de dados ($PGDATA):
ls -lhd $PGDATA/pg_tblspc/*
psql
[>] Via consulta, exibir os tablespaces criados com seu oid e nome:
oid | spcname
-------+----------
16418 | ts_alpha
16419 | ts_beta
16420 | ts_gamma
\c db_ts
\d tb_teste
Table "public.tb_teste"
Column | Type | Collation | Nullable | Default
---------+---------+-----------+----------+--------------------------------------
id | integer | | not null | nextval('tb_teste_id_seq'::regclass)
campo_2 | text | | |
campo_3 | integer | | |
Indexes:
"tb_teste_pkey" PRIMARY KEY, btree (id), tablespace "ts_alpha"
Tablespace: "ts_beta"
\dit+
62 4. Noções básicas
List of relations
Schema | Name | Type | Owner | Table | Persistence | Size | Description
--------+---------------+-------+----------+----------+-------------+--------+-------------
public | tb_teste | table | postgres | | permanent | 365 MB |
public | tb_teste_pkey | index | postgres | tb_teste | permanent | 107 MB |
[>] Dentro do psql, com comandos de shell do sistema operacional, verificar os tamanhos dos
diretórios de tablespaces:
\! du -hs /var/db_storage/*
108M /var/db_storage/ts_alpha
366M /var/db_storage/ts_beta
8.0K /var/db_storage/ts_gamma
\! du -hs /var/db_storage/*
108M /var/db_storage/ts_alpha
12K /var/db_storage/ts_beta
366M /var/db_storage/ts_gamma
Aviso
A operação de mover objetos entre tablespaces é demorada, efetue lock nos objetos envolvidos
e gere grande carga de IO.
Recomenda-se que este tipo de operação seja executado em janela de manutenção.
63 4. Noções básicas
Movendo tablespaces
É possível mover tablespaces para outros locais manualmente desde que o PostgreSQL esteja
parado.
Mova o diretório do tablespace para outro local e, então, ajuste o respectivo link simpólico
de pg_tblspc para apontar para o novo local (diretório ou ponto de montagem).
64 4. Noções básicas
Schemas
Também conhecido como namespace, é uma forma lógica de organizar objetos dentro de um
banco de dados, permitindo que um mesmo tipo de objeto seja criado mais de uma vez com
nome igual, mas em schemas diferentes.
Hierarquicamente, é a estrutura logo abaixo de uma base de dados, a qual contém outros tipos
de objetos como tabelas, views, funções e outros.
Quando criamos objetos, os mesmos pertencem ao schema público (public).
Sendo assim, quando fazemos uma consulta, não precisamos especificar o schema, pois está
como padrão no search_path.
Todo schema criado é registrado no catálogo de sistema pg_namespace.
search_path
É um parâmetro cujo contexto (user) permite a qualquer usuário alterá-lo durante a sessão.
Seu objetivo é definir “caminhos padrões” de busca de nomes de schemas.
Alterar o search_path facilita a referência a objetos, dispensando o uso de nomes qualificados
(schmea.objeto). No entanto, apesar de termos esse recurso disponível, é aconselhável não
alterá-lo utilizando nomes qualificados.
Há exceções para isso, quando, por exemplo, em uma determinada aplicação desejamos colocar
seus dados iniciais em um schema diferente.
SHOW search_path;
search_path
-----------------
"$user", public
O retorno do comando mostra duas opções que respectivamente são um schema, com o próprio
nome do usuário seguido por public.
\c db_schema
\dt
List of relations
Schema | Name | Type | Owner
----------+----------+-------+----------
sc_teste | tb_teste | table | postgres
RESET search_path;
SHOW search_path;
search_path
-----------------
"$user", public
\dt
Nada foi listado, pois o search_path foi alterado e agora temos que listar pelo próprio schema.
\dt sc_teste.*
List of relations
Schema | Name | Type | Owner
----------+----------+-------+----------
sc_teste | tb_teste | table | postgres
schemaname | relname
------------+----------
public | tb_teste
sc_teste | tb_teste
Nota-se que há duas tabelas de mesmo nome, mas, por estarem em namespaces diferentes,
isso é possível.
67 4. Noções básicas
schemaname | relname
------------+-----------
public | tb_teste
sc_teste | tb_teste2
sc_teste | tb_teste
A princípio, não foi possível apagar o schema, pois ele não está vazio.
Dessa vez, não houve erro. Foi utilizada a cláusula CASCADE, que permitiu que todos objetos
dentro do namespace também fossem apagados.
69 4. Noções básicas
Revisao
1. P: Metacomandos do psql são executados pelo servidor como SQL?
R:
2. P: Quais são as vantagens do uso de tablespaces?
R:
3. P: É possível criar bancos de dados e tablespaces dentro de schemas?
R:
4. P: Como posso mudar a lista de schemas usadas para uma sessão?
R:
5. P: Em qual schema objetos novos são criados?
R:
5
Autenticação e autorização
• Roles: gerenciamento de usuários no Postgres
• Gerenciamento de permissões
• Autenticação
• RLS: Row Level Security - Segurança em nível de linha
70
71 5. Autenticação e autorização
Permissões de papéis
• SUPERUSER
Contorna quase todas as verificações de autorização (exceto as cobertas por LOGIN e
REPLICATION);
• LOGIN
Pode estabelecer conexões novas com bancos de dados da instância (desde que exista ao
menos um método de autenticação para ele); grupos são papéis comuns sem a permissão
de login;
• REPLICATION
Pode estabelecer conexões que afetam toda a instância, como backups e replicações;
• CREATEROLE
Pode criar outros papéis (usuários e grupos);
• CREATEDB
Pode criar bases de dados;
72 5. Autenticação e autorização
• BYPASSRLS
Pode contornar políticas de RLS (Row Level Security );
• INHERIT
Pode usar os privilégios adquiridos por outros papéis sobre objetos locais sem executar
SET ROLE.
Papéis usuários podem fazer parte de papéis grupos. Dessa forma, as permissões atribuí-
das ao papel grupo estarão disponíveis para todos os membros dele.
-- Papéis grupos
CREATE ROLE dev; -- Grupo de desenvolvedores
CREATE ROLE sysadm; -- Grupo de administradores de sistema
CREATE ROLE sre; -- Grupo de SRE (Site Reliability Engineering)
CREATE ROLE masters; -- Grupo geral masters
CREATE ROLE dba SUPERUSER; --Grupo com atributo SUPERUSER para DBAs
-- Papel usuário
CREATE ROLE beethoven PASSWORD '123456' LOGIN;
\du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+----------------------
dev | Cannot login | {}
bach | | {sre,masters}
beethoven | | {masters}
sysadm | Cannot login | {}
dba | Superuser, Cannot login | {}
sre | Cannot login | {}
masters | Cannot login | {}
mozart | | {dev,sysadm,masters}
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
vivaldi | | {dev,masters}
Na segunda coluna, referente a atributos, nota-se que alguns papéis constam como “Cannot
login”, ou seja, não pode logar. Portanto, esses papéis são grupos.
Na terceira coluna, é exibido a que outro papel pertence.
\c db_zero
Como diz a mensagem, conexão ao banco de dados db_zero como usuário postgres.
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+----------
public | tb_dev | table | postgres
public | tb_sysadm | table | postgres
public | tb_sre | table | postgres
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+-------
public | tb_dev | table | dev
public | tb_sysadm | table | sysadm
public | tb_sre | table | sre
\c db_zero mozart
Agora a mensagem informa que a conexão foi feita como usuário mozart.
session_user | current_user
--------------+--------------
mozart | mozart
\du mozart
List of roles
Role name | Attributes | Member of
-----------+------------+----------------------
mozart | | {dev,sysadm,masters}
[>] Pode-se também verificar informações de filiação de um role via consulta em catálogo:
SELECT
pg_get_userbyid(roleid) AS membro_de
FROM pg_auth_members
WHERE pg_get_userbyid(member) = 'mozart'
ORDER BY membro_de;
membro_de
-----------
dev
sysadm
masters
76 5. Autenticação e autorização
[>] Para facilitar ainda mais, é possível criar uma função PL/pgSQL:
SELECT fc_member_of('mozart');
fc_member_of
--------------
dev
sysadm
masters
A tabela pertence ao papel grupo sre e mozart não pertence a esse grupo, então ele não tem
permissão alguma nela.
77 5. Autenticação e autorização
\c db_zero bach
SELECT fc_member_of('bach');
fc_member_of
--------------
sre
masters
session_user | current_user
--------------+--------------
bach | bach
[>] Fazer o INSERT que não foi possível com o usuário mozart:
\c db_zero beethoven
SELECT fc_member_of('beethoven');
78 5. Autenticação e autorização
fc_member_of
--------------
masters
dba
session_user | current_user
--------------+--------------
beethoven | beethoven
O usuário não foi bem sucedido em nenhuma tabela, pois não pertence ao grupo de nenhuma
delas e nem tem qualquer permissão.
Os modificadores SESSION (padrão) e LOCAL são os mesmos utilizados pelo comando SET.
LOCAL tem seu efeito apenas dentro de uma transação, enquanto SESSION para toda sessão.
\du dba
List of roles
Role name | Attributes | Member of
-----------+-------------------------+-----------
dba | Superuser, Cannot login | {}
session_user | current_user
--------------+--------------
beethoven | dba
. . .
Dessa vez, não houve erro, pois o papel atual tem o privilégio SUPERUSER, o que lhe dá poderes
absolutos.
RESET role;
session_user | current_user
--------------+--------------
beethoven | beethoven
[>] Testar dentro de uma transação, executar um comando por vez e observar o resultado:
BEGIN;
session_user | current_user
--------------+--------------
beethoven | masters
session_user | current_user
--------------+--------------
beethoven | dba
-- Fechar a transação
COMMIT;
session_user | current_user
--------------+--------------
beethoven | beethoven
A partir do momento em que a transação é fechada, volta ao papel atual que estava antes
dela.
82 5. Autenticação e autorização
\c db_zero bach
session_user | current_user
--------------+--------------
bach | bach
SELECT fc_member_of('bach');
fc_member_of
--------------
sre
masters
\c db_zero beethoven
session_user | current_user
--------------+--------------
beethoven | masters
session_user | current_user
--------------+--------------
beethoven | dba
Através do comando REASSIGN OWNED, podemos transferir a propriedade de todos objetos da base
de dados atual de um usuário para outro.
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+-----------
public | tb_dev | table | beethoven
public | tb_sysadm | table | sysadm
public | tb_sre | table | sre
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+-----------
public | tb_dev | table | beethoven
public | tb_sysadm | table | beethoven
public | tb_sre | table | beethoven
Gerenciamento de permissões
Privilégios
Quando um objeto é criado, é assimilado um dono a ele: normalmente o papel que executou
o comando de criação.
Para a maioria dos tipos de objetos, o estado inicial é que apenas o dono (ou um superusuário)
possa fazer qualquer coisa com o objeto.
Para permitir que outros papéis possam utilizar esse objeto, privilégios têm que ser concedidos.
Os privilégios que são aplicáveis a um objeto em particular variam dependendo do tipo de objeto
(tabela, função, etc.).
Somente o dono do objeto pode modificá-lo ou destruí-lo (DDL).
Um objeto pode ser assimilado a um novo proprietário com o comando ALTER do tipo apropriado
de objeto, e. g. ALTER TABLE.
Superusuários podem sempre fazer isso. No entanto, um papel comum só poderá fazer se for
o atual proprietário do objeto (ou membro de um grupo proprietário) e um membro do novo
papel proprietário.
Tipos de privilégios
SELECT (r)
Permite SELECT em qualquer coluna, ou colunas especificadas por lista, de uma tabela, view
ou sequência.
Também permite o uso de COPY TO.
Esse privilégio é também necessário para referenciar valores existentes de colunas em UPDATE
ou DELETE.
Para sequências, esse privilégio também permite o uso da função currval.
Para grandes objetos (large objects), permite que seja lido.
INSERT (a)
UPDATE (w)
Permite UPDATE de qualquer coluna, ou em colunas especificadas por lista, de uma tabela
especifica.
Na prática, qualquer UPDATE fora do comum vai requerer também o privilégio SELECT, uma vez
86 5. Autenticação e autorização
que as colunas de uma tabela devem ser referenciadas para determinar quais linhas devem ser
atualizadas, e / ou para calcular novos valores para as colunas.
SELECT ... FOR UPDATE e SELECT ... FOR SHARE também requerem esse privilégio em, pelo menos,
uma coluna, em adição ao privilégio SELECT.
Para sequências, este privilégio permite o uso das funções nextval e setval.
Para grandes objetos (BLOBs), este privilégio permite gravar ou truncar o objeto.
DELETE (d)
TRUNCATE (D)
REFERENCES (x)
Para criar uma restrição (constraint) chave estrangeira (foreign key ), é necessário ter este
privilégio em ambas as colunas referenciadas.
O privilégio deve ser concedido para todas as colunas de uma tabela ou apenas em específicas.
TRIGGER (t)
CREATE (C) Para bases de dados, permite novos esquemas serem criados internamente.
Para esquemas, permite que novos objetos sejam criados dentro dele.
Para renomear um objeto que já existe, deve ser o proprietário do objeto e ter este privilégio
no esquema que o contém.
Para tablespaces, permite que tabelas, índices e arquivos temporários sejam criados dentro
do tablespace. Além disso, permite que bases de dados seja criadas com o tablespace como
padrão.
Se revogado este privilégio, não altera a localização de objetos existentes.
CONNECT (c)
TEMPORARY/TEMP (T)
EXECUTE (X)
Permite o uso de uma função especificada e o uso de quaisquer operadores que são implemen-
tados no topo da função.
É o único tipo de privilégio aplicável a funções.
USAGE (U)
Para linguagens procedurais, permite o uso de uma linguagem especificada para criação de
funções.
Para esquemas, permite acesso a objetos (levar em conta também os privilégios dos próprios
objetos).
Essencialmente, isso permite que sejam feitas buscas em objetos dentro do esquema.
Sem essa permissão, ainda é possível ver os nomes de objetos, e. g. consultando tabelas de
sistema.
Também, após revogar essa permissão, backends existentes podem ter statements que tiveram
feito previamente essa busca, então isso não é completamente seguro para prevenir acessos a
objetos.
Para sequências, este privilégio permite o uso das funções currval e nextval.
Para tipos e domínios, este privilégio permite o uso do tipo ou domínio na criação de tabelas,
funções e outros objetos de esquema.
Observe que ele não controla o uso geral de tipo, tal como valores de tipo aparecendo em
consultas, apenas previne que objetos criados dependam desse tipo.
O principal propósito do privilégio é controlar que usuários criem dependências em um tipo, o
que poderia impedir o proprietário de alterar o tipo mais tarde.
Para foreign-data wrappers, este privilégio os habilita a criar novos servidores que utilizem
dados externos encapsulados (foreign-data wrapper ).
Para servidores, este privilégio permite criar, alterar e descartar usuário de seu próprio mapea-
mento de usuários associados com o servidor.
Também habilita a consultar as opções do servidor e mapeamentos de usuários associados.
Gerenciando privilégios
GRANT
Concede privilégios de acesso a objetos para papéis.
REVOKE
Revoga (tira) privilégios de acesso a objetos de papéis.
Permissões em tabelas
\c db_zero beethoven
\z tb_dev
ou
\dp tb_dev
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+----------+-------+-------------------+-------------------+----------
public | tb_dev | table | | |
[>] Como superusuário, dar permissão de SELECT na tabela tb_dev para o role masters:
RESET role;
has_table_privilege
---------------------
t
has_table_privilege
---------------------
f
\z tb_dev
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+----------+-------+---------------------+-------------------+----------
public | tb_dev | table | dev=arwdDxt/dev+ | |
| | | masters=r/dev | |
91 5. Autenticação e autorização
SELECT
unnest(relacl) AS privilegios
FROM pg_class
WHERE relname = 'tb_dev';
privilegios
---------------------
dev=arwdDxt/dev
masters=r/dev
Primeira linha: role dev tem todos os privilégios que foram concedidos para ele por ser dono
do objeto.
Segunda linha: role master tem o privilégio de SELECT (r) que foi concedido por dev (ou um
superusuário).
Quando consultamos privilégios de um objeto, eles vêm com uma notação própria, conforme
segue abaixo:
campo1
--------
1
[>] Dar privilégios nas tabelas tb_sysadm e tb_sre para masters e, por fim, voltar a ser o
role beethoven:
SELECT
relname,
relacl AS privilegios
FROM pg_class
WHERE relname IN ('tb_dev', 'tb_sysadm', 'tb_sre')
ORDER BY relname;
relname | privilegios
-----------+---------------------------------------
tb_dev | {dev=arwdDxt/dev,masters=r/dev}
tb_sysadm | {sysadm=arwdDxt/sysadm,masters=ard/sysadm}
tb_sre | {sre=arwdDxt/sre,masters=w/sre}
93 5. Autenticação e autorização
-- INSERT em tb_sysadm
INSERT INTO tb_sysadm (campo1) VALUES (9885);
-- UPDATE em tb_sysadm
UPDATE tb_sysadm SET campo1 = 0 WHERE campo1 = 9885;
-- UPDATE em tb_sre
UPDATE tb_sre SET campo1 = 0 WHERE campo1 > 100;
[>] Dar O privilégio de SELECT na tabelas tb_sre e, por fim, voltar a ser o role beethoven:
privilegios
----------------------------------------
{sre=arwdDxt/sre,masters=rw/sre}
SELECT
relname,
relacl AS privilegios
FROM pg_class
WHERE relname IN ('tb_dev', 'tb_sysadm', 'tb_sre')
ORDER BY relname;
relname | privilegios
-----------+-----------------------
tb_dev | {dev=arwdDxt/dev}
tb_sysadm | {sysadm=arwdDxt/sysadm}
95 5. Autenticação e autorização
tb_sre | {sre=arwdDxt/sre}
Permissões em colunas
\z tb_dev
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+----------+-------+---------------------+----------------------+----------
public | tb_dev | table | dev=arwdDxt/dev | campo2: +|
| | | | masters=arwx/dev |
Nota-se, em “Column privileges”, que campo2 tem todos privilégios (de uma coluna) para
masters.
RESET role;
campo2
--------
1
97 5. Autenticação e autorização
[>] Função has_column_privilege para verificar o privilégio SELECT nas colunas campo1 e
campo2:
-- campo1
SELECT has_column_privilege(current_user, 'tb_dev', 'campo1', 'SELECT');
has_column_privilege
----------------------
f
-- campo2
SELECT has_column_privilege(current_user, 'tb_dev', 'campo2', 'SELECT');
has_column_privilege
----------------------
t
Após a execução do comando, nenhum usuário (exceto super usuários) poderá se conectar a
este banco.
No entanto, as conexões existentes são mantidas. Assim, caso queira “expulsar” um usuário
forçadamente, existe a função de sistema pg_terminate_backend, que tem como parâmetro o pid
do processo da conexão que deseja terminar.
\l db_zero
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
---------+----------+----------+-------------+-------------+-----------------------
db_zero | postgres | UTF8 | pt_BR.UTF-8 | pt_BR.UTF-8 | =T/postgres +
| | | | | postgres=CTc/postgres+
| | | | | dev=Cc/postgres
has_database_privilege
------------------------
f
RESET role;
\c db_zero vivaldi
\du vivaldi
List of roles
Role name | Attributes | Member of
-----------+------------+-----------------
vivaldi | | {dev,masters}
Por vivaldi ser membro do grupo dev, sua conexão foi bem sucedida.
\c db_zero beethoven
\c db_zero aluno
SELECT
unnest(datacl) AS permissoes
FROM pg_database
WHERE datname = 'db_zero';
permissoes
-----------------------
=Tc/postgres
postgres=CTc/postgres
dev=C/postgres
\c db_zero beethoven
\c db_zero vivaldi
101 5. Autenticação e autorização
Permissões em schemas
SELECT
r.rolname role_proprietario
FROM pg_namespace AS n
INNER JOIN pg_roles AS r
ON (r.oid = n.nspowner)
WHERE n.nspname = 'sc_dev';
role_proprietario
-------------------
dev
\dn sc_dev
List of schemas
Name | Owner
----------+-------
sc_dev | dev
\du vivaldi
List of roles
Role name | Attributes | Member of
-----------+------------+-----------------
vivaldi | | {dev,masters}
\dt sc_dev.*
List of relations
Schema | Name | Type | Owner
----------+----------+-------+---------
sc_dev | tb_teste | table | vivaldi
\c db_zero beethoven
INSERT mal sucedido porque, apesar de ter todos privilégios na tabela, não tinha no schema
ao qual pertence.
103 5. Autenticação e autorização
\c db_zero vivaldi
\dn+ sc_dev
List of schemas
Name | Owner | Access privileges | Description
----------+-------+-------------------+-------------
sc_dev | dev | dev=UC/dev + |
| | masters=U/dev |
\c db_zero beethoven
[>] Agora, com as devidas permissões, fazer o INSERT na tabela tb_teste dentro do schema
sc_dev:
campo1
--------
1
104 5. Autenticação e autorização
Privilégios padrão
A forma padrão de privilégios a objetos. Ou seja, mesmo para objetos criados posteriormente,
os privilégios serão mantidos.
\c db_zero postgres
[>] Alterar os privilégios padrão no schema sc_dev para conceder SELECT nas tabelas para
o role master:
SELECT
n.nspname||'.'||c.relname AS tabela,
relacl AS privilegios
FROM pg_class AS c
INNER JOIN pg_namespace AS n
ON (n.oid = c.relnamespace)
WHERE n.nspname = 'sc_dev'
ORDER BY relname;
tabela | privilegios
--------------------+---------------------------------------------
sc_dev.tb_teste | {dev=arwdDxt/dev,masters=arwdDxt/dev}
sc_dev.tb_teste2 | {dev=arwdDxt/dev,masters=r/dev}
sc_dev.tb_teste3 | {dev=arwdDxt/dev,masters=r/dev}
[>] Permissões para objetos posteriores criados pelo role dev para o role master:
\dn sc_dev*
List of schemas
Name | Owner
-----------+-------
sc_dev | dev
sc_dev2 | dev
session_user | current_user
--------------+--------------
postgres | dev
\dt sc_dev2.*
List of relations
Schema | Name | Type | Owner
-----------+-----------+-------+-------
sc_dev2 | tb_dev2 | table | dev
\dt
List of relations
Schema | Name | Type | Owner
--------+-----------+-------+-------
public | tb_dev | table | dev
public | tb_dev2 | table | dev
public | tb_sysadm | table | sysadm
public | tb_sre | table | sre
\c db_zero beethoven
107 5. Autenticação e autorização
session_user | current_user
--------------+--------------
beethoven | beethoven
Autenticação
pg_hba.conf - host-based authentication (autenticação baseada em máquina)
No PostgreSQL, cada nova conexão recebida precisa passar por uma etapa de autenticação
antes de ser permitido o acesso aos dados do banco de dados de destino.
Essa autenticação é baseada em diversas configurações, permissões de usuário e em uma
tabela de regras contida no arquivo pg_hba.conf.
As conexões ao PostgreSQL são estabelecidas via socket. Para fins de autenticação, conexões
do tipo UNIX local (da família AF_UNIX) são classificadas com o tipo local, enquanto
conexões TCP/IP (tanto IPv4, AF_INET, quanto IPv6, AF_INET6) são classificadas como
host. Além dessa informação, é necessário informar um usuário e um banco de dados.
Com esses dados, o PostgreSQL percorre o arquivo pg_hba.conf de cima a baixo, buscando
uma regra de autenticação que seja válida para aquela conexão. A primeira linha encontrada é
usada para decidir qual método de autenticação será aplicado à nova conexão. Isso é análogo
a como tabelas de firewall e roteamento funcionam, e permite a escrita de regras poderosas
pelo uso de máscaras de rede e regras de aceitação e rejeição.
Linhas iniciadas com uma cerquilha (#) são comentários e, portanto, são ignoradas. Outras
linhas seguem uma estrutura de colunas bem definida e separadas por um ou mais espaços.
Formato de um registro do pg_hba.conf:
Campos do pg_hba.conf
TYPE
Primeira coluna. É o tipo de conexão utilizada que pode ser:
DATABASE
Segunda coluna. Especifica que nome de base(s) de dados combina(m) com o registro.
É possível especificar mais de uma base de dados no registro, separando os nomes por vírgulas.
Também é possível especificar um arquivo separado que contenha uma lista de nomes de bases
de dados.
O nome desse arquivo deve ser precedido com o caractere “@”.
USER
Terceira coluna. Especifica que usuário(s) do banco de dados combina(m) com o registro.
Pode-se especificar mais de um nome de usuário, separando-os por vírgulas.
Um arquivo separado que contenha nomes de usuários pode ser especificado. O nome desse
arquivo deve ser precedido por “@”.
O valor all especifica que combina com todos usuários.
Caso contrário, este é o nome de um papel de banco de dados específico, ou um nome de
grupo precedido por “+”.
ADDRESS
Quarta coluna. Quando o tipo de conexão não for local, caso contrário ela não existirá.
Especifica endereços de máquinas clientes que combinem com esse registro.
Este campo pode conter um hostname, endereço IP, endereço de rede ou uma palavra-chave.
Palavras-chave
METHOD
Define o método de autenticação utilizado.
• trust
Aceita a conexão; útil para testes e laboratórios, mas não recomendado para ambientes
de produção;
• reject
Rejeita a conexão; útil para implementar blacklists de configurações indesejáveis.
• scram-sha-256
Autenticação por senha usando o método de encriptação SCRAM-SHA-256;
• md5
Autenticação por senha usando método de encriptação MD5;
• peer
Aceita a conexão caso o usuário do sistema operacional do cliente tenha o mesmo nome
do usuário informado na conexão com o banco de dados; disponível apenas para conexões
do tipo local;
• ident
Semelhante a peer, mas para conexões dos outros tipos; depende de um servidor ident
confiável na rede local;
• ldap
Autentica usando um servidor LDAP;
• cert
Autentica usando certificados SSL de cliente.
[https://www.postgresql.org/docs/current/auth-methods.html] (https://www.postgresql.org/docs/current/
methods.html)
OPTIONS
Após o campo de método de autenticação, podem ter campos na forma nome = valor que
especificam opções para o método de autenticação.
111 5. Autenticação e autorização
[$] Para os nossos testes, vamos alterar o parâmetro password_encryption para scram-sha-256
e liberar que o PostgreSQL escute em todas as interfaces do sistema operacional:
vim ${PGDATA}/postgresql.conf
listen_addresses = '*'
password_encryption = scram-sha-256
pg_ctl restart
cp ${PGDATA}/pg_hba.conf ${PGDATA}/pg_hba.conf.bkp
Criação de regras:
[$] Liberar conexões locais em qualquer base de dados para usuário postgres sem senha e
conexões locais ao banco db_zero. Provenientes de usuários do grupo dev devem fornecer
senha:
cat << EOF > ${PGDATA}/pg_hba.conf && pg_ctl reload && clear && cat ${PGDATA}/pg_hba.conf
local all postgres trust
host db_zero +dev 192.168.56.0/24 scram-sha-256
EOF
-[ RECORD 1 ]--------------
line_number | 1
type | local
database | {all}
user_name | {postgres}
address |
netmask |
auth_method | trust
options |
error |
-[ RECORD 2 ]--------------
line_number | 2
type | host
database | {db_zero}
user_name | {+dev}
address | 192.168.56.0
netmask | 255.255.255.0
auth_method | scram-sha-256
options |
error |
[$] Dado que apenas mozart e vivaldi pertencem a dev. Vamos realizar a tentativa de conexão
com o usuário beethoven:
psql: error: could not connect to server: FATAL: no pg_hba.conf entry for host "[local]", user "beethoven", d
Nenhuma das linhas combinou com os dados de conexão; nenhuma entrada para o usuário
beethoven. . .
113 5. Autenticação e autorização
Para cada um, foi pedida senha e, posteriormente, exibida a mensagem de teste “Hello, World”.
[$] Redefinir o pg_hba.conf para aceitar conexões locais (via TCP) provenientes de qualquer
usuário e qualquer banco, mediante autenticação scram-sha-256:
cat << EOF > ${PGDATA}/pg_hba.conf && pg_ctl reload && clear && cat ${PGDATA}/pg_hba.conf
local all postgres trust
host all all 127.0.0.1/32 scram-sha-256
host db_zero +dev 192.168.56.0/24 scram-sha-256
EOF
psql: error: FATAL: no pg_hba.conf entry for host "192.168.56.2", user "beethoven", database "db_zero", SSL o
FATAL: no pg_hba.conf entry for host "192.168.56.2", user "beethoven", database "db_zero", SSL off
Hello, World
O arquivo .pgpass
127.0.0.1:5432:db_one:beethoven:123
# host:port:dbname:user:password
127.0.0.1:5432:*:*:123
Para simplificar a vida do DBA, o PostgreSQL também permite guardar todas as informações
de conexão em um arquivo simples na raiz do diretório do usuário, chamado .pg_service.conf,
e, então, acessar o banco de dados através de um apelido simples.
Assim como .pgpass, pode-se customizar sua localização / nome do arquivo utilizando a
variável de ambiente PGSERVICEFILE.
[$] Então podemos acessá-lo com um comando simplificado, fornecendo apenas o nome do
serviço:
psql service=meubd
117 5. Autenticação e autorização
• name
Nome da política;
• table_name
Nome da tabela;
• FOR
Para qual comando DML (SELECT, INSERT, UPDATE, DELETE) a política será usada; (A palavra-
chave ALL representa todos comandos);
• role_name
Papel (grupo / usuário) de aplicação da política;
• USING (using_expression)
Valida acesso a linhas preexistentes;
\c db_rls;
[>] Permissões:
Terminal 2
\c db_rls admin;
TABLE tb_usuario;
Se a tabela tiver o recurso de RLS habilitado, mas nenhuma política definida, por padrão a
política de negação será assumida.
120 5. Autenticação e autorização
Terminal 1
Com true para USING e WITH CHECK, tudo será permitido para o usuário admin.
Terminal 2
TABLE tb_usuario;
Agora, com uma política criada para o role admin, ele consegue enxergar as linhas.
TABLE tb_usuario;
\c db_rls alice
Mesmo só selecionando as colunas que lhe são permitidas, não foi possível visualizar qualquer
linha na tabela.
Não há política para o role alice.
Terminal 1
[>] Usuários normais podem atualizar seus registros, mas limita que shells um usuário normal
pode configurar:
Terminal 2
[>] Após ter sido criada uma política para visualização de de dados para todos usuários,
tentar novamente:
[>] Tentativa de alice atualizar uma linha que não lhe pertence:
UPDATE tb_usuario
SET (nome_real, shell) = ('Alice Silva', '/bin/fish')
WHERE username = 'alice';
ERROR: new row violates row-level security policy for table "tb_usuario"
UPDATE tb_usuario
SET (nome_real, shell) = ('Alice Silva', '/bin/zsh')
WHERE username = 'alice';
Terminal 1
policyname
---------------------------
po_admin_all_priv_usuario
po_all_view_usuario
po_users_mod_usuario
BEGIN;
Para apagar uma política, é preciso informar em qual tabela foi aplicada.
Como a tabela das políticas foi apagada, as políticas também deixaram de existir.
ROLLBACK;
124 5. Autenticação e autorização
[>] Permissões:
Terminal 2
\c db_rls joana;
TABLE tb_anotacao;
-- Listar os registros
TABLE tb_anotacao;
TABLE tb_anotacao;
Como esperado, o usuário admin é o único que pode ver todos os registros.
Os outros usuários só podem ver os seus próprios nesta tabela.
6
Instâncias PostgreSQL
• Gerenciamento de instâncias
• Arquivos Físicos e OID
128
129 6. Instâncias PostgreSQL
Gerenciamento de instâncias
Conceito de instância / cluster PostgreSQL
initdb
O utilitário initdb tem como objetivo criar clusters (instâncias) de bases de dados PostgreSQL.
Quando executado, ele cria um diretório (PGDATA) contendo os dados iniciais de uma instân-
cia.
Sintaxe:
O systemd é o sistema de inicialização (init system) que foi adotado pelas principais dis-
tribuições Linux da atualidade (provavelmente também pela maioria).
Apesar de opiniões diversas sobre, o systemd trouxe melhorias comparando-se aos seus ante-
cessores, como o SysVinit, por exemplo.
Hoje, o trabalho de gerenciamento de serviços no Linux foi muito facilitado graças ao systemd.
O utilitário systemctl é o responsável por gerenciar serviços dentro do systemd.
Para apenas ver o estado do serviço (status), não é preciso ser o usuário root, diferente do
que acontece com enable, disable, start, stop e restart.
read -p \
'Digite a versão majoritária: ' \
PGMAJOR
130 6. Instâncias PostgreSQL
[$] Listando unit files do systemd e filtrando por postgres e apenas a primeira coluna:
postgresql-13.service
read -p \
'Digite a versão majoritária: ' \
PGMAJOR
Removed /etc/systemd/system/multi-user.target.wants/postgresql-13.service.
131 6. Instâncias PostgreSQL
Nota-se que, na linha “Loaded”, aparece agora “disabled”, pois o serviço foi desabilitado.
pgrep -a postgres
751
751
132 6. Instâncias PostgreSQL
Geralmente usado para aplicar determinadas alterações de configuração que exigem que o
serviço seja reiniciado.
Não causa parada no serviço, faz com que determinadas configurações alteradas sejam apli-
cadas.
A interessante opção ‘--now‘ faz com que além do serviço ser habilitado ele inicie imediata-
mente.
134 6. Instâncias PostgreSQL
Utilizando o pg_ctl
server signaled
Modos de parada
O PostgreSQL tem três modos de parada, cada um com tratamentos diferentes sobre transações
e buffers:
• smart [s]
– Transações: aguarda o término normal das transações, pelo encerramento das
conexões e pela finalização de backups em andamento
– Buffers: escreve os buffers e deixa a instância em estado consistente.
– Tempos: stop lento e start rápido.
– Sinal: SIGTERM (15)
136 6. Instâncias PostgreSQL
• immediate [i]
– Transações: aborta transações em andamento, encerra conexões e backups em
andamento.
– Buffers: descarta buffers sujos e deixa a instância em estado inconsistente, crash-
recovery na próxima vez que o servidor subir através dos logs de transação.
– Tempos: stop rápido e start lento.
– Sinal: SIGQUIT (3)
Como visto acima, cada modo pode ser acionado pelo uso de um sinal diferente enviado ao
postgres/postmaster, o que pode ser feito por meio do comando kill.
Aviso
[$] Testando. . . :
port
------
5433
[$] Parando o cluster de teste e mudando a variável de ambiente PGDATA para o diretório
do cluster de teste:
pg_ctl start
port
------
5433
138 6. Instâncias PostgreSQL
OIDs
OIDs são os identificadores de objetos do PostgreSQL. OIDs são números seriais únicos, por
tipo de objeto, dentro de um cluster.
Bancos de dados, tabelas, índices e tablespaces sempre terão um OID.
Linhas de tabelas terão um OID único em cada tabela, desde que a tabela seja criada com a
opção WITH OIDS.
Embora os OIDs sejam visíveis para usuários e aplicações, não é uma boa prática referir-se
aos objetos pelo OID, deixando a cargo do PostgreSQL administrar esses valores.
A utilidade do OID para o administrador é identificar os objetos dentro do layout físico do
cluster em disco, permitindo algumas ações de manutenção, por exemplo, mover tablespaces
de um diretório para outro ou medir o espaço físico ocupado por um banco de dados ou tabela
(embora o PostgreSQL ofereça funções que retornem esses valores).
Não há utilidade prática para os OIDs de linhas, por isso, a criação de tabelas com OIDs de
linha foi tornada opcional há muitos anos, devendo ser usada para retrocompatibilidade de
sistemas legados.
Para visualizar OIDs, vamos utilizar os catálogos de sistema do PostgreSQL, executando co-
mandos diretamente no psql.
https://www.postgresql.org/docs/current/storage-file-layout.html
• Verifique o diretório “pg_tblspc”, onde existirão links para cada um dos tablespaces-
existentes;
• O número do link é o OID do tablespace correspondente.
Armazenamento físico
Por exemplo, para saber onde estão armazenados os dados do banco “postgres”:
. . .
140 6. Instâncias PostgreSQL
ls ${PGDATA}/main/base/ -lh
total 124K
drwx------ 2 postgres postgres 4.0K 2013-04-21 18:34 1
drwx------ 2 postgres postgres 4.0K 2011-04-16 00:23 11563
drwx------ 2 postgres postgres 4.0K 2013-04-29 09:30 11564
c = tipo composto
i = índice
m = view materializada
p = tabela particionada
r = tabela comum
S = sequência
t = tabela TOAST
v = visão
141 6. Instâncias PostgreSQL
SELECT oid
FROM pg_database
WHERE datname = 'postgres';
oid
-------
16412
\d tb_tmp
Table "public.tb_tmp"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('tb_tmp_id_seq'::regclass)
Indexes:
"tb_tmp_pkey" PRIMARY KEY, btree (id)
Nota-se que, por causa da declaração do campo id, serial é um inteiro com um valor default
atrelado a uma sequence; tb_tmp_id_seq.
Essa tabela também tem um índice; tb_tmp_pkey.
142 6. Instâncias PostgreSQL
SELECT
oid, relfilenode, relname, relkind
FROM pg_class
WHERE relname ~ 'tb_tmp' ORDER BY oid;
ID, arquivo de dados, nome e tipo de relação. Observa-se que até então oid e relfilenode são
iguais.
ls $PGDATA/base/`psql -Aqtc \
"SELECT oid FROM pg_database WHERE datname = 'postgres';"`
. . .
SELECT
oid, relfilenode, relname, relkind
FROM pg_class
WHERE relname ~ 'tb_tmp'
ORDER BY oid;
SELECT
oid, relfilenode, relname, relkind
FROM pg_class
WHERE relname ~ 'tb_tmp'
ORDER BY oid;
Após o TRUNCATE, mudou também o filenode da sequence, por causa da cláusula RESTART
IDENTITY.
144 6. Instâncias PostgreSQL
SELECT
relname "Nome do objeto",
CASE relkind
WHEN 'r' THEN 'tabela comum'
WHEN 'i' THEN 'índice'
WHEN 'S' THEN 'sequência'
WHEN 'v' THEN 'visão'
WHEN 'c' THEN 'tipo composto'
WHEN 't' THEN 'tabela TOAST'
WHEN 'p' THEN 'tabela particionada'
ELSE '---'
END "Tipo de objeto"
FROM pg_class WHERE relfilenode = 16451;
Revisao
1. P: Como são referenciados os objetos do PostgreSQL internamente ?
R:
2. P: É uma boa prática habilitar OID para linhas ?
R:
3. P: Tenho o OID de uma tabela, mas não encontro o arquivo fisico. O que pode estar
acontecendo?
R:
4. P: Tenho um tabela onde armazeno arquivos grandes, mas os filenodes referentes a
minha tabela não condizem com o tamanho dos arquivos armazenados. O que está
ocorrendo ?
R:
7
Configuração
• postgresql.conf
• Formas alternativas de configurar parâmetros
• A view pg_settings
• O comando ALTER SYSTEM
146
147 7. Configuração
postgresql.conf
O postgresql.conf é o arquivo principal de configurações do PostgreSQL.
Seu formato consiste em cada linha válida ter parâmetro = valor.
Comentários são feitos pelo caractere sustenido (#).
Há configurações cujos valores são de memória ou tempo com suas respectivas unidades, que
para memória pode ser em kilobytes ou blocos (cada um de 8kb, normalmente) e para tempo
milissegundos, segundos ou minutos.
Os padrões de unidades são encontrados no catálogo pg_settings, no campo unit, tais como:
s, min, kB, 8kB, ms.
Para memória, o multiplicador é 1024 e não 1000.
Caso desejar, pode ser especificada uma unidade diferente de forma explícita.
Exemplos de unidades de memória: kB (kilobytes), MB (megabytes) e GB (gigabytes).
Unidades válidas de tempo são: ms (milissegundos), s (segundos), min (minutos), h (horas)
e d (dias).
Configuração inválidas no postgresql.conf impedem que o serviço inicie / reinicie.
O arquivo de configuração é relido sempre que o processo principal de servidor recebe um sinal
SIGHUP (que é mais facilmente emitido pelo comando no shell pg_ctl reload).
O processo servidor principal também propaga esse sinal para todos os processos atuais que
estejam rodando, de forma que suas sessões existentes vão já trabalhar com o novo valor.
Alternativamente, é possível mandar o sinal para um único processo diretamente.
Alguns parâmetros podem ser configurados apenas na inicialização do servidor; quaisquer
mudanças no arquivo de configuração serão ignoradas até que o servidor seja reiniciado.
Caso um mesmo parâmetro seja declarado mais de uma vez, será considerado seu último valor.
Referências:
• https://www.postgresql.org/docs/current/config-setting.html
• https://www.postgresql.org/docs/current/runtime-config.html
Directivas include
Sintaxe:
include = 'nome_do_arquivo'
Se o nome do arquivo não estiver em um caminho absoluto, será tomado como referência o
diretório onde está o arquivo referenciador.
É permitido fazer inclusões aninhadas.
A directiva include_if_exists atua da mesma forma que a directiva include, exceto pelo com-
portamento quando o arquivo referenciado não existe ou não pode ser lido. A directiva include
considera isso uma condição de erro, enquanto include_if_exists apenas adiciona uma men-
sagem de log e continua a processar o arquivo referenciador. Por fim, temos a directiva
include_dir, cuja finalidade é incluir não somente um arquivo de configuração, mas sim um
diretório que lerá todos arquivos com a extensão .conf dentro.
SHOW
Sua única função é exibir o valor de uma determinada configuração do PostgreSQL.
SHOW port;
port
------
5432
SET
Altera qualquer parâmetro de configuração que possa ser mudado dentro de uma sessão, além
de sobrescrever qualquer outro meio de configuração.
pg_ctl stop
postgres \
-c log_connections=yes \
-c log_destination='stderr' \
-c port=5433 \
-c log_disconnections=yes \
-c logging_collector=off
O prompt está preso e todas mensagens de log estarão na tela deste terminal.
psql -p 5433
Agora observe no primeiro terminal que nas mensagens de log apareceu algo como:
2021-01-26 09:40:33.738 -03 [1272] LOG: connection received: host=[local]
2021-01-26 09:40:33.739 -03 [1272] LOG: connection authorized: user=postgres database=postgres application_na
pg_ctl start
[$] A variável de ambiente PGOPTIONS pode ser utilizada para esse propósito no lado do
cliente:
12MB
Isso funciona para qualquer aplicação cliente baseada na libpq, não apenas o psql.
Vale lembrar que só serão aceitos parâmetros ajustáveis em sessões. Além disso, é possível
atribuir um conjunto de configurações de parâmetros para um usuário ou base de dados.
Sempre que uma sessão é iniciada, as configurações padrões para o usuário e base de dados
são carregadas.
Os comandos ALTER ROLE e ALTER DATABASE, respectivamente, são usados para isso.
Configurações por base de dados sobrescrevem tudo vindo de linha de comando ou do
arquivo de configuração. Por sua vez, são substituídas pelas configurações por usuário. Ambas
são substituídas pelas configurações por sessão.
152 7. Configuração
A view pg_settings
É a visão (view ) de sistema que tem informações sobre as configurações do PostgreSQL.
Suas informações vêm da função pg_show_all_settings.
https://www.postgresql.org/docs/current/view-pg-settings.html
[>] Pela view pg_settings, verificar valor, contexto, tipo de valor e unidade:
SELECT
setting, -- Valor
context, -- Contexto
vartype, -- Tipo de dado
unit -- Unidade
FROM pg_settings
WHERE name = 'shared_buffers';
SHOW application_name;
application_name
------------------
psql
SELECT
setting, context, vartype, unit
FROM pg_settings
WHERE name = 'application_name';
Para a parte prática, recomenda-se estar conectado a dois terminais, sendo que o primeiro
dentro do psql e o segundo no shell do sistema operacional como usuário postgres.
SHOW port;
port
------
5432
pg_ctl restart -m f
Isso fará com que o terminal onde estava o shell do psql seja desconectado.
psql -p 5433
155 7. Configuração
cat $PGDATA/postgresql.auto.conf
cat $PGDATA/postgresql.auto.conf
156
157 8. Arquitetura e funcionamento interno do PostgreSQL
Fundamentos da arquitetura
Uma sessão do PostgreSQL consiste nos seguintes processos (programas):
• backend
Um processo servidor que gerencia os arquivos físicos do banco de dados.
Aceita conexões ao banco, vindas de aplicações clientes, e executa ações em prol desses
clientes. Antigamente, esse aplicativo servidor se chamava postmaster, hoje, é simples-
mente chamado de postgres.
• frontend
A aplicação cliente do usuário que executa operações na base de dados.
Aplicações clientes podem ser de natureza diversificada: um cliente pode ser uma ferra-
menta em modo texto, um aplicativo gráfico, um servidor web (como com PHP, Django,
Ruby On Rails, JBoss, etc), ou uma ferramenta especializada em manutenção de banco
de dados.
Alguns aplicativos clientes são distribuídos com o próprio PostgreSQL.
libpq
Blocos / páginas
Os arquivos de dados (data files) e os logs de transação (WAL) do PostgreSQL são divididos
em páginas (blocos), cujo padrão é 8kb, que respectivamente configura-se nos parâmetros
block_size e wal_block_size.
Esses parâmetros são de contexto interno (internal), seus valores só podem ser alterados na
compilação (opções --with-blocksize --with-wal-blocksize do configure).
Tamanhos maiores de blocos de arquivos costumam ser mais efetivos em data warehouses.
Escritas e leituras em disco são feitas por páginas, a não ser quando o parâmetro full_page_writes
estiver desabilitado.
Cada chamada (system call) de fsync (sincronia de arquivos em disco), então, vai trabalhar
com pedaços de 8kb.
Os processos do PostgreSQL
O PostgreSQL tem, para suas diferentes tarefas, diferentes processos rodando, cada um
cumprindo seu papel.
Esses processos são iniciados e parados pelo processo principal, que é o postgres.
Eles também têm a opção de se anexarem à memória compartilhada e se conectarem a bases
de dados internamente.
Outros processos também podem ser iniciados na hora que o PostgreSQL inicializa pela in-
clusão de um nome de módulo no parâmetro shared_preload_libraries.
pgrep -a postgres
postgres
É o nome do processo principal, que cria um novo processo servidor para cada conexão es-
tabelecida e faz um fork de si mesmo. Cada um desses processos servidores chamamos de
backend.
Também conhecido como processo supervisor, gerencia uma coleção de bases de dados de uma
instância, cuja coleção, que é um agrupamento, é chamada de cluster de banco de dados.
O processo supervisor gerencia outros processos servidores (backends), bem como outros tipos
de processos.
archiver
autovacuum launcher
autovacuum worker
É o processo responsável por gravar as páginas sujas (dirty pages) dos shared buffers nos data
files.
Ele tenta fazer com que processos backends não tenham que aguardar por escritas em disco,
pois o background writer fará isso.
O bgwriter fica permanentemente verificando essas páginas sujas nos shared buffers, utilizando
o algoritmo LRU para descartar as menos utilizadas recentemente e, assim, decidir quais blocos
devem ser gravados.
O parâmetro de configuração bgwriter_delay define os intervalos de tempo (em ms), objeti-
vando evitar picos de I/O.
O parâmetro bgwriter_lru_maxpages limita a quantidade máxima de páginas que podem ser es-
critas por rodada. A cada rodada, o bgwriter define a quantidade de páginas a serem gravadas,
baseado na média das paginas limpas em rodadas anteriores junto com um multiplicador que
é definido no parâmetro de configuração bgwriter_lru_multiplier. Esse multiplicador permite
que o número previsto seja igual à média anterior (1.0), menor que a média anterior (< 1.0)
ou maior que a média anterior (> 1.0).
Utilizar o multiplicador protege contra picos de atividade do banco de dados, pois mais buffers
limpos serão deixados no cache.
O número máximo de buffers em bgwriter_lru_maxpages será, então, sempre respeitado.
Valores muito baixos nos parâmetros do background writer reduzem o I/O causado por ele,
mas tornará mais provável que os processos de sessão façam escritas, prejudicando as consultas
dos clientes.
O PostgreSQL foi projetado pensando na utilização do cache de arquivos do sistema opera-
cional (page cache), por isso shared buffers pequenos costumam obter bom desempenho.
checkpointer
stats collector
logger
walwriter
Gerencia escritas no WAL e evita que processos clientes realizem essa gravação por si mesmos.
Quando são feitas modificações, elas são primeiro escritas nos shared buffers e registros dessas
mudanças vão para o buffer do WAL. As mudanças são gravadas nos segmentos de WAL
quando são efetivadas (committed).
165 8. Arquitetura e funcionamento interno do PostgreSQL
Shared buffers
Shared buffers é a área de memória compartilhada que o PostgreSQL utiliza como buffer /
cache para leitura e escrita.
Seu tamanho é definido pelo parâmetro de configuração shared_buffers, cuja unidade é em
páginas (padrão 8kb). É também possível ajustar de forma mais amigável, utilizando sufixos
de unidades de medidas de dados, e. g. MB, GB.
Ao iniciar o PostgreSQL, esse segmento de memória é totalmente alocado.
Dependendo do mecanismo de memória compartilhada selecionado pelo parâmetro de config-
uração shared_memory_type e em versões mais antigas do PostgreSQL, o sistema operacional
precisará ser configurado para aceitar a quantidade de memória especificada.
Os dados não persistidos em disco (nos data files), que estão nos shared buffers são também
escritos nos logs de transação, também conhecidos como WAL (write ahead log). Por meio
desses é garantida a persistência.
Aviso
Um valor muito alto para o parâmetro shared_buffers pode prejudicar a performance, porque
o bgwriter terá que varrer uma quantidade de blocos muito grande, causando maior demora
de execução e ainda por cima fazendo desperdício de recursos desnecessariamente.
Shared buffers é também uma área de memória dividida em páginas (blocos), as quais refletem
os dados do armazenamento.
As páginas dos shared buffers podem ter dois estados:
• Limpa: página que ainda não sofreu modificação. O que ela contém é igual ao seu
correspondente em disco;
• Suja: Página modificada cujo conteúdo não tem correspondência em disco ainda.
Páginas sujas (dirty pages) posteriormente serão gravadas em disco via checkpoint, por um
backend que necessite gravar em uma página que já está suja ou pelo processo background
writer.
Em termos de performance, o ideal é que essas páginas sujas sejam gravadas por checkpoints.
Já o pior cenário se sucede quando um backend precisar fazer uma gravação (view de sistema
pg_stat_bgwriter, coluna buffers_backend).
166 8. Arquitetura e funcionamento interno do PostgreSQL
Depois de se tornarem sujos, os blocos devem ser gravados em armazenamento, que acontece
em três situações possíveis:
• Por checkpoint: todas as páginas sujas são gravadas em armazenamento a cada check-
point;
• Por um backend: quando um backend precisa gravar em uma página que já está suja;
Do ponto de vista de desempenho, o ideal é que as páginas sejam gravadas via checkpoints.
Já o pior cenário é quando um backend precisa realizar a gravação.
167 8. Arquitetura e funcionamento interno do PostgreSQL
Parâmetros WAL
• max_wal_size
Tamanho máximo permitido para crescimento do WAL durante checkpoints automáti-
cos.
Esse limite é flexível e o tamanho do WAL pode exceder max_wal_size em circunstâncias
especiais, como cargas pesadas, quando o archive_command falha, quando há atraso no
consumo de WALs por parte de réplicas em uma replicação via streaming ou devido à
configuração wal_keep_size.
Aumentar o valor de max_wal_size melhora a performance de escrita, porém, em uma
eventual recuperação de crash, vai demorar mais.
• min_wal_size
Com o espaço ocupado de WAL permanecendo abaixo de min_wal_size, os arquivos anti-
gos são sempre reclicados para uso posterior em checkpoint em vez de removidos.
Isso pode ser usado para garantir que haja espaço suficiente para que WAL lide com
picos em seu uso, como pode acontecer ao rodar grandes tarefas em lote.
• wal_keep_size
Tamanho mínimo para manter WALs anteriores (que podem ser reciclados).
168 8. Arquitetura e funcionamento interno do PostgreSQL
Checkpoint
Sobre Checkpoint
Checkpoint é o processo de escrita das páginas sujas (dirty pages) dos shared buffers para os
arquivos físicos (data files).
Em caso de crash de sistema, o procedimento de recuperação verifica o último registro de
checkpoint para determinar o ponto no log (também chamado registro redo) de onde deve
partir a operação REDO.
Quaisquer alterações nos data files antes desse ponto são garantidos já em disco.
Após um checkpoint, os segmentos de log anteriores ao que contém o registro REDO não são
mais necessários e podem ser reciclados ou removidos.
Quando o arquivamento de WAL está pronto, os segmentos de log devem ser arquivados antes
de serem reciclados ou removidos.
Parâmetros checkpoint
• checkpoint_timeout
Tempo máximo entre checkpoints feitos de forma automática.
• checkpoint_completion_target
É a fração de tempo de checkpoint_timeout para completar a escrita nos data files.
Parâmetro muito útil para evitar picos de I/O, suavizando a escrita.
• checkpoint_flush_after
Se o volume de dados gravados, durante um checkpoint, for maior que seu valor, tenta
forçar o sistema operacional a fazer essas escritas. Então, limita a quantidade de “dirty
data” (dados modificados) no cache de páginas do kernel, reduzindo a probabilidade
de paralisação quando um fsync é feito no final do checkpoint ou quando o sistema
operacional grava dados em lotes maiores em segundo plano.
Pode resultar em uma redução de latência de transação, mas há casos, especialmente
com cargas maiores que shared_buffers, em que poderá degradar a performance, embora
menor do que o cache de página do sistema operacional.
170 8. Arquitetura e funcionamento interno do PostgreSQL
• checkpoint_warning
Escreve uma mensagem no log se os checkpoints por preenchimento de segmentos de
WAL ocorrerem antes de seu valor.
Não serão gerados avisos (warnings) se o checkpoint_timeout for menor que o parâmetro
checkpoint_warning.
vim ${PGDATA}/postgresql.conf
wal_level = replica
postmaster
[$] Devido ao contexto do parâmetro alterado ser postmaster, será preciso reiniciar o serviço:
pg_ctl restart
while :; do
clear
# Utilizando regex para arquivos
# com 24 caracteres hexadecimais
ls -1 ${PGDATA}/pg_wal | \
awk '/^[0-9,A-F]+$/ {if (length($1) == 24) {print $1} }'
# Cada iteração de loop a cada 3 segundos
sleep 3;
done
171 8. Arquitetura e funcionamento interno do PostgreSQL
[$] [Y] Criação de tabela com muitos registros (também como usuário postgres):
QTD_WAL=`ls -1 ${PGDATA}/pg_wal | \
awk '/^[0-9,A-F]+$/ {if (length($1) == 24) {print $1} }' | wc -l`
echo ${QTD_WAL}
64
1GB
1024
Para garantir que dados sejam gravados em seu respectivos dispositivos de armazenamento,
para que também não permaneçam somente em cache, há uma chamada de sistema (system
call), fsync.
172 8. Arquitetura e funcionamento interno do PostgreSQL
Com fsync habilitado (e deve estar!!!), é possível escolher o método usado para forçar atual-
izações do WAL em disco (dispositivo de armazenamento) com o parâmetro wal_sync_method.
É um parâmetro cujos valores são enumerados e suas opções são:
• open_datasync:
grava em arquivos WAL com open() e a opção O_DSYNC;
• fdatasync:
chama fdatasync() a cada commit. Padrão no Linux;
• fsync: chama fsync() a cada commit;
• fsync_writethrough: chama fsync() a cada commit, forçando write-through de qualquer
cache de escrita de disco, se for suportado;
• open_sync: grava em arquivos WAL com open() e opção O_SYNC.
Cada opção pode ter sua disponibilidade suportada ou não pelo sistema operacional.
O padrão não é necessariamente o ideal, é preciso mudar essa configuração, testar e comparar
para alcançar uma configuração segura contra falhas e ter um melhor desempenho.
Se for selecionada uma opção não suportada pela plataforma, o serviço não iniciará.
Se uma parada abrupta acontecer durante o momento que uma página estiver sendo gravada,
é provável que após a recuperação do arquivo tenham dados novos e antigos misturados.
O PostgreSQL, para evitar que isso aconteça, faz escritas de páginas completas por vez por
padrão, não importa quantos dados serão gravados.
Para poder recuperar corretamente uma página, é necessário armazená-la por completo em
detrimento do aumento da quantidade de dados gravados no WAL.
Aumentando o intervalo entre os pontos de controle (checkpoints) (parâmetro checkpoint_timeout
), reduz o impacto de I/O.
O parâmetro full_page_writes controla essa característica de gravar ou não páginas inteiras.
Desativando full_page_writes, assim como fsync, também aumento o desempenho de escrita.
Tem um risco menor inclusive, mas não é recomendada sua desativação.
173 8. Arquitetura e funcionamento interno do PostgreSQL
Parâmetro de configuração que controla se a efetivação (commit) vai ser síncrona ou assín-
crona, o que significa que quando uma transação só será efetivada após as escritas em disco
do WAL tiverem terminado (modo síncrono).
Quando o commit é assíncrono, a transação é efetivada antes que haja uma garantia que o
WAL seja gravado.
Em muitas situações, desabilitar synchronous_commit para transações que não sejam críticas
melhora o desempenho de escrita, similar a desligar fsync sem os riscos inerentes de corrupção
de dados.
Se o parâmetro synchronous_standby_names for vazio, as únicas configurações significativas serão
on e off; remote_apply, remote_write and local fazem com que tenham o mesmo nível de sin-
cronização que on.
O comportamento de local para todos modos não-off é aguardar pela escrita local do WAL
em disco.
Modo A B C D
remote_apply Sim Sim Sim Sim
on Sim Sim Sim
remote_write Sim Sim
local Sim
off
É o período de tempo que o walwriter “dorme” após gravar no WAL, exceto se for “acordado”
por uma efetivação assíncrona.
immediate shutdown.
E, por último, logical que fornece o necessário para decodificação lógica.
Em um servidor com muitas transações concorrentes, pove haver uma carga de I/O de ar-
mazenamento muito alta, podendo agrupar várias efetivações (commits) em um único fsync
fazendo uso do “commit atrasado”, que aguarda uma quantidade de tempo (commit_delay) ou
de transações (commit_siblings) antes de fazer o fsync e retornar as efetivações.
Por padrão, commit_delay tem seu valor igual a 0 (zero), ou seja, sem espera, então ajusta-se
para um valor desejado em milissegundos.
O parâmetro commit_siblings, de forma similar, configura a quantidade mínima de transações
simultâneas. Ambos reduzem a carga de I/O de escrita de WAL em servidores com muita
concorrência, mas em servidores com pouca concorrência pode gerar atraso desnecessário e
degradar a performance.
[>] Exibir nome, atual configuração, contexto e valores enumerados dos parâmetros:
SELECT
name, setting, context, enumvals
FROM pg_settings
WHERE name IN (
'fsync',
'full_page_writes',
'synchronous_commit',
'wal_level',
'wal_sync_method',
'wal_writer_delay');
Revisao
1. P: A arquitetura do PostgreSQL é baseada em threads?
R:
2. P: Cite 3 processos essenciais para funcionamento do PostgreSQL?
R:
3. P: Qual o papel do WAL no PostgreSQL?
R:
4. P: O que pode disparar um checkpoint ?
R:
5. P: Qual o tamanho de um segmento de log de transação ?
R:
6. P: Qual o tamanho de uma página no PostgreSQL? ?
R:
7. P: O que são os “shared buffers” ?
R:
8. P: Qual a finalidade do “bgwriter” ?
R:
9
MVCC: Multi Version
Concurrency Control
• Sobre MVCC
• Travas (Locks)
176
177 9. MVCC: Multi Version Concurrency Control
Sobre MVCC
Controle de concorrência de várias versões de transações é a forma que o Postgres utiliza para
garantir o ACID.
Seu funcionamento baseia-se em manter a consistência de uma transação enquanto ela existir,
ainda que outras transações incidam sobre a mesma linha simultaneamente.
É a maneira que o Postgres evita que uma transação que fez uma modificação não propague
para uma transação concorrente.
Alguns outros SGBD, em vez disso, implementam a trava completa de linha (full row lock).
Isso faz com que uma linha modificada por uma transação não possa ser vista por outras
transações até que essa termine, o que não é a implementação correta de ACID.
O PostgreSQL implementa o MVCC utilizando trava compartilhada de linha (shared row
lock). Quando uma linha é modificada por uma transação, essa linha pode ser visível por
outra transação concorrente, mas essa outra transação terá acesso à versão original até que a
transação que está modificando a linha finalize, assim, implementando corretamente ACID.
O MVCC faz com que, ao modificar uma linha, ele efetivamente não altere a linha original.
Em vez disso, cria uma nova linha com as alterações requisitadas, que é uma nova versão.
SELECT txid_current();
txid_current
--------------
532
178 9. MVCC: Multi Version Concurrency Control
SELECT txid_current();
txid_current
--------------
534
BEGIN;
SELECT txid_current();
txid_current
--------------
535
O comportamento acima aparenta ser “estranho”, pois como podemos estar enxergando linhas
com XMAX menor que da transação atual? E por que linhas válidas estão sendo com XMAX
preenchido? Esse comportamento ocorre devido a, nesses cenários, o PostgreSQL, ao invés de
criar um terceira versão da tupla no rollback, ele apenas usa um hint bit (“xmax_rolled_back”),
indicando que o XMAX da tupla original não foi efetivado. Por isso, não deve ser considerado.
180 9. MVCC: Multi Version Concurrency Control
Travas (Locks)
No PostgreSQL, travas (locks) podem ser explícitas, ou seja, requisitadas pela aplicação /
cliente, ou ímplícitas.
Uma trava compartilhada (shared lock) é o tipo de trava que permite que outras transações
possam ler uma linha modificada em uma transação que ainda não finalizou, mas não permite
escrita para essas outras.
Por outro lado, uma trava exclusiva (exclusive lock) é o tipo de trava que não permite nem
sequer leitura enquanto a transação não termina.
Deadlocks
oid
-------
16453
SELECT pg_backend_pid();
pg_backend_pid
----------------
959
SELECT pg_backend_pid();
pg_backend_pid
----------------
961
BEGIN;
SELECT txid_current();
txid_current
--------------
551
182 9. MVCC: Multi Version Concurrency Control
BEGIN;
SELECT txid_current();
txid_current
--------------
552
SELECT
pid, locktype, relation, tuple, transactionid, mode, granted
FROM pg_locks
WHERE pid IN (959, 961)
ORDER BY pid;
[>] (A) Modificar uma linha e em seguida exibir todas as linhas da tabela na transação:
campo
-------
2
3
4
5
0
183 9. MVCC: Multi Version Concurrency Control
SELECT
pid, locktype, relation, tuple, transactionid, mode, granted
FROM pg_locks
WHERE pid IN (959, 961)
ORDER BY pid;
[>] (B) Modificar uma linha e, em seguida, exibir todas as linhas da tabela na transação:
campo
-------
1
2
4
5
33
184 9. MVCC: Multi Version Concurrency Control
SELECT
pid, locktype, relation, tuple, transactionid, mode, granted
FROM pg_locks
WHERE pid IN (959, 961)
ORDER BY pid;
Em ambas as conexões, foram feitas modificações que ainda não têm uma linha em comum.
[>] (B) Modificar a mesma linha que foi modificada na outra transação concorrente:
SELECT
pid, locktype, relation, tuple, transactionid, mode, granted
FROM pg_locks
WHERE pid IN (959, 961)
ORDER BY pid;
[>] (A) Aqui também vai ser feita uma tentativa de modificação de uma linha que outra
transação concorrente está modificando:
Ocorreu um deadlock, onde a transação B estava aguardando para obter a trava compartilhada
na linha de valor 1 (que pertencia à transação A) e a transação A esperando para ter a trava
compartilhada da linha de valor 3 (que por sua vez pertence à transação B).
Nota-se que há um conflito, o deadlock, após sua detecção finaliza, a transação, o que significa
que todas suas operações foram perdidas.
Existe um parâmetro de configuração denominado log_lock_waits, que ao ser habilitado registra
em log quando uma transação estiver esperando para obter uma trava a um tempo maior que
deadlock_timeout.
Isso é muito útil para verificar problemas de performance causados por deadlocks.
SELECT
pid, locktype, relation, tuple, transactionid, mode, granted
FROM pg_locks
WHERE pid IN (959, 961)
ORDER BY pid;
TABLE tb_foo;
campo
-------
2
4
5
33
11
COMMIT;
10
Rotinas de manutenção
• Vacuum
• Analyze
• Autovacuum
• Vacuum freeze
187
188 10. Rotinas de manutenção
Vacuum
No modelo MVCC, certas operações criam tuplas mortas em tabelas. Então, é preciso conter
o acúmulo dessas, pois ocupam espaço e fazem com que a tabela tenha um tamanho maior
que efetivamente tem em dados (tuplas vivas). A existência de tuplas mortas em uma tabela
é conhecida como inchaço ou, em inglês, bloat.
Operações que geram tuplas mortas:
• DELETE;
• UPDATE;
• INSERT (apenas quando uma transação é cancelada: ROLLBACK).
O comando VACUUM do PostgreSQL é utilizado para vacumização manual, ou seja, feito de uma
forma não automatizada, como é o processo de autovacuum, que será explicado mais adiante.
O comando VACUUM tem que processar cada tabela em uma base pelos seguintes motivos:
Vacumização padrão
• C) Foi feita a vacumização padrão na tabela e suas tuplas mortas (vermelho) foram
remarcadas como reutilizáveis (azul);
• D) Após a vacumização padrão, a tabela recebe mais dois registros via INSERT, reaprovei-
tando tuplas que estavam marcadas para reutilização. Ainda sobrou uma tupla
marcada como reutilizável para um futuro INSERT.
190 10. Rotinas de manutenção
Vacumização completa
• D) Após a vacumização completa, a tabela recebe mais dois registros via INSERT e
esses foram para o final.
Mapa de visibilidade
A vacumização mantém um mapa de visibilidade pra cada tabela para manter o controle de
que páginas contêm apenas tuplas que devem ser visíveis para todas transações ativas (e todas
transações futuras, até a página ser novamente modificada).
Isso tem dois propósitos. Primeiro, a vacumização por si só pode pular tais páginas na próxima
vez que rodar, desde que não tenha nada para limpar. Segundo, permite ao PostgreSQL
responder algumas consultas usando apenas índices, sem referenciar a tabela subjacente.
Desde que índices PostgreSQL não contenham informação de visibilidade de tuplas, uma
pesquisa normal de índice busca a pilha de tupla para cada entrada de índice correspondente,
para verificar se deve ser visto pela transação atual. Uma busca index-only, por outro lado,
verifica o mapa de visibilidade primeiro.
Se é todas as tuplas na página são visíveis, a busca em pilha pode ser ignorada.
Isso é mais perceptível em grandes conjuntos de dados, onde o mapa de visibilidade pode
impedir os acessos ao disco.
O mapa de visibilidade é muito menor do que a pilha, de modo que pode ser facilmente
armazenado em cache, mesmo quando a pilha é muito grande.
Freeze
Como já visto, no modelo MVCC o Postgres utiliza a id de transação (xid) para definir a
visibilidade das linhas.
Essa id de transação é um número inteiro de 32 bits, que limita a 4 bilhões de transações até
zerar.
Zerando essas transações, pode causar transtornos, pois indicaria que todas as linhas estariam
no futuro e não estariam mais visíveis.
193 10. Rotinas de manutenção
Para evitar isso, antecedendo o limite alcançado, o vacuum marca as tuplas com uma xid
especial chamada FroozenXID, indicando que essa tupla está no passado.
Ao executar o VACUUM, todas tuplas cuja idade (em número de transações) maior que o
parâmetro vacuum_freeze_min_age terão suas xids substituídas pelo FroozenXID.
Por padrão, o vacuum apenas analisa páginas onde existam tuplas mortas, mas é preciso que
a análise da tabela seja completa quando alcança uma determinada idade, cuja definição está
no parâmetro de configuração vacuum_freeze_table_age.
O parâmetro autovacuum_freeze_max_age define o limite de idade para execução de vacuum em
uma tabela, mesmo que o autovacuum esteja desabilitado.
O comando VACUUM
https://www.postgresql.org/docs/current/sql-vacuum.html
Sintaxe:
FULL [ boolean ]
FREEZE [ boolean ]
VERBOSE [ boolean ]
ANALYZE [ boolean ]
DISABLE_PAGE_SKIPPING [ boolean ]
SKIP_LOCKED [ boolean ]
INDEX_CLEANUP [ boolean ]
TRUNCATE [ boolean ]
PARALLEL integer
• FULL
Vacumização “completa”, recupera espaço, recria índices, mas leva mais tempo e exige
trava exclusiva de tabela. Precisa de espaço extra, pois é feita uma nova cópia da
tabela e não libera a cópia antiga até que a operação termine. Deve apenas ser usado
194 10. Rotinas de manutenção
• FREEZE
Faz a vacumização com “congelamento” agressivo de tuplas.
Equivalente a realizar a vacumização com o parâmetro. vacuum_freeze_min_age = 0;
• VERBOSE
Modo verboso;
• ANALYZE
Atualiza estatísticas usadas pelo planejador para determinar o caminho mais eficiente
para executar uma consulta;
• DISABLE_PAGE_SKIPPING
Desabilita ignorar páginas para fins de visibilidade.
Utilizar esta opção em casos de suspeita do conteúdo do mapa de visiblidade;
• SKIP_LOCKED
Faz com que não aguarde por qualquer trava conflitante seja liberada. Se isso acontecer,
a relação será ignorada, e o VACUUM passará para a próxima;
• INDEX_CLEANUP
Remoção de entradas de índices que apontam para tuplas mortas;
• TRUNCATE
Trunca todas as páginas vazias no fim da tabela, liberando espaço;
• PARALLEL
Executa as fases de VACUUM de limpeza de índices em paralelo, usando um número inteiro
de background workers;
• table_name
O nome (opcionalmente qualificado de esquema) de uma tabela específica para vacumiza-
ção;
• column_name
O nome de uma coluna específica para analisar. Por padrão, todas as colunas. Se uma
lista de colunas for especificada, ANALYZE está implícito.
195 10. Rotinas de manutenção
Para evitar picos de I/O durante a execução do vacuum, é possível definir um custo máximo
através do parâmetro de configuração vacuum_cost_limit e um período de pausa definido no
parâmetro de configuração vacuum_cost_delay.
Esse custo é baseado nos valores das seguintes opções:
• vacuum_cost_page_hit
Custo estimado para fazer limpeza de um buffer encontrado no cache.
Representa o custo para travar o pool, procurar a tabela hash e varrer o conteúdo da
página;
• vacuum_cost_page_miss
Similar ao item anterior, mas para buffers que terão de ser lidos em disco;
• vacuum_cost_page_dirty
Similar ao item vacuum_cost_page_hit, mas para páginas que foram modificadas, pois
existirá um I/O extra para despejar o buffer em disco.
• maintenance_work_mem
Define o limite de memória utilizado para cada operação. Caso o valor seja excedido, o
processo continuará com arquivos temporários.
• max_worker_processes
Número máximo de processos background que o sistema suportará.
Ao mudar este parâmetro, é preciso considerar também ajustar os parâmetros
max_parallel_workers, max_parallel_maintenance_workers e
max_parallel_workers_per_gather.
• max_parallel_workers_per_gather
Número máximo de workers que podem ser iniciados por um único Gather ou Gather
Merge node.
Se este parâmetro for denido como zero, desabilita consultas paralelas.
• max_parallel_workers
Número máximo de workers que o sistema pode suportar para operações paralelas.
Ao mudar seu valor, deve-se considerar alterar também max_parallel_maintenance_workers
e max_parallel_workers_per_gather.
Se mudar seu valor para maior que max_worker_processes, não terá efeito, pois workers
paralelos vêm do pool de workers estabelecido nesse parâmetro.
• max_parallel_maintenance_workers
O processo de vacuum pode ser paralelizado por meio da opção PARALLEL, que permite que
uma rotina de vacuum possa ser executada de forma mais rápida, distribuindo trabalho
196 10. Rotinas de manutenção
oid | relfilenode
-------+-------------
24750 | 24750
SELECT pg_size_pretty(pg_relation_size('tb_vacuum'));
pg_size_pretty
----------------
35 MB
198 10. Rotinas de manutenção
[>] Execução de VACUUM com TRUNCATE, paralelo com 2 workers e modo verboso:
. . .
oid | relfilenode
-------+-------------
24750 | 24750
Ainda iguais. . .
SELECT pg_size_pretty(pg_relation_size('tb_vacuum'));
pg_size_pretty
----------------
24 MB
Após a execução do VACUUM com TRUNCATE, as tuplas mortas que estavam no final foram elimi-
nadas, resultando em uma tabela menor.
SELECT pg_size_pretty(pg_relation_size('tb_vacuum'));
pg_size_pretty
----------------
73 MB
199 10. Rotinas de manutenção
. . .
SELECT pg_size_pretty(pg_relation_size('tb_vacuum'));
pg_size_pretty
----------------
24 MB
A tabela foi totalmente reescrita pelo VACUUM FULL, resultando em seu tamanho como uma
tabela desfragmentada.
oid | relfilenode
-------+-------------
24750 | 24755
Agora, notamos que o relfilenode mudou, pois com o VACUUM FULL a tabela foi totalmente ree-
scrita criando um novo filenode e apagando o antigo ao término da operação.
200 10. Rotinas de manutenção
Analyze
O comando ANALYZE faz estatísticas sobre o conteúdo de tabelas na base de dados e armazena
os resultados no catálogo de sistema pg_statistic.
Subsequentemente, o planejador de consultas usa essas estatísticas para determinar o mais
eficiente plano de execução.
Sem parâmetros, ANALYZE examina todas as tabelas na base de dados atual.
Com um parâmetro, examina apenas aquela tabela.
Há também a possibilidade de fornecer uma lista de nomes de colunas. Nesse caso, as estatís-
ticas serão coletadas apenas para elas.
O parâmetro default_statistics_target define a quantidade de linhas de amostras ao coletar
para estatísticas por tabela. Essa quantidade é seu valor multiplicado por 300, sendo seu valor
padrão 100, então são 30000 (trinta mil) linhas de amostra por padrão.
Quanto maior seu valor, aumentará o tempo necessário para executar o ANALYZE, mas em
compensação aumentará a qualidade de estimativa do planejador de consultas.
Aviso
Sempre que for alterada significativamente a distribuição dos dados dentro de uma tabela,
rodar o comando ANALYZE é fortemente recomendado.
ANALYZE (ou VACUUM ANALYZE) atualiza as estatísticas da tabela.
Sem estatísticas obsoletas, o planejador de consultas toma decisões ruins, causando redução
de performance.
Se o daemon autovacuum estiver habilitado, ele deve fazer o ANALYZE automaticamente.
201 10. Rotinas de manutenção
SET default_statistics_target = 1;
[>] Verificando no catálogo pg_stats se existe algum registro para a tabela criada:
count
-------
0
SET default_statistics_target = 2;
[>] Verificando no catálogo pg_stats se existe algum registro para a tabela criada:
count
-------
1
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=107800.30..107800.31 rows=1 width=8) (actual time=655.477..657.230 rows=1 loops=1)
-> Gather (cost=107800.08..107800.29 rows=2 width=8) (actual time=655.315..657.226 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=106800.08..106800.09 rows=1 width=8) (actual time=650.688..650.688 rows=
-> Parallel Seq Scan on tb_teste_stat (cost=0.00..106748.00 rows=20833 width=0) (actual time=
Filter: ((campo % 17) = 0)
Rows Removed by Filter: 3137520
Planning Time: 0.111 ms
Execution Time: 657.264 ms
SET default_statistics_target = 1;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=107800.60..107800.61 rows=1 width=8) (actual time=4500.159..4502.102 rows=1 loops=1
-> Gather (cost=107800.38..107800.59 rows=2 width=8) (actual time=4500.041..4502.095 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=106800.38..106800.39 rows=1 width=8) (actual time=4494.869..4494.870 row
-> Parallel Seq Scan on tb_teste_stat (cost=0.00..106748.30 rows=20833 width=0) (actual time=
Filter: ((campo % 17) = 0)
Rows Removed by Filter: 3136695
Planning Time: 0.095 ms
Execution Time: 4502.136 ms
Com uma quantidade menor de amostras, o planejador de consultas faz uma execução pobre
e, consequentemente, a performance é degradada.
205 10. Rotinas de manutenção
Autovacuum
O autovacuum é um recurso opcional, porém altamente recomendado para automatizar pro-
cessos de vacuum e analyze nas tabelas, assim marcando tuplas mortas como reutilizáveis e
atualizando estatísticas.
O processo de autovacuum busca por tabelas que tenham sofrido por grandes escritas DML
(INSERT, UPDATE ou DELETE).
Além do parâmetro de configuração autovacuum, o parâmetro track_counts também precisa es-
tar habilitado, devido ao autovacuum utilizar a facilidade de coleção de estatístias.ounts para
buscar por tabelas do postgresql.conf habilitadas. O daemon autovacuum é composto por
múltiplos processos. O processo daemon persistente, chamado autovacuum launcher, é re-
sponsável por criar os processos autovacuum workers para todas bases de dados.
O autovacuum launcher distribui o trabalho ao longo do tempo, tentando iniciar um autovac-
uum worker em cada banco de dados a cada autovacuum_naptime segundo. Sendo assim, se a
instância tem N bases de dados, um novo worker será executado a cada autovacuum_naptime/N
segundos.
A quantidade de workers para rodar ao mesmo tempo é definida por autovacuum_max_workers.
Caso exista mais bases de dados do que o valor definido em autovacuum_max_workers, a próxima
base a ser processada ocorrerá assim que um worker que já estiver rodando terminar.
Cada worker, no banco de dados em que estiver atuando, vai verificar cada tabela executando
VACUUM e / ou ANALYZE conforme necessário.
Como dica de ajuste de autovacuum_max_workers, considere o número de grandes tabelas + 1,
pois, mesmo que os processos workers fiquem ocupados durante muito tempo, sempre haverá
um processo sobrando para outras.
O parâmetro de configuração log_autovacuum_min_duration pode ser usado para monitorar a
atividade de autovacuum.
Pode-se ter mais de um autovacuum worker atuando na mesma base de dados, porém, cada
worker tenta não repetir o que já foi feito por outros workers.
A quantidade de workeres não é contabilizada para os limites max_connections ou
superuser_reserved_connections.
Tabelas cujo valor no campo relfrozenxid do catálogo pg_class for maior que autovacuum_freeze_max_age
transações antigas são sempre vacumizadas. Isso também se aplica para aquelas tabelas
cujo tempo máximo de congelamento tenha sido modificado via parâmetros de armazena-
mento. Se o número de tuplas obsoletas desde o último vacuum exceder o “vacuum thresh-
old”, a tabela será vacumizada.
O autovacuum faz uso de travas (locks) SHARE UPDATE EXCLUSIVE, que normalmente
só conflitam com operações de alteração de estruturas de objetos. Ainda assim, se algum
comando precisar desse tipo de lock, o autovacuum é interrompido e a trava liberada.
As operações de autovacuum (vacuum e analyze) têm limitação de memória definida pelo
valor do parâmetro de configuração autovacuum_work_mem. Se esse valor for insuficiente, será
necessário criar arquivos temporários, resultando em mais I/O, o que pode causar degradação
206 10. Rotinas de manutenção
de desempenho.
• autovacuum_vacuum_threshold
Quantidade mínima de linhas atualizadas ou removidas para disparar um vacuum na
tabela. Padrão 50;
• autovacuum_analyze_threshold
Quantidade mínima de linhas atualizadas ou removidas para disparar um analyze na
tabela. Padrão 50;
• autovacuum_analyze_scale_factor
Fração da quantidade de tuplas da tabela para somar com autovacuum_analyze_threshold
e, então, disparar um analyze na tabela. Padrão 0.1.
vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples
Onde:
SHOW autovacuum_naptime;
autovacuum_naptime
--------------------
1min
207 10. Rotinas de manutenção
SELECT pg_reload_conf();
pg_reload_conf
----------------
t
SHOW autovacuum_naptime;
autovacuum_naptime
--------------------
3s
SELECT
(s1.setting::REAL + s2.setting::REAL * c.reltuples::REAL)
AS "vacuum threshold"
FROM pg_settings AS s1, pg_settings AS s2, pg_class AS c
WHERE s1.name = 'autovacuum_vacuum_threshold'
AND s2.name = 'autovacuum_vacuum_scale_factor'
AND c.relname = 'tb_teste_threshold';
208 10. Rotinas de manutenção
vacuum threshold
------------------
200050
209 10. Rotinas de manutenção
SELECT
autovacuum_count
FROM pg_stat_user_tables
WHERE relname = 'tb_teste_threshold';
autovacuum_count
------------------
1
SELECT
n_dead_tup,
autovacuum_count
FROM pg_stat_user_tables
WHERE relname = 'tb_teste_threshold';
n_dead_tup | autovacuum_count
------------+------------------
0 | 2
Nenhuma tupla morta e, mais uma vez, o autovacuum atuou na tabela por conta das tuplas
mortas geradas ser um número maior que vacuum threshold.
SELECT pg_reload_conf();
210 10. Rotinas de manutenção
SHOW autovacuum_naptime;
autovacuum_naptime
--------------------
1min
Revisao
1. P: O que são tuplas mortas?
R:
2. P: Além da “limpeza” de tuplas mortas, qual a importância do vacuum?
R:
3. P: Qual a importância do autovacuum?
R:
4. P: É necessário realizar vacuum full periodicamente?
R:
5. P: O que acontece se o PostgreSQL atingir o número limite de transações sem que
ocorra o freeze?
R:
11
Performance
• Performance / Tuning do PostgreSQL
• Hardware
• Sistema Operacional
• Configurações do PostgreSQL (postgresql.conf)
• Aplicação
• Otimização de consultas
• Pool de conexões
• Bases de dados OLTP (OnLine Transaction Processing)
• Bases de dados OLAP (OnLine Analytical Processing)
• pgbench e benchmarks
• Visões materializadas
• Tabelas não logadas - Unlogged tables
• Fillfactor - Fator de preenchimento
• Plano de execução
• Huge pages
212
213 11. Performance
Hardware
É a parte física e também a camada mais baixa de todo o sistema que envolve a aplicação.
A escolha do hardware deve ser criteriosa e avaliar não somente o desempenho proporcionado,
mas também outros quesitos como relação de custo-benefício, durabilidade (MTBF – Mean
Time Between Failures: Tempo Médio Entre Falhas), compatibilidades (entre outros hardwares
e sistema operacional), garantia do fornecedor, suporte etc. Os itens físicos mais importantes,
por ordem decrescente, são: armazenamento -> memória -> processador -> rede.
215 11. Performance
Disco / armazenamento
Write-back e write-through
Sua ideia consiste em agrupar discos físicos e formar uma única unidade lógica.
Cada disco que faz parte do conjunto é chamado de spindle.
É preferível mais spindles de menor capacidade para compor o RAID do que menos discos de
216 11. Performance
menor capacidade, pois grandes discos tendem a ser mais lentos. Ou seja, na prática, é melhor
20 discos de 1 TB do que 10 de 2 TB, por exemplo.
RAID 0 (striping)
Vantagens:
• Velocidade;
• Custo baixo de expansão;
• Aproveitamento máximo de capacidade.
Desvantagem:
• Insegurança; basta que um dos discos se corrompa para que se perca tudo.
RAID 1 (mirroring)
Vantagens:
Desvantagens:
RAID 5
Mínimo de 3 (três) discos, utiliza um sistema ECC (Error Correcting Code – Detecção e
Correção de Erros) que distribui informações de paridade ao longo dos discos.
Por sua distribuição de informações de paridade, a leitura de dados sequenciais é prejudicada.
Seu nível de segurança é discutível; não permite que mais de 1 (um) disco falhe.
Levando em conta que se for feita uma aquisição de discos de mesmo lote, considerando o
MTBF de cada um, a probabilidade de mais de um dar problema ao mesmo tempo existe, o
que poderia ser catastrófico neste tipo de arranjo. Ainda assim é bem mais seguro do que o
RAID 0.
Vantagens:
Desvantagens:
• Segurança;
• Velocidade de escrita reduzida devido à complexidade do sistema de ECC;
• Desempenho de leitura para dados sequenciais.
RAID 6
Sistema similar ao RAID 5, tem um mínimo de 4 (quatro) discos, seu sistema de ECC é o
dobro de dados de paridade comparado ao RAID 5.
O RAID 6 é relativamente novo, não suportado por todas controladoras e pouco mais seguro
do que o RAID 5, pois permite que até 2 (dois) discos falhem.
Vantagens:
Desvantagens:
• Segurança;
• Escrita reduzida devido à complexidade do sistema de ECC;
• Desempenho de leitura para dados sequenciais reduzido.
218 11. Performance
RAID 10
Vantagens:
• Segurança;
• Velocidade.
Desvantagens:
• Custo;
• Baixo aproveitamento de dados.
219 11. Performance
• Escalável;
• Gerenciável.
220 11. Performance
• Custo alto;
• Não muito confiável para bases de dados, pois geralmente usam algo como NFS;
• Altamente configurável;
• Altamente gerenciável;
• Custo alto;
• Limitado pela banda de rede que é quase sempre mais lenta (exceto de 10 Gb) do que
o DAS;
• Altamente configurável;
• Altamente gerenciável;
• Recurso compartilhado.
222 11. Performance
Memória RAM
É também muito importante para performance por usar memória em vez de disco.
Determina a quantidade de memória usada para operações de consulta, tais como ordenação
e tabelas de dispersão (hash tables). ORDER BY, DISTINCT, agregações baseadas em hash e
subconsultas IN são casos que pode-se obter mais desempenho aumentando seu valor.
O diretório $PGDATA/base/pgsql_tmp é o destino de arquivos temporários criados caso seu valor
seja insuficiente para uma operação. Esses arquivos em sua nomenclatura têm o PID do
processo que está rodando a query que os gerou.
Como dica de ajuste, é possível utilizar a seguinte fórmula:
Quanto menor for o parâmetro max_connections, menos recursos alocados para conexões e,
assim, pode-se aumentar o valor de work_mem.
Aviso
work_memdeve ser ajustado com cautela, pois para uma consulta complexa que tenha várias
operações de classificação ou hash podem ser executadas paralelamente. Cada uma dessas
operações está permitida a usar o valor especificado, o que pode levar a um esgotamento de
memória.
223 11. Performance
CPU
Rede
A rede normalmente não representa um grande problema para bancos de dados, mas even-
tualmente pode sim ser um gargalo.
Problemas podem ser ocasionados por switches, cabeamento, máquinas sobrecarregadas ou
mesmo uma rede de baixa velocidade.
Há ainda o problema de perda de pacotes causado quando a máquina de destino não respondeu
em tempo hábil, por congestionamento, colisão, porta fechada ou indisponibilidade.
Entre o servidor de banco de dados e o servidor de aplicação ou em caso de replicação um
servidor secundário (se apenas 2 (dois)), pode até usar um cabo crossover.
Levar em consideração também backups remotos, ou seja, o quanto de acordo com o volume
de dados.
No PostgreSQL, problemas de rede o afetam com connection timeout (tempo esgotado de
conexão), represamento de logs de transação causado ou por um standby que ainda não con-
sumiu esses arquivos ou devido ao arquivamento ter falhado.
AS views de sistema pg_stat_replication e pg_stat_archiver, respectivamente, monitoram repli-
cação e arquivamento.
225 11. Performance
Sistema Operacional
O sistema operacional e suas particularidades influenciam também no desempenho do banco.
Sistemas operacionais da família Unix (Linux, BSDs, AIX, Solaris, etc) comprovadamente
entregam maior desempenho para o PostgreSQL do que o Windows.
Os temas tratados nesta sessão serão todos relativos ao Linux.
Kernel
Comunicação inter-processos
Em inglês Inter process communication (IPC), é um mecanismo que permite aos processos se
comunicarem e sincronizarem suas ações.
A comunicação entre esses processos podem ser encarada como uma forma de cooperação
entre eles. Processos podem comunicar entre si por meio de memória compartilhada, troca de
mensagens e semáforos.
O PostgreSQL utiliza memória compartilhada e semáforos para fazer a comunicação inter-
processos.
O utilitário ipcs pode ser utilizado para verificar informações IPC.
Memória compartilhada
É uma parte da RAM separada para que um determinado grupo de processos possa acessá-la,
evitando dados redundantes.
Mesmo não utilizando o SysVIPC para alocação dos shared buffers, o PostgreSQL continua
realizando uma pequena alocação de memória compartilhada por SysVIPC para seu funciona-
mento.
Atualmente, é possivel utilizar o parâmetro shared_memory_type, configurado para que o Post-
greSQL utilize o SysVIPC para alocação dos shared buffers.
226 11. Performance
ipcs -m
Uma linha por segmento de memória compartilhada do usuário postgres. Uma tem 8 processos
e a outra, 6.
Semáforos
São recursos providos pelo kernel para tratar condições de corrida, os quais, assim como
a memória compartilhada, podem ser obtidos através do “SysVIPC” ou através do padrão
POSIX.
Até a versão 9.6, apenas o “SysVIPC” era utilizado, a partir da versão 10, em ambientes
Linux e FreeBSD o POSIX passou a ser o mecanismo utilizado. Essa mudança ocorreu para
simplificar a configuração dos limites de semáforos do “SysVIPC”.
Caso esteja utilizando algum S.O. onde o SysVIPC é utilizado, segue a documentação abaixo
para realizar os cálculos de limite necessários:
https://www.postgresql.org/docs/current/kernel-resources.html
Swapiness
É a maneira que o kernel do Linux fornece para ajustar a configuração que controla a frequência
que a swap é usada, cuja faixa de valores varia de 0 (zero) a 100 (cem).
Cujo valor corresponde à porcentagem de RAM restante, que é o ponto de partida para se
começar a fazer swap.
Vale lembrar que o I/O, como é feito o processo de swap, é muito mais lento do que o uso de
RAM, portanto, impacta diretamente na performance do sistema.
Uma configuração vm.swappiness = 1, por exemplo, significa que será evitado fazer swap a não
ser que seja absolutamente necessário, ou seja, tem 99% de RAM usada.
sysctl vm.swappiness
vm.swappiness = 60
227 11. Performance
sysctl -p /etc/sysctl.d/pgsql.conf
sysctl vm.swappiness
vm.swappiness = 1
Memory overcommit
• overcommit_memory (vm.overcommit_memory)
Define o recurso memory overcommit, cujos possíveis valores são:
sysctl vm.overcommit_memory
vm.overcommit_memory = 0
sysctl -p /etc/sysctl.d/pgsql.conf
229 11. Performance
sysctl vm.overcommit_memory
vm.overcommit_memory = 2
• overcommit_ratio (vm.overcommit_ratio)
Ao se configurar overcommit_memory para 2, o espaço comprometido (commited) não está
permitido a exceder swap mais o percentual da memória RAM referida no valor deste
parâmetro, conforme segue a fórmula abaixo:
Isso indica que o processo postgres foi terminado devido à pressão de memória.
Embora as conexões à base de dados continuem a funcionar normalmente, nenhuma conexão
nova será aceita. Para se recuperar, o PostgreSQL precisa ser reiniciado.
• vm.dirty_background_ratio
É o percentual de memória preenchido com páginas sujas que precisam ser escritas em
segundo plano (background) no armazenamento.
Seu valor é inteiro e seu intervalo é de 0 a 100, seu valor padrão costuma ser 10 na
maior parte das distribuições Linux.
Para operações de escritas intensas pode-se obter maior desempenho diminuindo seu
230 11. Performance
valor.
• vm.dirty_background_bytes
Sua ideia é parecida com a de vm.dirty_background_ratio, porém seu valor é absoluto em
bytes.
Apenas um dos dois deve ser especificado.
• vm.dirty_ratio
Tem a mesma lógica que vm.dirty_background_ratio, mas para escritas em primeiro plano,
fazendo com que a aplicação seja bloqueada.
Seu valor deve ser maior que vm.dirty_background_ratio, o que garante que processos em
segundo plano serão lançados antes dos de primeiro para evitar bloqueio da aplicação
tanto quanto possível.
Deve-se ajustar a diferença entre os dois ratios dependendo da carga de I/O.
• vm.dirty_bytes
Mesma lógica de vm.dirty_ratio, porém com valores absolutos em bytes.
Quando há muita memória RAM (e. g.: 1TB), pequenas frações já representarão uma quanti-
dade enorme de dados. Ao atingir vm.dirty_ratio ou vm.dirty_bytes é preciso aguardar a escrita
volumosa de dados, o que impactará o sistema. Nesse caso, é melhor esses valores reduzidos.
Readahead
Recurso do kernel Linux para melhorar performance de leitura de arquivos, em que é feita uma
busca antecipada de arquivos.
Se o kernel tiver alguma razão para acreditar que um determinado arquivo será lido na se-
quência, ele vai tentar colocá-lo em memória antes de a aplicação requisitá-lo.
É uma chamada de sistema que carrega o conteúdo de um arquivo em páginas de cache.
Ao acessar um arquivo posteriormente, seu conteúdo é lido da memória em vez do armazena-
mento, que é muito mais rápido.
Muitas distribuições Linux usam o recurso readahead em uma lista de arquivos mais comu-
mente usados para acelerar o processo de boot.
O valor padrão é 256 para drives comuns e as unidades são normalmente em 512 bytes, assim
sendo o valor padrão igual a 128kb (256 * 512 bytes).
231 11. Performance
[#] Com o utilitário blockdev fazer o relatório e filtrar apenas pelos dispositivos de armazena-
mento disponíveis (exemplo):
256
Observação
O comando blockdev --setra não é persistente, ou seja, ao reiniciar o servidor, tudo voltará ao
padrão.
Caso queira que em caso de reboot o servidor já suba com esse comando, pode inseri-lo no
arquivo /etc/rc.local.
232 11. Performance
exit 0
EOF
chmod +x /etc/rc.local
Sistema de arquivos
O sistema de arquivos utilizado por um banco de dados influencia diretamente em sua segu-
rança e confiabilidade.
A confiabilidade de um sistema de arquivos está no recurso de journaling, que por outro lado
tem um custo de I/O.
Isso pode ser contornado utilizando um disco com partições específicas para guardar os logs
(journals) dos outros, mas é necessário fazer testes para verificar cada caso se o desempenho
ganho vale a pena financeiramente.
A respeito de qual utilizar, primeiro devemos analisar o que vai ser armazenado e posterior-
mente fazer testes de carga anotando os resultados pra decidir pelo mais rápido. A performance
de um tipo de filesystem pode variar entre versões do kernel do Linux.
De uma forma geral podemos colocar da seguinte maneira as partições (para Linux):
Observação
Exceto o último, que é um armazenamento virtual em memória RAM, para os outros é acon-
selhável que tenham uma unidade de armazenameno dedicada para evitar concorrência de
I/O.
* Somente para controladoras que têm uma BBU e a opção write-back, caso contrário não
utilizar nobarrier.
234 11. Performance
Journaling
É o recurso de sistemas de arquivos que evita perdas de dados e permite recuperação numa
situação de crash.
Ao gravar um arquivo em armazenamento, há três fases:
O recurso de journaling permite a recuperação de dados mais rápida e confiável cujo funciona-
mento é gravar metadados ou os próprios dados em uma divisão denominada “journal” antes
de escrever nos blocos.
Após a escrita bem sucedida nos blocos, o conteúdo do journal pode ser removido.
Em caso de crash durante escrita, com journaling, a recuperação é feita a partir dos dados
contidos no journal. Esse mecanismo é mais eficiente pelo fato de ter que buscar no journal
em vez de todos os blocos do sistema de arquivos.
Com journaling ganha-se em confiabilidade e agilidade (para uma possível necessidade de re-
cuperação de crash), porém, há um pouco de perda de desempenho causado pelo overhead.
Isso conforme o sistema de arquivos, já que a escrita de dados pode ser feita praticamente
duas vezes antes de ser considerada finalizada. Assim, é importante saber como é feito o
journaling do sistema de arquivos escolhido.
Write barriers
Recurso, bem menos popular que journaling, é de grande importância para uso com SGBDs,
pois é ele que garante que os dados sejam fisicamente gravados após um fsync, garantindo
que o conteúdo do cache da controladora seja despejado no fisicamente no dispositivo de
armazenamento.
Sem write barriers, após um fsync os dados são despejados do cache do sistema operacional
mas podem permanecer no cache da controladora e se um crash ocorrer nesse momento, os
mesmos serão perdidos pois o cache da controladora é baseado em memória volátil.
O uso de write barriers aumenta a confiabilidade porém praticamente inutiliza todas as van-
tagens de desempenho de se possuir cache na controladora, pois toda gravação com fsync vai
precisar esperar pela escrita no dispositivo físico de armazenamento.
É possível desligar writer barriers com segurança e aproveitar o desempenho da escrita no
cache quando se utiliza controladoras/storage que possuem bateria. Assim, em caso crash, a
bateria mantém o conteúdo do cache da controladora durante horas ou dias dependendo do
equipamento.
Em caso de se utilizar controladora sem bateria é preciso manter o recurso de write barriers
habilitado. Em sistemas de arquivos sem esse recurso deve-se desabilitar o cache.
No caso de se utilizar controladora/storage com bateria, recomenda-se desligar write barriers.
235 11. Performance
UUID no fstab
Não é obrigatório, mas o uso de UUID ao invés do endereço do dispositivo é uma boa prática,
pois se os discos forem trocados de lugar não será levada em conta a ordem que estão, mas
sim sua ID no sistema.
• Serviços concorrentes
Ter o mínimo possível de serviços rodando no servidor, pois os mesmos também con-
somem recursos que vão interferir no desempenho do banco de dados.
Não faz sentido ter um servidor de banco de dados rodando junto com um servidor web,
um servidor de e-mails ou um servidor de arquivos.
• Interface gráfica
Toda a administração pode ser feita tranquilamente via comandos e edição de arquivos
que podem ser executados pessoalmente ou remotamente via SSH.
Uma interface gráfica é útil para desktops, pois o usuário precisa o tempo todo ver o
que está fazendo e utilizar o mouse com frequência.
No entanto, isso tem custos (memória, processamento, etc. . . ), o que para um servidor
de banco de dados é precioso.
Por isso, um servidor Windows tende a ser mais lento do que um servidor da família de
sistemas operacionais Unix.
236 11. Performance
Aplicação
Modelagem
Indexação
Crie índices para campos que são utilizados em condições de consultas (cláusula WHERE). Pelo
menos as consultas mais frequentes.
Crie índices para campos de chaves estrangeiras.
Fillfactor (fator de preenchimento) para um índice é a porcentagem que determina como o
método de indexação encherá as páginas de índices.
O quão cheias essas páginas ficarão em porcentagem.
Tabelas estáticas pode-se deixar em 100 (representando 100%).
Tabelas que sofrem muitas alterações um valor de 80 ou menos pode ser mais adequado, mas
quanto menor for o fator de preenchimento, mais espaço ocupará.
ORMs
Otimização de Consultas
Testar consultas para otimizar
Para fazer a mesma coisa podem ter jeitos SQL diferentes para isso. Saber qual é o mais
eficiente só testando. Como?
Conforme o resultado obtido em cada consulta é possível otimizar o que deseja ser feito.
Para bases já prontas, cujas consultas já existem, também tem como melhorar.
O pgBadger [1], que através dos logs de atividades gera um relatório em formato HTML,
mostra as consultas mais lentas do sistema.
[1] https://github.com/darold/pgbadger
Dicas SQL
• UNION em vez de OR
UNION pode combinar os conjuntos de resultados de duas ou mais consultas.
Utilizar UNION no lugar de OR na cláusula WHERE é mais rápido.
• Cache de consultas
A memória RAM é milhares de vezes mais rápida do que discos.
Utilizar aplicativos que fazem cache de consultas vai fazer com que seu sistema tenha
uma maior performance.
Um bom projeto desse tipo para PostgreSQL é o pgmencache
238 11. Performance
http://pgfoundry.org/projects/pgmemcache/.
• Evite subconsultas
Em muitos casos em vez de subqueries pode-se utilizar junções (joins) ou CTEs (Com-
mon Table Expression).
• Evite exibição de linhas em excesso
Uma aplicação que exibe mais que 100 tuplas de uma vez precisa ser revista.
Recomenda-se usar a técnica keyset pagination.
Pool de conexões
Pool de conexões (aglomeração de conexões) faz multiplexação de conexões, ou seja, faz com
que mais de uma conexão à aplicação utilize apenas uma conexão no banco de dados.
Essa estratégia faz com que o uso de recursos seja realizado de uma maneira mais eficiente,
reduzindo o tempo de transações e consequente aumento de performance como um todo.
A implementação de pool de conexões pode ser feita pela própria aplicação ou como um
componente externo conhecido como proxy. Seja em outra máquina ou na própria onde o
PostgreSQL está instalado, é vantagem utilizar um proxy, pois as conexões nele estabelecidas
custam menos que no PostgreSQL.
Redirecionamento de conexões para diferentes servidores é uma característica interessante que
um proxy externo pode ter, com chaveamento de servidores em um cluster de alta disponibil-
idade e balanceamento de carga entre eles.
Até o momento, não há uma implementação nativa.
PgPool II
PgBouncer
Modos de operação:
Seguem abaixo detalhes e procedimentos para sua instalação via compilação de código-fonte.
Arquitetura de diretórios
Tipo Localização
Instalação /usr/local/pgbouncer/
Configuração /etc/pgbouncer
Binários /usr/local/pgbouncer/bin/
# Pacotes comuns
PKG='gcc make pkg-config'
# Pacotes Debian
PKG_DEB="libsystemd-dev libevent-dev libssl-dev"
apt update && apt install -y wget ${PKG} ${PKG_DEB} && apt clean
useradd \
-c 'PgBouncer system user' \
-s /usr/sbin/nologin \
-d /var/local/pgbouncer \
-g postgres \
-m -r pgbouncer &> /dev/null
ln -s ~pgbouncer /etc/
242 11. Performance
[#] Atribuição de variável de ambiente para versão completa do PogBouncer via prompt:
read -p \
'Número de versão completo (X.Y.Z) do PgBouncer a ser baixado: ' \
VERSION
URL="http://www.pgbouncer.org/downloads/files/\
${VERSION}/pgbouncer-${VERSION}.tar.gz"
# Acessar /tmp
cd /tmp
# Descompactar o arquivo
tar xvf pgbouncer-${VERSION}.tar.gz
./configure \
--prefix /usr/local/pgbouncer \
--with-pam \
--with-systemd \
--with-openssl
[#] Criar arquivos de lista de usuários, de configuração e diretório de logs com permissões
somente para usuário e grupo:
# Diretório de logs
mkdir /var/log/pgbouncer
# Permissão de diretórios
chmod 770 ~pgbouncer /etc/pgbouncer /var/log/pgbouncer
# Permissão de arquivos
chmod 660 /etc/pgbouncer/*
O arquivo foi criado com o nome do usuário e seu hash de senha, cada um envolvido por
aspas.
244 11. Performance
[pgbouncer]
listen_addr = 127.0.0.1
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
logfile = /var/log/pgbouncer/pgbouncer.log
admin_users = postgres
pool_mode = session
default_pool_size=90
max_client_conn=3000
EOF
[Service]
Type=notify
User=pgbouncer
ExecStart=/usr/local/pgbouncer/bin/pgbouncer /etc/pgbouncer/pgbouncer.ini
ExecReload=/bin/kill -HUP \${MAINPID}
KillSignal=SIGINT
#LimitNOFILE=1024
[Install]
WantedBy=multi-user.target
EOF
245 11. Performance
createdb db_bench
pgbench -i db_bench
100
. . .
pgbench: error: connection to database "db_bench" failed: FATAL: sorry, too many clients already
. . .
247 11. Performance
[$] Teste com pgbench, 500 conexões simultâneas e 10 transações cada, mas desta vez se
conectando no pgbouncer:
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 500
number of threads: 1
number of transactions per client: 10
number of transactions actually processed: 5000/5000
latency average = 1602.790 ms
tps = 311.956029 (including connections establishing)
tps = 311.959382 (excluding connections establishing)
248 11. Performance
• Transações bancárias;
• Sistemas de vendas online em geral;
• Sistemas de cadastros em geral etc.
Características
WAL
Devido à sua escrita intensa, os logs de transação, podem virar um gargalo de performance.
É altamente aconselhável ter uma unidade de armazenamento dedicada.
Pode ser interessante alterar os parâmetros de configuração commit_siblings e commit_delay para
agrupar mais transações por fsync, assim diminuirá a carga de I/O que a escrita de WAL gera.
No entanto, se a aplicação tem uma natureza mais tranquila, com poucas transações, pode
gerar atraso nas efetivações.
Shared buffers
Também, devido à natureza de grandes escritas, ter um valor grande para shared_buffers,
de forma a poder armazenar o fluxo de dados que será gravado, é interessante para sempre
ter páginas disponíveis. Isso evita que backends precisem fazer as escritas por si mesmos.
A porcentagem de 25% a 40% da memória RAM para shared_buffers é uma recomendação
inicial.
249 11. Performance
Checkpoints
Há de se evitar que haja uma frequente ocorrência de checkpoints, então é altamente re-
comendável aumentar o parâmetro max_wal_size, cujo padrão é 1GB. De maneira similar, mas
por tempo, o parâmetro checkpoint_timeout, cujo valor padrão é 5min, pode ser aumentado
também.
O parâmetro log_checkpoints registra em log quando ocorre um checkpoint, então pode ser
muito útil para monitorar o quão frequente está.
250 11. Performance
Parametrizações no PostgreSQL
WAL
Aumentar a quantidade dos arquivos WAL vai beneficiar as cargas que serão feitas.
Memória
pgbench e benchmarks
O pgbench é um simples utilitário para rodar testes de benchmarks no PostgreSQL.
Ele roda a mesma sequência de comandos SQL de forma contínua, podendo ser de forma
concorrente, com várias conexões simultâneas. Então, calcula a média de taxa de transações
(transações por segundo).
Por padrão, o pgbench testa um cenário vagamente baseado no TPC-B, envolvendo cinco
comandos SELECT, UPDATE, and INSERT commands por transação.
Facilmente pode-se fazer testes personalizados, scripts SQL personalizados.
A base de dados do teste foi criada nos termos de um banco hipotético. O banco (financeiro)
tem um ou mais agências (branches). Cada agência tem múltiplos caixas de banco (tellers). O
banco tem muitos clientes, cada um com uma conta (accounts). A base de dados representa
o dinheiro em cada entidade (agência, caixa e conta) e um histórico das transações recentes
executadas pelo banco. A transação representa o trabalho executado quando um cliente faz
um depósito ou saque de sua conta. A transação é feita pelo caixa em alguma agência.
• Testar mais de uma vez para certificar-se de que o resultado não foi apenas um ruído;
• Inicializar os dados novamente pois o acúmulo de tuplas não vigentes e espaço reutilizável
podem mudar os resultados;
• Não executar com um número de clientes (-c) maior do que o fator de escala (-s
). Assim será medido somente contenção de atualização. O número de registros na
tabela pgbench_branches é igual ao fator de escala e toda transação tem uma atualização
nessa tabela; então, se o número de clientes exceder o fator de escala, teremos muitas
transações bloqueadas esperando por outras transações;
• O próprio pgbench pode se tornar um gargalo se o número de clientes for muito alto.
Execute o pgbench de outra máquina, que tenha uma baixa latência de rede.
Também pode ser útil executar várias instâncias do pgbench em diferentes máquinas.
252 11. Performance
createdb db_bench
pgbench -i db_bench
[$] Teste do pgbench com 50 conexões simultâneas, cada uma executando 100 transações:
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 50
number of threads: 1
number of transactions per client: 100
number of transactions actually processed: 5000/5000
latency average = 129.741 ms
tps = 385.382845 (including connections establishing)
tps = 385.440467 (excluding connections establishing)
253 11. Performance
Visões materializadas
O comando CREATE MATERIALIZED VIEW define uma view materializada de uma consulta.
A consulta é executada e usada para popular a view na hora que o comando é dado (exceto se
for usado WITH NO DATA) e pode ser atualizada posteriormente utilizando REFRESH MATERIALIZED
VIEW.
CREATE MATERIALIZED VIEW é similar a CREATE TABLE AS, exceto que também lembra a consulta
usada para iniciar a view, de modo que possa ser atualizada posteriormente sob demanda.
Uma view materializada tem muitas das mesmas propriedades de uma tabela, mas não há
suporte para views materializadas temporárias ou geração automática de OIDs.
Sua grande vantagem com relação à uma view comum é o desempenho.
Uma visão materializada precisa do comando REFRESH para ter seus dados atualizados com
relação à tabela de origem.
Visões materializadas por terem um desempenho melhor que as visões tradicionais acabam
sendo muito úteis em tabelas que sofrem gravações pontuais em que a sincronização com a
visão materializada se dará com o comando REFRESH.
Então as consultas da aplicação podem ser direcionadas para essa visão em vez da tabela ou
mesmo uma visão comum.
count
---------
1000000
count
---------
1500000
255 11. Performance
count
---------
1000000
count
---------
1500000
EXPLAIN ANALYZE
SELECT numero, texto FROM vw_foo;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..37467.00 rows=15000 width=12) (actual time=6.658..583.693 rows=1500000 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tb_foo (cost=0.00..34967.00 rows=6250 width=12) (actual time=0.010..139.185 rows=
Filter: ((id_ % 2) = 0)
Rows Removed by Filter: 500000
Planning Time: 0.069 ms
Execution Time: 665.537 ms
256 11. Performance
EXPLAIN ANALYZE
SELECT numero, texto FROM mv_foo;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Seq Scan on mv_foo (cost=0.00..23109.00 rows=1500000 width=12) (actual time=0.048..89.577 rows=1500000 loops
Planning Time: 0.109 ms
Execution Time: 130.894 ms
Tabelas não logadas são muito úteis quando não é importante a durabilidade dos dados. Por
exemplo: cache de dados, informações temporárias acessíveis a todas sessões abertas, cargas
ETL e etc.
\timing on
258 11. Performance
pg_ctl -m i restart
Aviso
Não utilizar o kill -9 ou killall -9, pois isso pode levar a uma corrupção de dados.
259 11. Performance
Sem registros!
A tabela foi truncada.
[>] No catálogo pg_class, utilizando de expressão regular que case com os nomes das tabelas
criadas, verificar o tipo de persistência:
SELECT
oid, relfilenode, relname, relpersistence, relkind
FROM pg_class
WHERE relname ~ '^tb_.*logged';
SELECT
oid, relfilenode, relname, relpersistence, relkind
FROM pg_class
WHERE relname ~ '^tb_.*logged';
Quando uma tabela muda seu tipo de persistência de dados, ela tem que ser totalmente
reescrita, sendo necessário criar um novo arquivo. Isso explica a mudança de relfilenode após
alterar as tabelas.
261 11. Performance
Fillfactor em tabelas
\timing on
Time: 883.443 ms
SELECT pg_size_pretty(pg_relation_size('tb_ff100'));
pg_size_pretty
----------------
35 MB
262 11. Performance
SELECT pg_size_pretty(pg_relation_size('tb_ff100'));
pg_size_pretty
----------------
69 MB
SELECT pg_size_pretty(pg_relation_size('tb_ff50'));
263 11. Performance
pg_size_pretty
----------------
69 MB
SELECT pg_size_pretty(pg_relation_size('tb_ff50'));;
pg_size_pretty
----------------
69 MB
Fillfactor 100 50
INSERT Inicial (ms) 883.443 1103.308
Tamanho da tabela após INSERT (MB) 35 69
UPDATE (ms) 1948.167 1301.622
Tamanho da tabela após UPDATE (MB) 69 69
264 11. Performance
Fillfactor em índices
\d tb_ff100
Table "public.tb_ff100"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
campo | integer | | |
Indexes:
"idx_ff100" btree (campo) WITH (fillfactor='100')
SELECT pg_size_pretty(pg_relation_size('idx_ff100'));
pg_size_pretty
----------------
19 MB
265 11. Performance
4517.604
SELECT pg_size_pretty(pg_relation_size('idx_ff100'));
pg_size_pretty
----------------
39 MB
[>] Verificando nas informações de estrutura da tabela as informações sobre o índice criado
para ela:
\d tb_ff50
Table "public.tb_ff50"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
campo | integer | | |
Indexes:
"idx_ff50" btree (campo) WITH (fillfactor='50')
SELECT pg_size_pretty(pg_relation_size('idx_ff50'));
pg_size_pretty
----------------
39 MB
SELECT pg_size_pretty(pg_relation_size('idx_ff50'));
pg_size_pretty
----------------
267 11. Performance
39 MB
Fillfactor 100 50
INSERT Inicial (ms) 2157.771 2597.165
Tamanho do índice após INSERT (MB) 19 39
UPDATE (ms) 4517.604 3648.516
Tamanho da índice após UPDATE (MB) 39 39
268 11. Performance
Plano de execução
Plano de execução é o caminho utilizado para executar um comando.
Conexão
Uma conexão de uma aplicação para o servidor PostgreSQL tem que ser estabelecida. A
aplicação transmite uma consulta para o servidor e aguarda seu resultado.
Parser (analisador)
No estágio do parser (analisador), ocorre uma checagem da consulta transmitida pela apli-
cação, verificando se a sintaxe está correta. Então, cria-se uma árvore de consulta.
Toma a árvore de consulta criada pelo parser e procura por quaisquer regras (armazenadas em
catálogos do sistema) para aplicá-las à árvore de consulta. Transformações são executadas de
acordo com os corpos das regras.
Um exemplo de utilização do sistema de reescrita é em views. Sempre que uma consulta é
feita em uma view, a consulta é reescrita de forma a acessar diretamente a(s) tabela(s) de sua
consulta de construção.
É o responsável pela interpretação dos comandos emitidos e por determinar qual é método de
execução mais eficiente.
Ele pega a árvore de consulta (reescrita) e cria um plano de consulta que será a entrada do
executor.
Primeiro, ele cria todos os caminhos possíveis que levam ao mesmo resultado.
Por exemplo, se houver um índice em uma tabela para ser buscado, há dois caminhos para
procurar. Um possivelmente é uma simples busca sequencial e outra possibilidade é fazer
uso do índice. O custo da execução de cada caminho é estimado e o que for mais barato é
escolhido.
269 11. Performance
O caminho menos custoso é expandido dentro de um plano completo que o executor pode
usar.
Para que o planejador de consultas do PostgreSQL tome melhores decisões para executar um
determinado comando, os dados de pg_statistic devem estar atualizados para todas as tabelas
envolvidas. O daemon autovacuum normalmente já faz isso de forma automática, mas se
uma tabela tem mudanças substanciais feitas recentemente, vai ser preciso fazer um ANALYZE
manual em vez de esperar um autovacuum para pegar essas mudanças. A quantidade de cache
que está em níveis abaixo do PosgreSQL, como cache de sistemas de arquivos, cache de con-
troladoras, cache de armazenamento e a própria memória RAM são fundamentais. Somando
todos esses, configura-se o parâmetro effective_cache_size. Assim, o planejador realizará o
cálculo de probabilidade de um determinado dado estar ou não em cache.
Com o parâmetro effective_io_concurrency, definimos a quantidade de unidades de armazena-
mento para execução de operações paralelas. Com essa informação, o planejador poderá
executar operalções de I/O assíncronas paralelamente quando se utilizar bitmap heap scans.
Executor
O comando EXPLAIN
SELECT
generate_series(1, 2000000) AS campo
INTO tb_foo;
[>] Criação de um índice parcial na tabela para valores divisíveis por 19:
EXPLAIN
SELECT campo FROM tb_foo
WHERE campo = 975421;
QUERY PLAN
-----------------------------------------------------------------------------------
Index Only Scan using idx_teste_comum on tb_foo (cost=0.43..4.45 rows=1 width=4)
Index Cond: (campo = 975421)
EXPLAIN ANALYZE
SELECT campo FROM tb_foo
WHERE campo = 975421;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_teste_comum on tb_foo (cost=0.43..4.45 rows=1 width=4) (actual time=0.105..0.107 r
Index Cond: (campo = 975421)
Heap Fetches: 0
Planning Time: 0.073 ms
Execution Time: 0.126 ms
271 11. Performance
[>] Plano de execução sem ANALYZE de uma consulta com números divisíveis por 19:
EXPLAIN
SELECT count(*) FROM tb_foo
WHERE campo % 19 = 0;
QUERY PLAN
-----------------------------------------------------------------------------------------------
Aggregate (cost=287.42..287.43 rows=1 width=8)
-> Index Only Scan using idx_teste_div19 on tb_foo (cost=0.42..262.42 rows=10000 width=0)
[>] Plano de execução com ANALYZE de uma consulta com números divisíveis por 19:
EXPLAIN ANALYZE
SELECT count(*) FROM tb_foo
WHERE campo % 19 = 0;
QUERY PLAN
-----------------------------------------------------------------------------------------------
Aggregate (cost=287.42..287.43 rows=1 width=8) (actual time=16.956..16.957 rows=1 loops=1)
-> Index Only Scan using idx_teste_div19 on tb_foo (cost=0.42..262.42 rows=10000 width=0) . . .
Heap Fetches: 0
Planning Time: 0.084 ms
Execution Time: 16.986 ms
[>] Plano de execução com ANALYZE, formato JSON, modo verboso, informação de buffers
para uma consulta com números divisíveis por 19:
QUERY PLAN
--------------------------------------------
[ +
{ +
"Plan": { +
"Node Type": "Aggregate", +
"Strategy": "Plain", +
"Partial Mode": "Simple", +
"Parallel Aware": false, +
"Startup Cost": 287.42, +
"Total Cost": 287.43, +
"Plan Rows": 1, +
"Plan Width": 8, +
"Actual Startup Time": 16.420, +
"Actual Total Time": 16.421, +
"Actual Rows": 1, +
"Actual Loops": 1, +
. . .
272 11. Performance
Custos do planejador
É possível alterar os custos estimados para cada operação dos planos de execução. Os custos
utilizam uma medida arbitrária, como se fosse uma moeda instituída para determinar custos
de operações.
• seq_page_cost(floating point)
Custo estimado de leitura de página em disco para páginas sequenciais.
• (floating point)
cpu_tuple_cost
Custo estimado de processamento por linha de uma consulta.
• (floating point)
cpu_index_tuple_cost
Custo estimado de processamento por entrada de índice.
• (floating point)
cpu_operator_cost
Custo estimado de processamento por operador ou função executada durante uma con-
sulta.
• effective_cache_size(integer)
Permite ao planejador estimar o tamanho efetivo do cache de disco disponível para uma
consulta.
Aumentar este valor fará com que o planejador prefira buscas em índices. Considere que
o cache disponível pode ser o próprio cache do PostgreSQL mais o cache do sistema
operacional.
Este valor não faz nenhuma alocação de memória, apenas diz ao planejador uma esti-
mativa de memória disponível.
273 11. Performance
Métodos de busca
Para se efetuar buscas nas tabelas o planejador do PostgreSQL utiliza um dos determinados
métodos de busca que serão vistos a seguir.
Cada método tem sua utilidade, sendo que sua aplicação depende de fatores como a própria
consulta feita e outros.
Sequential scan
Index scan
A varredura é feita conforme um índice da consulta que aponta para as linhas que contenham
o valor que satisfaça o critério de busca.
Esse tipo de busca, quando é possível, acaba sendo muito mais rápido que a busca sequencial,
pois vai direto onde estão os valores.
Seu funcionamento é parecido com index scan, se diferencia justamente como seu nome indice:
a varredura é feita apenas na estrutura de dados de índices.
Bitmap scan
Esse tipo de busca é uma mistura da busca sequencial e a busca por índice, na qual se tenta
resolver a desvantagem da busca por índice, mas mantém as vantagens.
TID scan
Busca for TID é um tipo de varredura muito específica do PostgreSQL onde no critério de
busca é usado um campo desse tipo, como a coluna de sistema ctid por exemplo.
Parallel scan
Tipo de varredura feito com auxílio de paralelização, em que o trabalho é dividido entre núcleos
de CPU.
Paralelização de execução
A paralelização de execuções iniciou-se no 9.6 e desde então a cada versão mais recursos
ganharam suporte à paralelização.
274 11. Performance
Muitas consultas podem rodar duas ou mais vezes mais rápidas quando se usa paralelização.
O uso de query paralelizadas pode aumentar o consumo de CPU do servidor.
Para se fazer uso de paralelismo, o otimizador de consultas avalia os seguintes parâmetros de
configuração:
• dynamic_shared_memory_type
O valor não pode ser nulo, pois consultas paralelas precisam de memória compartilhada
dinâmica para passar os dados entre os processos. No Linux, seu valor costuma ser
posix.
• parallel_setup_cost
Define a estimativa de custo do planejador para lançar um processo worker paralelo.
• parallel_tuple_cost
Define a estimativa de custo do planejador para transferir uma tupla de um processo
worker para outro.
• min_parallel_table_scan_size
Determina o tamanho mínimo dos dados de uma tabela que devem ser varridos para
uma busca paralela ser considerada.
• min_parallel_index_scan_size
Define o tamanho mínimo de dados de índice que deve ser vasculhado para uma busca
paralela ser considerada.
• max_worker_processes
Configura a quantidade máxima de processos background que o sistema pode suportar.
• max_parallel_workers_per_gather
Determina a quantidade máxima de workers que podem ser iniciados por um único nó
Gather ou Gather Merge.
Os workers paralelos são retirados do pool de processos estabelecido por
max_worker_processes, limitado por max_parallel_workers.
Nota-se que o número solicitado de workers pode não estar realmente disponível no
tempo de execução. Se isso ocorrer, o plano será executado com menos workers do que
o esperado, o que pode ser ineficiente.
Definir seu valor como 0 desativa a execução de consulta paralela.
275 11. Performance
Quando se tem uma certa quantidade de joins muito grande, não há o que possa ser feito
pelo planejador de consultas para poder avaliá-las exaustivamente e ainda retornar planos de
execução em um tempo razoável.
Em situações como essa é que o otimizador de consultas genético (genetic query optimizer -
GEQO) atua.
É feita a numeração de cada tabela que faz parte da junção começando por 1.
O GEQO começa criando alguns desses planos aleatoriamente, então eles são avaliados com
relação às suas adequações, principalmente com relação ao custo de execução de cada um.
Desses planos, os melhores são mantidos e os piores descartados. Algumas mudanças são
feitas, mutações, e o processo se repete por algumas gerações.
Devido à aleatoriedade desse processo, não se deve esperar que os planos gerados pelo GEQO
sejam os mesmos.
É possível controlar isso para ter planos mais consistentes, tornando fixa a semente aleatória
e, então, ter sempre o mesmo valor.
276 11. Performance
Huge pages
O gerenciamento de memória é feito em blocos (páginas). Por padrão, cada bloco tem 4096
bytes (4kb). Sendo assim, um 1MB terá 256 blocos (1 MB = 1024kb; 1024 / 4 = 256).
Um processo, ao utilizar uma porção de memória, faz com que a CPU marque a RAM usada
por esse processo. Para ser mais eficiente, a CPU aloca por blocos de memória e também
para swap.
Endereços de processos são virtuais, então, a CPU e o sistema operacional têm que saber qual
página pertence a qual processo. Quanto mais páginas tiver, mais tempo levará para encontrar
o mapeamento. Por exemplo, um processo que usa 1GB de memória tem 262144 entradas
para procurar (1 GB / 4 kb).
Atualmente, a maior parte das arquiteturas de CPU suportam páginas maiores, o que faz com
que CPU e SO tenham menos entradas para procurar, assim obtendo mais desempenho.
No Linux, o nome é huge pages, em BSDs super pages e no Windows large pages.
Resumindo, huge pages são uma forma de fazer o sistema operacional aperfeiçoar o gerencia-
mento de grandes quantidades de memória por meio do aumento de tamanho dos blocos. Esse
aumento de tamanho de blocos pode ser para 2MB ou 1GB, dependendo da CPU utilizada,
em vez dos “míseros” 4kb. Sendo assim, qual utilizar quando disponível? De maneira geral,
para escalar e gerenciar memória, 2MB são ideais para gigabytes de memória enquanto 1GB
melhores para terabytes de memória.
Menos buscas na tabela de blocos implica em maior performance.
A utilização de huge pages reduz o overhead quando se acessa grandes pedaços contíguos de
memória, no PostgreSQL, para grandes valores de shared_buffers.
Nas flags de CPU pode-se encontrar quanto é suportado para aumentar o tamanho de uma
página, sendo que se houver a flag pse = 2MB e pdpe1gb = 1GB.
Para habilitar huge pages em um servidor PostgreSQL, é necessário fazer modificações no sis-
tema operacional (parâmetros de kernel) e habilitar o parâmetro huge_pages do postgresql.conf,
que é um parâmetro enumerado que aceita três valores: off, on e try. Respectivamente
desabilita, habilita e tenta utilizar huge pages caso esteja disponível no sistema operacional.
Caso não estiver, o serviço vai iniciar sem erro.
277 11. Performance
É um sistema de gerenciamento de memória do Linux para criar huge pages de forma trans-
parente no Linux.
Para sistemas gerenciadores de bancos de dados, é um recurso que pode degradar a perfor-
mance. Cargas em bancos de dados costumam ter acessos à memória mais esparsos do que
padrões contíguos.
É extremamente recomendável para servidores de bancos de dados terem esse recurso desabil-
itado para maior desempenho.
Há um processo do kernel que roda em segundo plano, o khugepaged, que tenta criar huge
pages a partir de blocos contíguos de memória encontrados, então convertendo-os em uma
huge page. A forma como o khugepaged trabalha acaba sendo muito onerosa computacional-
mente e pode causar paralisações, picos de latência em determinadas situações e as páginas
são travadas enquanto ele as estiver manipulando.
[#] Instalar o utilitário bc para cálculos em linha de comando e apagar pacotes baixados em
seguida:
[#] Verificar as flags de CPU e filtrar por cor diferente caso encontrar suporte a huge pages:
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36
clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt rdtscp lm constant_tsc
rep_good nopl nonstop_tsc cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 cx16
sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx rdrand hypervisor lahf_lm cmp_legacy
cr8_legacy abm sse4a misalignsse 3dnowprefetch ssbd vmmcall fsgsbase avx2 rdseed
clflushopt arat
No retorno do comando destacou-se apenas pse para este servidor, indicando que o mesmo só
tem suporte a huge pages de 2M.
278 11. Performance
cat /sys/kernel/mm/transparent_hugepage/enabled
THP habilitado.
sed 's:\(GRUB_CMDLINE_LINUX=".*\)":\
\1 transparent_hugepage=never hugepagesz=2M default_hugepagesz=2M":g' \
-i /etc/default/grub && update-grub2
try
createdb db_bench
init 6
[#] Após conectar-se novamente, verificar se o recurso THP está habilitado no sistema op-
eracional:
cat /sys/kernel/mm/transparent_hugepage/enabled
THP desabilitado.
export HUGE_PAGES_TOTAL_KB=\
`echo "\`cat ~postgres/vmpeak.tmp\` * 1.2" | bc | cut -f1 -d.`
# ID do grupo de sistema postgres, que tem autorização para usar huge pages:
echo "vm.hugetlb_shm_group = `id -g postgres`" >> /etc/sysctl.d/pgsql.conf
# Aplicar as mofificações
sysctl -p /etc/sysctl.d/pgsql.conf
on
12
Observabilidade
• Observabilidade
• Logs
• Análise de logs com pgbadger
• Monitoramento pontual
• Monitoramento contínuo
• Catálogos e views de sistema
• Colunas de sistema
• O Módulo pg_stat_statements
283
284 12. Observabilidade
Observabilidade
É a capacidade de fazer inferências em um sistema a partir de suas saídas para saber de suas
condições internas, trazendo informações importantes como o quê, onde e por quê.
A observabilidade de um sistema auxilia para que haja monitorado, pois para isso é preciso,
antes de mais nada, ser observável.
Métricas, rastreamento e logs são a base da observabilidade.
Métricas
São o alicerce do monitoramento, valores que têm certas informações a respeito de um estado
interno de um sistema. Métricas, habitualmente, são determinadas como números, medidas
ou quantidades que são coletados dentro de um período. Métricas informam coisas como
quanto de disco foi consumido para uma tarefa que gerou arquivos temporários, quanto de
memória foi utilizada para um processamento etc.
Rastreamento
Rastreamento conta com detalhes o caminho de uma solicitação que vai de uma máquina para
outra em um sistema distribuído. Por meio do rastreamento, podem ser vistos efeitos não
planejados de uma solicitação e a perceptibilidade da estrutura dela. Rastreios possibilitam
detalhamento de determinadas solicitações a fim de definir os componentes que acarretaram
erros de sistema, assim, monitorando a fluência entre os componentes e descobrindo gargalos
de performance.
Logs
Monitoramento
Logs
São registros de atividades de sistema de suma importância para administradores, sejam eles
DBAs ou sys admins.
Através dos logs, podemos observar se as coisas estão fluindo normalmente e, caso não estejam,
por meio dos registros de atividades de sistema é possível
tomar uma ação e corrigir o que não estiver conforme o esperado.
https://www.postgresql.org/docs/current/runtime-config-logging.html
Onde registrar
• log_destination
Método para registrar as mensagens do servidor. Parâmetro que aceita os seguintes
valores: stderr (padrão), csvlog e syslog.
Pode-se escolher mais de um valor, declarando-os entre vírgulas.
• logging_collector
Habilita o logging collector, que é um processo background que captura mensagens de
log enviadas e as redireciona para arquivos de log.
• log_directory
Diretório de logs.
• log_filename
String que determina o padrão do nome de arquivo de log.
• log_rotation_age
Depende de logging_collector estar habilitado. Determina quanto tempo vai durar o
arquivo de log até ele ser rotacionado. Essa rotação para outro arquivo só acontecerá
se log_filename for um padrão dinâmico e não fixo, o qual no seu padrão faz menção ao
tempo.
• log_rotation_size
Mesma ideia de log_rotation_age, mas com a rotação de arquivos baseada no tamanho
286 12. Observabilidade
e não no tempo.
• log_truncate_on_rotation
Quando habilitado faz com que o arquivo de log seja sobrescrito em vez de as linhas
novas serem simplesmente adicionadas.
Quando registrar
• log_min_messages
Nível de mensagem que será registrada no log.
Valores válidos são DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NO-
TICE, WARNING (padrão), ERROR, LOG, FATAL, e PANIC.
Cada nível inclui todos os níveis que o seguem e quanto mais avançado for, menos
mensagens serão enviadas para o log.
• log_min_error_statement
Controla se comandos SQL que causam uma condição de erro são registrados em log.
Então, o comando é incluído na entrada de log para qualquer mensagem da severidade
ou maior.
Valores válidos são DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NO-
TICE, WARNING, ERROR (padrão), LOG, FATAL, e PANIC.
• log_min_duration_statement
Tempo mínimo de duração de um comando em que a partir desse tempo de duração o
comando é registrado em log. Ajustando seu valor para Zero, registrará todos comandos
dados, enquanto que se for ajustado para -1, o desabilita.
O que registrar
• application_name
String customizável para identificar o nome da aplicação que se conecta.
• log_checkpoints
Registra ou não em log quando ocorre um checkpoint.
• log_connections
Registra ou não em log quando ocorre uma conexão.
• log_disconnections
Registra ou não em log quando ocorre uma desconexão.
• log_duration
Faz com que a duração de cada comando completado seja registrada em log.
287 12. Observabilidade
• log_error_verbosity
Nível de detalhe a ser escrito em log.
Valores válidos: TERSE, DEFAULT, e VERBOSE.
Cada um adiciona mais campos para serem exibidos nas mensagens.
• log_hostname
Habilitando este parâmetro faz com que se registre o hostname também.
Dependendo da resolução de nome, isso pode causar uma sensível perda de performance.
• log_line_prefix
É um padrão em string que determina como é o início de cada linha de log.
• log_lock_waits
Registrar ou não quando em uma sessão houver uma espera maior que deadlock_timeout
para conseguir uma trava.
Muito útil para determinar se essas esperas estão causando um deterioramento de per-
formance.
• log_statement
Que tipo de comandos SQL são registrados em log.
Valores válidos: none (desabilitado), ddl, mod, e all (todos comandos).
• log_replication_commands
Faz com que cada comando de replicação seja registrado em log.
• log_temp_files
Controla se registra o uso de arquivos temporários e seus respectivos tamanhos.
Zero registra todos e -1 desabilita este parâmetro.
• log_timezone
Configura o timezone (fuso horário) usado para escrever os logs.
• log_autovacuum_min_duration
Registra a execução do autovacuum
288 12. Observabilidade
vim ${PGDATA}/postgresql.conf
log_destination = 'stderr,csvlog'
log_directory = '/var/log/pgsql/<Versão Majoritária>'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_min_duration_statement = 0
sighup
superuser
pg_ctl reload
ls -1 /var/log/pgsql/${PGMAJOR}/
. . .
postgresql-2021-07-21_112620.csv
postgresql-2021-07-21_112620.log
SELECT 1;
SELECT 'foo';
SELECT 5 + 2;
. . .
vim ${PGDATA}/postgresql.conf
sighup
superuser
superuser-backend
pg_ctl reload
createdb db_pgbadger
pgbench -i -s 20 db_pgbadger
292 12. Observabilidade
[$] Executa o pgbench por pouco mais de 5 minutos para gerar volume de logs:
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 90
number of threads: 1
duration: 530 s
number of transactions actually processed: 184227
latency average = 259.026 ms
tps = 347.454849 (including connections establishing)
tps = 347.456221 (excluding connections establishing)
[$] Rodar o pgbadger usando paralelismo para gerar o relatório em formato HTML:
O pgbager com a opção -o determina o arquivo HTML que será o relatório. Como último
parâmetro, há o diretório de logs com todos seus arquivos.
Monitoramento pontual
É fundamental que se conheça as principais ferramentas de monitoramento pontual, bem
como suas métricas para detectar problemas e gargalos. Assim, é possível tomar as devidas
providências.
A seguir, serão vistas ferramentas de S. O. a serem utilizadas para mensurar consumo de
recursos do PostgreSQL.
top
Utilitário muito popular em sistemas operacionais da família Unix. Fornece uma visão dinâmica
em tempo real do sistema, exibe informações sumarizadas, bem como uma lista de processos
ou threads que estão sendo gerenciados pelo kernel do Linux.
top
Acima, temos uma amostra de uma tela do top, que é composta por um cabeçalho com
informações de recursos. Após esse cabeçalho, consta a lista detalhada de processos que estão
rodando no momento.
Informações do cabeçalho:
• load average
A carga média do sistema, a média da quantidade de processos aguardando na fila de
execução (run queue) mais a quantidade de processos em execução, contidos no último
período de 1, 5 e 15 minutos.
Quando um desses números for muito maior que a quantidade de núcleos de CPU,
294 12. Observabilidade
significa que há uma grande carga no sistema e isso pode causar problemas.
• Tasks
Quantidade de processos.
• MiB Mem
Uso de memória RAM.
• MiB Swap
Uso de swap.
htop
O htop é um top melhorado graficamente, pois, apesar de ser em modo texto, apresenta suas
medições CPU, memória RAM e swap em pequenas barras verticais coloridas.
ps
[#] Com a opção -F (extra full format), listar os processos do usuário postgres:
ps -Fu postgres
[#] Com a opção -F (extra full format), listar os processos do comando postgres:
ps -FC postgres
A listagem igual ao comando anterior se deve por causa do processo principal do PostgreSQL
ter o mesmo nome do usuário; postgres.
vmstat
vmstat 5
A primeira coleta (primeira linha) deve ser descartada, pois ela não representa a real situação
do sistema. Diante disso, sempre é necessário informar uma taxa de atualização das coletas,
que neste caso foi de 5 segundos. Caso contrário, ele apresentará somente a primeira coleta.
Descrição:
• procs
– r: quantidade de processos executáveis (em execução ou aguardando o tempo de
execução);
– b: o número de processos em hibernação ininterrupta.
• memory
– swpd: quantidade de memória virtual usada;
– free: quantidade de memória ociosa;
– buff: quantidade de memória usada como buffers;
– cache: quantidade de memória usada como cache;
– inact: quantidade de memória inativa (opção -a);
– active: quantidade de memória ativa (opção -a).
• Swap
– si: quantidade de memória trocada do disco (/s);
– so: quantidade de memória trocada para o disco (/s).
• IO
– bi: blocos recebidos de um dispositivo de bloco (blocos/s);
– bo: blocos enviados para um dispositivo de bloco (blocos/s).
• System
– in: o número de interrupções por segundo, incluindo o relógio;
– cs: o número de mudanças de contexto por segundo.
• CPU
– us: tempo gasto executando código não kernel (tempo de usuário, incluído tempo
nice);
– sy: tempo gasto executando o código do kernel (tempo de sistema);
– id: tempo gasto inativo;
– wa: tempo gasto esperando por IO;
– st: tempo roubado de uma máquina virtual.
297 12. Observabilidade
• Número de trocas de contexto Apresentado na coluna “cs”, este valor tende a subir
quando se aumenta o número de processos.
iostat
iostat /dev/sda 3
iostat -x /dev/sda 3
• Porcentagem de utilização
Apresentado na coluna %util, traz uma aproximação do uso do disco quando atinge
valores próximos a 100%. Pode-se dizer que o disco está saturado.
Aviso
Apesar de ser útil para determinar problemas de desempenho em disco, a métrica de “tempo
de serviço” não é mais confiável no Linux e será removida das próximas versões do iostat.
A métrica de porcentagem de utilização %util não é confiavel para dispositivos que processam
requisições paralelas, como discos SSD, discos que utilizam RAID e quando temos storage
conectado com fibra e multipath.
iotop
O iotop é similar ao top, mas suas informações são de I/O por processo.
Extremamente útil para localizar qual processo está causando uma grande carga de I/O.
iotop
Total DISK READ: 0.00 B/s | Total DISK WRITE: 0.00 B/s
Current DISK READ: 0.00 B/s | Current DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % systemd --switched-root --system --
deserialize 18
2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd]
3 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_gp]
4 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_par_gp]
6 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kworker/0:0H-kblockd]
8 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [mm_percpu_wq]
9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0]
10 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_sched]
. . .
pg_top
Outra ferramenta similar ao top, sendo que esta é dedicada para exibir apenas sobre processos
PostgreSQL.
pg_top
. . .
PID USERNAME PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND
718 postgres 20 0 205M 5648K sleep 0:00 0.00% 0.00% postgres: background writer
719 postgres 20 0 205M 9860K sleep 0:00 0.00% 0.00% postgres: walwriter
. . .
10 segments retransmitted
0 bad segments received
Nota-se que houve retransmissão de pacotes (10 segments retransmitted), o que significa que
houve perda de pacotes.
302 12. Observabilidade
Monitoramento contínuo
O monitoramento pontual é muito útil para extrair informações rápidas e imediatas, mas o
monitoramento contínuo permite ter histórico.
O histórico de um monitoramento contínuo é muito importante para se extrair conclusões
(insights) conforme o comportamento de algo monitorado ao longo do tempo.
O histórico de monitoramento permite também investigar melhor questões de resolução de
problemas (troubleshooting) e performance.
Há soluções de monitoramento contínuo open source com interface web que facilitam muito
a visualização de dados através de seus gráficos. Também há outras funcionalidades, como
alertas para evitar ou avisar a respeito de incidentes, por exemplo:
• Prometheus / Grafana;
• Zabbix.
SAR
O log binário tem os seguintes formatos, um somente dia atual, o outro data completa:
• /var/log/sysstat/saDD (prioridade)
• /var/log/sysstat/saYYYYMMDD
sar -A
rm -f /var/log/sysstat/*
[#] Criar uma coleta com intervalo de 1 segundo e 50 amostras no formato YYYYMMDD:
sar -A
Após a geração do log binário, o utilitário sar pode exibir os dados coletados.
304 12. Observabilidade
sar 2 10
[$] 1 coleta, 1 segundo, modo de impressão em tela pretty, por cada dispositivo de armazena-
mento:
sar -p -d 1 1
Os catálogos de sistema são os locais onde o sistema gerenciador de banco de dados armazena
os metadados, tais como informações sobre tabelas e colunas, além de informações de controle
interno.
O sistema de catálogos do PostgreSQL são tabelas regulares.
É possível apagar e recriar as tabelas, adicionar colunas, inserir e alterar valores, e claro, dan-
ificar seu sistema dessa forma.
Normalmente, não se deve alterar os catálogos do sistema manualmente, há sempre os co-
mandos SQL para fazer isso. (Por exemplo, CREATE DATABASE insere uma linha no catálogo
pg_database e cria o banco de dados na hora em disco).
Há algumas exceções para operações particularmente esotéricas (incomuns), como a adição
de métodos de acesso de índice.
A maior parte dos catálogos de sistema é copiada do banco de dados modelo (template) du-
rante a criação de uma base de dados. Depois disso, um banco de dados específico.
Alguns poucos catálogos são fisicamente compartilhados através de todas bases de dados em
um cluster ; esses são observados nas descrições dos catálogos individuais.
Os catálogos do PostgreSQL estão nos schemas pg_catalog e information_schema.
https://www.postgresql.org/docs/current/static/catalogs.html
SELECT
schemaname||'.'||relname AS catalogo
FROM pg_stat_sys_tables
WHERE schemaname IN ('pg_catalog', 'information_schema')
ORDER BY schemaname, relname;
catalogo
--------------------------------------------
information_schema.sql_features
information_schema.sql_implementation_info
information_schema.sql_parts
. . .
Views de sistema
Em adição aos catálogos, o PostgreSQL oferece uma série de views embutidas (built in).
Algumas views de sistema fornecem acesso facilitado para algumas consultas em catálogos do
sistema.
Outras views oferecem acesso ao estado interno do servidor.
306 12. Observabilidade
https://www.postgresql.org/docs/current/static/views-overview.html
SELECT
n.nspname||'.'||c.relname AS view_de_sistema
FROM pg_class AS c
INNER JOIN pg_namespace AS n
ON (c.relnamespace = n.oid)
WHERE n.nspname IN ('pg_catalog', 'information_schema')
AND c.relkind = 'v'
ORDER BY random();
view_de_sistema
----------------------------------------------------------
information_schema.parameters
pg_catalog.pg_stat_wal_receiver
pg_catalog.pg_stat_sys_indexes
. . .
Views estatísticas
O PostgreSQL tem estatísticas de utilização de objetos e recursos, que são coletadas pelo
coletor de estatísticas e disponibilizadas em forma de views de sistema.
Essas visões (views) estão no schema pg_catalog e têm sua nomenclatura iniciada por pg_stat
ou pg_statio, sendo que esse último tipo são para estatísticas de I/O de objetos.
Dentre essas views:
• pg_stat_activity
Uma linha por processo, exibindo informações relativas à atividade atual do processo;
• pg_stat_bgwriter
Sempre terá uma única linha, a qual exibe informações globais da instância a respeito
do bgwriter ;
• pg_stat_database
307 12. Observabilidade
Uma linha por base de dados e para objetos compartilhados exibindo suas estatísticas;
• pg_stat_(all,user,sys)_tables
Informações estatísticas de tabelas.
• pg_stat_(all,user,sys)_indexes
Informações estatísticas de índices.
• pg_statio_(all,user,sys)_tables
Informações estatísticas de I/O de tabelas.
• pg_statio_(all,user,sys)_indexes
Informações estatísticas de I/O de índices.
• pg_statio_(all,user,sys)_sequences
Informações estatísticas de I/O de sequências.
Information Schema
https://www.postgresql.org/docs/current/static/information-schema.html
column_name | data_type
-------------+-----------
id_ | integer
campo1 | smallint
campo2 | integer
campo3 | bigint
campo4 | text
Colunas de sistema
• oid: identificador de objeto (object ID) de uma linha
Está presente apenas se a tabela foi criada usando WITH OIDS, seu tipo também se chama
oid.
• cmin:
Identificador de comando
Começa por zero dentro da transação de inserção.
O Módulo pg_stat_statements
O módulo pg_stat_statements fornece meios de acompanhar estatísticas de execução de todos
comandos SQL executados no servidor.
Este módulo deve ser carregado adicionando pg_stat_statements ao parâmetro shared_preload_libraries
no postgresql.conf, porque ele precisa de memória compartilhada adicional e é necessário um
restart no serviço.
Ao carregar esse módulo, ele rastreia estatísticas através de todas as bases do servidor.
Para acessar e manipular essas estatísticas, é fornecida uma view de mesmo nome e as funções
pg_stat_statements_reset e pg_stat_statements.
https://www.postgresql.org/docs/current/static/pgstatstatements.html
• - integer - postmaster
pg_stat_statements.max
Quantidade máxima de comandos rastreados pelo módulo.
• - enum - superuser
pg_stat_statements.track
Define que comandos serão contados.
– top: comandos top-level, são feitos diretamente pelos clientes;
– all: todos comandos;
– none: desabilita.
• - boolean - superuser
pg_stat_statements.track_utility
Define se comandos de utilidade (SELECT, INSERT, UPDATE e ‘DELETE) são rastreados.
• - boolean - superuser
pg_stat_statements.track_planning
Controla se operações de planejamento e duração são rastreadas. Habilitando este
parâmetro, pode resultar em uma perceptível perda de performance.
mkdir ${PGDATA}/conf.d
vim ${PGDATA}/postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
include_dir = 'conf.d'
read -p \
'Digite a versão majoritária: ' \
PGMAJOR
createdb db_bench
314 12. Observabilidade
pgbench -i db_bench
. . .
dropdb db_bench
13
Particionamento de tabelas
• O que é particionamento de tabelas
• Particionamento por faixa de valores (RANGE)
• Particionamento por lista enumerada (LIST)
• Particionamento por hash
• Particionamento multinível
315
316 13. Particionamento de tabelas
https://www.postgresql.org/docs/current/ddl-partitioning.html
• Desempenho
Tabelas são representadas fisicamente por data files. Se uma tabela é particionada,
não será mais somente um arquivo. Assim, em consultas cujos dados estejam em uma
determinada partição, sua leitura ou escrita serão mais rápidas por não precisar ler blocos
de partes que não sejam pertinentes.
Os índices também serão menores, pois, ao criar um índice para uma tabela particionada,
na verdade serão vários índices variando de quantidade conforme o número de partições,
ou seja, cada partição terá seu próprio índice.
• Remoções de dados
A operação de DELETE tem alto custo, pois produz tuplas mortas e continua ocupando
espaço em disco.
Se uma certa quantidade de dados for exatamente de acordo com o critério de uma
partição, pode-se utilizar ALTER TABLE ... DETACH PARTITION ou um simples DROP TABLE na(s)
partição(es), o que faz com que se evite overhead por conta de uma operação de vacuum.
317 13. Particionamento de tabelas
• Redução de custo
Dados usados raramente podem ser migrados para mídias de armazenamento mais
baratas e mais lentas.
• Manutenção
Para operações de VACUUM ou ANALYZE, em vez de ser executado em uma grande tabela,
será em suas partições. Dependendo da quantidade de memória, o tamanho de uma
partição pode caber dentro dela, assim, agilizando todo o processo.
• Travas (locks)
Redução de locks graças a um certo grau de autonomia que as partições têm.
• Conteúdo da tabela precisa ser distribuído em dispositivos diferentes conforme seu uso;
• Muitos problemas com locks;
• Desempenho degradado;
• Quando consultas cujas condições trazem um conjunto específico de dados em vez de
percorrer toda tabela;
• Tabelas de log ou de histórico;
• Tabelas cujos tamanhos superam a memória RAM;
• Como ponto de partida de tamanho, tabelas a partir de 20GB devem ser consideradas.
É a partição definida para pegar valores que não combinam com quaisquer outras partições.
Partition pruning
Partition pruning é o termo utilizado para descrever a escolha de um plano pelo planejador de
consultas em que a consulta satisfaz o critério de particionamento de uma tabela, de forma a
evitar ler partições desnecessárias da tabela particionada. Assim reduzindo I/O significativa-
mente.
O parâmetro de configuração enable_partition_pruning, que é booleano, define se esse recurso
é habilitado ou não. Por padrão, é habilitado.
318 13. Particionamento de tabelas
Padronização
Crie as partições seguindo um padrão de forma que futuras partições possam ser criadas
dinamicamente obedecendo um critério.
Por exemplo, uma tabela que se chama tb_vendas e que será particionada por faixas de meses:
tb_vendas_201901, tb_vendas_201902, tb_vendas_201903. . .
Namespaces (schemas)
Crie schemas próprios para as partições, pois assim facilitará a visualização das tabelas exis-
tentes.
É sempre bom organizar objetos e, conforme dito anteriormente, faça isso de forma padronizada.
319 13. Particionamento de tabelas
Na chave primária, é necessário ter o campo dt, que é critério de particionamento, assim
formando a chave de particionamento.
[>] Utilizando o recurso de CTE, gerar uma massa de dados com um milhão de registros,
utilizando datas aleatórias com um intervalo entre 01/01/2019 a 31/03/2020:
SELECT
relname AS tabela,
pg_size_pretty(pg_relation_size(relname::regclass)) AS tamanho,
reltuples::int8 AS registros
FROM pg_class
WHERE relname ~ 'tb_intervalo'
AND relkind = 'r'
ORDER BY relname;
Nota-se que, ao colocar uma parte da faixa de datas fora do intervalo. elas foram parar na
tabela default.
SHOW enable_partition_pruning;
enable_partition_pruning
--------------------------
on
323 13. Particionamento de tabelas
[>] Executar uma consulta verificando seu plano execução com partition pruning habilitado:
EXPLAIN ANALYZE
SELECT count(*)
FROM tb_intervalo
WHERE dt BETWEEN '2019-07-01' AND '2019-07-27';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Aggregate (cost=1460.84..1460.85 rows=1 width=8) (actual time=8.072..8.072 rows=1 loops=1)
-> Seq Scan on tb_intervalo_2019_07 tb_intervalo (cost=0.00..1313.84 rows=58800 width=0) (actual time=0.0
Filter: ((dt >= '2019-07-01'::date) AND (dt <= '2019-07-27'::date))
Rows Removed by Filter: 8642
Planning Time: 0.095 ms
Execution Time: 8.087 ms
Observa-se que a partir da condição de consulta, só foi necessário verificar dados em uma
única partição.
[>] Executar uma consulta verificando seu plano execução com partition pruning desabilitado:
EXPLAIN ANALYZE
SELECT count(*)
FROM tb_intervalo
WHERE dt BETWEEN '2019-07-01' AND '2019-07-27';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=14439.58..14439.59 rows=1 width=8) (actual time=26.967..29.905 rows=1 loops=1)
-> Gather (cost=14439.37..14439.58 rows=2 width=8) (actual time=26.838..29.900 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=13439.37..13439.38 rows=1 width=8) (actual time=24.758..24.762 rows=1 lo
-> Parallel Append (cost=0.00..13378.09 rows=24512 width=0) (actual time=19.380..23.679 rows=
-> Parallel Seq Scan on tb_intervalo_default tb_intervalo_13 (cost=0.00..2636.58 rows=1
Filter: ((dt >= '2019-07-01'::date) AND (dt <= '2019-07-27'::date))
Rows Removed by Filter: 198966
-> Parallel Seq Scan on tb_intervalo_2019_08 tb_intervalo_8 (cost=0.00..909.25 rows=1 w
Filter: ((dt >= '2019-07-01'::date) AND (dt <= '2019-07-27'::date))
Rows Removed by Filter: 68595
. . .
Planning Time: 0.553 ms
Execution Time: 29.989 ms
Nesse caso, pode ser notado que a execução demorou muito além de desnecessariamente ler
partições que não continham os dados que satisfazem a condição da consulta.
324 13. Particionamento de tabelas
Se novos dados pertinentes ao seu critério forem inseridos na tabela particionada, essa partição
não receberá mais os registros.
Ao anexar uma partição, que pode ser qualquer tabela que tenha a mesma estrutura da tabela
particionada, precisamos determinar o critério de dados válidos da partição.
Um simples DROP TABLE resolve. É muito útil quando preciso liberar espaço em casos que não
há mais necessidade dos dados ali presentes.
Muito melhor para a base de dados do que dar um DELETE.
[>] Partições:
-- Região Sul
CREATE TABLE tb_cidade_sul
PARTITION OF tb_cidade
FOR VALUES IN ('RS', 'SC', 'PR');
-- Região Sudeste
CREATE TABLE tb_cidade_sudeste
PARTITION OF tb_cidade
FOR VALUES IN ('SP', 'RJ', 'MG', 'ES');
-- Região Nordeste
CREATE TABLE tb_cidade_nordeste
PARTITION OF tb_cidade
FOR VALUES IN ('MA', 'PI', 'CE', 'RN', 'PB', 'PE', 'AL', 'SE', 'BA');
-- Região Norte
CREATE TABLE tb_cidade_norte
PARTITION OF tb_cidade
FOR VALUES IN ('AC', 'AM', 'RO', 'RR', 'AP', 'PA', 'TO');
326 13. Particionamento de tabelas
[>] Dados:
-- Todas cidades
SELECT uf, nome FROM tb_cidade;
uf | nome
----+-----------------------
RO | Porto Velho
RN | Natal
SP | São Paulo
SP | Santo André
SP | São Bernardo do Campo
RJ | Niterói
MG | Belo Horizonte
MG | Varginha
RS | Porto Alegre
PR | Curitiba
-- Cidades do Sul
SELECT uf, nome FROM tb_cidade_sul;
uf | nome
----+--------------
RS | Porto Alegre
PR | Curitiba
327 13. Particionamento de tabelas
-- Cidades do Sudeste
SELECT uf, nome FROM tb_cidade_sudeste;
uf | nome
----+-----------------------
SP | São Paulo
SP | Santo André
SP | São Bernardo do Campo
RJ | Niterói
MG | Belo Horizonte
MG | Varginha
Para a parte prática, serão criadas 5 (cinco) partições. O módulo será 5 e para cada uma e
terá um resto variando de 0 (zero) a 4 (quatro).
[>] Dados:
SELECT
relname AS tabela,
pg_size_pretty(pg_relation_size(relname::regclass)) AS tamanho,
reltuples::int8 AS registros
FROM pg_class
WHERE relname ~ 'tb_hash'
AND relkind = 'r'
ORDER BY relname;
WITH t AS (
SELECT
relname AS tabela,
pg_size_pretty(pg_relation_size(relname::regclass)) AS tamanho,
reltuples::int8 AS registros
FROM pg_class
WHERE relname ~ 'tb_hash_'
AND relkind = 'r'
ORDER BY relname)
SELECT round(avg(registros)) AS media FROM t;
media
--------
200000
Particionamento multinível
Há situações em que é válido fazer particionamento aninhado de tabelas, um particionamento
multinível.
Cada nível de particionamento pode ser do mesmo tipo entre si ou não.
Neste case, as vendas foram divididas em estados e cada estado por cidade. Em ambos os
níveis, o tipo de particionamento utilizado é por lista, mas poderia ser misto.
Fig. 13.1: Particionamento multinível: de vendas geral para estado e de estado para cidade
-- SP ------------------------------------------------------------------------
CREATE TABLE tb_venda_sp
PARTITION OF tb_venda
FOR VALUES IN ('SP')
PARTITION BY LIST (cidade);
-- RJ ------------------------------------------------------------------------
CREATE TABLE tb_venda_rj
PARTITION OF tb_venda
FOR VALUES IN ('RJ')
PARTITION BY LIST (cidade);
-- MG ------------------------------------------------------------------------
CREATE TABLE tb_venda_mg
PARTITION OF tb_venda
FOR VALUES IN ('MG')
PARTITION BY LIST (cidade);
\dt
List of relations
Schema | Name | Type | Owner
--------+----------------------------+-------------------+----------
public | tb_venda | partitioned table | postgres
public | tb_venda_mg | partitioned table | postgres
public | tb_venda_mg_belo_horizonte | table | postgres
public | tb_venda_mg_betim | table | postgres
public | tb_venda_rj | partitioned table | postgres
public | tb_venda_rj_niteroi | table | postgres
public | tb_venda_rj_rio_de_janeiro | table | postgres
public | tb_venda_sp | partitioned table | postgres
public | tb_venda_sp_bauru | table | postgres
public | tb_venda_sp_santo_andre | table | postgres
public | tb_venda_sp_santos | table | postgres
public | tb_venda_sp_sao_paulo | table | postgres
334 13. Particionamento de tabelas
COPY tb_venda (uf, cidade, dt, total) FROM STDIN DELIMITER ';';
SP;São Paulo;2019-11-23;52157121
SP;São Paulo;2019-12-05;875441
SP;São Paulo;2020-07-08;71511524
SP;São Paulo;2020-09-01;257851
SP;São Paulo;2021-05-18;215478478
SP;São Paulo;2021-11-23;225485745
SP;Bauru;2020-08-23;545485
SP;Bauru;2021-10-05;897951
SP;Santos;2021-03-08;6981548
SP;Santos;2021-09-09;5487878
SP;Santo André;2021-04-19;215484
SP;Santo André;2021-07-13;21545
RJ;Rio de Janeiro;2020-04-20;187851
RJ;Rio de Janeiro;2021-06-19;105678475
RJ;Rio de Janeiro;2021-02-16;175485723
RJ;Niterói;2020-11-14;545985
RJ;Niterói;2021-12-31;1045477
MG;Belo Horizonte;2020-07-05;197008452
MG;Belo Horizonte;2020-08-03;197775703
MG;Belo Horizonte;2021-01-05;388391
MG;Belo Horizonte;2021-10-15;507922
MG;Betim;2020-11-11;3998000
MG;Betim;2021-06-27;4799014
MG;Betim;2021-07-17;159999
EXPLAIN ANALYZE
SELECT count(*)
FROM tb_venda
WHERE cidade = 'Betim';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Aggregate (cost=21.51..21.52 rows=1 width=8) (actual time=0.013..0.013 rows=1 loops=1)
-> Seq Scan on tb_venda_mg_betim tb_venda (cost=0.00..21.50 rows=5 width=0) (actual time=0.009..0.010 row
Filter: (cidade = 'Betim'::text)
Planning Time: 0.110 ms
Execution Time: 0.032 ms
14
Backup e Restauração
• Sobre Backup e Restauração
• Preparação do ambiente
• Backup Físico Off Line: Snapshot
• Backup Físico Online
• pg_basebackup
• Dump
• PITR - Point In Time Recovery
335
336 14. Backup e Restauração
Backup físico
É o tipo de backup que é feito fazendo uma cópia de segurança dos arquivos de dados (data
files).
Costuma ser mais rápido que um dump, pois não há processamento dos dados, mas sim cópia
de arquivos.
Essa abordagem permite fazer o backup full e o backup incremental.
Como o próprio nome sugere, é feita a cópia de segurança de todos os arquivos de dados da
base.
Backup incremental
Nesta estratégia de backup, é necessário que já haja um backup completo, pois apenas serão
adicionados os arquivos que foram modificados e / ou adicionados, relativos a esse backup
completo.
Backup lógico
Também conhecido como dump, é a transcrição lógica da base de dados para um arquivo ou
diretório (conforme o tipo de dump).
Um dump contém instruções SQL que devem ser processadas uma por uma, tanto no processo
de gerar o dump quanto em sua restauração.
Muito vantajoso para fazer migração para uma versão mais nova. No entanto, devido ao
processamento de cada instrução SQL, pode ser muito mais demorado do que um backup
físico.
Um dump pode ser de toda instância (com pg_dumpall), de apenas uma base de dados, de
um schema ou de uma tabela, ou seja, tem granulariadade.
337 14. Backup e Restauração
É uma política de backup que diz respeito à tolerância de perda de dados que se pode ter em
caso de desastre.
RPO tem relação direta com a frequência com que são feitos os backups. Por exemplo, em
uma organização, o backup é feito diariamente. Então, o RPO é de 1 dia.
Quanto menor o RPO, melhor, pois em uma possível perda de dados o impacto será menor
também.
Preparação do ambiente
Procedimentos para o laboratório de backup e restauração.
[$] Apagar qualquer base de dados que não seja postgres, template0 ou template1:
psql -Atqc \
"
SELECT datname
FROM pg_database
WHERE datname NOT IN ('template0', 'template1', 'postgres')
" | xargs -i dropdb {}
psql -Atqc \
"
SELECT spcname
FROM pg_tablespace
WHERE spcname NOT IN ('pg_default', 'pg_global');
" | xargs -i psql -qc "DROP TABLESPACE {};"
wget https://ftp.postgresql.org/pub/projects/pgFoundry/dbsamples/pagila/\
pagila/pagila-0.10.1.zip -P /tmp/ && \
cd /tmp && \
unzip pagila-0.10.1.zip
339 14. Backup e Restauração
createdb pagila
Diretórios
A seguir, a descrição de diretórios que serão utilizados para os exercícios de backup conforme
a finalidade.
Backup físico
Descrição Diretório
Backup de dados /var/backups/pgsql/<versão majoritária>/data
Backup de WAL /var/backups/pgsql/<versão majoritária>/wal
Descrição Diretório
Compacto /var/backups/pgsql/<versão majoritária>/dump/compact
Custom /var/backups/pgsql/<versão majoritária>/dump/custom
Diretório /var/backups/pgsql/<versão majoritária>/dump/dir
Tar /var/backups/pgsql/<versão majoritária>/dump/tar
Texto plano /var/backups/pgsql/<versão majoritária>/dump/text
tree /var/backups/pgsql
/var/backups/pgsql
|-- 13
|-- data
|-- dump
| |-- compact
| |-- custom
| |-- dir
| |-- tar
| |-- text
|-- wal
9 directories, 0 files
341 14. Backup e Restauração
pg_ctl stop
cd `dirname "${PGDATA}"`
[$] A partir do diretório pai, criar um tar.gz em /tmp/ do diretório do cluster (data):
vim /tmp/data/postgresql.conf
Desativar quem têm vínculos com o diretório de dados do cluster principal, por isso, precisam
ser comentados:
• data_directory
• hba_file
• ident_file
• external_pid_file
342 14. Backup e Restauração
template1
template0
postgres
pagila
pg_ctl start
343 14. Backup e Restauração
Arquivamento contínuo
Logs de transação (WAL) possibilitam usar uma terceira estratégia para fazer backup de bases
de dados: podemos combinar backup em nível de sistema de arquivos com o backup de
arquivos do WAL.
Se precisar fazer uma recuperação, restaura-se o backup do sistema de arquivos e, então,
reaplica-se os arquivos do WAL que foram guardados em backup (arquivados) para trazer o
sistema ao estado atual. O arquivamento contínuo é feito a partir do arquivamento do WAL
(arquivos de log de transação), em que cada WAL arquivado pode ser utilizado para replay
(reaplicação).
Essa abordagem é mais complexa para administrar do que cada uma das outras abordagens,
mas tem alguns benefícios significantes:
• Não precisa fazer o replay de entradas do WAL de todo caminho até o fim. Poderia parar
o replay a qualquer ponto e ter um snapshot consistente da base de dados como era
antes. Assim, essa técnica suporta recuperação em um ponto no tempo (point-in-time
recovery ). É possível restaurar a base de dados para seu estado em qualquer tempo
desde que seu backup foi feito.
• Estratégico para grandes bases de dados, onde não é conveniente fazer backups inteiros
frequentemente.
Para iniciar o servidor em modo de recuperação de backup (recovery mode), é preciso criar
um arquivo chamado recovery.signal dentro do diretório de dados - apenas um arquivo vazio
344 14. Backup e Restauração
Até a versão 11 do PostgreSQL, para fins de restauração de backup online ou PITR, havia
um arquivo dentro do $PGDATA denominado recovery.conf.
Era um arquivo de configuração com as mesmas características do postgresql.conf, mas para
fins de restauração.
A partir do PostgreSQL 12 o recovery.conf foi abolido, tendo seus parâmetros absorvidos pelo
próprio postgresql.conf.
Suas configurações só se aplicam durante a recuperação.
Não podem ser alteradas uma vez que a operação de restauração se iniciou.
• %f:
apenas o nome do arquivo WAL;
• %p: caminho completo do arquivo WAL;
• %r: nome do arquivo que contém o último ponto de reinício válido, o qual pode ser
tomado como base para remover seus antecessores e economizar espaço;
• %%: escreve o caractere %.
• pg_wal_replay_pause
Pausa uma recuperação.
Enquanto uma recuperação estiver pausada, nenhuma alteração será aplicada.
Se for um servidor standby, todas novas consultas feitas enxergarão o mesmo snapshot
consistente do cluster.
Restrita a super usuários.
• pg_wal_replay_resume
Retoma uma recuperação que foi pausada.
• pg_is_wal_replay_paused
Retorna true se uma recuperação estiver pausada.
• pg_promote
Promove um standby para primário.
https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-RECOVERY-
345 14. Backup e Restauração
Três parâmetros precisam que o serviço seja reinicializado para serem aplicados.
vim ${PGDATA}/postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'rsync -a %p /var/backups/pgsql/<versão majoritária>/wal/%f'
archive_cleanup_command = 'pg_archivecleanup /var/backups/pgsql/<versão majoritária>/wal %r
'
restore_command = 'rsync -a /var/backups/pgsql/<versão majoritária>/wal/%f %p'
346 14. Backup e Restauração
Aviso
pg_ctl restart
NOW=`date +%Y%m%d-%H%M`
echo ${NOW}
20210308-1516
[$] Função pg_start_backup que avisa o servidor que será feito um backup:
echo ${CHKPNT_START}
0/13000028
cat ${PGDATA}/backup_label
pg_walfile_name
--------------------------
000000020000000000000013
echo ${CHKPNT_FINAL}
348 14. Backup e Restauração
0/13000138
pg_walfile_name
--------------------------
000000020000000000000013
touch ${PGDATA}/recovery.signal
pg_ctl start
postgres
pagila
template1
template0
349 14. Backup e Restauração
pg_basebackup
O pg_basebackup é um utilitário para fazer backups online de clusters PostgreSQL. Os back-
ups são feitos sem afetar outros clientes na base e podem ser usados para PITR (Point In
Time Recovery ) e / ou como ponto de partida para log shipping ou servidores standby de
replicação via streaming.
Case
Será feito o backup físico online de uma instância que tem um tablespace.
pg_basebackup \
-X stream \
-T /var/local/pgsql/${PGMAJOR}/ts/alpha=/var/backups/pgsql/${PGMAJOR}/ts/alpha \
-D /var/backups/pgsql/${PGMAJOR}/data/pg_basebackup
350 14. Backup e Restauração
# data_directory
sed 's/^\(data_directory.*\)/#\1/g' -i \
/var/backups/pgsql/${PGMAJOR}/data/pg_basebackup/postgresql.conf
# hba_file
sed 's/^\(hba_file.*\)/#\1/g' -i \
/var/backups/pgsql/${PGMAJOR}/data/pg_basebackup/postgresql.conf
# ident_file
sed 's/^\(ident_file.*\)/#\1/g' -i \
/var/backups/pgsql/${PGMAJOR}/data/pg_basebackup/postgresql.conf
# external_pid_file
sed 's/^\(external_pid_file.*\)/#\1/g' -i \
/var/backups/pgsql/${PGMAJOR}/data/pg_basebackup/postgresql.conf
List of tablespaces
Name | Owner | Location
------------+----------+--------------------------------
pg_default | postgres |
pg_global | postgres |
ts_alpha | postgres | /var/backups/pgsql/13/ts/alpha
351 14. Backup e Restauração
postgres
pagila
template1
template0
db_teste
Dump
A ideia por trás deste método é gerar um arquivo com comandos SQL que, ao ser utilizado
para uma restauração do servidor, recriará o banco de dados ou o cluster no mesmo estado
como estava na hora do dump.
É também conhecido como backup lógico.
Não permite a estratégia de backup incremental.
Não permite recuperar dados em uma parte do tempo (PITR: Point In Time Recovery ).
Pode ser usado como método de migração entre uma versão e outra do PostgreSQL; faz-se o
dump na versão antiga e restaura na nova.
A partir da versão 9.3 do PostgreSQL, o pg_dump conta com uma opção muito interessante:
-j njobs ou --jobs=n jobs
Tal opção permite o dump rodar em paralelo, dividindo em N trabalhos (jobs) simultanea-
mente.
Essa opção reduz o tempo em que o dump é feito, mas também aumenta a carga no servidor
de banco de dados.
Só pode ser usada com o formato de saída de diretório (-Fd), porque é o único formato em
que múltiplos processos podem escrever seus dados ao mesmo tempo.
O pg_dump abre N jobs + 1 conexões, o que depende da configuração de max_connections ser
alta o suficiente para acomodar todas.
353 14. Backup e Restauração
pg_restore
O pg_restore é um utilitário para restaurar uma base PostgreSQL de um arquivo criado pelo
pg_dump. Ele emitirá comandos necessários para reconstruir a base ao estado como era no
momento que o arquivo de dump foi salvo.
Os arquivos de dump são projetados para serem portáveis entre arquiteturas.
Dentre seus parâmetros de configuração, é interessante comentar sobre:
Dividindo a tarefa em vários jobs, é reduzido o tempo, porém, exige mais do servidor.
tabela
----------
actor
address
category
city
country
tabela
----------
actor
address
category
city
country
tabela
----------
actor
address
category
city
country
tabela
----------
actor
address
category
city
country
dropdb pagila
psql -f /var/backups/pgsql/${PGMAJOR}/dump/text/pagila.sql
tabela
----------
actor
address
357 14. Backup e Restauração
category
city
country
pg_dump -C pagila | \
gzip -9 > /var/backups/pgsql/${PGMAJOR}/dump/compact/pagila.gz
dropdb pagila
tabela
----------
actor
address
category
city
country
pg_dump -C pagila | \
bzip2 -9 > /var/backups/pgsql/${PGMAJOR}/dump/compact/pagila.bz2
dropdb pagila
359 14. Backup e Restauração
tabela
----------
actor
address
category
city
country
du -hs /var/backups/pgsql/${PGMAJOR}/dump/* | \
fgrep -v compact && \
du -hs /var/backups/pgsql/${PGMAJOR}/dump/compact/*
696K /var/backups/pgsql/13/dump/custom
748K /var/backups/pgsql/13/dump/dir
2.9M /var/backups/pgsql/13/dump/tar
2.8M /var/backups/pgsql/13/dump/text
476K /var/backups/pgsql/13/dump/compact/pagila.bz2
612K /var/backups/pgsql/13/dump/compact/pagila.gz
No final das contas, vemos que podemos reduzir o tempo do dump, do tamanho do arquivo
ou diretório resultante conforme o tipo de dump utilizado.
pg_dumpall
postgres
template1
template0
postgres
pagila
template1
template0
361 14. Backup e Restauração
actor
address
category
city
country
[$] Redirecionando a saída do dump para o compactador bzip2 que cria um arquivo com-
pactado:
. . .
real 0m1.753s
user 0m0.021s
sys 0m0.009s
. . .
real 0m1.841s
user 0m0.109s
sys 0m0.018s
Ao utilizar compactação, economizará espaço, mas, se for preciso fazer uma restauração a
partir de um dump compactado, levará mais tempo.
Backup
|
Antes do backup | Pós Backup
|
Linha do tempo: -------------------o------------------------------------->
Como demonstrado acima, conforme a linha do tempo, somente a partir do momento em que
se faz um backup é possível utilizar o PITR.
[$] Variável que guarda um ponto no tempo em que a base de dados está OK:
sleep 7
psql -d pagila -qc 'DELETE FROM actor WHERE actor_id > 200 RETURNING *;'
archive_mode | on | postmaster
restore_command | rsync -a /var/backups/pgsql/13/wal/%f %p | postmaster
wal_level | replica | postmaster
pg_ctl stop
cp ${PGDATA}/postgresql.conf \
/var/backups/pgsql/${PGMAJOR}/data/pitr/postgresql.conf
touch /var/backups/pgsql/${PGMAJOR}/data/pitr/recovery.signal
Roberto Vivar
Ramón Bolaños
Florinda de las Nieves
Carlos Valdez
367 14. Backup e Restauração
pg_ctl start
Roberto Vivar
Ramón Bolaños
Florinda de las Nieves
Carlos Valdez
María Antonieta Meza
Edgar Villagrán
pg_ctl promote
369 14. Backup e Restauração
370
371 15. Replicação
O que é replicação
É o processo de copiar dados de um servidor a outro de forma contínua entre eles.
Conceitos de replicação
• Upstream
É o servidor de origem, onde outro(s) servidor(es) busca(m) seus dados.
• Replicação cascateada
Quando um standby é standby de outro standby e não de um servidor primário. Ou
seja, quando um standby tem como upstream outro standby.
• Replicação física
Envia alterações de blocos byte a byte do WAL.
• Replicação lógica
É um método em que são replicados objetos e suas alterações, linha por linha.
Permite um controle granular tanto da replicação de dados quanto da segurança.
• Replicação síncrona
Confirma se as alterações feitas em uma transação foram devidamente replicadas para
os servidores standbys. Isso estende o nível padrão de durabilidade que é oferecido por
uma efetivação.
Com replicação síncrona faz com que tenha muito mais demora nas efetivações.
• Replicação assíncrona
Mesmo que irrisório há um tempo de propagação entre primário e standbys.
É uma abordagem mais rápida que a replicação síncrona, durante algum tempo, mesmo
que pouquíssimo tempo, os standbys estarão defasados com relação ao primário.
Se o servidor primário sofre parada abrupta (crash), certas transações que foram efe-
tivadas podem não ter sido replicadas para os standbys, causando perda de dados. A
quantidade de perda de dados é proporcional ao atraso da replicação no momento do
failover.
• Failover
Situação em que o servidor primário está fora e um dos standbys assume como primário
para fins de alta disponibilidade.
• Failback ou rejoin
Processo de reintegrar ao cluster de replicação um servidor que falhou.
373 15. Replicação
Slots de replicação
Fornecem uma maneira automatizada para garantir que o servidor master não removerá ar-
quivos do WAL até que eles tenham sido recebidos por todos standbys e que o master não
remova linhas que poderiam causar um conflito de recuperação quando o standby estiver fora.
É possível prevenir a remoção de segmentos do WAL utilizando o parâmetro wal_keep_size em
vez de slots de replicação. Ou também armazenando os segmentos usando o archive_command.
No entanto, esses métodos alternativos aos slots de replicação frequentemente resultam em
reter mais segmentos de WAL do que realmente precisa, enquanto que os slots de replicação
mantêm apenas o que for necessário.
É importante saber que slots de replicação podem reter muitos segmentos de WAL que podem
encher o espaço alocado para o pg_wal (diretório de armazenamento padrão dos segmentos
de WAL). O parâmetro max_slot_wal_keep_size limita o tamanho de arquivos WAL retidos por
slots de replicação.
De forma parecida, os parâmetros hot_standby_feedback e vacuum_defer_cleanup_age fornecem
proteção contra tuplas que são relevantes sendo removidas por vacuum, mas o primeiro não
protege enquanto o standby estiver desconectado e o segundo frequentemente precisa ser
ajustado para um alto valor para proteger adequadamente.
O uso de slots de replicação superam essas desvantagens.
374 15. Replicação
Hostname Papel IP
srv0.local Primário 192.168.56.70
srv1.local Standby 192.168.56.71
Reinicie ambos os servidores e os acesse novamente via SSH com os novos IPs.
• cluster_name
Define um nome para a instância, que serve para vários propósitos. É o nome que
aparece no título de processo para todos processos dessa instância. É usado como
o nome de aplicação (application name) para conexão standby (veja o parâmetro
synchronous_standby_names).
• synchronous_commit
Define se a efetivação da transação aguardará que os registros do WAL sejam gravados
no armazenamento antes que o comando retorne uma indicação de esse comando foi
bem sucedido.
É um parâmetro enumerado que aceita somente os valores local, remote_write,
remote_apply, on e off.
376 15. Replicação
Modo A B C D
remote_apply Sim Sim Sim Sim
on Sim Sim Sim
remote_write Sim Sim
local Sim
off
É importante salientar que quanto mais garantias o modo tiver, mais as efetivações vão
demorar.
• max_wal_senders
Número máximo de conexões de replicação para standbys ou ferramentas que utilizem
esse tipo de conexão, como o pg_basebackup, por exemplo.
• max_replication_slots
Especifica o número máximo de slots de replicação.
• wal_keep_size
Determina o tamanho mínimo de segmanos de WAL anteriores a serem mantidos no
diretório do WAL caso o standby precise buscá-los para replicação via streaming.
• max_slot_wal_keep_size
Tamanho máximo que slots de replicação estão permitidos a manter no diretório do
WAL no momento de checkpoint.
• synchronous_standby_names
377 15. Replicação
• vacuum_defer_cleanup_age
Especifica a quantidade de transações pelas quais VACUUM e HOT updates vão adiar
a limpeza de versões de linhas mortas.
• primary_conninfo
String de conexão do standby para o master.
• primary_slot_name
Nome do slot de replicação para ser usado quando se conecta ao primário.
• hot_standby_feedback
Determina se envia ou não um retorno (feedback) ao master ou standby logo acima
(em caso de cascateamento) sobre consultas que estão sendo executadas no momento
no standby.
Pode ser usado para eliminar cancelamento de consultas causado por limpeza de reg-
istros, mas que causar inchaço (bloat) no primário para algumas cargas.
Se for o caso de uma replicação em cascata, o feedback é passado até que se alcance o
primário.
• pg_create_physical_replication_slot
Cria slot de de replicação.
• pg_drop_replication_slot
Remove slot de de replicação.
• pg_is_in_recovery
Retorna true se há recuperação em progresso.
• pg_last_wal_receive_lsn
Retorna a última localização WAL recebida e sincronizada para disco por replicação
streaming.
• pg_last_wal_replay_lsn
Retorna a última localização WAL replicada durante recuperação.
• pg_last_xact_replay_timestamp
378 15. Replicação
vim ${PGDATA}/postgresql.conf
password_encryption = scram-sha-256
wal_level = replica
max_wal_senders = 3
max_replication_slots = 2
include_dir = '/var/local/pgsql/<VERSÃO MAJORITÁRIA>/conf.d'
name | context
-----------------------+------------
cluster_name | postmaster
max_replication_slots | postmaster
password_encryption | user
wal_level | postmaster
pg_ctl restart
[$] [srv0] Adicionar novas linhas no pg_hba.conf para todos os nós da replicação:
[$] [srv0] Recarregar as configurações para aplicar o que foi feito no pg_hba.conf:
pg_ctl reload
# Execução do pg_basebackup
pg_basebackup -D ${PGDATA} -Fp -Xs -P -R -d "${DBCONN}"
-D PGDATA.
-Fp Formato plano (p: não tar).
-Xs Método de inclusão de WAL stream (s).
-P Exibe o progresso.
-R Cria o arquivo standby.signal faz com que o PostgreSQL entre em modo standby e também
dentro do postgresql.auto.conf coloca a string de conexão ao master.
-d String de conexão.
382 15. Replicação
Por ser uma replicação física em que o que é replicado são os registros do WAL, o processo
de autovacuum é desnecessário em standbys.
[$] [srv1] Muda o permissionamento para somente o usuário dono poder ler e gravar, grupo e
outros não têm qualquer permissão:
pg_ctl start
real 0m0.595s
user 0m0.000s
sys 0m0.005s
template1
template0
postgres
db_teste
• pg_stat_replication
Uma linha por processo WAL sender, exibe estatísticas sobre replicação que o servidor
de envio coleta do standby.
• pg_replication_slots
Uma linha por slot de replicação que existem na instância.
• pg_stat_wal_receiver
Exibe apenas uma linha com estatísticas sobre o WAL receiver.
pid | 793
usesysid | 16438
usename | user_rep
application_name | srv1
client_addr | 192.168.56.71
client_hostname |
client_port | 59830
backend_start | 2021-03-11 19:03:08.157981-03
backend_xmin |
state | streaming
sent_lsn | 0/60001DB0
write_lsn | 0/60001DB0
flush_lsn | 0/60001DB0
replay_lsn | 0/60001DB0
write_lag |
384 15. Replicação
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
reply_time | 2021-03-11 19:03:53.858385-03
385 15. Replicação
slot_name | rs_srv1
plugin |
slot_type | physical
datoid |
database |
temporary | f
active | t
active_pid | 793
xmin |
catalog_xmin |
restart_lsn | 0/60001DB0
confirmed_flush_lsn |
wal_status | reserved
safe_wal_size |
[$] [srv1] Verificar informações a respeito de recebimento de dados WAL via replicação stream-
ing:
pid | 771
status | streaming
receive_start_lsn | 0/60000000
receive_start_tli | 1
written_lsn | 0/60001DB0
flushed_lsn | 0/60001DB0
received_tli | 1
last_msg_send_time | 2021-03-11 19:06:24.25615-03
last_msg_receipt_time | 2021-03-11 19:06:24.262388-03
latest_end_lsn | 0/60001DB0
latest_end_time | 2021-03-11 19:03:53.852802-03
slot_name | rs_srv1
sender_host | 192.168.56.70
sender_port | 5432
conninfo | user=user_rep password=******** . . .
00:02:11.925001
386 15. Replicação
[$] [srv0] Obter em bytes a diferença entre a localização de WAL atual e a última replicada:
37732400
[$] [srv0] Obter humanamente legível a diferença entre a localização de WAL atual e a última
replicada:
19 MB
[$] [srv1] Funções de informações de recebimento das últimas informações WAL via replicação
streaming:
user
sighup
Não exigem que seja dado um restart no serviço, um simples reload já é suficiente para
aplicá-los.
pg_ctl reload
388 15. Replicação
pid | 793
usesysid | 16438
usename | user_rep
application_name | srv1
client_addr | 192.168.56.71
client_hostname |
client_port | 59830
backend_start | 2021-03-11 19:03:08.157981-03
backend_xmin |
state | streaming
sent_lsn | 0/9C2B8830
write_lsn | 0/9C2B8830
flush_lsn | 0/9C2B8830
replay_lsn | 0/9C2B8830
write_lag |
flush_lag |
replay_lag |
sync_priority | 1
sync_state | sync
reply_time | 2021-03-11 19:16:00.402276-03
slot_name | rs_srv1
plugin |
slot_type | physical
datoid |
database |
temporary | f
active | t
active_pid | 793
xmin |
catalog_xmin |
restart_lsn | 0/9C2B8830
confirmed_flush_lsn |
wal_status | reserved
safe_wal_size |
389 15. Replicação
real 0m1.093s
user 0m0.006s
sys 0m0.000s
template1
template0
postgres
db_teste
db_teste2
Failover
O processo de failover ocorre quando um standby se torna primário após o primário original
ser dado como fora de serviço.
Um failover pode ser automático ou manual, esse último é o que será visto a seguir.
pg_ctl stop
pg_ctl promote
dropdb db_teste
dropdb db_teste2
template1
template0
postgres
# autovacuum = on
sed 's/\(^autovacuum = off\)/#\1\nautovacuum = on/g' -i \
/var/local/pgsql/${PGMAJOR}/conf.d/rep.conf
pg_ctl reload
Failback
[$] [srv0] Com o utilitário pg_rewind, fazer a sincronia de srv1 para srv0:
# Sincronização
pg_rewind -P -R \
-D ${PGDATA} \
--source-server="${DBCONN}"
pg_ctl start
392 15. Replicação
Replicação lógica
É um metódo de replicar dados de objetos e suas mudanças baseando-se em sua identidade
de replicação (normalmente uma chave primária).
O termo “lógica” é um contraste à replicação física que usa endereços de bloco exatos para
se fazer a replicação byte a byte. O PostgreSQL suporta ambos os mecanismos ao mesmo
tempo.
A replicação lógica permite um controle granular sobre replicação de dados e segurança.
Na replicação lógica, é usado o modelo de publicação (publication) e subscrição (subscription)
com um ou mais assinantes em uma ou mais publicações no modo publicador.
Replicação lógica de uma tabela normalmente começa com um snapshot dos dados na base
de dados publicadora (publisher ) e copiá-los para o assinante (subscriber ). Uma vez feito isso,
as mudanças no publicador são enviadas ao assinante em tempo real assim que ocorrerem.
O assinante aplica os dados na mesma ordem do publicador de forma que a consistência
transacional seja garantida para publicações dentro de uma única assinatura. Esse método de
replicação de dados é também conhecido como replicação transacional.
• Disparo de gatilhos para mudanças individuais assim que elas chegam ao assinante;
• Consolidar várias bases de dados em uma única, para fins analíticos, por exemplo;
• Replicação do PostgreSQL entre plataformas diferentes (de FreeBSD para Linux, por
exemplo);
Publicação
Uma publicação pode ser definida em qualquer servidor master de replicação física. O nó
onde uma publicação é definida é chamado de publicador. Uma publicação é um conjunto de
mudanças gerado de uma tabela ou um grupo de tabelas, e pode também ser descritq como
um conjunto de mudanças ou conjunto de replicação. Cada publicação existe em apenas uma
base de dados.
Publicações são diferentes de schemas e não afetam como uma tabela é acessada. Cada tabela
pode ser adicionada para múltiplas publicações se for necessário.
Atualmente, publicações podem conter apenas tabelas. Objetos devem ser adicionados ex-
plicitamente, exceto quando uma publicação é criada para ALL TABLES.
Uma tabela publicada deve ter uma “identidade de replicação” configurada para que operações
de UPDATE e DELETE sejam replicadas de forma que para modificar ou remover sejam devidamente
identificadas pelo lado do assinante. Por padrão, é a chave primária, se houver. Outro índice
único (com certos requerimentos adicionais) podem também ser configurados para ser iden-
tidade de réplica. Se a tabela não tiver uma chave adequada, então ela poderá ser definida
como identidade de réplica completa (replica identity full ), o que significa que toda linha se
tornará a chave. Porém, isso é muito ineficiente e deve apenas ser usado como um substituto
se não houver outra solução. Se uma identidade de réplica diferente de “full” for definida no
lado do publicador, uma identidade de réplica compreendendo as mesmas ou menos colunas
também deve ser definida no lado do assinante.
Se uma tabela sem uma identidade de réplica for adicionada à publicação que replica UPDATE
ou DELETE, quando houver um dos dois causará um erro no publicador.
Operações de INSERT são independentes de identidade de réplica.
Toda publicação pode ter vários assinantes.
Uma publicação é criada usando o comando CREATE PUBLICATION.
Tabelas individuais podem ser adicionadas ou removidas dinamicamente usando o comando
ALTER PUBLICATION.
Uma assinatura de uma replicação lógica é o lado que recebe os dados. O nó onde a assinatura
é definida é chamado de assinante (susbscriber ).
Uma assinatura define a conexão a outro banco de dados e conjunto de publicações (um ou
mais) para o qual ele se inscreve.
O banco de dados assinante se comporta da mesma maneira que qualquer outra instância
PostgreSQL e pode ser usado como publicador para outras bases de dados definindo suas
próprias publicações.
Cada assinatura ativa recebe mudanças de um slot de replicação do lado publicador. Normal-
mente, o slot de replicação é criado de forma automática quando a assinatura é criada usando
o comando CREATE SUBSCRIPTION e é removido também automaticamente quando a assinatura é
removida com DROP SUBSCRIPTION. Em algumas situações, no entanto, pode ser útil ou necessário
394 15. Replicação
manipular a assinatura e seu respectivo slot de replicação separadamente, como nos casos:
• Ao criar a assinatura, o slot de replicação já existe. Nesse caso, a assinatura pode ser
criada usando a opção create_slot = false para associar ao slot existente.
• Quando se remove uma assinatura, mas o slot de replicação deve ser mantido.
Isso é útil quando o banco de dados assinante está sendo movido para um host difer-
ente e será ativado por lá. Nesse caso, desassociar o slot da assinatura usando ALTER
SUBSCRIPTION antes de tentar remover a assinatura.
• Ao remover uma assinatura, o host remoto não está alcançável. Nesse caso, desassociar
o slot da assinatura usando ALTER SUBSCRIPTION antes de tentar remover a assinatura.
Se a instância de base de dados remota não existir mais, nenhuma ação adicional será
necessária.
Se, porém, a instância remota estiver apenas inacesível, o slot de replicação deve então
ser removido manualmente, caso contrário ela continuaria a reservar WAL e eventual-
mente pode causar disco cheio.
Conflitos
Replicação lógica se comporta de forma parecida com operações DML em que os dados serão
atualizados mesmo que tenham sido alterados localmente no nó do assinante. Se os dados que
chegam violam constraints, a replicação vai parar. Isso é considerado um conflito. Ao replicar
operações de UPDATE ou DELETE, dados faltantes não produzirão um conflito e tais operações
serão ignoradas. Um conflito vai produzir um erro e vai parar a replicação; isso deve ser
resolvido manualmente. Maiores detalhes sobre conflitos podem ser encontrados nos logs do
serivor assinante.
A solução pode ser feita mudando dados no assinante de forma que não conflite com uma
alteração que venha ou ignorando a transação que cause conflitos com os dados existentes.
A transação pode ser ignorada chamando a função pg_replication_origin_advance() com o
parâmetro node_name correspondente ao nome do nó assinante e uma posição. A atual posição
de origem pode ser vista na view de sistema pg_replication_origin_status.
395 15. Replicação
Restrições
• A parte estrutural da base de dados não é replicada, ou seja, comandos DDL não repli-
cados.
A estrutura inicial pode ser copiada via pg_dump --schema-only.
Mudanças posteriores na estrutura precisariam ser mantidas manualmente. Não há ne-
cessidade para as estruturas serem abolutamente as mesmas em ambos os lados. A
replicação lógica é robusta quando as definições de estrutura mudam em um banco de
dados ativo: quando a estrutura é alterada no publicador e os dados replicados começam
a chegar ao assinante, mas não se encaixam no estrutura da tabela, a replicação apre-
sentará um erro até que o estrutura seja corrigida.
Em muitos casos, erros intermitentes podem ser evitados aplicando alterações de estru-
tura aditivas primeiro ao assinante.
• Replicação do comando TRUNCATE é suportada, mas é preciso ter algum cuidado ao fazer
isso para trupos de tabelas conectadas por chaves estrangeiras.
Ao replicar um TRUNCATE, o assinante vai truncar o mesmo grupo de tabelas que foram
no publicador, ou explictamente via opção CASCADE, exceto tabelas que não fazem parte
da assinatura. Isso vai funcionar corretamente se todas tabelas afetadas forem parte
da mesma assinatura. Mas se algumas tabelas a serem truncadas no assinante tiverem
ligações de chave estrangeira para tabelas que não fazem parte da mesma (ou qualquer)
assinatura, então a aplicação de truncamento no assinante vai falhar.
• Grandes objetos (large objects) não são replicados. Não há solução para isso, a não ser
armazenar dados em tabelas normais.
Arquitetura
Snapshot inicial
A replicação lógica começa pela cópia do instantâneo (snapshot) dos dados em uma base
publicadora.
Uma vez que isso é feito, mudanças no publicador serão enviadas ao assinante em tempo
real. O assinante aplica os dados na ordem que as efetivações foram feitas no publicador para
consistência transacional.
A replicação lógica é construída com uma arquitetura similar à replicação física via streaming.
É implementada por walsender e aplicar processos.
Monitoramento
Por causa de sua similaridade na arquitetura com a replicação física via streaming, o moni-
toramento em um nó de publicação é similar a monitorar um master de replicação física.
Informações de monitoramento sobre assinatura estão na view pg_stat_subscription. Essa view
contém uma linha para cada worker assinante.
Uma assinatura pode ter zero ou mais workers ativos de assinatura dependendo de seu estado.
Normalmente, há um único processo de aplicação rodando para uma assinatura habilitada.
Uma assinatura desabilitada ou uma assinatura quebrada não terá linhas nessa view.
Se os dados iniciais de sincronização de qualquer tabela estiverem em progresso, haverá workers
adicionais para as tabelas sendo sincronizadas.
Configuração do publicador
wal_level = logical
max_replication_slots= pelo menos o número de assinaturas esperadas para conectar mais
alguma reserva para sincronização de tabela.
max_wal_senders = max_replication_slots + réplicas físicas, se estiverem conectadas ao mesmo
tempo
Configuração do assinante
max_replication_slots = número de assinaturas
max_logical_replication_workers = pelo menos o número de assinaturas + reserva para sin-
cronização de tabela.
max_worker_processes = pelo menos max_logical_replication_workers + 1
397 15. Replicação
Preparação do laboratório
Hostname Papel IP
srv0.local Publicador 192.168.56.70
srv1.local Assinante 192.168.56.71
Reinicie ambos os servidores e os acesse novamente via SSH com os novos IPs.
vim ${PGDATA}/postgresql.conf
password_encryption = scram-sha-256
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10
[$] [srv0] No publicador, adicionar uma nova linha ao pg_hba.conf para permitir acesso do
assinante:
398 15. Replicação
vim ${PGDATA}/postgresql.conf
password_encryption = scram-sha-256
max_replication_slots = 10
max_logical_replication_workers = 10
max_worker_processes = 8
pg_ctl restart
1
2
3
4
5
Nenhum resultado.
É preciso dar um refresh na assinatura.
1
2
3
4
5
O repmgr é uma ferramenta open source, desenvolvida inicialmente pela 2ndQuadrant (foi
adquirida pela EDB), para gerenciamento de clusters de replicação PostgreSQL.
O repmgr traz facilidades de monitoramento de um cluster de replicação e dentre seus recursos
destaca-se o failover automático.
https://repmgr.org
Patroni
https://github.com/zalando/patroni
https://github.com/ClusterLabs/PAF
CitusDB
É uma solução de banco de dados distribuído que é otimizado para cargas de trabalho em
tempo real.
O CitusDB escala o PostgreSQL através de um cluster de servidores via sharding e replicação.
Seu avançado motor de consultas paraleliza consultas vindas desses servidores para respostas
em tempo real.
O CitusDB se recupera de falhas fazendo failover automaticamente, assim fazendo alta disponi-
bilidade.
O CitusDB não é um fork do PostgreSQL, mas sim o extende.
https://www.citusdata.com
402 15. Replicação
Greenplum
Greenplum Database (GPDB) é um avançado data warehouse com muitos recursos de código
aberto baseado em PostgreSQL.
Ele fornece análises poderosas e rápidas em volumes de dados em escala de petabyte.
Voltado exclusivamente para análise big data, o Greenplum Database tem um otimizador de
consultas baseado em custos avançado que entrega alto desempenho de consultas analíticas
em grandes volumes de dados.
https://github.com/greenplum-db/gpdb
YugabyteDB
O YugabyteDB um fork do PostgreSQL, uma solução de banco de dados distribuído para dar
poder a aplicações globais em escala de Internet.
Pode ser implementado em nuvens privadas ou públicas bem como ambientes Kubernetes com
facilidade.
Ele tem também um certo grau de compatibilidade com o Apache Cassandra, que permite que
aplicações escritas para ele utilizem o YugaByteDB.
É uma solução com muitas características cloud native.
https://www.yugabyte.com
403 15. Replicação
Witness
Diferente de servidores dos tipos primário e standby, apesar de também ser uma instância
PostgreSQL, não faz parte do cluster de replicação.
Seu objetivo é guardar metadados do cluster de replicação repmgr e ficar vigiando, ser “teste-
munha” em caso de falha do servidor primário e fazer com que um dos standbys assuma como
primário, no processo de promover o failover automático.
Ambiente de laboratório
[#] [todos] De acordo com o planejado de laborário, adicionar linhas no arquivo /etc/hosts:
init 6
Arquitetura de diretórios
[#] [todos] Variável de ambiente para versão majoritária do PostgreSQL (como root):
read -p \
'Digite a versão majoritária: ' \
PGMAJOR
405 15. Replicação
export PG_HOME="/usr/local/pgsql/${PGMAJOR}"
export PGDATA="/var/local/pgsql/${PGMAJOR}/data"
export PGBIN="${PG_HOME}/bin"
export REPMGR_HOME="/usr/local/repmgr/${PGMAJOR}"
export PATH="${PGBIN}:${PATH}"
export PKG="\
gcc \
make \
flex \
libxslt1-dev \
libxml2-dev \
libselinux1-dev \
libpam0g-dev \
libssl-dev \
libkrb5-dev \
libedit-dev \
zlib1g-dev \
libreadline-dev
"
[#] [todos] Atualizar o repositório, instalar os pacotes e fazer a limpeza dos pacotes baixados:
apt update && apt install -y wget ${PKG} && apt clean
export REPMGR_URL=\
"https://repmgr.org/download/repmgr-${REPMGR_VERSION}.tar.gz"
406 15. Replicação
[#] [todos] Ir ao diretório onde o arquivo foi baixado, descompactá-lo e acessar o diretório:
[#] [todos] Após a compilação ter sido bem sucedida, remover os pacotes instalados:
[#] [todos] Criação de arquivo para criação de diretório temporário para arquivo de pid:
systemd-tmpfiles --create
408 15. Replicação
Configurações de nós
[#] [todos] Variável de ambiente para o Node ID (inteiro maior que zero):
Digite os IPs dos nós (separados por um espaço): 192.168.56.71 192.168.56.72 192.168.56.73
192.168.56.74
# Cabeçalho:
cat << EOF >> ${PGDATA}/pg_hba.conf
# REPMGR ======================================================================
# repmgr database
EOF
# Replication
EOF
# REPMGR ======================================================================
# repmgr database
host db_repmgr user_repmgr 192.168.56.71/32 trust
host db_repmgr user_repmgr 192.168.56.72/32 trust
host db_repmgr user_repmgr 192.168.56.73/32 trust
host db_repmgr user_repmgr 192.168.56.74/32 trust
# Replication
host replication user_repmgr 192.168.56.71/32 trust
host replication user_repmgr 192.168.56.72/32 trust
host replication user_repmgr 192.168.56.73/32 trust
host replication user_repmgr 192.168.56.74/32 trust
# =============================================================================
vim ${PGDATA}/postgresql.conf
listen_addresses = '*'
wal_level = replica
archive_mode = on
archive_command = '/bin/true'
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
shared_preload_libraries = 'repmgr'
vim ${PGDATA}/postgresql.conf
listen_addresses = '*'
shared_preload_libraries = 'repmgr'
[#] [todos] Criar um arquivo com todos os IPs e hostnames possíveis de cada nó do cluster
de replicação:
Nó primário
node_id | 1
upstream_node_id |
active | t
node_name | alpha.local
type | primary
location | default
priority | 100
conninfo | host=alpha.local user=user_repmgr dbname=db_repmgr connect_timeout=2
repluser | user_repmgr
slot_name | repmgr_slot_1
config_file | /etc/repmgr/13/repmgr.conf
413 15. Replicação
Nós standbys
pg_ctl -m i stop
pg_ctl start
Nó witness
Database system identifier é um inteiro de 64 bits cujo propósito de ser uma identificação que
espera-se ser única para a instância.
Em casos que ao provisionar uma nova máquina e esse provisionamento for baseado em uma
máquina modelo (template), todos servidores PostgreSQL (se o mesmo tiver sido instalado
nesse modelo) terão a mesma identificação de instância.
O repmgr não permite que o servidor witness seja a mesma instância do servidor primário,
então ele faz a validação disso utilizando o database system identifier.
Para contornar esse problema, antes de registrar o nó como witness , execute alguns procedi-
mentos, inclusive apagar a instância e recriá-la com o initdb.
read -p \
'Digite a versão majoritária: ' \
PGMAJOR
cp -v ${PGDATA}/{postgresql,pg_hba}.conf /tmp/
pg_ctl -m i stop
rm -fr /var/local/pgsql/${PGMAJOR}/{wal,data}
initdb \
-D ${PGDATA} \
-E utf8 \
-U postgres \
-k \
--locale=pt_BR.utf8 \
--lc-collate=pt_BR.utf8 \
--lc-monetary=pt_BR.utf8 \
--lc-messages=en_US.utf8 \
-T portuguese \
-X /var/local/pgsql/${PGMAJOR}/wal
mv /tmp/{postgresql,pg_hba}.conf ${PGDATA}/
pg_ctl start
psql -f /tmp/omega.sql
repmgr \
-d db_repmgr \
-U user_repmgr \
-h alpha \
-f ${REPMGRDCONF} \
--verbose witness register
Failover
[#] [todos] Em todos os nós como root, dar restart no serviço repmgrd e em seguida verificar
seu estado:
tail -F /var/log/repmgr/${PGMAJOR}/repmgrd.log
417 15. Replicação
. . .
[2021-03-18 11:37:05] [DETAIL] following new primary "beta.local" (ID: 2)
[2021-03-18 11:37:05] [INFO] searching for primary node
[2021-03-18 11:37:05] [INFO] checking if node 2 is primary
[2021-03-18 11:37:05] [INFO] current primary node is 2
[2021-03-18 11:37:05] [INFO] witness monitoring connection to primary node "beta.local" (ID: 2)
Pelos logs e pelo comando cluster show do repmgr verificou-se que um dos standbys, o servidor
beta assumiu como primário.
Failback
repmgr -f ${REPMGRDCONF} \
node rejoin \
-d 'host=beta user=user_repmgr dbname=db_repmgr port=5432' \
--force-rewind
Switchover
[$] [omega] Registrar o servidor como witness novamente para seguir o novo master:
repmgr -F \
-d db_repmgr \
-U user_repmgr \
-h alpha \
-f ${REPMGRDCONF} \
--verbose witness register
419
420 16. Anexo 1 - Índices
Sobre Índice
Índice (index ) é um recurso que agiliza buscas em tabelas.
Imagine que você está em uma biblioteca e gostaria de procurar “O Senhor dos Anéis”, de
Tolkien. O que seria mais fácil? Começar a vasculhar a biblioteca inteira até achar o livro
desejado (busca sequencial) ou buscar no arquivo da biblioteca (busca indexada), nas fichas
que estão ordenados por autor? Logicamente, se escolher buscar nas fichas, será muito mais
rápido, pois não será necessário vasculhar livro por livro na biblioteca, porque haverá uma
ficha do autor e daquele livro que mostrará exatamente onde está o livro desejado. É um
apontamento para a localização do livro. Um índice de banco de dados funciona de forma
semelhante, cujo funcionamento consiste em criar ponteiros para dados gravados em campos
específicos. Quando não existe índice em um campo usado como critério de filtragem, é feita
uma varredura em toda a tabela, de maneira a haver execuções de I/O desnecessárias, além
de também desperdiçar processamento.
Um índice é criado implicitamente para constraints UNIQUE e PRIMARY KEY.
Tipos de Índices
O PostgreSQL em seu core dispõe dos seguintes tipos de índices: Btree (padrão), Hash, GiST,
SP-GiST, GIN e BRIN, que serão vistos aqui.
Há também outros tipos de índices que são oferecidos como extensões.
Dicas Gerais
• Crie índices para campos cujas consultas o envolvam em uma das seguintes cláusulas:
WHERE, DISTINCT, ORDER BY, GROUP BY e LIKE;
• Crie índices para campos de chaves estrangeiras e em campos envolvidos como critérios
de junção (JOIN);
• Se houver uma consulta frequente utilize índices parciais com sua condição conforme a
consulta;
• Para consultas que buscam faixas de valores é bom ter um índice clusterizado para isso;
• Após criar um índice atualize as estatísticas da tabela com ANALYZE [1] ou VACCUM
ANALYZE [2];
421 16. Anexo 1 - Índices
• Antes de colocar uma base em produção faça testes com massas de dados considerável
e utilize o comando EXPLAIN ANALYZE [3] para verificar o plano de execução.
[1] https://www.postgresql.org/docs/current/static/sql-analyze.html
[2] https://www.postgresql.org/docs/current/static/sql-vacuum.html
[3] https://www.postgresql.org/docs/current/static/sql-explain.html
422 16. Anexo 1 - Índices
Índices B-tree
Pode lidar com consultas de igualdade ou de faixa de valores cujos dados podem ser ordenados.
O planejador de consultas do PostgreSQL considerará usar um índice B-tree sempre que um
campo indexado estiver envolvido em uma comparação usando um dos seguintes operadores:
<, <=, =, >=, > ou BETWEEN, IN, IS [NOT] NULL.
O otimizador pode também usar índices B-tree para consultas envolvendo operadores de padrão
de combinação LIKE e ~ se o padrão for uma constante e estiver no início da string, por exemplo:
coluna LIKE 'foo%' ou coluna ~ '^foo', mas não coluna LIKE '%bar'. Porém, se sua base não usa
locale C, será necessário criar o índice com uma classe de operador especial para suportar
indexação de consultas de casamento de padrões.
É possível também usar índices B-tree para ILIKE e ~*, mas apenas se o padrão começar com
caracteres não-alfabéticos, i. e., caracteres que não são afetados pela conversão upper/lower
case (letras maiúsculas/minúsculas).
Índices B-tree podem também ser usados para buscar dados ordenadamente. Isso não é sempre
mais rápido do que uma busca simples e ordenação, mas é sempre útil.
SELECT
generate_series(1, 2000000)::int AS campo1, -- 2 milhões de registros
round((random()*10000))::int2 AS campo2,
round((random()*10000))::int2 AS campo3 INTO tb_foo;
EXPLAIN ANALYZE
SELECT campo1
FROM tb_foo
WHERE campo2 BETWEEN 235 AND 587;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..23350.62 rows=10000 width=4) (actual time=0.253..164.844 rows=70936 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tb_foo (cost=0.00..21350.62 rows=4167 width=4) (actual time=0.949..140.846 rows=2
Filter: ((campo2 >= 235) AND (campo2 <= 587))
Rows Removed by Filter: 643021
Planning Time: 0.053 ms
Execution Time: 166.946 ms
A tabela aínda não tem índice, portanto, a busca vai ser sequencial.
423 16. Anexo 1 - Índices
Na criação do índice, não foi especificado seu tipo, portanto, esse é um índice btree, que é o
tipo padrão do PostgreSQL.
EXPLAIN ANALYZE
SELECT campo1
FROM tb_foo
WHERE campo2 BETWEEN 235 AND 587;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=138.93..9559.68 rows=10000 width=4) (actual time=5.573..42.467 rows=70936 l
Recheck Cond: ((campo2 >= 235) AND (campo2 <= 587))
Heap Blocks: exact=8848
-> Bitmap Index Scan on idx_tb_foo_campo2 (cost=0.00..136.43 rows=10000 width=0) (actual time=3.986..3.98
Index Cond: ((campo2 >= 235) AND (campo2 <= 587))
Planning Time: 0.230 ms
Execution Time: 44.851 ms
Comparando antes e depois da criação do índice, vemos que antes foi usada a busca sequencial
(Seq Scan) e levou 8.456 ms.
Após criar o índice, sendo que foi criado para o campo utilizado na condição do WHERE, vemos
que foi utilizada busca via índice (Bitmap Index Scan) e levou 44.851 ms para executar.
Por meio desse experimento, podemos ver o quão útil é ter um índice, devido à forma como
ele agiliza uma busca.
424 16. Anexo 1 - Índices
Índices Hash
Índices hash podem apenas lidar com comparações de igualdade. O planejador de consulta
considerará usar um índice hash sempre que uma coluna indexada estiver envolvida em uma
comparação com o operador =.
Até a versão 9.6 do PostgreSQL havia um aviso na documentação oficial desencorajando esse
tipo de índice, pois até então eles não eram escritos no WAL, não eram replicados e numa
eventual situação de recuperação de crash índices desse tipo deveriam ser reconstruídos com
uma reindexação (REINDEX). A partir da versão 10 do PostgreSQL, esse aviso desencorajando foi
removido. No entanto, devido à sua baixa amplitude de operadores e com o desenvolvimento
do tipo btree, seu uso ainda é questionável.
[>] Criação de uma tabela de teste que tenha dez milhões de registros:
SELECT
generate_series(1, 10000000) AS numero
INTO tb_foo;
\timing on
Nota-se que a criação do índice hash demorou mais do que a do índice btree.
Foram criados dois índices para a mesma coluna da tabela, sendo um hash e o outro btree.
A intenção aqui é testar para validar se vale a pena criar um índice hash.
425 16. Anexo 1 - Índices
SELECT
pg_size_pretty(pg_relation_size('idx_hash')) AS indice_hash,
pg_size_pretty(pg_relation_size('idx_btree')) AS indice_btree;
indice_hash | indice_btree
-------------+--------------
320 MB | 214 MB
Podemos notar que o índice btree é 33% menor do que o índice hash.
EXPLAIN ANALYZE
SELECT numero
FROM tb_foo
WHERE numero = 195773;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_btree on tb_foo (cost=0.43..4.45 rows=1 width=4) (actual time=0.571..0.572 rows=1
Index Cond: (numero = 195773)
Heap Fetches: 0
Planning Time: 7.447 ms
Execution Time: 1.072 ms
Aqui, vemos que o índice hash não foi utilizado, mas sim o btree.
Isso significa que vai ser sempre assim? A resposta é: depende.
Há versões do PostgreSQL que o índice hash pode ser proveitoso, o que não aconteceu nesse
teste.
426 16. Anexo 1 - Índices
Índices GiST
Índices GiST não são um único tipo de índice, mas sim uma infraestrutura interna que muitas
estratégias diferentes de indexação podem ser implementadas. Portanto, os operadores em
particular com que um índice GiST pode ser usado varia dependendo da estratégia de indexação
(a classe de operador). Como um exemplo, a distribuição padrão do PostgreSQL inclui classes
de operador GiST para vários tipos de dados geométricos de duas dimensões, que suporta
consultas indexadas usando os seguintes operadores: <<, &<, &>, >>, <<|, &<|, |&>, |>>, @>, <@,
~=, &&.
Muitas outras classes de operadores GiST estão disponíveis na coleção contrib ou como projetos
separados.
Índices GiST são também capazes de otimizar buscas “nearest-neighbor ” (vizinho mais próx-
imo) como:
Essa consulta encontra os dez lugares mais perto de um dado ponto alvo. A capacidade de
fazer isso é, novamente, dependente da classe de operador particular a ser usada.
Operadores de
Classe Tipo Operadores Ordenação
box_ops box &&, &>, &<, &<\|, >>, <<, <<\|, <@, @>, @,
\|&>, \|>>,~, ~=
cir- circle &&, &>, &<, &<\|, >>, <<, <<\|, <@, @>, @, <->
cle_ops \|&>, \|>>,~, ~=
inet_ops inet, cidr &&, >>, >>=, >, >=, <>, <<, <<=, <, <=, =
point_ops point >>, >^, <<, <@, <@, <@, <^, ~= <->
poly_ops polygon &&, &>, &<, &<\|, >>, <<, <<\|, <@, @>, @, <->
\|&>, \|>>,~, ~=
range_ops qualquer tipo &&, &>, &<, >>, <<, <@, -\|-, =, @>
de faixa
ts- tsquery <@, @>
query_ops
tsvec- tsvector @@
tor_ops
427 16. Anexo 1 - Índices
A classe inet_ops não é a classe padrão para tipos cidr e inet. Para utilizá-la, mencione o
nome da classe na criação do índice:
[>] Criação de índices GiST sem declarar a classe de operador e declarando, respectivamente:
EXPLAIN ANALYZE
SELECT id, faixa
FROM tb_foo WHERE faixa @> 777;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=199.03..6536.91 rows=5000 width=36) (actual time=9.086..18.256 rows=5618 lo
Recheck Cond: (faixa @> 777)
Heap Blocks: exact=3695
-> Bitmap Index Scan on idx_gist_op_class (cost=0.00..197.78 rows=5000 width=0) (actual time=8.729..8.730
Index Cond: (faixa @> 777)
Planning Time: 3.417 ms
Execution Time: 18.810 ms
Índices SP-GiST
Como índices GiST, índices SP-GiST, oferecem uma infraestrutura que suporta vários tipos
de buscas. Permite implementar uma vasta faixa de diferentes estruturas de dados baseadas
em disco não balanceadas, como quadtrees, k-d trees e radix trees (tentativas).
Como um exemplo, a distribuição padrão do PostgreSQL inclui classes de operadores SP-GiST
para pontos de duas dimensões, que suportem consultas usando estes operadores: <<, >>, ~=,
<@, <^, >^.
Das duas classes de operadores para o tipo de ponto, quad_point_ops é o padrão. kd_point_ops
suporta os mesmos operadores, mas usa uma estrutura diferente de dados de índice que pode
oferecer um melhor desempenho em algumas aplicações.
[>] Criação de índices SP-GiST, respectivamente, sem declarar e declarando classe de oper-
ador:
EXPLAIN ANALYZE
SELECT id, faixa FROM tb_foo WHERE faixa @> 777;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=175.11..6516.21 rows=5010 width=18) (actual time=2.543..25.354 rows=5497 lo
Recheck Cond: (faixa @> 777)
Heap Blocks: exact=3731
-> Bitmap Index Scan on idx_spgist_op_class (cost=0.00..173.86 rows=5010 width=0) (actual time=1.906..1.9
Index Cond: (faixa @> 777)
Planning Time: 0.314 ms
Execution Time: 25.596 ms
Como aconteceu com os índices GiST, aqui também o planejador de consultas escolheu o
índice cuja classe de operador foi declarada.
SELECT
indexname indice,
pg_size_pretty(pg_relation_size(indexname::text)) tamanho
FROM pg_indexes
WHERE tablename = 'tb_foo';
indice | tamanho
---------------------+---------
idx_gist | 62 MB
idx_gist_op_class | 61 MB
idx_spgist | 52 MB
idx_spgist_op_class | 52 MB
Os índices SP-GiST são menores do que os índices GiST, e o planejador de consultas utilizou o
índice SP-GiST de classe de operador declarada. Nesse caso, foi a melhor escolha de indexação
de acordo com a consulta.
Índices GIN
Índices GIN são “índices invertidos”, apropriados para valores de dados que contêm valores
de componentes múltiplos, como arrays. Um índice invertido contém uma entrada para cada
valor componente e pode eficientemente lidar com consultas que testam a presença de valores
em componentes específicos.
Como GiST e SP-GiST, GIN pode suportar muitas diferentes estratégias de indexação definidas
por usuário, e os operadores com que um índice GIN podem ser usados variam de acordo com
a estratégia de indexação.
Como exemplo, a distribuição padrão de PostgreSQL inclui uma classe de operador GIN para
arrays, que suporta consultas indexadas usando estes operadores: <@, @>, =, &&.
Muitos outros operadores de classes GIN estão disponíveis na coleção contrib como projetos
separados.
[>] Criação dos índices sem e com declaração de classe de operador, respectivamente:
EXPLAIN ANALYZE
SELECT vetor FROM tb_foo WHERE vetor @> ARRAY[754532];
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=62.75..7672.14 rows=5000 width=33) (actual time=0.995..1.011 rows=2 loops=1
Recheck Cond: (vetor @> '{754532}'::integer[])
Heap Blocks: exact=2
-> Bitmap Index Scan on idx_gin_op_class (cost=0.00..61.50 rows=5000 width=0) (actual time=0.978..0.979 r
Index Cond: (vetor @> '{754532}'::integer[])
Planning Time: 2.249 ms
Execution Time: 1.043 ms
O índice com classe de operador declarada foi escolhido pelo planejador de consultas.
Índices BRIN
Índices BRIN (uma abreviação para Block Range INdexes) armazena sumários sobre os valores
armazenados em faixas de blocos físicos consecutivos de uma tabela.
Assim como GiST, SP-GiST e GIN, BRIN pode suportar muitas diferentes estratégias e oper-
adores com que um índice BRIN pode ser usado - o que varia de acordo com a estratégia de
indexação.
Para tipos de dados que tem uma ordem de classificação linear, os dados indexados corre-
spondem ao mínimo e máximo de valores na coluna para cada faixa de bloco. Isso suporta
consultas indexadas usando estes operadores: <, <=, =, >=, >.
Um índice BRIN para uma busca de uma consulta é uma mistura de busca sequencial e busca
indexada porque o que essa busca indexada está armazenando é uma faixa de dados dado um
número fixo de blocos de dados.
\timing on
É de se notar que a criação do índice BRIN é muito mais rápida do que a do índice B-tree.
Em índices BRIN, exist um parâmetro que controla a quantidade de páginas por faixa,
pages_per_range, cujo valor padrão é 128.
Quanto mais páginas por faixa, menor o índice será. No entanto, a parte de busca sequencial
será maior.
[>] Criação de índices com diferentes configurações para páginas por faixa:
SELECT
indexname indice,
pg_size_pretty(pg_relation_size(indexname::text)) tamanho
FROM pg_indexes
WHERE tablename = 'tb_temperatura_log'
ORDER BY pg_relation_size(indexname::text);
indice | tamanho
-----------------------+---------
idx_brin_op_class_512 | 32 kB
idx_brin_op_class_256 | 40 kB
idx_brin | 72 kB
idx_brin_op_class | 72 kB
idx_brin_op_class_64 | 128 kB
idx_btree | 840 MB
Índices Compostos
São aqueles que contêm em sua composição mais de um campo.
Sintaxe:
EXPLAIN ANALYZE
SELECT id_
FROM tb_foo
WHERE (campo2 BETWEEN 235 AND 587)
AND campo3 = 1000;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Scan using idx_tb_index_campo2_campo3 on tb_foo (cost=0.42..12.72 rows=1 width=4) (actual time=0.032..
Index Cond: ((campo2 >= 235) AND (campo2 <= 587) AND (campo3 = 1000))
Planning Time: 0.354 ms
Execution Time: 0.047 ms
Índices Parciais
Índice parcial é aquele que aponta para registros de acordo com uma condição.
Sintaxe:
EXPLAIN ANALYZE
SELECT * FROM tb_foo WHERE campo1 % 19 = 0;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..12175.00 rows=5000 width=4) (actual time=0.203..69.548 rows=52631 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tb_foo (cost=0.00..10675.00 rows=2083 width=4) (actual time=0.010..32.741 rows=17
Filter: ((campo1 % 19) = 0)
Rows Removed by Filter: 315790
Planning Time: 0.093 ms
Execution Time: 71.113 ms
Por não ter índices, foi usada uma busca sequencial (Seq Scan).
EXPLAIN ANALYZE
SELECT * FROM tb_foo WHERE campo1 % 19 = 0;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..12175.00 rows=5000 width=4) (actual time=0.203..69.548 rows=52631 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on tb_foo (cost=0.00..10675.00 rows=2083 width=4) (actual time=0.010..32.741 rows=17
Filter: ((campo1 % 19) = 0)
Rows Removed by Filter: 315790
Planning Time: 0.093 ms
Execution Time: 71.113 ms
EXPLAIN ANALYZE
SELECT * FROM tb_foo WHERE campo1 % 19 = 0;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_19 on tb_foo (cost=0.29..131.29 rows=5000 width=4) (actual time=0.034..7.213 rows=
Heap Fetches: 0
Planning Time: 0.233 ms
Execution Time: 10.035 ms
A condição do índice parcial combinou com a consulta feita. Então, o índice foi utilizado.
442 16. Anexo 1 - Índices
[>] Análise com uma consulta de condição diferente de números divíveis por 19:
EXPLAIN ANALYZE
SELECT * FROM tb_foo
WHERE campo1 BETWEEN 241 AND 875;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_total on tb_foo (cost=0.42..21.14 rows=636 width=4) (actual time=0.012..0.106 rows
Index Cond: ((campo1 >= 241) AND (campo1 <= 875))
Heap Fetches: 0
Planning Time: 0.306 ms
Execution Time: 0.150 ms
SELECT
indexname indice,
pg_size_pretty(pg_relation_size(indexname::text)) tamanho
FROM pg_indexes
WHERE tablename = 'tb_foo'
ORDER BY pg_relation_size(indexname::text);
indice | tamanho
-----------+---------
idx_19 | 1168 kB
idx_total | 21 MB
Conclusão
Contatou-se o que foi dito na teoria: antes da criação dos índices, a primeira busca foi
sequencial.
Após a criação dos índices, o planejador de consultas já podia contar com eles, optando por
usar o índice com maior restrição de valores. Isso leva a um tempo menor, usufruindo de uma
busca agora indexada por um índice parcial.
443 16. Anexo 1 - Índices
Índices de cobertura
Também conhecidos pelo termo em inglês covering indexes, permitem que sejam feitas buscas
apenas em índices (index-only scans) se a listagem da consulta combinar com as colunas
incluídas no índice. Para isso, foi adotada a cláusula INCLUDE. A cláusula INCLUDE especifica
uma lista de colunas que serão incluídas no índice como colunas não-chave.
Sintaxe:
EXPLAIN ANALYZE
SELECT
numero, id_
FROM tb_foo
WHERE numero > 25000
ORDER BY numero, id_ LIMIT 20;
QUERY PLAN
--------------------------------------------------------------------------------------------
Limit (cost=0.43..1.00 rows=20 width=8) (actual time=0.080..0.087 rows=20 loops=1)
-> Index Only Scan using idx_teste on tb_foo (cost=0.43..55555.52 rows=1951034 width=8) . . .
Index Cond: (numero > 25000)
Heap Fetches: 0
Planning Time: 0.351 ms
Execution Time: 0.107 ms
EXPLAIN ANALYZE
SELECT
numero, id_, texto
FROM tb_foo
WHERE numero > 25000
ORDER BY numero, id_ LIMIT 20;
QUERY PLAN
--------------------------------------------------------------------------------------------
Limit (cost=0.43..1.44 rows=20 width=16) (actual time=0.025..0.061 rows=20 loops=1)
-> Index Scan using idx_teste on tb_foo (cost=0.43..98799.48 rows=1951034 width=16) (ac . . .
Index Cond: (numero > 25000)
Planning Time: 0.199 ms
Execution Time: 0.081 ms
EXPLAIN ANALYZE
SELECT
numero, id_, texto
FROM tb_foo
WHERE numero > 25000
ORDER BY numero, id_ LIMIT 20;
QUERY PLAN
--------------------------------------------------------------------------------------------
Limit (cost=0.43..1.09 rows=20 width=16) (actual time=0.042..0.048 rows=20 loops=1)
-> Index Only Scan using idx_teste2 on tb_foo (cost=0.43..64159.52 rows=1951034 width=1 . . .
Index Cond: (numero > 25000)
Heap Fetches: 0
Planning Time: 0.273 ms
Execution Time: 0.065 ms
Foi utilizado o segundo índice criado, que tem as três colunas em sua composição.
[>] Criação de índice de cobertura onde o campo “texto” é o campo incluído na cobertura:
[>] Plano de execução com uma consulta que lista os três campos:
EXPLAIN ANALYZE
SELECT
numero, id_, texto
FROM tb_foo
WHERE numero > 25000
ORDER BY numero, id_ LIMIT 20;
QUERY PLAN
--------------------------------------------------------------------------------------------
Limit (cost=0.43..1.09 rows=20 width=16) (actual time=0.042..0.047 rows=20 loops=1)
-> Index Only Scan using idx_include on tb_foo (cost=0.43..64159.52 rows=1951034 width= . . .
Index Cond: (numero > 25000)
Heap Fetches: 0
Planning Time: 0.233 ms
Execution Time: 0.064 ms
446 16. Anexo 1 - Índices
SELECT
indexname indice,
pg_size_pretty(pg_relation_size(indexname::text)) tamanho
FROM pg_indexes
WHERE tablename = 'tb_foo'
ORDER BY pg_relation_size(indexname::text);
indice | tamanho
-------------+---------
tb_foo_pkey | 43 MB
idx_teste | 43 MB
idx_teste2 | 60 MB
idx_include | 60 MB
• Um índice que acabou ficando “inchado”, ou seja, que contém muitas páginas vazias
ou quase vazias. Isso pode ocorrer com índices B-tree sob certos padrões incomuns
de acesso. REINDEX fornece uma maneira de reduzir o consumo de espaço do índice,
escrevendo uma nova versão sem as páginas mortas;
Sintaxe:
Onde:
TABLE tb_foo;
id_ | cor
-----+----------
1 | Laranja
2 | Preto
3 | Branco
4 | Azul
5 | Amarelo
6 | Vermelho
7 | Verde
8 | Cinza
[>] Atualização:
TABLE tb_foo;
id_ | cor
-----+---------
1 | Laranja
2 | Preto
3 | Branco
4 | Azul
5 | Amarelo
7 | Verde
8 | Cinza
6 | Vinho
\d tb_foo
Table "pg_temp_3.tb_foo"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+----------------------------------
id_ | integer | | not null | generated by default as identity
cor | text | | |
Indexes:
"tb_foo_pkey" PRIMARY KEY, btree (id_)
Como a tabela já foi criada com uma chave primária, automaticamente essa chave primária
terá um índice atrelado a ela: tb_foo_pkey.
TABLE tb_foo;
id_ | cor
-----+---------
1 | Laranja
2 | Preto
3 | Branco
4 | Azul
5 | Amarelo
6 | Vinho
7 | Verde
8 | Cinza
Agora, os registros foram agrupados ordenando-se pelo seu índice, cujo campo é o id_.
Exclusão de índices
Não remova um índice de seu banco sem antes saber o quão útil ele é.
A visão (view ) de sistema pg_stat_user_indexes é extremamente útil para essa decisão de excluir
um índice, pois ela traz dados de índices criados por usuários, informando o quanto cada um
foi usado em diferentes aspectos.
[>] Plano de execução de uma consulta efetivamente executada usando como critério o id_:
EXPLAIN ANALYZE
SELECT campo2 FROM tb_foo
WHERE id_ BETWEEN 5 AND 20000;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=113.91..8661.20 rows=5608 width=32) (actual time=1.687..6.697 rows=19996 lo
Recheck Cond: ((id_ >= 5) AND (id_ <= 20000))
Heap Blocks: exact=187
-> Bitmap Index Scan on tb_foo_pkey (cost=0.00..112.51 rows=5608 width=0) (actual time=1.640..1.640 rows=
Index Cond: ((id_ >= 5) AND (id_ <= 20000))
Planning Time: 0.112 ms
Execution Time: 7.643 ms
453 16. Anexo 1 - Índices
[>] Plano de execução de uma consulta efetivamente executada usando como critério o
campo1:
EXPLAIN ANALYZE
SELECT campo2 FROM tb_foo
WHERE campo1 BETWEEN 5 AND 20000;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb_foo (cost=81.91..8629.20 rows=5608 width=32) (actual time=38.723..208.996 rows=999584
Recheck Cond: ((campo1 >= 5) AND (campo1 <= 20000))
Heap Blocks: exact=9346
-> Bitmap Index Scan on idx_1 (cost=0.00..80.51 rows=5608 width=0) (actual time=37.848..37.848 rows=99958
Index Cond: ((campo1 >= 5) AND (campo1 <= 20000))
Planning Time: 0.079 ms
Execution Time: 236.472 ms
SELECT
indexrelname, relname, idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes;
454
455 17. Anexo 2 - Scripts de conveniência
Em distribuições Linux da família Debian (Debian, Ubuntu, Linux Mint, etc), os scripts de
conveniência são fornecidos pelo pacote postgresql-common.
Maiores detalhes sobre cada um dos scripts que compõem estão disponíveis em:
https://manpages.debian.org/stretch/postgresql-common/index.html
pg_lsclusters
Foi criado um novo cluster (instância) cujo nome é “foo” e, após os dois hífens, foram passadas
opções do initdb para usar checksums para páginas de dados, autenticação local sem senha e
autenticação TCP/IP senhas criptografadas com hash scram-sha-256.
pg_lsclusters
Nota-se que à nova instância criada já foi atribuído um número de porta diferente para evitar
conflito com a main, que é a padrão e já existia.
No entanto, essa instância ainda não está rodando (down).
457 17. Anexo 2 - Scripts de conveniência
pg_lsclusters
Agora ambos os clusters estão rodando, cada um com sua própria porta.
Para remover uma instância, é necessário que a ela esteja parada, então a opção --stop faz
com que a remoção seja possível.
pg_lsclusters
459
460 18. Anexo 3 - Estratégias de atualização
Processo razoavelmente simples em que é feito um dump da base de dados ou mesmo de toda
instância de uma versão mais antiga e restaurado em uma instância com uma versão mais
nova.
Temos a vantagem de na restauração não ter bloats (inchaços) nas tabelas, mas é um processo
muito demorado, pois cada comando (SQL) tem que ser processado individualmente.
pg_upgrade
Aviso
Sobre o laboratório
Instância antiga
Versão ${PGMAJOR_OLD}
Porta 5432
PGDATA /var/local/pgsql/${PGMAJOR_OLD}/data
Binários /usr/local/pgsql/${PGMAJOR_OLD}/bin
Instância nova
Versão ${PGMAJOR_NEW}
Porta 5433 (provisória)
PGDATA /var/local/pgsql/${PGMAJOR_NEW}/data
Binários /usr/local/pgsql/${PGMAJOR_NEW}/bin
PGDATAOLD="/var/local/pgsql/${PGMAJOR_OLD}/data"
PGDATANEW="/var/local/pgsql/${PGMAJOR_NEW}/data"
PGBINOLD="/usr/local/pgsql/${PGMAJOR_OLD}/bin"
PGBINNEW="/usr/local/pgsql/${PGMAJOR_NEW}/bin"
pg_upgrade
. . .
[$] Mudar a configuração de porta de escuta do cluster novo para a porta padrão:
./analyze_new_cluster.sh
./delete_old_cluster.sh
464 18. Anexo 3 - Estratégias de atualização
Instância antiga
Versão ${PGMAJOR_OLD}
Porta 5432
PGDATA /var/lib/postgresql/${PGMAJOR_OLD}/main
Binários /usr/lib/postgresql/${PGMAJOR_OLD}/bin
Instância nova
Versão ${PGMAJOR_NEW}
Porta 5433 (provisória)
PGDATA /var/lib/postgresql/${PGMAJOR_NEW}/main
Binários /usr/lib/postgresql/${PGMAJOR_NEW}/bin
export CONFIGOLD="/etc/postgresql/${PGMAJOR_OLD}/main"
export CONFIGNEW="/etc/postgresql/${PGMAJOR_NEW}/main"
export PGDATAOLD="/var/lib/postgresql/${PGMAJOR_OLD}/main"
export PGDATANEW="/var/lib/postgresql/${PGMAJOR_NEW}/main"
export PGBINOLD="/usr/lib/postgresql/${PGMAJOR_OLD}/bin"
export PGBINNEW="/usr/lib/postgresql/${PGMAJOR_NEW}/bin"
${PGBINNEW}/pg_upgrade \
-o "-c config_file=${CONFIGOLD}/postgresql.conf" \
-O "-c config_file=${CONFIGNEW}/postgresql.conf"
[$] Mudar a configuração de porta de escuta do cluster novo para a porta padrão:
./analyze_new_cluster.sh
./delete_old_cluster.sh
466 18. Anexo 3 - Estratégias de atualização
Instância antiga
Versão ${PGMAJOR_OLD}
Porta 5432
PGDATA /var/lib/pgsql/${PGMAJOR_OLD}/data
Binários /usr/pgsql-${PGMAJOR_OLD}/bin
Instância nova
Versão ${PGMAJOR_NEW}
Porta 5433 (provisória)
PGDATA /var/lib/pgsql/${PGMAJOR_NEW}/data
Binários /usr/pgsql-${PGMAJOR_NEW}/bin
export PGDATAOLD="/var/lib/pgsql/${PGMAJOR_OLD}/data"
export PGDATANEW="/var/lib/pgsql/${PGMAJOR_NEW}/data"
export PGBINOLD="/usr/pgsql-${PGMAJOR_OLD}/bin"
export PGBINNEW="/usr/pgsql-${PGMAJOR_NEW}/bin"
${PGBINNEW}/pg_upgrade
[$] Mudar a configuração de porta de escuta do cluster novo para a porta padrão:
./analyze_new_cluster.sh
./delete_old_cluster.sh
19
Anexo 4 - FDW: Foreign-Data
Wrappers
• Sobre Foreign-Data Wrappers
• postgres_fdw: acessando outro servidor PostgreSQL
• mysql_fdw: acessando o MySQL ou MariaDB
• file_fdw: acesso aos arquivos
468
469 19. Anexo 4 - FDW: Foreign-Data Wrappers
[1] http://wiki.postgresql.org/wiki/Foreign_data_wrappers
[2] http://pgxn.org
Aviso
Tenha em mente que a maioria dos FDWs não são suportados oficialmente pelo PGDG
(PostgreSQL Global Development Group) e que alguns desses projetos ainda estão em sua
versão beta.
Servidor IP Papel
srv0 192.168.56.70 Origem - postgres_fdw
srv1 192.168.56.71 Destino - PostgreSQL
vim ${PGDATA}/postgresql.conf
password_encryption = scram-sha-256
pg_ctl reload
\c db_teste2
\c db_teste1
[>] [srv0] Criação da tabela estrangeira que vai se relacionar com a tabela criada em srv1:
Testes
id | campo_a | campo_b
----+---------+---------
1 | 700 | foo
id | campo_a | campo_b
----+---------+---------
1 | 700 | foo
475 19. Anexo 4 - FDW: Foreign-Data Wrappers
[>] [srv0] Remover tudo o que foi criado para o teste de FDW:
https://github.com/EnterpriseDB/mysql_fdw
Servidor IP Papel
srv0 192.168.56.70 Origem - mysql_fdw
srv1 192.168.56.71 Destino - MariaDB / MySQL
mysql db_teste2
[>] [srv1] Conceder todos privilégios a todos objetos do banco ao usuário e servidor
PostgreSQL:
source ~postgres/.pgvars
export C_INCLUDE_PATH="${C_INCLUDE_PATH}:/usr/include/mariadb"
478 19. Anexo 4 - FDW: Foreign-Data Wrappers
[#] [srv0] Com a variável de ambiente USE_PGXS habilitada iniciar a compilação do FDW:
createdb db_teste1
psql db_teste1
INSERT INTO ft_teste (id, campo_a, campo_b) VALUES (2, 500, ’’bar);
id | campo_a | campo_b
----+---------+---------
1 | 700 | foo
2 | 500 | bar
+----+---------+---------+
| id | campo_a | campo_b |
+----+---------+---------+
| 1 | 700 | foo |
| 2 | 500 | bar |
+----+---------+---------+
482 19. Anexo 4 - FDW: Foreign-Data Wrappers
https://www.postgresql.org/docs/current/static/file-fdw.html
id | nome | populacao
----+----------------+-----------
SP | São Paulo | 12106920
RJ | Rio de Janeiro | 6520266
MG | Belo Horizonte | 2523794
ES | Vitória | 363140
id | nome | populacao
----+----------------+-----------
RJ | Rio de Janeiro | 6520266
MG | Belo Horizonte | 2523794
ES | Vitória | 363140