Trabalhar com o histórico de tabelas

Para as tabelas Apache Iceberg e Delta Lake, cada operação que modifica uma tabela cria uma nova versão da tabela. Use informação histórica para auditar operações, reverter uma tabela ou consultar uma tabela num ponto específico no tempo usando viagens no tempo.

Note

O Databricks não recomenda usar o histórico de tabelas como solução de backup a longo prazo para arquivamento de dados. Use apenas os últimos 7 dias para operações de viagem no tempo, a menos que tenha definido tanto as configurações de dados como de retenção de registos para um valor maior.

Recuperar histórico da tabela

Execute o DESCRIBE HISTORY comando para recuperar informações incluindo as operações, o utilizador e o carimbo temporal de cada gravação numa tabela. As operações são retornadas em ordem cronológica inversa.

A retenção do histórico da tabela é determinada pela configuração da tabela logRetentionDuration, que é de 30 dias por padrão.

Note

A viagem no tempo e o histórico das tabelas são controlados por diferentes limites de retenção. Ver Viagem no tempo.

DESCRIBE HISTORY table_name       -- get the full history of the table

DESCRIBE HISTORY table_name LIMIT 1  -- get the last operation only

Para obter detalhes da sintaxe do Spark SQL, consulte DESCRIBE HISTORY.

Para detalhes da sintaxe em Scala, Java e Python, consulte a documentação da API Delta Lake.

O Explorador de Catálogos mostra visualmente o histórico da tabela no separador Histórico .

Esquema do histórico

A saída da operação history tem as seguintes colunas.

Coluna Tipo Description
versão long A versão da tabela gerada pela operação.
carimbo de data/hora timestamp Quando esta versão foi cometida.
userId string O ID do utilizador que executou a operação.
userName string O nome do utilizador que executou a operação.
operação string O nome da operação.
parâmetros de operação map Os parâmetros da operação (por exemplo, predicados.)
tarefa struct Os detalhes da tarefa do Lakeflow que executou a operação. É preenchido apenas para commits efetuados por uma tarefa do Lakeflow. Caso contrário, null.
bloco de notas struct Os detalhes do caderno Databricks a partir do qual a operação foi executada. É preenchido apenas para commits efetuados a partir de um notebook do Databricks. Caso contrário, null.
clusterId string O ID do cluster onde a operação decorria.
lerVersão long A versão da tabela que foi lida para realizar a operação de escrita.
isolationLevel string O nível de isolamento utilizado nesta operação.
isBlindAppend boolean Se esta operação anexou dados.
operationMetrics map As métricas da operação (por exemplo, número de linhas e ficheiros modificados.)
userMetadata string Os metadados de commit definidos pelo utilizador, caso tenham sido especificados.
+-------+-------------------+------+--------+---------+--------------------+----+--------+---------+-----------+-----------------+-------------+--------------------+
|version|          timestamp|userId|userName|operation| operationParameters| job|notebook|clusterId|readVersion|   isolationLevel|isBlindAppend|    operationMetrics|
+-------+-------------------+------+--------+---------+--------------------+----+--------+---------+-----------+-----------------+-------------+--------------------+
|      5|2019-07-29 14:07:47|   ###|     ###|   DELETE|[predicate -> ["(...|null|     ###|      ###|          4|WriteSerializable|        false|[numTotalRows -> ...|
|      4|2019-07-29 14:07:41|   ###|     ###|   UPDATE|[predicate -> (id...|null|     ###|      ###|          3|WriteSerializable|        false|[numTotalRows -> ...|
|      3|2019-07-29 14:07:29|   ###|     ###|   DELETE|[predicate -> ["(...|null|     ###|      ###|          2|WriteSerializable|        false|[numTotalRows -> ...|
|      2|2019-07-29 14:06:56|   ###|     ###|   UPDATE|[predicate -> (id...|null|     ###|      ###|          1|WriteSerializable|        false|[numTotalRows -> ...|
|      1|2019-07-29 14:04:31|   ###|     ###|   DELETE|[predicate -> ["(...|null|     ###|      ###|          0|WriteSerializable|        false|[numTotalRows -> ...|
|      0|2019-07-29 14:01:40|   ###|     ###|    WRITE|[mode -> ErrorIfE...|null|     ###|      ###|       null|WriteSerializable|         true|[numFiles -> 2, n...|
+-------+-------------------+------+--------+---------+--------------------+----+--------+---------+-----------+-----------------+-------------+--------------------+

Note

Compreensão partitionBy dos parâmetros de operação

O partitionBy campo no histórico da tabela só tem significado para operações CREATE e OVERWRITE que definem ou alteram o esquema de partição de uma tabela.

Para operações de acréscimo a tabelas existentes (APPEND, INSERT, UPDATE, DELETE, MERGE), este campo pode mostrar uma matriz [] vazia ou colunas de partição, dependendo do método de gravação usado (.save() vs .saveAsTable()).

Esta inconsistência é um comportamento esperado e não afeta a forma como os dados são escritos nas partições. Não deves usá-lo para validar operações de anexação.

Example

Considere uma tabela particionada pela date coluna. Quando crias a tabela, partitionBy é preenchido:

df.write.format("delta") \
  .partitionBy("date") \
  .saveAsTable("sales_data")

A operação CREATE no histórico mostra:

operationParameters: {
  "mode": "ErrorIfExists",
  "partitionBy": "[\"date\"]"
}

Quando adiciona dados a esta tabela, partitionBy mostra um array vazio:

new_df.write.format("delta") \
  .mode("append") \
  .saveAsTable("sales_data")

A operação APPEND mostra:

operationParameters: {
  "mode": "Append",
  "partitionBy": "[]"
}

O valor vazio partitionBy é esperado. Os dados ainda são gravados nas partições corretas com base no esquema de partição existente da tabela. Tenha em atenção que .save() para um caminho pode mostrar colunas de partição neste campo, mas esta diferença é um detalhe de implementação e não afeta o comportamento de escrita.

Métricas de operação

A operação history retorna uma coleção de métricas de operações no mapa de colunas operationMetrics.

As tabelas a seguir listam as definições de chave do mapa por operação.

WRITE, CREATE TABLE AS SELECT, REPLACE TABLE AS SELECT, COPY INTO

As seguintes métricas estão disponíveis para estas operações:

Nome da métrica Description
numFiles O número de ficheiros escritos.
numOutputBytes O tamanho em bytes do conteúdo escrito.
numOutputRows O número de linhas escritas.

STREAMING UPDATE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numAddedFiles O número de ficheiros adicionados.
numRemovedFiles O número de ficheiros removidos.
numOutputRows O número de linhas escritas.
numOutputBytes O tamanho da operação de escrita, em bytes.

DELETE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numAddedFiles O número de ficheiros adicionados. Não fornecido quando as partições da tabela são excluídas.
numRemovedFiles O número de ficheiros removidos.
numDeletedRows O número de linhas removidas. Não fornecido quando as partições da tabela são excluídas.
numCopiedRows O número de linhas copiadas no processo de eliminação de ficheiros.
executionTimeMs O tempo necessário para executar toda a operação.
scanTimeMs O tempo necessário para analisar os ficheiros à procura de correspondências.
rewriteTimeMs O tempo necessário para reescrever os ficheiros correspondentes.

TRUNCATE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numRemovedFiles O número de ficheiros removidos.
executionTimeMs O tempo necessário para executar toda a operação.

MERGE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numSourceRows O número de linhas no DataFrame de origem.
numTargetRowsInserted O número de linhas inseridas na tabela alvo.
numTargetRowsUpdated O número de linhas atualizadas na tabela alvo.
numTargetRowsDeleted O número de linhas eliminadas na tabela alvo.
numTargetRowsCopied O número de linhas alvo copiadas.
numOutputRows O número total de linhas escritas.
numTargetFilesAdded O número de ficheiros adicionados ao sumidouro (alvo).
numTargetFilesRemoved O número de ficheiros removidos do lavatório (alvo).
executionTimeMs O tempo necessário para executar toda a operação.
scanTimeMs O tempo necessário para analisar os ficheiros à procura de correspondências.
rewriteTimeMs O tempo necessário para reescrever os ficheiros correspondentes.

UPDATE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numAddedFiles O número de ficheiros adicionados.
numRemovedFiles O número de ficheiros removidos.
numUpdatedRows O número de linhas atualizadas.
numCopiedRows O número de linhas foi simplesmente copiado durante o processo de atualização dos ficheiros.
executionTimeMs O tempo necessário para executar toda a operação.
scanTimeMs O tempo necessário para analisar os ficheiros à procura de correspondências.
rewriteTimeMs O tempo necessário para reescrever os ficheiros correspondentes.

FSCK

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numRemovedFiles O número de ficheiros removidos.

CONVERT

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numConvertedFiles O número de ficheiros Parquet que foram convertidos.

OPTIMIZE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numAddedFiles O número de ficheiros adicionados.
numRemovedFiles O número de ficheiros otimizados.
numAddedBytes O número de bytes adicionados após a tabela foi otimizado.
numRemovedBytes O número de bytes removidos.
minFileSize O tamanho do ficheiro mais pequeno após a tabela foi otimizado.
p25FileSize O tamanho do ficheiro no 25.º percentil depois de a tabela ter sido otimizada.
p50FileSize O tamanho mediano do ficheiro após a tabela foi otimizado.
p75FileSize O tamanho do ficheiro do percentil 75 depois de a tabela ter sido otimizada.
maxFileSize O tamanho do maior ficheiro após a tabela foi otimizado.

CLONE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
sourceTableSize O tamanho em bytes da tabela de origem na versão que foi clonada.
sourceNumOfFiles O número de ficheiros na tabela de origem na versão que foi clonada.
numRemovedFiles O número de ficheiros removidos da tabela de destino se uma tabela anterior fosse substituída.
removedFilesSize O tamanho total em bytes dos ficheiros removidos da tabela de destino se uma tabela anterior foi substituída.
numCopiedFiles O número de ficheiros que foram copiados para a nova localização. 0 para clones superficiais.
copiedFilesSize O tamanho total em bytes dos ficheiros que foram copiados para a nova localização. 0 para clones superficiais.

RESTORE

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
tableSizeAfterRestore O tamanho da tabela em bytes após a restauração.
numOfFilesAfterRestore O número de ficheiros na tabela após a restauração.
numRemovedFiles O número de ficheiros removidos pela operação de restauro.
numRestoredFiles O número de ficheiros adicionados como resultado da restauração.
removedFilesSize O tamanho em bytes dos ficheiros removidos pela restauração.
restoredFilesSize O tamanho em bytes dos ficheiros adicionados pela restauração.

VACUUM

As seguintes métricas estão disponíveis para esta operação:

Nome da métrica Description
numDeletedFiles O número de ficheiros apagados.
numVacuumedDirectories O número de diretórios limpos com o aspirador.
numFilesToDelete O número de ficheiros a apagar.

Viagem no tempo

A viagem no tempo suporta consultar versões anteriores das tabelas, registadas no registo de transações, com base no timestamp ou na versão da tabela. Você pode usar a viagem no tempo para aplicativos como os seguintes:

  • Recriar análises, relatórios ou resultados, como o resultado de um modelo de aprendizagem automática. Isto pode ser útil para depuração ou auditoria, especialmente em indústrias reguladas.
  • Escrever consultas temporais complexas.
  • Corrigir erros nos seus dados.
  • Proporcionar isolamento ao nível de instantâneo para um conjunto de consultas em tabelas de rápida mutação.

Note

No Databricks Runtime 18.0 e superiores, as consultas de versão temporal são bloqueadas se solicitarem uma versão anterior à propriedade da tabela deletedFileRetentionDuration (padrão 7 dias). Para tabelas geridas pelo Unity Catalog, isto aplica-se ao Databricks Runtime 12.2 e superiores.

Sintaxe da viagem no tempo

Consulta uma tabela com viagem no tempo adicionando uma cláusula após a especificação do nome da tabela.

  • timestamp_expression pode ser qualquer um:
    • '2018-10-18T22:15:12.013Z', ou seja, uma cadeia de caracteres que pode ser convertida num timestamp
    • cast('2018-10-18 13:36:32 CEST' as timestamp)
    • '2018-10-18', ou seja, uma cadeia de caracteres de data
    • current_timestamp() - interval 12 hours
    • date_sub(current_date(), 1)
    • Qualquer outra expressão que seja ou possa ser convertida num marca temporal
  • version é um valor longo que pode ser obtido a partir da saída de DESCRIBE HISTORY table_spec.

Nem timestamp_expression nem version podem ser subconsultas.

Somente cadeias de caracteres de data ou carimbo de data e hora são aceitas. Por exemplo, "2019-01-01" e "2019-01-01T00:00:00.000Z". Consulte o seguinte código para exemplo de sintaxe:

SQL

SELECT * FROM people10m TIMESTAMP AS OF '2018-10-18T22:15:12.013Z';
SELECT * FROM people10m VERSION AS OF 123;

Python

df1 = spark.read.option("timestampAsOf", "2019-01-01").table("people10m")
df2 = spark.read.option("versionAsOf", 123).table("people10m")

Você também pode usar a sintaxe @ para especificar o carimbo de data/hora ou a versão como parte do nome da tabela. O carimbo de data/hora deve estar no formato yyyyMMddHHmmssSSS. Pode especificar uma versão com @v. Consulte o seguinte código para exemplo de sintaxe:

SQL

-- Timestamp version
SELECT * FROM people10m@20190101000000000
-- Version number
SELECT * FROM people10m@v123

Python

# Timestamp version
spark.read.table("people10m@20190101000000000")
# Version number
spark.read.table("people10m@v123")

Configurar a retenção de dados para consultas de viagem no tempo

Para consultar uma versão anterior da tabela, deve manter tanto o registo como os ficheiros de dados dessa versão:

  • Os arquivos de dados são eliminados quando VACUUM é executado numa tabela.
  • Os ficheiros de registo são removidos automaticamente após a criação de pontos de verificação das versões da tabela.

Para aumentar o limiar de retenção de dados para tabelas, deve configurar as seguintes propriedades da tabela, substituindo <format> por delta ou iceberg:

  • <format>.logRetentionDuration = "interval <interval>": controla por quanto tempo o histórico de uma tabela é mantido. A predefinição é interval 30 days.
    • Em Databricks Runtime 18.0 e superiores, logRetentionDuration deve ser maior ou igual a deletedFileRetentionDuration. Para tabelas geridas pelo Unity Catalog, isto aplica-se ao Databricks Runtime 12.2 e superiores.
  • <format>.deletedFileRetentionDuration = "interval <interval>": determina o limite VACUUM usa para remover arquivos de dados que não são mais referenciados na versão atual da tabela. A predefinição é interval 7 days.

Por exemplo, para aceder a 30 dias de dados históricos, defina delta.deletedFileRetentionDuration = "interval 30 days", que corresponde à definição padrão para delta.logRetentionDuration.

Important

Aumentar o limite de retenção de dados pode fazer com que os custos de armazenamento aumentem, à medida que mais arquivos de dados são mantidos.

Pode especificar propriedades de tabela durante a criação da tabela ou defini-las com uma ALTER TABLE instrução. Consulte Referência de Propriedades da Tabela.

Exemplos de viagens no tempo

Para corrigir eliminações acidentais numa tabela para o utilizador 111:

INSERT INTO my_table
  SELECT * FROM my_table TIMESTAMP AS OF date_sub(current_date(), 1)
  WHERE userId = 111

Para corrigir atualizações acidentais e incorretas numa tabela:

MERGE INTO my_table target
  USING my_table TIMESTAMP AS OF date_sub(current_date(), 1) source
  ON source.userId = target.userId
  WHEN MATCHED THEN UPDATE SET *

Para questionar o número de novos clientes adicionados na última semana:

SELECT
(
  SELECT count(distinct userId)
  FROM my_table
)
-
(
  SELECT count(distinct userId)
  FROM my_table TIMESTAMP AS OF date_sub(current_date(), 7)
) AS new_customers

Pontos de verificação do registo de transações

O registo de transações regista as versões das tabelas como ficheiros JSON dentro do diretório do registo de transações, juntamente com os dados das tabelas.

Para otimizar a consulta de checkpoints, as versões das tabelas são agregadas em ficheiros de checkpoint Parquet, o que melhora o desempenho ao evitar a necessidade de ler todas as versões JSON do histórico das tabelas. Os utilizadores não precisam de interagir diretamente com os pontos de controlo.

O Azure Databricks otimiza a frequência de pontos de verificação para o tamanho dos dados e a carga de trabalho. A frequência do ponto de verificação está sujeita a alterações sem aviso prévio.

Restaurar uma tabela a um estado anterior

Use o RESTORE comando para restaurar uma tabela a uma versão ou carimbo temporal anterior, incluindo nestes cenários:

  • Você pode restaurar uma tabela já restaurada.
  • Você pode restaurar uma tabela clonada .

Considere os seguintes requisitos:

  • Para restaurar uma tabela, tem de ter a permissão MODIFY para a tabela.
  • Depois de os ficheiros de dados serem eliminados, manualmente ou por VACUUM, não podes restaurar uma tabela para uma versão antiga que faça referência a esses ficheiros. A restauração parcial para esta versão ainda é possível se spark.sql.files.ignoreMissingFiles estiver definida como true.
  • Para restaurar por carimbo temporal, use os formatos yyyy-MM-dd HH:mm:ss ou yyyy-MM-dd.
RESTORE TABLE target_table TO VERSION AS OF <version>;
RESTORE TABLE target_table TO TIMESTAMP AS OF <timestamp>;

Para obter detalhes de sintaxe, consulte RESTORE.

Comportamento de streaming

A restauração é uma operação que altera dados e pode resultar em dados duplicados para cargas de trabalho posteriores. As entradas de registo adicionadas pelo RESTORE comando contêm dataChange definido como true.

Para cargas de trabalho a jusante, como um trabalho de streaming estruturado que processa as atualizações de uma tabela, as entradas do registo de alterações de dados adicionadas pela operação de restauro são consideradas novas atualizações de dados, e o seu processamento pode resultar em dados duplicados.

Por exemplo:

Versão da tabela Operation Atualizações dos registos Registos nas atualizações do registo de alterações de dados
0 INSERT AddFile(/path/to/file-1, dataChange = true) (nome = Viktor, idade = 29), (nome = George, idade = 55)
1 INSERT AddFile(/path/to/file-2, dataChange = true) (nome = George, idade = 39)
2 OPTIMIZE AddFile(/path/to/file-3, dataChange = false), RemoveFile(/path/to/file-1), RemoveFile(/path/to/file-2) Nenhum registo. OPTIMIZE a compactação não altera os dados na tabela.
3 RESTORE(version=1) RemoveFile(/path/to/file-3), AddFile(/path/to/file-1, dataChange = true), AddFile(/path/to/file-2, dataChange = true) (nome = Viktor, idade = 29), (nome = George, idade = 55), (nome = George, idade = 39)

No exemplo anterior, o RESTORE comando resulta em atualizações que já tinham sido vistas ao ler as versões 0 e 1 da tabela. Se uma consulta de streaming ler esta tabela novamente, então estes ficheiros são considerados dados recém-adicionados e são processados novamente.

Restaurar métricas

Após concluir, RESTORE reporta as seguintes métricas como uma única linha de DataFrame:

  • table_size_after_restore: O tamanho da tabela após a restauração.

  • num_of_files_after_restore: O número de arquivos na tabela após a restauração.

  • num_removed_files: Número de ficheiros removidos (logicamente eliminados) da tabela.

  • num_restored_files: Número de arquivos restaurados devido à reversão.

  • removed_files_size: Tamanho total, em bytes, dos ficheiros que são removidos da tabela.

  • restored_files_size: Tamanho total em bytes dos arquivos que são restaurados.

    Exemplo de métricas de restauração

Encontre a última versão de commit

Para obter o número da versão do último commit feito pelo atual SparkSession em todos os threads e em todas as tabelas, execute uma consulta na configuração SQL spark.databricks.<format>.lastCommitVersionInSession. Substitua <format> por delta ou iceberg, consoante o formato da sua tabela.

Por exemplo:

SQL

SET spark.databricks.delta.lastCommitVersionInSession

Python

spark.conf.get("spark.databricks.delta.lastCommitVersionInSession")

Scala

spark.conf.get("spark.databricks.delta.lastCommitVersionInSession")

Se nenhuma confirmação tiver sido feita pelo SparkSession, consultar a chave retornará um valor vazio.

Note

Se partilhar o mesmo SparkSession entre vários threads, é semelhante a partilhar uma variável entre vários threads. Podes encontrar condições de corrida para atualizações simultâneas do valor de configuração.