WAL no PostgreSQL: 4. Configuração do log

Assim, nos familiarizamos com o dispositivo do cache de buffer e, usando seu exemplo, percebemos que quando o conteúdo da RAM desaparece em caso de falha, é necessário um log de pré-registro para recuperação. O tamanho dos arquivos de log necessários e o tempo de recuperação são limitados devido a um ponto de verificação executado periodicamente.

Nos artigos anteriores, já examinamos um número bastante grande de configurações importantes relacionadas ao periódico, de uma maneira ou de outra. Neste artigo (o último desta série), consideraremos os problemas de ajuste que ainda não foram discutidos: níveis de log e sua finalidade, bem como a confiabilidade e o desempenho do log.

Níveis de log


O objetivo principal do log de pré-registro é fornecer a capacidade de recuperar-se de uma falha. Porém, se você ainda precisar manter um diário, ele poderá ser adaptado para outras tarefas, adicionando uma certa quantidade de informações adicionais a ele. Existem vários níveis de log. Eles são definidos pelo parâmetro wal_level e são organizados de modo que o log de cada próximo nível inclua tudo o que cai no log do nível anterior, além de outra coisa nova.

Mínimo


O nível mínimo possível é definido pelo valor wal_level = minimal e garante apenas a recuperação após uma falha. Para economizar espaço, as operações relacionadas ao processamento de dados em massa (como CREATE TABLE AS SELECT ou CREATE INDEX) não são registradas. Em vez disso, os dados necessários são gravados imediatamente no disco e um novo objeto é adicionado ao diretório do sistema e fica visível quando a transação é confirmada. Se ocorrer uma falha durante a operação, os dados já registrados permanecem invisíveis e não violam a consistência. Se a falha ocorrer após a conclusão da operação, todo o necessário já foi para o disco e não precisará ser registrado.

Vamos ver Primeiro, defina o nível necessário (para isso, você também precisará alterar outro parâmetro - max_wal_senders ).

=> ALTER SYSTEM SET wal_level = minimal; => ALTER SYSTEM SET max_wal_senders = 0; 

 student$ sudo pg_ctlcluster 11 main restart 

Observe que alterar o nível requer uma reinicialização do servidor.

Lembre-se da posição atual no log:

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353927BC (1 row) 

Agora vamos criar a tabela (CREATE TABLE AS SELECT) e escrever a posição no log novamente. A quantidade de dados selecionados pela instrução SELECT não importa neste caso, portanto, nos limitaremos a uma linha.

 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353A7DFC (1 row) 

Com o familiar utilitário pg_waldump, vejamos as entradas de log.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353927BC -e 0/353A7DFC 

Alguns detalhes, é claro, podem diferir de lançamento para lançamento, mas, neste caso, foi o que aconteceu. A entrada do gerenciador Heap2 refere-se à limpeza; aqui está uma limpeza na página de uma das tabelas no catálogo do sistema (os objetos do sistema são facilmente distinguidos a olho nu pelo número “curto” em rel):

 rmgr: Heap2 len (rec/tot): 59/ 7587, tx: 0, lsn: 0/353927BC, prev 0/35392788, desc: CLEAN remxid 101126, blkref #0: rel 1663/16386/1247 blk 8 FPW 

Depois, há um registro sobre como obter o próximo OID da tabela que vamos criar:

 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/35394574, prev 0/353927BC, desc: NEXTOID 82295 

Agora, a criação real da tabela:

 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/35394594, prev 0/35394574, desc: CREATE base/16386/74103 

No entanto, a inserção de dados em uma tabela não é registrada. Existem inúmeras entradas sobre a inserção de linhas em diferentes tabelas e índices - este PostgreSQL registra a tabela criada no diretório do sistema (eu a dou de forma abreviada):

 rmgr: Heap len (rec/tot): 203/ 203, tx: 101127, lsn: 0/353945C0, prev 0/35394594, desc: INSERT off 71, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 685, tx: 101127, lsn: 0/3539468C, prev 0/353945C0, desc: INSERT_LEAF off 37, blkref #0: rel 1663/16386/2703 blk 2 FPW ... rmgr: Btree len (rec/tot): 53/ 2393, tx: 101127, lsn: 0/353A747C, prev 0/353A6788, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

E, finalmente, a fixação da transação:

 rmgr: Transaction len (rec/tot): 34/ 34, tx: 101127, lsn: 0/353A7DD8, prev 0/353A747C, desc: COMMIT 2019-07-23 18:59:34.923124 MSK 

Réplica


Quando restauramos o sistema a partir do backup, começamos a partir de algum estado do sistema de arquivos e, gradualmente, levamos os dados ao ponto de recuperação, reproduzindo as entradas de diário arquivadas. O número desses registros pode ser muito grande (por exemplo, vários dias), ou seja, o período de recuperação cobrirá não um ponto de controle, mas muitos. Portanto, é claro que o nível mínimo do log não é suficiente - se alguma operação não for registrada, simplesmente não saberemos que ela precisa ser repetida. Para restaurar de um backup, todas as operações devem ser registradas.

O mesmo se aplica à replicação - qualquer coisa que não seja registrada não será transferida para a réplica e não será reproduzida. Mas, se queremos executar solicitações em uma réplica, ainda é complicado.

Primeiro, precisamos de informações sobre bloqueios exclusivos que ocorrem no servidor principal, pois eles podem entrar em conflito com solicitações na réplica. Esses bloqueios são registrados e aplicados na réplica (em nome do processo de inicialização).

Em segundo lugar, você precisa criar instantâneos de dados e, para isso, como lembramos, são necessárias informações sobre transações em andamento. No caso de uma réplica, estamos falando não apenas de transações locais, mas também de transações no servidor principal. A única maneira de transmitir essas informações é gravá-las periodicamente no log (isso acontece a cada 15 segundos).

O nível do log, que garante a capacidade de recuperar de um backup e a possibilidade de replicação física, é definido pelo valor wal_level = replica . (Antes da versão 9.6, havia dois níveis separados de archive e hot_standby, mas eles eram combinados em um comum.)

Começando com o PostgreSQL 10, é esse nível que é definido por padrão (e antes disso era mínimo). Portanto, basta redefinir os parâmetros para os valores padrão:

 => ALTER SYSTEM RESET wal_level; => ALTER SYSTEM RESET max_wal_senders; 

 student$ sudo pg_ctlcluster 11 main restart 

Excluímos a tabela e repetimos exatamente a mesma sequência de ações da última vez:

 => DROP TABLE wallevel; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353AF21C (1 row) 
 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353BE51C (1 row) 

Agora verifique os lançamentos no diário.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353AF21C -e 0/353BE51C 

Limpando, obtendo OID, criando uma tabela e registrando-se no diretório do sistema - por enquanto, tudo está como estava:

 rmgr: Heap2 len (rec/tot): 58/ 58, tx: 0, lsn: 0/353AF21C, prev 0/353AF044, desc: CLEAN remxid 101128, blkref #0: rel 1663/16386/1247 blk 8 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/353AF258, prev 0/353AF21C, desc: NEXTOID 82298 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/353AF278, prev 0/353AF258, desc: CREATE base/16386/74106 rmgr: Heap len (rec/tot): 203/ 203, tx: 101129, lsn: 0/353AF2A4, prev 0/353AF278, desc: INSERT off 73, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 717, tx: 101129, lsn: 0/353AF370, prev 0/353AF2A4, … rmgr: Btree len (rec/tot): 53/ 2413, tx: 101129, lsn: 0/353BD954, prev 0/353BCC44, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

Mas algo novo. O registro de um bloqueio exclusivo relacionado ao gerenciador de espera - neste caso, está bloqueando o número da transação (por que é necessário, falaremos detalhadamente na próxima série de artigos):

 rmgr: Standby len (rec/tot): 42/ 42, tx: 101129, lsn: 0/353BE2D8, prev 0/353BD954, desc: LOCK xid 101129 db 16386 rel 74106 

E este é um registro sobre a inserção de linhas em nossa tabela (compare o número do arquivo rel com o indicado acima no registro CREATE):

 rmgr: Heap len (rec/tot): 59/ 59, tx: 101129, lsn: 0/353BE304, prev 0/353BE2D8, desc: INSERT+INIT off 1, blkref #0: rel 1663/16386/74106 blk 0 

Confirmar registro:

 rmgr: Transaction len (rec/tot): 421/ 421, tx: 101129, lsn: 0/353BE340, prev 0/353BE304, desc: COMMIT 2019-07-23 18:59:37.870333 MSK; inval msgs: catcache 74 catcache 73 catcache 74 catcache 73 catcache 50 catcache 49 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 snapshot 2608 relcache 74106 snapshot 1214 

E outro registro, que ocorre periodicamente e não está vinculado à transação concluída, refere-se ao gerente em espera e relata as transações atualmente em andamento:

 rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 0/353BE4E8, prev 0/353BE340, desc: RUNNING_XACTS nextXid 101130 latestCompletedXid 101129 oldestRunningXid 101130 

Logical


Finalmente, o último nível é definido pelo valor do parâmetro wal_level = logic e fornece a possibilidade de decodificação lógica e replicação lógica. Ele deve estar ativado no servidor de publicação.

Do ponto de vista das entradas no diário, esse nível é praticamente diferente da réplica - são adicionados registros relacionados às origens da replicação e entradas lógicas arbitrárias que podem ser adicionadas ao log do aplicativo. Basicamente, a decodificação lógica depende de informações sobre transações em andamento, pois é necessário criar uma captura instantânea de dados para rastrear alterações no catálogo do sistema.

Agora, não entraremos em detalhes da operação de backup e replicação - este é um tópico importante para uma série separada de artigos.

Grave confiabilidade


É claro que o mecanismo de registro no diário deve ser confiável e fornecer garantias da possibilidade de recuperação em qualquer situação (obviamente não relacionada a danos ao suporte de dados). A confiabilidade é influenciada por muitos fatores, dos quais consideraremos cache, corrupção de dados e atomicidade das gravações.

Armazenamento em cache


Existem vários caches no caminho de dados para o armazenamento não volátil (como uma unidade de disco rígido).

Quando um programa (qualquer, mas no nosso caso o PostgreSQL) solicita que o sistema operacional grave algo no disco, o sistema operacional transfere os dados para o cache na RAM. A gravação real ocorre de forma assíncrona, dependendo das configurações do agendador de E / S do sistema operacional.

Quando o sistema operacional decide gravar dados, eles caem no cache da unidade (disco rígido). A eletrônica do inversor também pode atrasar a gravação, por exemplo, a coleta de dados em grupos que são mais lucrativos para gravar ao mesmo tempo. E se um controlador RAID for usado, outro nível de cache aparecerá entre o SO e a unidade.

Portanto, se você não tomar medidas especiais, não será totalmente claro quando os dados serão realmente armazenados com segurança. Isso geralmente não é importante, mas há locais críticos onde o PostgreSQL precisa ter certeza de que os dados sejam gravados com segurança. Antes de tudo, trata-se de registro no diário (se a entrada do diário não atingir o disco, ela desaparecerá junto com o restante do conteúdo da RAM) e um ponto de verificação (deve-se garantir que as páginas sujas sejam realmente gravadas no disco). Mas existem outras situações, por exemplo, a execução de operações não registradas no diário no nível mínimo, etc.

O sistema operacional fornece ferramentas que devem garantir a gravação imediata dos dados na memória não volátil. Existem várias opções, mas elas se resumem a duas principais: um comando de sincronização é dado após a gravação (fsync, fdatasync) ou ao abrir um arquivo (ou gravá-lo), um sinalizador especial é indicado para sincronização ou mesmo gravação direta, ignorando o cache do SO.

Quanto ao log, o utilitário pg_test_fsync permite escolher o método mais adequado para um sistema operacional específico e um sistema de arquivos específico, e ele é instalado no parâmetro de configuração wal_sync_method . Arquivos regulares são sempre sincronizados usando o fsync.

O ponto sutil é que, ao escolher um método, as características do equipamento devem ser levadas em consideração. Por exemplo, se você usar um controlador suportado por uma bateria de reserva, não há motivo para não usar seu cache, pois a bateria salvará os dados no caso de uma falta de energia.

A documentação contém muitos detalhes sobre este assunto.

De qualquer forma, a sincronização é cara e ocorre com mais freqüência do que o absolutamente necessário (retornaremos a esse problema um pouco mais baixo quando falarmos sobre desempenho).

De um modo geral, a sincronização pode ser desativada (o parâmetro fsync é responsável por isso), mas, nesse caso, você deve esquecer a confiabilidade do armazenamento. Ao desativar o fsync , você concorda que os dados podem ser irremediavelmente perdidos a qualquer momento. Provavelmente, a única opção razoável para usar essa opção é aumentar temporariamente a produtividade, quando os dados podem ser facilmente restaurados de outra fonte (por exemplo, durante a migração inicial).

Corrupção de dados


O equipamento é imperfeito e os dados podem ser danificados na mídia ao transmitir dados por cabos de interface, etc. Alguns desses erros são processados ​​no nível do hardware, mas outros não.

Para detectar o problema a tempo, as entradas do diário são sempre fornecidas com somas de verificação.

As páginas de dados também podem ser protegidas com somas de verificação. Por enquanto, isso só pode ser feito quando o cluster é inicializado, mas no PostgreSQL 12 será possível ativá-lo e desativá-lo usando o utilitário pg_checksums (embora ainda não esteja em andamento, mas apenas quando o servidor estiver parado).

Em um ambiente de produção, as somas de verificação devem ser incluídas, apesar da sobrecarga de seu cálculo e controle. Isso reduz a probabilidade de uma falha não ser detectada a tempo.

Reduz, mas não elimina.
Em primeiro lugar, as somas de verificação são verificadas apenas ao acessar a página - portanto, os danos podem passar despercebidos até que entrem em todos os backups. É por isso que o pg_probackup verifica somas de verificação de todas as páginas do cluster durante o backup.
Em segundo lugar, uma página cheia de zeros é considerada correta - se o sistema de arquivos "anular" por engano, isso pode passar despercebido.
Em terceiro lugar, as somas de verificação protegem apenas a camada principal dos arquivos de dados. As camadas restantes e outros arquivos (por exemplo, status de transação XACT) não são protegidos por nada.
Infelizmente.

Vamos ver como isso funciona. Primeiro, verifique se as somas de verificação estão ativadas (lembre-se de que este não é o caso ao instalar um pacote em sistemas semelhantes ao Debian):

 => SHOW data_checksums; 
  data_checksums ---------------- on (1 row) 

O parâmetro data_checksums é somente leitura.

Aqui está o arquivo em que nossa tabela está localizada:

 => SELECT pg_relation_filepath('wallevel'); 
  pg_relation_filepath ---------------------- base/16386/24890 (1 row) 

Pare o servidor e altere alguns bytes na página zero, por exemplo, exclua a última entrada do log do cabeçalho LSN.

 student$ sudo pg_ctlcluster 11 main stop 

 postgres$ dd if=/dev/zero of=/var/lib/postgresql/11/main/base/16386/24890 oflag=dsync conv=notrunc bs=1 count=8 
 8+0 records in 8+0 records out 8 bytes copied, 0,0083022 s, 1,0 kB/s 

Em princípio, o servidor não pôde ser parado. Basta que a página tenha sido gravada em disco e forçada a sair do cache (caso contrário, o servidor funcionará com a página a partir do cache). Mas esse cenário é mais difícil de reproduzir.

Agora, iniciamos o servidor e tentamos ler a tabela.

 student$ sudo pg_ctlcluster 11 main start 

 => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 ERROR: invalid page in block 0 of relation base/16386/24890 

Mas e se os dados não puderem ser restaurados a partir do backup? O parâmetro ignore_checksum_failure permite que você tente ler a tabela, naturalmente com o risco de obter dados distorcidos.

 => SET ignore_checksum_failure = on; => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 n --- 1 (1 row) 

É claro que, neste caso, tudo corre bem, porque confundimos apenas o título da página, e não os dados em si.

E mais uma coisa. Quando as somas de verificação são ativadas, bits de avisos são gravados no log (nós os examinamos anteriormente), pois uma alteração em qualquer bit, mesmo que não seja essencial, também leva a uma alteração na soma de verificação. Quando as somas de verificação estão desativadas, o parâmetro wal_log_hints é responsável por gravar bits de dica no log .

As alterações nos bits da dica de ferramenta são sempre registradas como uma imagem de página inteira (FPI, imagem de página inteira), o que aumenta o tamanho do log na ordem. Nesse caso, faz sentido habilitar a compactação de imagens completas usando o parâmetro wal_compression (esse parâmetro apareceu na versão 9.5). Abaixo, examinamos números específicos.

Registro de atomicidade


E, finalmente, há o problema de atomicidade do registro. A página do banco de dados ocupa pelo menos 8 KB (pode ser 16 ou 32 KB) e, em um nível baixo, a gravação ocorre em blocos geralmente menores (geralmente 512 bytes ou 4 KB). Portanto, no caso de falta de energia, a página de dados pode ser parcialmente registrada. É claro que, durante a recuperação, não faz sentido aplicar lançamentos contábeis comuns a essa página.

Para proteção, o PostgreSQL permite gravar no log a imagem completa da página quando ela é alterada pela primeira vez após o início do ponto de verificação (a mesma imagem é gravada quando os bits da dica de ferramenta são alterados). O parâmetro full_page_writes controla isso e é ativado por padrão.

Se uma imagem da página é encontrada durante a recuperação em um log, ela é gravada incondicionalmente (sem verificação de LSN) no disco: há mais confiança nela, porque, como qualquer registro de log, ela é protegida por uma soma de verificação. E já são aplicados lançamentos regulares a essa imagem correta garantida.

Embora o PostgreSQL exclua espaço não alocado da imagem da página inteira ( examinamos anteriormente a estrutura do bloco), o volume de lançamentos gerados no diário aumenta significativamente. Como já mencionado, a situação pode ser melhorada compactando as imagens completas (parâmetro wal_compression ).

Para sentir de alguma forma a mudança no tamanho do log, realizaremos um experimento simples usando o utilitário pgbench. Vamos inicializar:

 student$ pgbench -i test 
 dropping old tables... creating tables... generating data... 100000 of 100000 tuples (100%) done (elapsed 0.15 s, remaining 0.00 s) vacuuming... creating primary keys... done. 

A opção full_page_writes está ativada:

 => SHOW full_page_writes; 
  full_page_writes ------------------ on (1 row) 

Execute o ponto de interrupção e execute imediatamente o teste por 30 segundos.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/38E04A08 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26851 latency average = 1.117 ms tps = 895.006720 (including connections establishing) tps = 895.095229 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C478 (1 row) 

Tamanho do registro:

 => SELECT pg_size_pretty('0/3A69C478'::pg_lsn - '0/38E04A08'::pg_lsn); 
  pg_size_pretty ---------------- 25 MB (1 row) 

Agora desative o parâmetro full_page_writes:

 => ALTER SYSTEM SET full_page_writes = off; => SELECT pg_reload_conf(); 

E repita o experimento.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C530 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 27234 latency average = 1.102 ms tps = 907.783080 (including connections establishing) tps = 907.895326 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87658 (1 row) 

Tamanho do registro:

 => SELECT pg_size_pretty('0/3BE87658'::pg_lsn - '0/3A69C530'::pg_lsn); 
  pg_size_pretty ---------------- 24 MB (1 row) 

Sim, o tamanho diminuiu, mas não é tão significativo quanto se poderia esperar.

O motivo é que o cluster é inicializado com somas de verificação nas páginas de dados e, portanto, você ainda precisa gravar imagens de página inteira no log ao alterar os bits da dica de ferramenta. Esses dados (no nosso caso) compõem cerca de metade do volume total, o que pode ser visto observando as estatísticas:

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump --stats -p /var/lib/postgresql/11/main/pg_wal -s 0/3A69C530 -e 0/3BE87658 
 Type N (%) Record size (%) FPI size (%) ---- - --- ----------- --- -------- --- XLOG 1721 ( 1,03) 84329 ( 0,77) 13916104 (100,00) Transaction 27235 ( 16,32) 926070 ( 8,46) 0 ( 0,00) Storage 1 ( 0,00) 42 ( 0,00) 0 ( 0,00) CLOG 1 ( 0,00) 30 ( 0,00) 0 ( 0,00) Standby 4 ( 0,00) 240 ( 0,00) 0 ( 0,00) Heap2 27522 ( 16,49) 1726352 ( 15,76) 0 ( 0,00) Heap 109691 ( 65,71) 8169121 ( 74,59) 0 ( 0,00) Btree 756 ( 0,45) 45380 ( 0,41) 0 ( 0,00) -------- -------- -------- Total 166931 10951564 [44,04%] 13916104 [55,96%] 

Para compactar, removi as zero linhas da tabela. Preste atenção à linha total (Total) e compare o tamanho das imagens completas (tamanho do FPI) com o tamanho dos registros comuns (tamanho do registro).

O parâmetro full_page_writes pode ser desativado apenas se o sistema de arquivos e o hardware usados ​​por eles mesmos garantirem a gravação atômica. Mas, como podemos ver, não há grandes razões para isso (assumindo que as somas de verificação estão incluídas).

Agora vamos ver como a compactação ajuda.

 => ALTER SYSTEM SET full_page_writes = on; => ALTER SYSTEM SET wal_compression = on; => SELECT pg_reload_conf(); 

Repita o mesmo experimento.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87710 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26833 latency average = 1.118 ms tps = 894.405027 (including connections establishing) tps = 894.516845 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3CBD3EA8 (1 row) 

Tamanho do registro:

 => SELECT pg_size_pretty('0/3CBD3EA8'::pg_lsn - '0/3BE87710'::pg_lsn); 
  pg_size_pretty ---------------- 13 MB (1 row) 

Conclusão: na presença de um grande número de imagens de página inteira (devido a somas de verificação ou full_page_writes , ou seja, quase sempre), provavelmente faz sentido usar a compactação, apesar de carregar o processador.

Desempenho


Durante a operação normal do servidor, ocorre uma gravação seqüencial contínua dos arquivos de log. Como não há acesso aleatório, os HDDs regulares também lidam com esta tarefa. Mas essa natureza da carga é significativamente diferente de como os arquivos de dados são acessados.

Portanto, geralmente é vantajoso colocar o log em um disco físico separado (ou matriz de discos) montado no sistema de arquivos do servidor. Em vez do diretório $ PGDATA / pg_wal, você precisa criar um link simbólico para o diretório correspondente.

Existem algumas situações em que os arquivos de log precisam não apenas ser gravados, mas também lidos. O primeiro é um caso compreensível de recuperação após uma falha. O segundo é menos trivial. Isso ocorre se a replicação de streaming for usada e a réplica não conseguir receber entradas de diário enquanto elas ainda estiverem nos buffers de RAM do servidor principal. Então o processo do walsender precisa ler os dados necessários do disco. Falaremos sobre isso em mais detalhes quando chegarmos à replicação.

O registro ocorre em um dos dois modos:

  • síncrono - quando uma transação é confirmada, a continuação do trabalho é impossível até que todos os lançamentos contábeis manuais sobre essa transação estejam no disco;
  • assíncrono - a transação é concluída imediatamente e o log é gravado em segundo plano.

O modo síncrono é determinado pelo parâmetro synchronous_commit e é ativado por padrão.

Como a sincronização está associada a E / S real (ou seja, lenta), é benéfico executá-lo o mínimo possível. Para fazer isso, o processo de manutenção que conclui a transação e grava um log faz uma pequena pausa, determinada pelo parâmetro commit_delay . , commit_siblings . , . , , - .

commit_siblings = 5, commit_delay = 0, . commit_delay , OLTP-.

LSN ( , ). .

( D ACID) — , . , ( COMMIT ) .

, synchronous_commit = off ( local).

wal writer, ( wal_writer_delay = 200ms ).

, , WAL. , , , , . (, : , , .)

, ( ) — ?

, , .

— . : , 3 × wal_writer_delay ( ).

— — .

: ( fsync = off), . , , , .

synchronous_commitpode ser definido como parte de transações individuais. Isso permite aumentar a produtividade sacrificando a confiabilidade de apenas parte da transação. Digamos, as transações financeiras sempre precisam ser corrigidas de forma síncrona e as mensagens de bate-papo às vezes podem ser negligenciadas.

Na realidade, esses dois modos funcionam juntos. Mesmo com a confirmação síncrona, os logs de transação longos serão gravados de forma assíncrona para liberar os buffers do WAL. E se, ao redefinir uma página do cache do buffer, a entrada de diário correspondente ainda não estiver no disco, ela será redefinida imediatamente no modo síncrono.

Para ter uma idéia do que a confirmação assíncrona fornece, tentamos repetir o teste pgbench nesse modo.

 => ALTER SYSTEM SET synchronous_commit = off; => SELECT pg_reload_conf(); 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 45439 latency average = 0.660 ms tps = 1514.561710 (including connections establishing) tps = 1514.710558 (excluding connections establishing) 

900 (tps), — 1500. , , , .

. - , . Obrigado a todos!

, .

Source: https://habr.com/ru/post/pt461523/


All Articles