Os locks garantem o isolamento de transações durante a execução de SQL. O Hologres utiliza tanto locks de Frontend (FE) quanto locks de Backend (BE), cada um com tipos, escopos e comportamentos de conflito distintos. Este tópico explica como esses locks funcionam e como solucionar problemas relacionados.
Contexto
Quando uma consulta é iniciada no Hologres, ela segue o caminho mostrado na figura a seguir.
O Frontend analisa a consulta, o Query Engine gera um plano de execução e o Storage Engine lê os dados. Dois tipos de locks estão envolvidos nesse processo.
-
Locks de Frontend (FE)
O Frontend é a camada de acesso e é compatível com o protocolo PostgreSQL; portanto, seus locks também são compatíveis com alguns locks do PostgreSQL. Os locks de FE gerenciam os metadados do FE.
-
Locks de Backend (BE)
O BE refere-se ao Query Engine e ao Fixed Plan. Ele utiliza locks próprios do Hologres, que gerenciam o schema e os dados do Storage Engine.
Alterações no comportamento dos locks
-
A partir do Hologres V2.0, um mecanismo sem lock é ativado por padrão no Frontend (FE). Isso significa que, se surgir um conflito entre uma instrução DDL e uma instrução DQL na mesma tabela (por exemplo, uma nova consulta enviada enquanto uma operação DDL na Tabela A está em andamento), a nova solicitação DQL falhará imediatamente. Para permitir que novas solicitações DQL aguardem a liberação do lock em vez de falhar imediatamente, desative o parâmetro GUC:
ALTER database <db_name> SET hg_experimental_disable_pg_locks = off; Desde o Hologres V2.1, o carregamento em massa de dados em uma tabela sem chave primária foi otimizado para adquirir apenas um lock de nível de linha em vez de um lock de nível de tabela.
Tipos de locks
-
Locks de FE
A camada de acesso do Hologres, o Frontend, é compatível com o PostgreSQL; portanto, os locks nessa camada também são compatíveis com o PostgreSQL. O PostgreSQL fornece três modos de lock para controlar o acesso simultâneo aos dados: locks de nível de tabela, locks de nível de linha e advisory locks. O Hologres é compatível com locks de nível de tabela e advisory locks.
NotaO Hologres não oferece suporte a bloqueio explícito ou UDFs relacionadas a advisory locks.
-
Locks de nível de tabela
-
Modos
Um lock de nível de tabela é um bloqueio em toda a tabela. Os modos são os seguintes.
Modo de lock
Descrição
Observação
ACCESS SHARE
O comando
SELECTadquire um lock deste modo nas tabelas referenciadas.N/A
ROW SHARE
Apenas os comandos
SELECT FOR UPDATEeSELECT FOR SHAREadquirem este lock na tabela de destino. Tabelas que não são de destino (como outras tabelas em um JOIN) adquirem apenas um lock ACCESS SHARE.O Hologres não oferece suporte aos comandos
SELECT FOR UPDATEeSELECT FOR SHARE; portanto, este modo de lock não se aplica.ROW EXCLUSIVE
Comandos DML que modificam dados, como
UPDATE,DELETEeINSERT, adquirem este lock.Considere isso em conjunto com os locks de BE.
SHARE UPDATE EXCLUSIVE
Um lock que evita conflitos entre
VACUUMe alterações simultâneas de schema. Os seguintes comandos adquirem este lock:-
lazy VACUUM(não incluiVacuum Full). -
ANALYZE. -
CREATE INDEX CONCURRENTLY.NotaAo executar este comando, o Hologres não adquire um lock
SHARE UPDATE EXCLUSIVE. Em vez disso, adquire um lockSHARE, semelhante aoCREATE INDEXnão simultâneo. -
CREATE STATISTICS(sem suporte no Hologres). -
COMMENT ON. -
ALTER TABLE VALIDATE CONSTRAINT(sem suporte no Hologres). -
ALTER TABLE SET/RESET (storage_parameter). O Hologres suporta apenas o uso deste comando para definir suas próprias propriedades estendidas e a propriedade nativa do PostgreSQLautovacuum_enabled. Definir essas propriedades não adquire nenhum lock de tabela. Modificar certos outros parâmetros de armazenamento integrados do PostgreSQL adquire este lock. Para obter mais informações, consulte ALTER TABLE. -
ALTER TABLE ALTER COLUMN SET/RESET options. -
ALTER TABLE SET STATISTICS(sem suporte no Hologres). -
ALTER TABLE CLUSTER ON(sem suporte no Hologres). -
ALTER TABLE SET WITHOUT CLUSTER(sem suporte no Hologres).
Atenção especial ao comando
ANALYZE.SHARE
Apenas um
CREATE INDEXnão simultâneo adquire este lock.NotaNo Hologres, a criação de um índice JSON adquire este lock.
Atenção especial ao comando
CREATE INDEX(para criação de índices relacionados a JSON).SHARE ROW EXCLUSIVE
Um lock que impede modificações simultâneas de dados. Os seguintes comandos adquirem este lock:
-
CREATE COLLATION(sem suporte no Hologres). -
CREATE TRIGGER(sem suporte no Hologres). -
Alguns comandos
ALTER TABLE:-
DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER(sem suporte no Hologres). -
ADD table_constraint(sem suporte no Hologres).
-
O Hologres não oferece suporte a comandos que adquirem este lock; portanto, este modo de lock não se aplica.
EXCLUSIVE
Apenas o comando
REFRESH MATERIALIZED VIEW CONCURRENTLYadquire este lock.O Hologres não oferece suporte ao comando
REFRESH MATERIALIZED VIEW CONCURRENTLY; portanto, este modo de lock não se aplica.ACCESS EXCLUSIVE
Um lock necessário para acesso totalmente exclusivo, que entra em conflito com todos os outros locks. Os seguintes comandos adquirem este lock:
-
DROP TABLE -
TRUNCATE TABLE -
REINDEX(sem suporte no Hologres). -
CLUSTER(sem suporte no Hologres). -
VACUUM FULL -
REFRESH MATERIALIZED VIEW (without CONCURRENTLY)(sem suporte no Hologres). -
LOCK: o comando LOCK explícito. Se nenhum tipo específico de lock for especificado, este lock será adquirido por padrão. (Sem suporte no Hologres). -
ALTER TABLE: Além das formas deALTER TABLEque adquirem locks específicos conforme mencionado acima, outras formas deALTER TABLEadquirem este lock por padrão.
Este é um lock crítico. Operações DDL no Hologres adquirem este lock, que entra em conflito com todos os outros tipos de lock.
-
-
Timeout
Os locks de FE não possuem um timeout padrão. Defina explicitamente um timeout para evitar tempos de espera de lock excessivamente longos. Consulte Gerenciar consultas.
-
Relações de conflito
A tabela a seguir mostra as relações de conflito entre os modos de lock. Um conflito significa que uma operação deve esperar que outra libere seu lock.
Notaindica ausência de conflito. indica um conflito.
Modo de lock solicitado
ACCESS SHARE
ROW SHARE
ROW EXCLUSIVE
SHARE UPDATE EXCLUSIVE
SHARE
SHARE ROW EXCLUSIVE
EXCLUSIVE
ACCESS EXCLUSIVE
ACCESS SHARE
ROW SHARE
ROW EXCLUSIVE
SHARE UPDATE EXCLUSIVE
SHARE
SHARE ROW EXCLUSIVE
EXCLUSIVE
ACCESS EXCLUSIVE
-
-
Advisory lock
Os advisory locks têm significados definidos pela aplicação. Na maioria dos cenários de negócios do Hologres, não é necessário prestar atenção extra a eles.
-
-
Locks de BE
-
Categorias
No Hologres, os locks de BE são categorizados da seguinte forma:
Categoria de lock
Introdução aos locks
Exclusive(X)
Um lock exclusivo (ou mutex lock) é adquirido quando uma transação precisa modificar dados, como com comandos DML como
DELETE,INSERTouUPDATE. Um lock exclusivo só pode ser concedido se não existirem outros locks compartilhados ou exclusivos no recurso. Uma vez adquirido, nenhum outro lock pode ser colocado nesse recurso.Shared(S)
Um lock compartilhado é solicitado quando uma transação precisa ler dados, impedindo que outras transações modifiquem os dados lidos. Vários locks compartilhados podem coexistir no mesmo recurso, permitindo que comandos DQL sejam executados simultaneamente porque não alteram o recurso.
Intent(I)
Um intent lock indica uma estrutura de bloqueio hierárquica, permitindo que vários intent locks coexistam no mesmo recurso. Após a aquisição de um intent lock, um lock exclusivo não pode mais ser concedido para esse recurso. Por exemplo, quando uma transação solicita um lock exclusivo em uma linha, ela também adquire um intent lock na tabela, impedindo que outras transações obtenham um lock exclusivo na tabela.
-
Timeout
O timeout padrão para locks de BE é de 5 minutos.
-
Relação de conflito
As relações de conflito para locks de BE são as seguintes. Um conflito significa que uma operação deve esperar que outra libere seu lock.
Notaindica ausência de conflito. indica um conflito.
Operação
DROP
ALTER
SELECT
UPDATE
DELETE
INSERT (incluindo INSERT ON CONFLICT)
DROP
ALTER
SELECT
UPDATE
DELETE
INSERT (incluindo INSERT ON CONFLICT)
-
Escopos dos locks
O escopo de um lock varia de acordo com a categoria.
-
Locks de FE
Os locks de FE afetam apenas objetos de tabela, não os dados da tabela. Um lock é adquirido ou bloqueado. Estar bloqueado indica um conflito de lock, o que significa que a operação está aguardando o lock.
-
Locks de BE
Os locks de BE afetam os dados da tabela ou o schema. Seus escopos são os seguintes:
Locks de dados de tabela: Adquirem um lock em todos os dados de uma tabela. A aquisição simultânea por várias tarefas pode causar atrasos na espera pelo lock.
Lock de dados de linha: Bloqueia uma linha inteira, resultando em maior eficiência de execução. Consultas aceleradas pelo Fixed Plan utilizam locks de dados de linha ou locks de schema de tabela.
-
Lock de schema de tabela: Bloqueia o schema da tabela. É adquirido por transações que precisam ler ou modificar o schema da tabela. A maioria das transações adquire um lock de schema. Tipos atuais de lock de schema:
SchX: Schema Exclusive Lock, usado para instruções DDL. Atualmente, suporta apenas o comando
DROP TABLE.SchU: Schema Update Lock, usado para instruções DDL que modificam a estrutura da tabela, incluindo os comandos
ALTER TABLEeset_table_property.SchE: Schema Existence Lock, usado para instruções DML e DQL para impedir que uma tabela seja excluída durante operações de leitura e gravação.
NotaO SchU fornece controle granular para locks DDL, permitindo que o DQL seja executado normalmente durante operações
ALTER TABLEsem espera. O SchX é o lock exclusivo DDL de granulação mais grossa, para o qual todas as operações DDL, DML e DQL devem esperar.Se uma consulta demorar muito para iniciar, ela pode estar aguardando locks de BE.
Os escopos de lock para comandos comuns no Hologres são os seguintes. indica que a operação adquire o lock especificado.
Gravações, atualizações e exclusões que não usam um Fixed Plan são consideradas operações Bulkload.
O comando
CREATE INDEXrefere-se à criação de índices relacionados a JSON.Os comandos DDL incluem
CREATE,DROP,ALTER, entre outros.
|
Operação / Escopo do lock |
Lock de nível de tabela |
Lock de dados de tabela |
Lock de dados de linha |
Lock de schema de tabela |
|
CREATE |
|
N/A |
||
|
DROP |
Nota
Depois que o comando |
N/A |
N/A |
Nota
Entra em conflito com todas as outras operações. |
|
ALTER |
Nota
Igual ao comando |
N/A |
N/A |
Nota
É possível executar comandos |
|
SELECT |
Nota
Enquanto o lock de tabela estiver mantido, comandos |
N/A |
N/A |
Nota
Durante a execução de um comando |
|
INSERT (incluindo INSERT ON CONFLICT) |
Nota
O comando |
Nota
O Bulkload entra em conflito com o Fixed Plan. |
Se a operação for realizada via Fixed Plan, um lock de dados de linha será adquirido. A partir do Hologres V2.1, o bulkload para uma tabela sem chave primária também adquire apenas um lock de dados de linha. Nota
|
Nota
Entra em conflito com DDL e DML. |
|
UPDATE |
Nota
Este lock entra em conflito com locks |
Nota
Entra em conflito com DDL e DML. |
||
|
DELETE |
Nota
Este lock entra em conflito com locks |
Nota
Entra em conflito com DDL e DML. |
||
Comportamento de locks relacionados a transações
Atualmente, o Hologres suporta transações explícitas apenas para DDL. Não há suporte para transações puramente DML ou transações mistas de DDL e DML.
O Hologres não suporta subtransações aninhadas.
-
Embora transações puramente DML sejam aceitas sintaticamente, elas não suportam efetivamente commits e rollbacks atômicos.
No exemplo DML a seguir, mesmo que o
INSERTseja bem-sucedido, os dados inseridos não sofrerão rollback se oUPDATEsubsequente falhar.begin; insert into t1(id, salary) values (1, 0); update t1 set salary = 0.1 where id = 1; commit; -
Transações puramente DDL funcionam conforme o esperado.
Se qualquer comando DDL na transação falhar, toda a transação sofrerá rollback. Por exemplo, no código a seguir, se o comando
ALTERfalhar, as operações anterioresCREATEeDROPserão revertidas.begin; create table t1(i int); drop table if exists t2; alter table t1 add column n text; commit; -
Transações com uma mistura de comandos DDL e DML são proibidas.
No exemplo a seguir, quando uma transação contém comandos DDL e DML, o comando DML reporta um erro.
begin; create table t1(i int); update t1 set i = 1 where i = 1; -- DML statement error ERROR: UPDATE in ddl transaction is not supported now. -
Os locks adquiridos por qualquer comando em uma transação explícita são liberados apenas quando toda a transação termina (por commit ou rollback).
No exemplo a seguir, uma operação
ALTERem uma tabela pai adquire locksACCESS EXCLUSIVEtanto na tabela pai (login_history) quanto na tabela filha (login_history_202001). Esses locks não são liberados imediatamente após o término do comando. Eles são mantidos até que oCOMMITfinal seja executado (independentemente de sucesso ou falha). Se oCOMMITnão for executado, os locks serão mantidos indefinidamente, e qualquer outra operação DDL nesta tabela será bloqueada e reportará um erro.-- suppose we have three tables create table temp1(i int, t text); create table login_history(ds text, user_id bigint, ts timestamptz) partition by list (ds); create table login_history_202001 partition of login_history for values in ('202001'); begin; alter table login_history_s1 add column user_id bigint; drop table temp1; create table tx2(i int); commit;
Solucionar problemas de locks de FE
Siga estas etapas para verificar e resolver locks de FE:
-
Se uma consulta demorar muito, verifique o campo
wait_event_typepara ver se a consulta está aguardando um lock.No exemplo de comando a seguir, se o campo
wait_event_typeno resultado forLock, isso indica que a consulta está aguardando um lock de FE.-- Hologres V2.0 and later: select query,state,query_id,transaction_id,pid,wait_event_type,wait_event,running_info,extend_info FROM hg_stat_activity where query_id = 200640xxxx; -- The following result is returned: ----------------+---------------------------------------------------------------- query | drop table test_order_table1; state | active query_id | 200640xxxx pid | 123xx transaction_id | 200640xxxx wait_event_type | Lock wait_event | relation running_info | {"current_stage":{"stage_duration_ms":47383,"stage_name":"PARSING"},"fe_id":1,"warehouse_id":0}+ | extend_info | {} + -- Hologres V1.3 and earlier: select datname, pid, application_name, wait_event_type, state,query_start, query from pg_stat_activity where backend_type in ('client backend'); -- The following result is returned: ----------------+---------------------------------------------------------------- datname | holo_poc pid | 321xxx application_name | PostgreSQL JDBC Driver wait_event_type | lock state | active query_start |2023-04-20 14:31:46.989+08 query | delete from xxx -
Visualize o detentor do lock.
Sintaxe:
select * from pg_locks where pid = <pid>;Substitua pid pelo valor
pidretornado na Etapa 1. -
Identifique qual processo está mantendo o lock.
Se o resultado da Etapa 2 mostrar que a consulta atual está aguardando um lock, use o seguinte comando com o
oid(Object ID) da tabela para encontrar o processo que mantém um lock nela. Um valort(verdadeiro) na colunagrantedindica que um processo está mantendo o lock.-- Query the process that holds the table lock. select pid from pg_locks where relation = <OID> and granted = 't'; -
Identifique a consulta que mantém o lock.
Use o PID do resultado da Etapa 3 e execute o seguinte comando para encontrar a consulta que mantém o lock.
select * from pg_stat_activity where pid = <PID>; -
Libere o lock.
Execute o seguinte comando para encerrar a consulta e liberar o lock.
select pg_cancel_backend(<pid>);
Solucionar problemas de locks de BE
Se o campo be_lock_waiters na view hg_stat_activity contiver dados, a consulta mantém um lock ou está bloqueada por um lock de BE. Solucione o problema seguindo estas etapas.
A hg_stat_activity é suportada apenas no Hologres V2.0 e posteriores.
-
Cenário 1: A consulta atual mantém um lock e outras consultas estão aguardando sua liberação.
Visualize a consulta que mantém o lock e as consultas bloqueadas:
select query_id, transaction_id, ((extend_info::json)->'be_lock_waiters'->>0)::text as be_lock_waiters FROM hg_stat_activity as h where h.state = 'active' and ((extend_info::json)->'be_lock_waiters')::text != ''; -- The following result is returned: ----------------+------------------ query_id | 10005xxx transaction_id | 10005xxx be_lock_waiters | 13235xxx -
Cenário 2: Verifique qual consulta está bloqueando a consulta atual.
Descubra qual consulta está mantendo o lock que a consulta atual aguarda.
select query_id, transaction_id, ((extend_info::json)->'be_lock_waiters')::text as be_lock_waiters FROM hg_stat_activity as h where h.state = 'active' and ((extend_info::jsonb)->'be_lock_waiters')::jsonb ? '10005xxx'; -[ RECORD 1 ]---+------------------------------------------ query_id | 200740017664xxxx transaction_id | 200740017664xxxx be_lock_waiters | ["200640051468xxxx","200540035746xxxx"]
Erros comuns e soluções
-
Erro:
internal error: Cannot acquire lock in time, current owners: [(Transaction =302xxxx, Lock Mode = SchS|SchE|X)].Possível causa: Sua consulta atingiu o timeout após 5 minutos de espera por um lock de BE. Outra consulta está mantendo um lock conflitante (neste caso, uma combinação de um SchS, um SchE e um lock Exclusivo) na mesma tabela.
Solução: Encontre a consulta bloqueadora e encerre-a. O ID da transação da mensagem de erro,
Transaction =302xxxx, é o ID da consulta bloqueadora. Procure este ID de consulta no log de consultas lentas ou na view de consultas ativas para identificar e gerenciar o processo.
-
Erro:
ERROR: The schema version update timed out because the server's current version (xxx) is older than the requested version (yyy).Possível causa: Uma instrução DDL primeiro atualiza a versão do schema no FE. Se uma nova consulta chegar antes que o Storage Engine tenha aplicado a alteração, a consulta deverá esperar. A consulta falhará se essa espera exceder o timeout de 5 minutos.
-
Solução:
Encerre a operação DDL que mantém o lock e execute novamente sua consulta.
Se o problema persistir, reinicie a instância para forçar a sincronização.
-
Erro:
The requested table name: xxx (id: 10, version: 26) mismatches the version of the table (id: 10, version: 28) from server.Possível causa: Após uma operação DDL, o Storage Engine atualiza sua versão, mas essa alteração precisa ser replicada em todos os nós de FE. Este erro ocorre quando sua consulta se conecta a um nó de FE que ainda não sincronizou com a versão mais recente da tabela do Storage Engine.
-
Solução:
Tente executar a consulta novamente várias vezes. O problema geralmente é resolvido ao conectar-se a um nó de FE atualizado.
Se tentar novamente não funcionar após alguns minutos, reinicie a instância para limpar o estado inconsistente.
-
Erro:
internal error: Cannot find index full ID: 86xxx (table id: 20yy, index id: 1) in storages or it is deleting!.Possível causa: Este erro ocorre quando uma operação DML (como
SELECTouDELETE) tenta acessar uma tabela enquanto um comandoDROP TABLEouTRUNCATE TABLEestá sendo executado nela. A consulta DML aguarda a liberação do lock DDL, mas, no momento em que é executada, a tabela ou seu índice já foi excluído.Solução: Certifique-se de que nenhuma outra consulta esteja sendo executada em uma tabela antes de executar comandos
DROPouTRUNCATEnela. Coordene as operações DDL e DML para evitar acesso simultâneo.