Estatísticas de bloqueio

O Spanner fornece estatísticas de bloqueio que permitem identificar as colunas de chave de linha e tabela que foram as principais fontes de conflitos de bloqueio de transação no seu banco de dados durante um determinado período. É possível recuperar essas estatísticas das tabelas do sistema SPANNER_SYS.LOCK_STATS* usando instruções SQL.

Disponibilidade

Os dados do SPANNER_SYS estão disponíveis somente por meio de interfaces SQL. Por exemplo:

Outros métodos de leitura única fornecidos pelo Spanner não são compatíveis com SPANNER_SYS.

Bloquear estatísticas por chave de linha

As tabelas a seguir rastreiam a chave de linha com o maior tempo de espera:

  • SPANNER_SYS.LOCK_STATS_TOP_MINUTE: chaves de linha com os maiores tempos de espera de bloqueio durante intervalos de 1 minuto.

  • SPANNER_SYS.LOCK_STATS_TOP_10MINUTE: chaves de linha com os maiores tempos de espera de bloqueio durante intervalos de 10 minutos.

  • SPANNER_SYS.LOCK_STATS_TOP_HOUR: chaves de linha com os maiores tempos de espera de bloqueio durante intervalos de 1 hora

Essas tabelas têm as seguintes propriedades:

  • Cada tabela contém dados para intervalos de tempo não sobrepostos do comprimento que o nome da tabela especifica.

  • Os intervalos são baseados em horas. Os intervalos de 1 minuto terminam no minuto, os intervalos de 10 minutos terminam a cada 10 minutos, começando na hora, e os intervalos de 1 hora terminam na hora. Após cada intervalo, o Spanner coleta dados de todos os servidores e os disponibiliza nas tabelas SPANNER_SYS logo depois.

    Por exemplo, às 11:59:30, os intervalos mais recentes disponíveis para as consultas SQL são:

    • 1 minuto: 11:58:00–11:58:59
    • 10 minutos: 11:40:00–11:49:59
    • 1 hora: 10:00:00–10:59:59
  • O Spanner agrupa as estatísticas iniciando o intervalo de chave de linha.

  • Cada linha contém estatísticas para o tempo de espera total de bloqueio de um determinado intervalo de chave de linha inicial para o qual o Spanner captura estatísticas durante o intervalo especificado.

  • Se o Spanner não conseguir armazenar informações sobre cada intervalo de chave de linha para esperas de bloqueio durante o intervalo, o sistema priorizará o intervalo de chave de linha com o maior tempo de espera de bloqueio durante o intervalo especificado.

Esquema de tabela

Nome da coluna Tipo Descrição
INTERVAL_END TIMESTAMP Fim do intervalo de tempo em que os conflitos de bloqueio incluídos ocorreram.
ROW_RANGE_START_KEY BYTES(MAX) Chave de linha em que ocorreu o conflito de bloqueio. Quando o conflito envolve um intervalo de linhas, esse valor representa a chave inicial desse intervalo. Um sinal de adição, +, significa um intervalo. Para mais informações, consulte O que é uma chave de início de intervalo de linhas.
LOCK_WAIT_SECONDS FLOAT64 O tempo de espera de bloqueio cumulativo de conflitos de bloqueio registrado para todas as colunas no intervalo de chaves de linha, em segundos.
SAMPLE_LOCK_REQUESTS ARRAY<STRUCT<
  column STRING,
  lock_mode STRING,
   transaction_tag STRING>>
Cada entrada nesta matriz corresponde a uma amostra de solicitação de bloqueio que contribuiu para o conflito, aguardando um bloqueio ou impedindo que outras transações tomem o bloqueio, na chave de linha determinada (intervalo). O número máximo de amostras nesta matriz é 20.
Cada amostra contém os três campos a seguir:
  • lock_mode: o modo de bloqueio solicitado. Para mais informações, consulte Modos de bloqueio .
  • column: a coluna que encontrou o conflito de bloqueio. O formato desse valor é tablename.columnname.
  • transaction_tag: a tag da transação que emitiu a solicitação. Para mais informações sobre o uso de tags, consulte Solução de problemas com tags de transação.
Todas as solicitações de bloqueio que contribuíram para os conflitos de bloqueio são amostradas de maneira uniforme e aleatória. Portanto, é possível que apenas metade de um conflito (o detentor ou o encarregado) seja registrada nessa matriz.

Modos de bloqueio

As operações do Spanner adquirem bloqueios quando fazem parte de uma transação de leitura e gravação. Essas transações não adquirem bloqueios. O Spanner usa diferentes modos de bloqueio para maximizar o número de transações que têm acesso a uma célula de dados específica em um determinado momento. Bloqueios diferentes têm características diferentes. Por exemplo, alguns bloqueios podem ser compartilhados entre várias transações, enquanto outros não.

Um conflito de bloqueio pode ocorrer quando você tenta adquirir um dos modos de bloqueio a seguir em uma transação.

  • Bloqueio ReaderShared: um bloqueio que permite que outras leituras ainda acessem os dados até que a transação esteja pronta para confirmação. Esse bloqueio compartilhado é adquirido quando uma transação de leitura/gravação lê dados.

  • Bloqueio WriterShared: é adquirido quando uma transação de leitura/gravação tenta confirmar uma gravação.

  • Bloqueio Exclusive: um bloqueio exclusivo é adquirido quando uma transação de leitura e gravação, que já adquiriu um bloqueio ReaderShared, tenta gravar dados após a conclusão da leitura. Um bloqueio exclusivo é um upgrade de um bloqueio ReaderShared. Um bloqueio exclusivo é um caso especial de uma transação que mantém os bloqueios ReaderShared e WriterShared ao mesmo tempo. Nenhuma outra transação pode adquirir qualquer bloqueio na mesma célula.

  • Bloqueio de WriterSharedTimestamp: um tipo especial de bloqueio WriterShared que é adquirido ao inserir novas linhas em uma tabela que tem um carimbo de data/hora de confirmação como parte da chave primária. Esse tipo de bloqueio evita que os participantes da transação criem exatamente a mesma linha e, portanto, entrem em conflito uns com os outros. O Spanner atualiza a chave da linha inserida para corresponder ao carimbo de data/hora de confirmação da transação que executou a inserção.

Para mais informações sobre os tipos de transações e os tipos de bloqueios disponíveis, consulte Transações.

Conflitos no modo de bloqueio

A tabela a seguir mostra os possíveis conflitos entre diferentes modos de bloqueio.

Modos de bloqueio ReaderShared WriterShared Exclusive WriterSharedTimestamp
ReaderShared Não Sim Sim Sim
WriterShared Sim Não Sim Não relevante
Exclusive Sim Sim Sim Não relevante
WriterSharedTimestamp Sim Não relevante Não relevante Sim

Os bloqueios WriterSharedTimestamp são usados apenas ao inserir novas linhas com um carimbo de data/hora como parte da chave primária. Os bloqueios WriterShared e Exclusive são usados ao gravar em células existentes ou inserir novas linhas sem carimbos de data/hora. Como resultado, WriterSharedTimestamp não pode entrar em conflito com outros tipos de bloqueios, e esses cenários são mostrados como Não relevante na tabela anterior.

A única exceção é ReaderShared, que pode ser aplicado a linhas não existentes e, portanto, pode entrar em conflito com WriterSharedTimestamp. Por exemplo, uma verificação de tabela completa bloqueia a tabela inteira mesmo para linhas que não foram criadas. Portanto, é possível que ReaderShared entre em conflito com WriterSharedTimestamp.

O que é uma chave de início de intervalo de linhas?

A coluna ROW_RANGE_START_KEY identifica a chave primária composta ou a chave primária inicial de um intervalo de linha que tem conflitos de bloqueio. O esquema a seguir é usado para ilustrar um exemplo.

CREATE TABLE Singers (
  SingerId   INT64 NOT NULL,
  FirstName  STRING(1024),
  LastName   STRING(1024),
  SingerInfo BYTES(MAX),
) PRIMARY KEY (SingerId);

CREATE TABLE Albums (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  AlbumTitle   STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId),
  INTERLEAVE IN PARENT Singers ON DELETE CASCADE;

CREATE TABLE Songs (
  SingerId     INT64 NOT NULL,
  AlbumId      INT64 NOT NULL,
  TrackId      INT64 NOT NULL,
  SongName     STRING(MAX),
) PRIMARY KEY (SingerId, AlbumId, TrackId),
  INTERLEAVE IN PARENT Albums ON DELETE CASCADE;

CREATE TABLE Users (
  UserId     INT64 NOT NULL,
  LastAccess TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
  ...
) PRIMARY KEY (UserId, LastAccess);

Conforme mostrado na tabela a seguir de intervalos de chave de linha e de chave de linha, um intervalo é representado por um sinal de adição, "+", na chave. A chave nesses casos representa a chave inicial de um intervalo de chaves em que ocorreu um conflito de bloqueio.

ROW_RANGE_START_KEY Explicação
singers(2) Tabela de cantores na chave SingerId=2
álbuns(2,1) Tabela de álbuns na chave SingerId=2,AlbumId=1
Músicas(2,1,5) Tabela de músicas na chave SingerId=2,AlbumId=1,TrackId=5
Músicas(2,1,5+) Intervalo de chaves da tabela de músicas a partir de SingerId=2,AlbumId=1,TrackId=5
álbuns(2,1+) Intervalo de chaves da tabela de álbuns a partir de SingerId=2,AlbumId=1
usuários(3, 2020-11-01 12:34:56.426426+00:00) Tabela "Usuários" na chave UserId=3, LastAccess=commit_timestamp

Agregar estatísticas

SPANNER_SYS também contém tabelas para armazenar dados agregados para estatísticas de bloqueio capturadas pelo Spanner em um período específico:

  • SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: estatísticas agregadas de todas as esperas de bloqueio durante intervalos de 1 minuto.

  • SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: estatísticas agregadas para todos os bloqueios de espera durante intervalos de 10 minutos.

  • SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: estatísticas agregadas para todas as esperas de bloqueio durante intervalos de 1 hora.

As tabelas de estatísticas agregadas têm as seguintes propriedades:

  • Cada tabela contém dados para intervalos de tempo não sobrepostos do comprimento que o nome da tabela especifica.

  • Os intervalos são baseados em horas. Os intervalos de 1 minuto terminam no minuto, os intervalos de 10 minutos terminam a cada 10 minutos, começando na hora, e os intervalos de 1 hora terminam na hora.

    Por exemplo, às 11h59m30s, os intervalos mais recentes disponíveis para consultas SQL em estatísticas de bloqueio agregadas são:

    • 1 minuto: 11:58:00–11:58:59
    • 10 minutos: 11:40:00–11:49:59
    • 1 hora: 10:00:00–10:59:59
  • Cada linha contém estatísticas para todas as esperas de bloqueio no banco de dados durante o intervalo especificado, agregadas juntas. Há apenas uma linha por intervalo de tempo.

  • As estatísticas capturadas nas tabelas SPANNER_SYS.LOCK_STATS_TOTAL_* incluem esperas de bloqueio que o Spanner não capturou nas tabelas SPANNER_SYS.LOCK_STATS_TOP_*.

  • Algumas colunas nestas tabelas são expostas como métricas no Cloud Monitoring. As métricas expostas são:

    • Tempo de espera de bloqueio

    Para mais informações, consulte Métricas do Spanner.

Esquema de tabela

Nome da coluna Tipo Descrição
INTERVAL_END TIMESTAMP Fim do intervalo de tempo em que ocorreu o conflito de bloqueio.
TOTAL_LOCK_WAIT_SECONDS FLOAT64 Tempo total de espera de bloqueio para conflitos de bloqueio registrados para todo o banco de dados, em segundos.

Exemplo de consultas

Veja a seguir um exemplo de instrução SQL que pode ser usada para recuperar estatísticas de bloqueio. É possível executar essas instruções SQL usando as bibliotecas de cliente, a gcloud spanner ou o Console do Google Cloud.

Liste as estatísticas de bloqueio do intervalo de um minuto anterior

A consulta a seguir retorna as informações de espera de bloqueio para cada chave de linha com um conflito de bloqueio, incluindo a fração do total de conflitos de bloqueio, durante o intervalo de tempo de um minuto mais recente.

A função CAST() converte o campo row_range_start_key BYTES em uma STRING.

SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
       t.total_lock_wait_seconds,
       s.lock_wait_seconds,
       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
       s.sample_lock_requests
FROM spanner_sys.lock_stats_total_minute t, spanner_sys.lock_stats_top_minute s
WHERE t.interval_end =
  (SELECT MAX(interval_end)
   FROM spanner_sys.lock_stats_total_minute)
AND s.interval_end = t.interval_end
ORDER BY s.lock_wait_seconds DESC;
Saída da consulta
row_range_start_key total_lock_wait_seconds lock_wait_seconds frac_of_total sample_lock_requests
Músicas(2,1,1) 2,37 1,76 0.7426 LOCK_MODE: ReaderShared

COLUNA: Singers.SingerInfo

LOCK_MODE: WriterShared

COLUNA: Singers.SingerInfo
Usuários(3, 2020-11-01 12:34:56.426426+00:00) 2,37 0,61 0.2573 LOCK_MODE: ReaderShared

COLUMN: users._exists1

LOCK_MODE: WriterShared

COLUMN: users._exists1

1 _exists é um campo interno usado para verificar se uma determinada linha existe ou não.

Retenção de dados

No mínimo, o Spanner mantém dados para cada tabela pelos períodos a seguir:

  • SPANNER_SYS.LOCK_STATS_TOP_MINUTE e SPANNER_SYS.LOCK_STATS_TOTAL_MINUTE: intervalos que abrangem as seis horas anteriores.

  • SPANNER_SYS.LOCK_STATS_TOP_10MINUTE e SPANNER_SYS.LOCK_STATS_TOTAL_10MINUTE: intervalos abrangendo os quatro dias anteriores.

  • SPANNER_SYS.LOCK_STATS_TOP_HOUR e SPANNER_SYS.LOCK_STATS_TOTAL_HOUR: intervalos que abrangem os 30 dias anteriores.

Resolver conflitos de bloqueio no seu banco de dados usando estatísticas de bloqueio

É possível usar o SQL ou o painel Bloquear insights para consultar conflitos de bloqueio no banco de dados.

Os tópicos a seguir mostram como investigar esses conflitos de bloqueio usando código SQL.

Selecione um período para investigar

Examine as métricas de latência do banco de dados do Spanner e descubra um período em que o aplicativo apresenta alta latência e uso da CPU. Por exemplo, o problema começou a ocorrer por volta das 22h50 em 12 de novembro de 2020.

Determine se a latência de confirmação da transação aumentou junto com o tempo de espera de bloqueio durante o período selecionado

Os bloqueios são adquiridos pelas transações. Portanto, se os conflitos de bloqueio causarem longos tempos de espera, poderemos ver o aumento na latência da confirmação da transação com o aumento no tempo de espera do bloqueio.

Depois de selecionar um período para iniciar nossa investigação, associaremos as estatísticas de transação TXN_STATS_TOTAL_10MINUTE às estatísticas de bloqueio LOCK_STATS_TOTAL_10MINUTE nesse período para nos ajudar a entender se o aumento da latência média de confirmação é contribuído pelo aumento do tempo de espera do bloqueio.

SELECT t.interval_end, t.avg_commit_latency_seconds, l.total_lock_wait_seconds
FROM spanner_sys.txn_stats_total_10minute t
LEFT JOIN spanner_sys.lock_stats_total_10minute l
ON t.interval_end = l.interval_end
WHERE
  t.interval_end >= "2020-11-12T21:50:00Z"
  AND t.interval_end <= "2020-11-12T23:50:00Z"
ORDER BY interval_end;

Vamos analisar os dados a seguir como um exemplo do resultado que recebemos da nossa consulta.

interval_end avg_commit_latency_seconds total_lock_wait_seconds
2020-11-12 21:40:00-07:00 0,002 0.090
2020-11-12 21:50:00-07:00 0.003 0.110
2020-11-12 22:00:00-07:00 0,002 0.100
2020-11-12 22:10:00-07:00 0,002 0.080
2020-11-12 22:20:00-07:00 0.030 0.240
2020-11-12 22:30:00-07:00 0.034 0.220
2020-11-12 22:40:00-07:00 0.034 0.218
2020-11-12 22:50:00-07:00 3.741 780.193
2020-11-12 23:00:00-07:00 0.042 0.240
2020-11-12 23:10:00-07:00 0.038 0.129
2020-11-12 23:20:00-07:00 0.021 0.128
2020-11-12 23:30:00-07:00 0.038 0.231

Esses resultados anteriores mostram um aumento significativo em avg_commit_latency_seconds e total_lock_wait_seconds durante o mesmo período de 2020-11-12 22:40:00 a 2020-11-12 22:50:00 e diminuíram depois disso. Observe que avg_commit_latency_seconds é o tempo médio gasto apenas para a etapa de confirmação. Por outro lado, total_lock_wait_seconds é o tempo de bloqueio agregado do período, de modo que o tempo é muito mais longo do que o tempo de confirmação da transação.

Agora que confirmamos que o tempo de espera de bloqueio está intimamente relacionado ao aumento na latência de gravação, investigaremos na próxima etapa quais linhas e colunas causam a longa espera.

Descobrir quais chaves de linha e colunas tiveram longos tempos de espera de bloqueio durante o período selecionado

Para descobrir quais chaves de linha e colunas tiveram os altos tempos de espera de bloqueio durante o período que estamos investigando, consultamos a tabela LOCK_STAT_TOP_10MINUTE, que lista as chaves de linha e as colunas que contribuem mais para a espera de bloqueio.

A função CAST() na consulta a seguir converte o campo row_range_start_key BYTES em uma STRING.

SELECT CAST(s.row_range_start_key AS STRING) AS row_range_start_key,
       t.total_lock_wait_seconds,
       s.lock_wait_seconds,
       s.lock_wait_seconds/t.total_lock_wait_seconds frac_of_total,
       s.sample_lock_requests
FROM spanner_sys.lock_stats_total_10minute t, spanner_sys.lock_stats_top_10minute s
WHERE
  t.interval_end = "2020-11-12T22:50:00Z" and s.interval_end = t.interval_end;
row_range_start_key total_lock_wait_seconds lock_wait_seconds frac_of_total sample_lock_requests
Singers(32) 780.193 780.193 1 LOCK_MODE: WriterShared

COLUNA: Singers.SingerInfo

LOCK_MODE: ReaderShared

COLUNA: Singers.SingerInfo

Nesta tabela de resultados, podemos ver que o conflito aconteceu na tabela Singers da chave SingerId=32. O Singers.SingerInfo é a coluna em que ocorreu o conflito de bloqueio entre ReaderShared e WriterShared.

Esse é um tipo comum de conflito quando há uma transação que tenta ler uma determinada célula e a outra transação está tentando gravar na mesma célula. Agora sabemos a célula de dados exata em que as transações estão disputando o bloqueio. Portanto, na próxima etapa, identificaremos as transações que competem pelos bloqueios.

Descobrir quais transações estão acessando as colunas envolvidas no conflito de bloqueio

Para identificar as transações com latência de confirmação significativa em um intervalo de tempo específico devido a conflitos de bloqueio, consulte as seguintes colunas da tabela SPANNER_SYS.TXN_STATS_TOTAL_10MINUTE:

  • fprint
  • read_columns
  • write_constructive_columns
  • avg_commit_latency_seconds

É necessário filtrar as colunas bloqueadas identificadas na tabela SPANNER_SYS.LOCK_STATS_TOP_10MINUTE:

  • Transações que leem qualquer coluna que incorreu em um conflito de bloqueio ao tentar adquirir o bloqueio ReaderShared.

  • transações que gravam em qualquer coluna que incorreu em um conflito de bloqueio ao tentar adquirir um bloqueio WriterShared;

SELECT
  fprint,
  read_columns,
  write_constructive_columns,
  avg_commit_latency_seconds
FROM spanner_sys.txn_stats_top_10minute t2
WHERE (
  EXISTS (
    SELECT * FROM t2.read_columns columns WHERE columns IN (
      SELECT DISTINCT(req.COLUMN)
      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
      WHERE req.LOCK_MODE = "ReaderShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
OR
  EXISTS (
    SELECT * FROM t2.write_constructive_columns columns WHERE columns IN (
      SELECT DISTINCT(req.COLUMN)
      FROM spanner_sys.lock_stats_top_10minute t, t.SAMPLE_LOCK_REQUESTS req
      WHERE req.LOCK_MODE = "WriterShared" AND t.interval_end ="2020-11-12T23:50:00Z"))
)
AND t2.interval_end ="2020-11-12T23:50:00Z"
ORDER BY avg_commit_latency_seconds DESC;

O resultado da consulta é classificado pela coluna avg_commit_latency_seconds para que você veja primeiro a transação com a maior latência de confirmação.

fprint read_columns write_constructive_columns avg_commit_latency_seconds
1866043996151916800


['Singers.SingerInfo',
'Singers.FirstName',
'Singers.LastName',
'Singers._exists']
['Singers.SingerInfo'] 4.89
4168578515815911936 [] ['Singers.SingerInfo'] 3.65

Os resultados da consulta mostram que duas transações tentaram acessar a coluna Singers.SingerInfo, que é a coluna que teve conflitos de bloqueio durante o período. Depois de identificar as transações que estão causando os conflitos de bloqueio, é possível analisá-las usando a impressão digital delas, fprint, para identificar possíveis problemas que contribuíram para o conflito de bloqueio.

Depois de analisar a transação com fprint=1866043996151916800, use as colunas read_columns e write_constructive_columns para identificar qual parte do código do aplicativo acionou a transação. Em seguida, é possível visualizar a DML subjacente que não está filtrando na chave primária, SingerId. Isso causou uma verificação completa da tabela e a bloqueou até que a transação fosse confirmada.

Para resolver o conflito de bloqueio, faça o seguinte:

  1. Use uma transação somente leitura para identificar os valores SingerId necessários.
  2. Use uma transação de leitura/gravação separada para atualizar as linhas dos valores SingerId obrigatórios.

Aplicar as práticas recomendadas para reduzir a contenção de bloqueio

Em nosso cenário de exemplo, conseguimos usar estatísticas de bloqueio e estatísticas de transação para restringir nosso problema a uma transação que não estava usando a chave primária da tabela ao fazer atualizações. Nós tivemos ideias para melhorar a transação com base no fato de que sabíamos as chaves das linhas que gostaríamos de atualizar antecipadamente ou não.

Ao analisar possíveis problemas na solução ou até mesmo ao projetar a solução, considere estas práticas recomendadas para reduzir o número de conflitos de bloqueio no seu banco de dados.

  • Evitar grandes leituras em transações de leitura e gravação

  • Use transações somente leitura sempre que possível, porque elas não adquirem bloqueios.

  • Evite verificações de tabela completas em uma transação de leitura/gravação. Isso inclui gravar uma condicional DML na chave primária ou atribuir um intervalo de chaves específico ao usar a API Read.

  • Mantenha o período de bloqueio curto confirmando a alteração assim que você ler os dados em uma transação de leitura/gravação. Uma transação de leitura/gravação garante que os dados permaneçam inalterados após a leitura até que você confirme a alteração. Para fazer isso, a transação exige o bloqueio das células de dados durante a leitura e a confirmação. Como resultado, se você deixar o período de bloqueio curto, as transações terão menos chances de conflitos.

  • Favoreça transações pequenas em lugar de transações grandes ou considere usar DML particionada para transações de DML de longa duração. Uma transação de longa duração adquire um bloqueio por muito tempo. Portanto, considere dividir uma transação que envolve milhares de linhas em várias transações menores que atualizam centenas de linhas sempre que possível.

  • Se você não precisar da garantia fornecida por uma transação de leitura e gravação, evite ler os dados da transação de leitura e gravação antes de confirmar a alteração. Por exemplo, leia os dados em uma transação separada de somente leitura. A maioria dos conflitos de bloqueio ocorre devido à forte garantia, para garantir que os dados permaneçam inalterados entre a leitura e a confirmação. Portanto, se a transação de leitura/gravação não ler dados, ela não precisará bloquear as células por muito tempo.

  • Especifique apenas o conjunto mínimo de colunas necessário em uma transação de leitura/gravação. Como os bloqueios do Spanner são feitos por célula de dados, quando uma transação de leitura/gravação lê muitas colunas, ela adquire um bloqueio ReaderShared nessas células. Isso pode causar conflitos de bloqueio quando outras transações adquirem um bloqueio WriterShared nas gravações nas colunas em excesso. Por exemplo, considere especificar um conjunto de colunas em vez de * na leitura.

  • Minimize chamadas de API em uma transação de leitura/gravação. A latência das chamadas de API pode levar a uma contenção de bloqueio no Spanner, porque as chamadas de API estão sujeitas a atrasos de rede e do serviço. Sempre que possível, recomendamos fazer chamadas de API fora das transações de leitura e gravação. Se você precisar executar chamadas de API dentro de uma transação de leitura/gravação, monitore a latência das chamadas de API para minimizar o impacto no período de aquisição de bloqueio.

  • Siga as práticas recomendadas para a concepção de esquemas.

A seguir