BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br - Aula 13 TRANSAÇÕES EM BANCO DE DADOS 1. TRANSAÇÃO O termo transação refere-se a uma coleção de operações que formam uma única unidade de trabalho lógica. Por exemplo, a transferência de dinheiro de uma conta para outra é uma transação consistindo de duas atualizações, uma para cada conta. Uma transação é uma unidade de execução do programa que acessa e possivelmente atualiza vários itens de dados. Para garantir a integridade dos dados, é necessário que o SGBD mantenha as seguintes propriedades das transações: atomicidade, consistência, isolamento e durabilidade. - Atomicidade: uma transação é uma unidade atômica de processamento; ou ela será executada em sua totalidade ou não será de modo nenhum. - Consistência: uma transação deve ser preservadora de consistência se sua execução completa fizer o banco de dados passar de um estado consistente para outro também consistente. - Isolamento: uma transação deve ser executada como se estivesse isolada das demais. Isto é, a execução de uma transação não deve sofrer interferência de quaisquer outras transações concorrentes. - Durabilidade: as mudanças aplicadas ao banco de dados por uma transação efetivada devem persistir no banco de dados. Essas mudanças não devem ser perdidas em razão de uma falha. Essas propriedades normalmente são conhecidas como propriedades ACID. Esse acrônimo é derivado da primeira letra de cada uma das quatro propriedades. Quando se trabalham com transações, é necessário que se faça pelo menos duas ressalvas. A primeira é que em certas situações é interessante se agregar vários comandos como sendo integrantes de uma mesma transação, como, por exemplo, em uma transferência bancária que envolve a retirada de dinheiro de uma conta e o acréscimo em outra como se fosse apenas uma única operação lógica. A segunda ressalva é que em outras situações se faz necessário sacrificar ou flexibilizar as características ACID em virtude da necessidade de maior desempenho. 1.1. Estados de uma Transação Na ausência de falhas, todas as transações são completadas com sucesso. Porém, uma transação nem sempre pode completar sua execução com sucesso. Caso isso ocorra, essa transação é considerada abortada. Se tivermos que garantir a propriedade de atomicidade, uma transação abortada não pode ter efeito sobre o estado do banco de dados. Assim, qualquer mudança que a transação abortada tenha feito no banco de dados deve ser desfeita. Quando as mudanças causadas por uma transação abortada tiverem sido desfeitas, dizemos que a transação foi revertida (rolled back). Se 1 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br uma transação foi executada com sucesso, diz-se que foi confirmada (committed). Assim, é possível estabelecer vários estados para as transações: - Active: corresponde ao estado inicial. A transação fica neste estado enquanto está sendo executada. - Partially Committed: Após a última instrução ter sido executada. - Failed: Depois de se descobrir que a execução normal não pode continuar. - Aborted: quando a transação foi revertida e a base de dados voltou ao estado anterior a transação - Committed: como dito antes, após uma conclusão da transação com êxito. Figura 1 - Estados das transações 1.2. Controle de concorrência Após uma abordagem inicial sobre transações, é fácil verificar que estas fazem sentido num âmbito concorrente. O controle de concorrência é uma das coisas mais importantes em bases de dados grandes. Tomemos novamente como exemplo o caso de um banco, em que várias pessoas podem acessar aos mesmos dados simultaneamente, fazendo várias operações iguais ou diferentes. Caso não haja um controlo de concorrência forte, poderiam existir problemas sérios. Para lidar com este problema, foram desenvolvidas técnicas específicas para lidar com esta concorrência. 2 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br - Protocolos Baseados em Locks: Estes protocolos baseiam-se, tal como o nome indica, na utilização de locks. Através destas propriedades, podemos garantir que o acesso a determinados dados são feitos de acordo com estes locks, mas basicamente a idéia é que se uma transação está acessando determinados dados, mais nenhuma outra transação pode acessar estes (modo exclusivo). Através de propriedades de locks e unlocks conseguimos estabelecer estas regras. Note que estes locks podem ser do tipo exclusivo, em que se uma transação obtém um exclusive-mode lock mais nenhuma transação poderá ler ou escrever sobre esses dados, ou também podem ser do tipo shared, em que se uma transação obtém o shared-mode lock qualquer outra transação poderá ler os mesmos dados, mas nunca escrever neles. Um protocolo deste gênero muito conhecido é o Two-Phase Locking Protocol, que consiste em duas fases distintas, Growing phase, onde são obtidos os locks sem nunca os libertar, e a shrinking phase, onde uma transação liberta os locks, não os podendo obter mais. - Protocolos Baseados em Time-Stamps: Para cada transação no sistema, é atribuído um time-stamp, antes de esta começar a execução. Há então duas formas de funcionamento: a atribuição do time-stamp pode ser com a utilização do clock do sistema ou utilizando um contador lógico, que é incrementado sempre que uma nova transação entra no sistema. Através destes time-stamps conseguem-se fazer um controle de concorrência. - Protocolos Multi-Versões: De forma a maximizar ainda mais a concorrência, este tipo de protocolo cria várias cópias do mesmo item. Assim, cada write (Q) cria uma nova versão de Q e quando Q é chamado para leitura, o sistema elege a versão do Q mais apropriada, garantida a serialização. É neste âmbito que surgem então os protocolos Multiversion Timestamp Ordering e o conhecido Two-Phase Locking. Existem muitos outros protocolos, no entanto esses se destacam como mais utilizados. 1.3. Gestão de Concorrência no PostgreSQL O PostGreSQL implementa uma política de auto-commit. Desta forma, cada instrução é tratada como uma transação. Um read é uma transação, um write é outra, etc… Há então duas formas de contornar esta situação: uma é simplesmente desligar o auto-commit, através da instrução \SET AUTOCOMMIT OFF. Assim, é tudo considerado uma transação até que a instrução commit seja executada. A outra solução passa por indicar explicitamente o início e o fim de uma transação usando o comando BEGIN no início da transação e o comando COMMIT; no final desta. Caso se pretenda anular a transação, ao invés do comando COMMIT é possível introduzir o comando ROLLBACK. 1.3.1. GARANTIA DE ISOLAMENTO O PostgreSQL, ao invés de muitos outros SGBD’s, mantém os dados coerentes através da utilização de modelos multi-versão (Multiversion Concurrency Control, MVCC). Assim, cada transação vê uma versão da base de dados (snapshot) tal como era em algum tempo atrás, independentemente do estado atual dos dados, evitando assim o problema de uma transação 3 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br poder ver os dados incoerentes. Protocolos em nível de locks de tabela e tupla também são possíveis no PostgreSQL para aplicações que não se adaptem bem ao funcionamento do modelo MVCC, sendo que é necessário estabelecer manualmente o nível de granularidade desejado. Contudo, um uso cuidado no MVCC é melhor que a utilização de locks. 1.3.2. ISOLAMENTO NAS TRANSAÇÕES O SQL standard define quatro níveis de isolamento de transações de acordo com três fenômenos que não devem acontecer entre transações concorrentes, sendo os fenômenos os seguintes: dirty reading, nonrepeatable read e phantom read. - Dirty reading: uma transação lê dados que foram modificados por uma outra transação concorrente que ainda não realizou o comando commit; - Nonrepeatable read: uma transação re-lê dados e descobre que os estes foram modificados por uma outra transação; - Phantom read: uma transação executa novamente uma pergunta e descobre que os valores que satisfazem a pergunta são diferentes da anterior, devido a um commit de uma outra transação. Os quatro níveis de isolamento das transações SQL são as seguintes: Tabela 1- Nível de isolamento das transações SQL Isolation level Read uncommitted Read committed Repeatable read Serializable Nonrepeatable Read Possible Possible Not Possible Not Possible Dirty Read Possible Not Possible Not Possible Not Possible Phantom Read Possible Possible Possible Not Possible O PostgreSQL suporta o Read Commited e o Serializable. Como o padrão SQL tem mais dois elementos, o Read Uncommitted é tratado como um Repetable Read que é tratado como Serializable. O Read Commited é o grau de isolamento padrão do sistema em questão. 1.3.3. NÍVEIS DE GRANULARIDADE (Locks) O PostgreSQL permite a utilização de locks com vários níveis de granularidade (explicit locking), em nível de tabela, em nível de tuplas e, em versões mais recentes, os chamados advisory locks. Para criar locks em nível de tabela, a instrução utilizada é a seguinte: LOCK [ TABLE ] [ ONLY ] name [, ...] [ IN lockmode MODE ] [ NOWAIT ] Sendo que lock mode é um dos seguintes: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE 4 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br 1.3.4. CONSISTÊNCIA DE DADOS NO POSTGRESQL Como no sistema PostgreSQL um leitor nunca realiza locks sobre os dados, dados lido numa transação podem ser escritos por cima por uma outra corrente transação, ou seja, os dados lidos podem não ser sempre os mais atuais. Para resolver este problema surge um pouco à necessidade de se utilizarem os locks, SELECT FOR UPDATE, SELECT FOR SHARE, ou fazendo locks sobre toda a tabela, como forma de impossibilitar a modificação dos dados em questão. Porém, a utilização excessiva de locks pode limitar, e muito, a concorrência de uma base de dados, que, é bastante proveitosa em inúmeras situações. 1.3.5. ATOMICIDADE E DURABILIDADE NO POSTGRESQL De acordo com as propriedades anteriores, é justo dizer que as transações funcionam como um todo. Ou a transação é executada na sua totalidade, ou nada dela é executada. Caso haja falha, a base de dados é restaurada, através de mecanismos de rollback, que serão abordados no seguinte tópico. O mesmo acontece com a durabilidade. Caso a transação se concretize os dados são guardados em memória estável e o utilizador é notificado do sucesso da operação. Em caso de falha, os dados não são guardados e o utilizador é notificado da falha que ocorreu. 1.3.6. MECANISMOS DE ROLLBACK E RECUPERAÇÃO NO POSTGRESQL Por vezes, em transações complexas e grandes, há necessidade de estabelecer save points. Estas são pontos de restauração do sistema, considerado um ponto especial na transação que permite a execução de todos os comandos após ter existido um rollback, restaurando o estado da transação para o estado atual no save point. Para criar um save point, basta introduzir o seguinte comando: SAVEPOINT savepoint_name Desta forma é então possível estabelecer um save point. Para se retornar ao estado atual num save point, é necessário invocar comandos de rollback, que podem seguir as seguintes definições: ROLLBACK [ WORK | TRANSACTION ] ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name A primeira opção restaura o sistema todo, ou seja, volta para o início da transação, anulando todas as operações efetuadas. A segunda efetua um rollback para um determinado save point. A seguir um exemplo concreto bem como os resultados de operações com save points e rollbacks: create table test (id integer, primary key (id)); commit; 5 BANCO DE DADOS II Prof. Ricardo Rodrigues Barcelar http://www.ricardobarcelar.com.br insert into test values (1); commit; insert into test values (2); savepoint y; delete from test; select * from test; //nada é mostrado rollback to y; select * from test; //são mostrados os valores 1 e 2 rollback; select * from test; // é mostrado o valor 1 6