O que são transações?
Transações são conjuntos de instruções enviadas ao banco de
dados que devem ser tratadas com uma única operação. Ou o servidor
realiza tudo ou não realiza nada. O exemplo clássico usado em 110%
dos cursos de bancos de dados é a famosa transação financeira de
transferência de fundos. Imagine que Mônica deseje transferir
100,00 reais para a conta de Cebolinha. Esta operação na realidade
se divide em duas. Um débito na conta de Mônica e um crédito de
mesmo valor na conta de Cebolinha (a ordem não interfere). Agora, se
logo após o débito na primeira conta, o servidor sair do ar antes
de que possa executar a segunda. Para onde foram os 100,00 reais?
Existem quatro características que os SGBDs devem garantir se
pretendem lidar com transações com segurança. O conjunto dessas
características é conhecido como ACID (atomicity, consistency,
isolation, durability). Vejamos o que significam:
Atomicidade
É o ponto chave. É quem determina o tudo-ou-nada. A comunidade
científica por longos anos acreditou que um átomo seria a unidade
mínima da matéria, sólido e indivisível. Já se sabe em simples
aulas de química que essa idéia caiu há séculos. No entanto, este
conceito serve de analogia para ocasiões onde deve se considerar a
impossibilidade de dividir algo em unidades menores.
Sendo assim, a atomicidade é a característica que torna um
conjunto de operações um agrupamento indivisível executado por
completo ou nada feito.
Consistência
Esta característica determina que o SGBD deve iniciar uma
transação em um estado consistente e terminá-la deixando os dados
em outro estado consistente. Caso alguma ação dentro de uma
transação dê problemas e fira alguma regra de consistência de
dados (como uma falha de escrita em disco, por exemplo), toda a
transação deve ser completamente abortada, deixando os dados em seu
estado consistente imediatamente anterior. O ato de abortar e voltar
ao ponto inicial é conhecido como ROLLBACK.
Inconsistências não se resumem a problemas físicos de dados. É
possível encarar uma situação fisicamente consistente como
logicamente inconsistente. No exemplo dos desaparecimento dos 100,00
reais, o banco ficou em um estado inconsistente uma vez que se
somarmos os saldos nas duas contas, independentemente do valor
transferido os resultados pré e pós transferência deveria dar
consistentemente o mesmo.
Isolamento
Esta característica remete a acessos concorrentes. Caso apenas
uma transação seja executada no SGBD em determinado intervalo de
tempo, o isolamento não vai lhe acrescentar nada. No entanto, como
sabemos que SGBDs, na maioria dos casos, são usados por sistemas
concorrentes, esta característica entra para garantir que
informações de uma transação não se misturem com de outras.
Esta é a única característica relativa das quatro. Ou seja, é
a única que não necessariamente é ruim quando infringida de alguma
forma. Por um lado, seria ruim dados de uma transação estarem
visíveis a outras como no problema clássico conhecido como Dirty
Read que ocorre quando uma transação tx1 modifica alguns registros,
uma transação tx2 ao selecionar alguns dados, visualiza esses
registros alterados por tx1, mas esta primeira é abortada por alguma
razão. Em resumo, tx2 leu dados errados, uma vez que eles nunca
deveriam ter sido alterados.
Por outro lado, a capacidade de trabalhar com múltiplas
transações ao mesmo tempo pode garantir o bom desempenho em
sistemas muito acessados, uma vez que uma não fica esperando pela
conclusão da outra. Elas são implementadas em ambientes
multi-processos ou multi-threads e podem explorar melhor o pontencial
multi-core dos hardwares atuais.
Durabilidade
Toda transação é encerrada com a execução da ação COMMIT.
Esta característica é a garantia dada pelo SGBD que quando retornar
um sinal COMMIT, os dados já devem estar gravados no meio
persistente e ele deve ser capaz de recuperar esses dados em
transações posteriores. Caso haja algum problema, a transação
deve ser abortada. Conforme a regra da consistência, caso não haja
problemas, o banco está jurando de pés juntos que gravou e você
vai poder ler depois.
Como tratado no capítulo 1, o PostgreSQL escreve em disco
registros de log WAL e assim que estejam completamente escritos,
retorna o sinal COMMIT. Já no capítulo 2, tratamos das diretivas do
postgresql.conf
fsync e
wal_sync_method. Para
recapitular, a primeira faz com que o SGBD após a execução do
comando de escrita do segmento WAL realize consultas ao sistema
operacional a fim de obter a garantia de que o valor escrito está
completamente descarregado no disco. Já a segunda determina o método
adotado nesta pesquisa. Desligar a diretiva fsync pode proporcionar
um maior desempenho, mas o risco da falta de durabilidade de uma
transação “commitada” em geral não faz valer a pena esse
tuning desesperado.
O método de sincronização com o disco de segmentos WAL
(
wal_sync_method) padrão do Linux é o
fdatasync.
O método
fsync aguarda o sistema operacional descarregar em
disco toda a pilha do buffer de I/O e atualizar o inode do sistema de
arquivos com a informação de última alteração do arquivo
escrito. O
fdatasync não espera pela atualização deste
metadado. Ele se preocupa apenas com o que mais importa que é a
atualização propriamente dita dos dados.
Transações e Write-ahead Logging
Ao confirmar cada transação, o servidor não registra em disco
as páginas de dados (aqueles 8Kb que ficam na memória). É bem
menos custoso registrar neste momento (e esperar pelo fsync), um
registro de segmento WAL uma vez que ele é linear (alterações na
base real podem envolver dados que não necessariamente estejam na
mesma página de dados) e por isso realizam movimentos menores da
controladora de disco, além de não requerer pesquisa por espaço
livre (FSM).
Por outro lado, a manipulação das páginas de memória
compartilhada que são carregadas do e para o disco é bem mais
vantajoso do que sair vasculhando registros de log de operações a
fim de obter os dados necessários nas consultas.
Sendo assim, o PostgreSQL precisa realizar essa tradução do que
é salvo em log para as páginas de dados de tempos em tempos. Este
processo de escrita em background (bgwriter) é denominado
CHECKPOINT. No PostgreSQL, checkpoints não têm ligação direta com
o COMMIT de uma transação, mas achei interessante ilustrar o que
deve ocorrer com algumas transações confirmadas entre um checkpoint
e um provável crash da base.
Problemas de isolamento
Transações já são interessantes mesmo em cenário standalone,
uma vez que é possível desfazer todo um bloco de procedimentos
apenas com um simples Rollback. Mas é em sistemas concorrentes que o
bicho pega. Caso o isolamento não seja garantido alguns problemas
podem ocorrer.
Dirty read
O primeiro deles já foi apontado. Dirty Read ou leitura suja
acontece quando uma transação lê algum dado alterado por outra que
acaba não confirmando suas alterações. É um problema extremo que
de fato não é comum que bancos de dados permitam que ocorra. O
PostgreSQL não permite. Confira no exercício abaixo.
Primeiramente,
vamos criar a base para os testes:
01 |
postgres=# CREATE DATABASE
exercicio; |
03 |
postgres=# \c exercicio |
04 |
You are now connected to database
"exercicio". |
05 |
exercicio=# create table
aluno (id serial primary key,
nome varchar(50)); |
06 |
NOTICE: CREATE TABLE
will create implicit
sequence "aluno_id_seq"
for serial
column "aluno.id" |
07 |
NOTICE: CREATE TABLE
/ PRIMARY KEY
will create implicit
index "aluno_pkey"
for table
"aluno" |
09 |
exercicio=# INSERT into
aluno (nome) values ('Huguinho'),
('Zezinho'), ('Luisinho'), ('Juquinha'), ('Joaozinho'); |
Criamos 5 alunos (só peça boa =) em uma nova base. Agora vamos
ao primeiro cenário. Para isto será necessário que você abra duas
instâncias do psql ou qualquer outra ferramenta cliente (que
certamente não será tão limpa e cheirosa quanto o psql).
Inicie uma transação em cada uma executando em ambas a
instrução:
Na primeira, execute o seguinte comando:
01 |
exercicio=# UPDATE aluno
set nome = 'Jose da Silva'
where id = 2; |
04 |
exercicio=# select *
from aluno; |
Veja que o nome de Zezinho agora é Jose da Silva.
Realize a mesma consulta na segunda instância do psql:
1 |
exercicio=# select *
from aluno; |
Como você atentou, os alunos continuam com seus nomes intactos. O
que a primeira transação modificou não foi sentido pela segunda. O
motivo é que se a primeira voltar atrás, a segunda já teria lido e
tomado suas próprias decisões baseando-se em dados inconsistentes.
Confira como faríamos para reverter (execute na primeira transação):
01 |
exercicio=# ROLLBACK ; |
03 |
exercicio=# select *
from aluno; |
Nossa base voltou ao estágio em que estava no momento do BEGIN.
Unrepeatable Reads
Leituras não repetíveis acontecem quando uma transação lê
algum dado em determinado momento, posteriormente outra transação
altera ou deleta e executa o COMMIT. A primeira volta a ler os mesmos
dados e constata que eles não estão mais como antes. Este não
chega a ser um problema em muitos sistemas, a ponto do PostgreSQL, em
seu nível de isolamento padrão, permitir que ocorra. Vejamos:
PSQL1: Iniciamos uma nova transação (aquela anterior foi
desfeita com ROLLBACK) e atualizamos novamente o nome de Zezinho.
3 |
exercicio=# UPDATE aluno
set nome = 'Jose da Silva'
where id = 2; |
PSQL2: Como visto antes, nada foi percebido pela outra transação.
1 |
exercicio=# select *
from aluno; |
PSQL1: Confirmamos a alteração
PSQL2: Agora a alteração foi sentida
1 |
exercicio=# select *
from aluno; |
Phantom Read
O problema das leituras fantasmas é parecido com o das leituras
não repetíveis, o que muda é que uma das transações em vez de
alterar o conjunto de dados, insere um dado novo.
Vamos refazer a dinâmica. Primeiramente, encerre as transações
das duas instâncias do psql. Neste momento, tanto faz encerrar com
COMMIT ou com ROLLBACK. Se a transação não estiver em curso, você
receberá uma mensagem como esta:
WARNING: there is no transaction in progress
PSQL1: Inicie a transação e insira duas novas alunas
3 |
exercicio=# INSERT INTO
aluno (nome) values ('Chiquinha'),
('Mariazinha'); |
PSQL2: Inicie a transação e consulte a lista de alunos
03 |
exercicio=# select *
from aluno; |
Ninguém novo até agora.
PSQL1: Confirme sua transação com um COMMIT
PSQL2: Veja que as novas alunas agora estão presentes
01 |
exercicio=# select *
from aluno; |
Níveis de isolamento
Existem quatro níveis de isolamento segundo o padrão SQL:
READ UNCOMMITED
Nível menos isolado. Aqui, todos os problemas citados podem
ocorrer, inclusive as porcas dirty reads. É muito difícil um SGBD
permitir que o DBA mesmo que deliberadamente configure o nível de
isolamento para READ UNCOMMITED. Em resumo, é um nível acadêmico,
apenas para você dizer que conhece.
READ COMMITED
Nível padrão do PostgreSQL. Aqui dirty reads estão barradas,
mas, como verificamos nos exercícios anteriores, unrepeatable reads
e phantom reads podem ocorrer.
REPEATABLE READ
Neste nível, apenas as phantom reads podem ocorrer. Os SGBDs
fazem bloqueio (lock) do conjunto de dados lidos em uma transação,
o que evita que as leituras posteriores apresentem resultados
modificados ou deletados confirmados por outra transação. No
entanto, não há como fazer lock de um registro que ainda não
existia na primeira leitura. Este é o motivo pelo qual as leituras
fantasmas podem assombrar por aqui.
SERIALIZABLE
Esse é o nível que leva o isolamento ao máximo. Aqui, nenhum
desses problemas podem ocorrer.
Em resumo:
A tabela abaixo mostra os níveis de isolamento em relação aos
problemas que podem ou não provocar.
Nível de isolamento |
Dirty Read |
Nonrepeatable Read |
Phantom Read |
Read uncommitted |
Possível |
Possível |
Possível |
Read committed |
Impossível |
Possível |
Possível |
Repeatable read |
Impossível |
Impossível |
Possível |
Serializable |
Impossível |
Impossível |
Impossível |
Para modificar o nível de isolamento da transação corrente,
existe o comando SET TRANSACTION que deve ser executado logo após o
BEGIN e antes de qualquer consulta SELECT, INSERT, DELETE, UPDATE,
FETCH ou COPY. Este comando possui o seguinte formato:
SET TRANSACTION [ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ
| READ COMMITTED | READ UNCOMMITTED } ] [ {READ WRITE | READ
ONLY} ]
Com ele, é possível determinar o nível de isolamento,
escrevendo ISOLATION LEVEL seguido por algum dos níveis já
apresentados e/ou informar as opções READ WRITE (padrão) ou READ
ONLY. Transações READ ONLY (somente leitura) não podem alterar
nenhuma tabela que não seja temporária.
Vamos a um exemplo:
03 |
exercicio=# set transaction
read only; |
05 |
exercicio=# UPDATE aluno
SET nome = 'teste'; |
06 |
ERROR: cannot execute
UPDATE in
a read-only transaction |
07 |
exercicio=# select *
from aluno; |
08 |
ERROR: current transaction
is aborted,
commands ignored until end of
transaction block |
Após o BEGIN, determinamos que nossa transação seria somente
leitura. Logo após, tentamos infringir o determinado em SET
TRANSACTION executando um UPDATE qualquer. Confira o erro resultante.
Segui adiante para mostrar algo que ainda não havia falado, mas
ocorre sempre que uma instrução retorna erro dentro de uma
transação. Qualquer nova instrução retorna o erro informando que
a transação foi abortada. Cabe-nos apenas executar o ROLLBACK para
encerrar a transação problemática.
O nível de isolamento REPEATABLE READ no PostgreSQL é encarado
como um SERIALIZABLE, ou seja, não permite leituras fantasmas.
Vejamos:
PSQL1: Determinando o nível de isolamento para REPEATABLE READ,
que no PostgreSQL funciona como um SERIALIZABLE
03 |
exercicio=# SET transaction
ISOLATION LEVEL
REPEATABLE READ
; |
05 |
exercicio=# select *
from aluno; |
PSQL2: Inserindo um novo aluno e confirmando a transação
3 |
exercicio=# INSERT INTO
aluno (nome) values ('Maradona'); |
PSQL1: Olha aí que Maradona ainda não foi inserido e não é
porque ele é argentino. Após a confirmação deste transação, ele
vai aparecer. Só espero que não estrague a festa.
01 |
exercicio=# select *
from aluno; |
15 |
exercicio=# select *
from aluno; |
O nível de isolamento READ UNCOMMITED é encarado como um READ
COMMITED (o padão). Assim, leituras sujas jamais são permitidas, no
PostgreSQL.
Controle de versão
Ao contrário de alguns bancos de dados, o PostgreSQL por padrão
trabalha com controle de versão ao invés de bloqueio de dados na
manipulação de transações. Este mecanismo, conhecido como MVCC
(multi-version concurrency control) costuma ser bem mais eficiente em
termos de desempenho em comparação aos bloqueios de registros que
já são bem melhores do que os bloqueios de tabelas. Além do
PostgreSQL, bancos como Oracle, Firebird, HiperSonic, SyBase, ZODB,
entre outros também se baseiam no MVCC para controle de isolamento
de transações.
Para cada registro de cada tabela sempre existem dois campos
utilizados no controle de transações. O campo xmin informa o id
(auto-sequenciado) da transação que gerou este registro. Já xmax
determina o id da transação que a deletou ou alterou seu conteúdo,
gerando um novo registro com xmin atualizado. Para ilustrar como
funciona o controle, vejamos o exemplo:
PSQL1:
03 |
exercicio=# select *,
xmin, xmax from aluno; |
04 |
id | nome
| xmin | xmax |
05 |
---+---------------+------+------ |
06 |
1 |
Huguinho | 664 |
0 |
07 |
3 |
Luisinho | 664 |
0 |
08 |
4 |
Juquinha | 664 |
0 |
09 |
5 |
Joaozinho | 664 |
0 |
10 |
2 |
Jose da Silva | 668 | 0 |
11 |
6 |
Chiquinha | 669 |
0 |
12 |
7 |
Mariazinha | 669 | 0 |
13 |
8 |
Maradona | 670 |
0 |
16 |
exercicio=# DELETE FROM
aluno WHERE nome
= 'Maradona'; |
18 |
exercicio=# select *,
xmin, xmax from aluno; |
19 |
id
| nome |
xmin | xmax |
20 |
----+---------------+------+------ |
21 |
1 |
Huguinho | 664 |
0 |
22 |
3 |
Luisinho | 664 |
0 |
23 |
4 |
Juquinha | 664 |
0 |
24 |
5 |
Joaozinho | 664 |
0 |
25 |
2 |
Jose da Silva | 668 | 0 |
26 |
6 |
Chiquinha | 669 |
0 |
27 |
7 |
Mariazinha | 669 | 0 |
29 |
exercicio=# UPDATE aluno
SET nome = 'Zezinho' where
id = 2; |
31 |
exercicio=# select *,
xmin, xmax from aluno; |
32 |
id
| nome | xmin | xmax |
33 |
----+------------+------+------ |
34 |
1 |
Huguinho | 664 | 0 |
35 |
3 |
Luisinho | 664 | 0 |
36 |
4 |
Juquinha | 664 | 0 |
37 |
5 |
Joaozinho | 664 | 0 |
38 |
6 |
Chiquinha | 669 | 0 |
39 |
7 |
Mariazinha | 669 | 0 |
No primeiro SELECT, podemos visualizar o xmin de cada registro.
Perceba que Jose da Silva possui um xmin maior do que os outros
alunos que foram inseridos no mesmo momento. Isto se deve aos UPDATEs
que executamos nos exemplos posteriores ao primeiro INSERT.
Realizamos um DELETE para excluir Maradona de onde ele não foi
chamado. O SELECT posterior não tem como vê-lo uma vez que o xmax é
justamente o id da transação atual. Após um UPDATE para voltarmos
com o nome Zezinho do aluno 2, e eis que surgiu um registro no fim da
fila com id de transação maior do que todos. O registro com Jose da
Silva também não está mais visível aqui.
Agora vamos a uma outra transação antes de confirmação as
alterações na atual.
PSQL2: Selecionando os registros em outra transação antes do
COMMIT da anterior
01 |
exercicio=# SELECT *,
xmin, xmax FROM aluno; |
02 |
id
| nome |
xmin | xmax |
03 |
----+---------------+------+------ |
04 |
1 |
Huguinho | 664 |
0 |
05 |
3 |
Luisinho | 664 |
0 |
06 |
4 |
Juquinha | 664 |
0 |
07 |
5 |
Joaozinho | 664 |
0 |
08 |
2 |
Jose da Silva | 668 | 671 |
09 |
6 |
Chiquinha | 669 |
0 |
10 |
7 |
Mariazinha | 669 | 0 |
11 |
8 |
Maradona | 670 | 671 |
Nela, é possível visualizar os xmax de Jose da Silva e Maradona
com o valor da transação em PSQL1. Como a primeira ainda não
confirmou a transação, ainda não estamos vendo os novos valores
aqui (só veríamos se o nível de isolamento fosse READ UNCOMMITED,
que permite dirty reads e não é suportado pelo PostgreSQL).
Vamos confirmar e visualizar o resultado final:
PSQL1: Confirmação e visualização do resultado
03 |
exercicio=# select *,
xmin, xmax from aluno; |
04 |
id
| nome | xmin | xmax |
05 |
----+------------+------+------ |
06 |
1 |
Huguinho | 664 | 0 |
07 |
3 |
Luisinho | 664 | 0 |
08 |
4 |
Juquinha | 664 | 0 |
09 |
5 |
Joaozinho | 664 | 0 |
10 |
6 |
Chiquinha | 669 | 0 |
11 |
7 |
Mariazinha | 669 | 0 |
PSQL2: Visualização atualizada. Se estivesse no nível de
isolamento SERIALIZABLE ou REPEATABLE READ, não seria possível ver
os registros atualizados e deletados.
01 |
exercicio=# SELECT *,
xmin, xmax FROM aluno; |
02 |
id | nome
| xmin | xmax |
03 |
----+------------+------+------ |
04 |
1 | Huguinho | 664
| 0 |
05 |
3 | Luisinho | 664
| 0 |
06 |
4 | Juquinha | 664
| 0 |
07 |
5 | Joaozinho | 664 |
0 |
08 |
6 | Chiquinha | 669 |
0 |
09 |
7 | Mariazinha | 669 |
0 |
MVCC e a necessidade de Vacuum
O MVCC traz uma série de benefícios sobre os bloqueios:
Processos de leitura não
bloqueiam processos de escrita e vice-versa
Praticamente elimina a incidência
de contenções de transações
Reduz drasticamente o risco de
DEAD LOCK
- Possui um desempenho bem superior
Mas também é possível detectar um ponto fraco no MVCC. Os
registros alterados e excluídos não ficam mais acessíveis mas não
são imediatamente riscados do mapa. Ou seja, continuam inchando o
disco. Para evitar que o banco tome proporções além do aceitável
(este aceitável é bem relativo), execute regularmente (este
regularmente é mais relativo ainda) a instrução VACUUM, que além
de apagar os registros (VACUUM FULL) ou simplesmente apontando nas
páginas de dados como área disponível para ser sobrescrita (VACUUM
simples), também atualiza o ID das transações para evitar estouro
e consequente reinício automático dos IDs de transações, o que
poderia proporcionar resultados catrastróficos (como uma espécie de
bug do milênio).
Como os IDs das transações são inteiros de 32 bits, a cada 4
bilhões lá vai fumaça de transações, isto poderá ocorrer.
Voltaremos a este assunto em uma seção específica para o comando
VACUUM, nos policiando para não realizar mais de 4 bilhões de
exemplos daqui até lá.
Bloqueios e deadlocks
Provavelmente, você não irá precisar trabalhar com bloqueios
explícitos no PostgreSQL, mas existem situações onde podem ser
úteis. A prova é que o PostgreSQL implicitamente trabalha com
bloqueios em algumas instruções. Pra variar, vamos a um exemplo:
PSQL1: Atualizando o nome de Mariazinha
3 |
exercicio=# UPDATE aluno
SET nome = 'Maria Francisca'
where id = 7; |
PSQL2: Atualizando os nomes de Chiquinha e Mariazinha em outra
transação
3 |
exercicio=# UPDATE aluno
SET nome = 'Francisca' where
id = 6; |
6 |
exercicio=# UPDATE aluno
SET nome = 'Maria Joaquina'
where id = 7; |
Até o primeiro UPDATE, fora o dilúvio que se abateu sobre Recife
e o temporal do lado de fora da janela, estava tudo normal. No
segundo, a chuva até que diminuiu, mas a alteração do nome de
Mariazinha ficou congelada (bloqueada) até que o PSQL1 confirme a
sua alteração. Caso não ficasse, a transação que encerrasse
primeiro teria suas alterações perdidas devido a outra que
encerrasse depois, independentemente de quem realizou o UPDATE
primeiro. Isto resultaria em um outro problema de isolamento
conhecido por Lost Update, que pode ainda ser mais grave se uma
transação precisar dos dados da outra para atualizar os seus, como
eles não estarão disponíveis até o COMMIT, o estado final
potencialmente deverá ferir o princípio da consistência.
Agora vamos voltar à primeira transação não para confirmar a
alteração e liberar o UPDATE na segunda, mas para pôr mais lenha
na fogueira e também esperar pela segunda devido a alteração do
nome de Chiquinha.
PSQL1: Atualizando um registro já modificado pela outra transação
1 |
exercicio=# UPDATE aluno
SET nome = 'Chica' where
id = 6; |
3 |
ERROR: deadlock detected |
4 |
DETAIL: Process 9019 waits for
ShareLock on transaction
674; blocked by process
8919. |
5 |
Process 8919 waits for ShareLock
on transaction 676;
blocked by process 9019. |
6 |
Process 9019: UPDATE aluno
SET nome = 'Maria Joaquina'
where id = 7; |
7 |
Process 8919: UPDATE aluno
SET nome = 'Chica' where
id = 6; |
8 |
HINT: See server log for
query details. |
Booom, DEAD LOCK. Game over. Façamos um breve parênteses para a
mensagem de erro do PostgreSQL que não é uma simples mensagem, é
uma aula sobre o assunto. Como sei que você é uma pessoa bastante
atenta, posso apostar que percebeu o termo ShareLock da mensagem de
erro acima. Ela significa bloqueio compartilhado.
Antes de seguir adiante, façamos mais uma dinâmica a fim de
colher informações do PostgreSQL acerca dos bloqueios realizados
implícita ou explicitamente em determinado momento. A view de
catálogo pg_locks recupera os locks gerenciados no momento pelo pelo
servidor. Unindo ela com as tabelas de catálogo pg_class e
pg_database podemos ter uma informação bem interessante:
PSQL1: Iniciando uma inofensiva transação para mais uma vez
alterar o nome de Chiquinha e consultando o bloqueio gerado
03 |
exercicio=# UPDATE aluno
SET nome = 'Chica' where
id = 6; |
06 |
exercicio=# select l.locktype,
l.mode, l.pid, d.datname, c.relname from pg_locks
l inner join pg_database
d on l.database =
d.oid inner join pg_class
c on l.relation = c.oid where
c.relkind = 'r' and
relname not like
'pg_%'; |
07 |
locktype |
mode | pid |
datname | relname |
08 |
----------+------------------+------+-----------+--------- |
09 |
relation | RowExclusiveLock | 8919 |
exercicio | aluno |
O PostgreSQL reconhece oito tipos de bloqueios: ACCESS SHARE, ROW
SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW
EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE.
É possível solicitar explicitamente um bloqueio sobre tabelas ou
mesmo registros através do comando Lock:
03 |
Description: lock a table |
05 |
LOCK [ TABLE ]
[ ONLY ] name [,
...] [ IN lockmode MODE ] [ NOWAIT ] |
07 |
where lockmode
is one of: |
09 |
ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE
| SHARE UPDATE EXCLUSIVE |
10 |
| SHARE | SHARE ROW EXCLUSIVE |
EXCLUSIVE | ACCESS EXCLUSIVE |
Access Share
Bloqueio requerido apenas por instruções SELECT e ANALYZE são
simples leitura em geral permitidas, a menos que alguma outra
transação tenha requerido Access Exclusive.
Row Share
A instrução SELECT com a cláusula FOR UPDATE obtém este nível
de bloqueio. Ele conflita apenas com os níveis mais retritos
Exclusive e Access Exclusive.
Row Exclusive
Bloqueio solicitado automaticamente por instruções INSERT,
UPDATE, DELETE. Conflita com quem precisar além de Access Exclusive
e Exclusive, também com Share Row Exclusive e Share.
Share Update Exclusive
Bloqueio padrão do comando VACUUM sem a opção FULL. Conflita
com todos os próximos níveis de bloqueio e com ele próprio.
Bloqueia a tabela inteira.
Share
A partir deste nível de bloqueio, já vimos com quem cada um
conflita. Bloqueia toda a tabela e é requerido por instruções
CREATE INDEX.
Share Row Exclusive
Similar ao Exclusive, mas apenas no nível de linha. Não há
instrução SQL que automaticamente solicite este nível de bloqueio.
Exclusive
Este só não conflita com Access Share. É requerido
automaticamente apenas em tabelas de catálogo em determinadas
operações.
Access Exclusive
Conflita com todo mundo. Ou seja, nenhuma outra transação pode
operar na mesma tabela bloqueada por alguma transação que solicitou
Access Exclusive. Instruções ALTER TABLE, DROP TABLE, TRUNCATE,
REINDEX, CLUSTER e VACUUM FULL necessitam deste modo de bloqueio.
Conclusão
Percebeu como é denso e fundamental o assunto
transações?
Elas são provavelmente o mais frequente motivo para desenvolvedores
optarem por trabalhar com SGBDs em vez de persistirem dados
diretamente em arquivos.
Acredito que tanto DBAs quanto desenvolvedores precisam conhecer
bem como o SGBD escolhido lida com elas e optar pelo PostgreSQL é
optar pelo que há de mais avançado no mercado sobre o assunto.
Origem:
http://especializa.com.br/blog/2010/06/27/transacoes-no-postgresql/
Nenhum comentário:
Postar um comentário