Por que as tabelas temporais registram a hora de início da transação?


8

Ao atualizar uma linha em uma tabela temporal, os valores antigos da linha são armazenados na tabela de histórico com a hora de início da transação como a SysEndTime. Os novos valores na tabela atual terão a hora de início da transação como o SysStartTime.

SysStartTimee SysEndTimesão datetime2colunas usadas por tabelas temporais para registrar quando uma linha era a versão atual. Hora de início da transação é o horário em que a transação que contém as atualizações foi iniciada.

BOL diz:

Os horários registrados nas colunas datetime2 do sistema são baseados no horário de início da transação. Por exemplo, todas as linhas inseridas em uma única transação terão o mesmo horário UTC registrado na coluna correspondente ao início do período SYSTEM_TIME.

Exemplo: começo a atualizar todas as linhas da minha tabela Pedidos em 20160707 11:00:00e a transação leva 5 minutos para ser executada. Isso cria uma linha na tabela de histórico para cada linha com SysEndTimecomo 20160707 11:00:00. Todas as linhas na tabela atual terão um SysStartTimede 20160707 11:00:00.

Se alguém executasse uma consulta em 20160707 11:01:00(enquanto a atualização estiver em execução), veria os valores antigos (assumindo o nível de isolamento confirmado de leitura padrão).

Mas se alguém usasse a AS OFsintaxe para consultar a tabela temporal como estava, 20160707 11:01:00eles veriam os novos valores porque eles SysStartTimeseriam 20160707 11:00:00.

Para mim, isso significa que não mostra essas linhas como estavam naquele momento. Se ele usasse o horário de término da transação, o problema não existiria.

Perguntas: É por design? Estou esquecendo de algo?

A única razão pela qual posso pensar que está usando a hora de início da transação é que ela é a única 'conhecida' quando a transação é iniciada. Ele não sabe quando a transação será encerrada quando for iniciada e levaria tempo para aplicar a hora final no final, o que invalidaria o horário final em que estava sendo aplicada. Isso faz sentido?

Isso deve permitir que você recrie o problema.


11
Você respondeu sua própria pergunta, se você usar o horário de término da transação, terá outra atualização no final da transação: a atualização será concluída 20160707 11:04:58e agora você atualiza todas as linhas com esse carimbo de data / hora. Mas essa atualização também é executada por alguns segundos e termina às 20160707 11:05:02, agora, qual carimbo de data e hora é o final correto da transação? Ou suponha que você usou Read Uncommitedat 20160707 11:05:00e tenha retornado linhas, mas depois AS OFnão as mostra.
Dnoeth 7/07

@dnoeth Sim, acho que essa 'pergunta' é mais um esclarecimento da minha teoria.
James Anderson

Eu não mergulhei na implementação do SQL Server, mas a Teradata possui tabelas bi-temporais há anos e eu sempre recomendo a leitura deste Estudo de Caso de Richard Snodgrass (o cara que "inventou" as consultas temporais)), baseado na sintaxe pré-ANSI SQL da Teradata , mas os conceitos são os mesmos: cs.ulb.ac.be/public/_media/teaching/infoh415/...
dnoeth

Respostas:


4

A idéia é acompanhar o tempo lógico versus o físico. Lógico simplesmente se refere ao que um usuário / aplicativo espera que seja o tempo de uma inserção / atualização / exclusão. O fato de a operação DML demorar um pouco, por qualquer motivo, não é significativo ou mesmo facilmente determinado e compreendido por um usuário. Se você já teve que explicar a contenção de bloqueio x trava para um contador (eu tenho), é uma situação comparável.

Por exemplo, quando Bob "diz" ao aplicativo que todos os funcionários do departamento de Bob começarão a ganhar US $ 42 / min às 20160707 11:00:00, Bob (e seus funcionários) espera que o pagamento de todos agora seja calculado em US $ 42 / min a partir desse momento. Bob não se importa que, para que isso ocorra, o aplicativo precisa fazer 2 leituras e 6 gravações no banco de dados por funcionário, e seus arquivos de dados e log ficam em um monte de unidades RAID-5 SATA II, o que leva cerca de 7 minutos. para concluir a tarefa para todos os 256 funcionários de Bob. Bob, seu contador e o gerente da folha de pagamento cuidam para que todos os seus funcionários recebam US $ 42 / min a partir 20160707 11:00:00. Senão, os funcionários que foram atualizados 20160707 11:00:01serão levemente aborrecidos, enquanto aqueles cujos registros foram atualizados 20160707 11:00:07estarão reunidos fora do departamento de folha de pagamento.

Existem casos de uso válidos para rastrear o tempo físico, como depuração e análise forense, mas, para o usuário final, geralmente não faz sentido. O Tlog mantém as informações de pedido e de tempo de cada uma das operações de gravação (entre outras coisas), para que esteja lá se você souber como procurar.


Pontos agradáveis. Eu acho que a tecnologia é adequada apenas para certos casos de uso como o que você mencionou. Pelas razões expostas acima, parece que seria um ajuste inadequado para rastrear preços ou valores de ações que podem ser alterados em períodos muito curtos.
James Anderson

Na verdade não. Esse é um problema de desempenho e escala. As tabelas temporais ainda funcionam se você precisar manter o histórico do preço das ações. Você só precisa garantir que as inserções sejam muito granulares e possam ser concluídas em uma janela muito pequena. Caso contrário, as alterações subsequentes serão bloqueadas e, se a taxa de entrada for alta o suficiente, ocorrerão tempos limite e possível perda de dados se o aplicativo não puder lidar com novas tentativas. Se você executar o IO de fusão do DB ou com tabelas otimizadas para memória, poderá lidar facilmente com dezenas de milhares de inserções por segundo e bem mais de cem mil por segundo.
SQLmojoe

3

Acredito que essa seja realmente uma falha de design, embora não seja específica do SQL Server 2016, pois todas as outras implementações existentes de tabelas temporais (tanto quanto eu sei) têm a mesma falha. Os problemas que podem surgir com as tabelas temporais por causa disso são bastante graves; o cenário no seu exemplo é moderado em comparação com o que pode dar errado em geral:

Referências de chave estrangeira quebradas : suponha que tenhamos duas tabelas temporais, com a tabela A tendo uma referência de chave estrangeira à tabela B. Agora, digamos que temos duas transações, ambas em execução no nível de isolamento READ COMMITTED: a transação 1 começa antes da transação 2, transação 2 insere uma linha na tabela B e confirma, a transação 1 insere uma linha na tabela A com uma referência à linha recém-adicionada de B. Como a adição da nova linha a B já foi confirmada, a restrição de chave estrangeira é satisfeita e a transação 1 é capaz de confirmar com sucesso. No entanto, se visualizarmos o banco de dados "AS OF" em algum momento entre o início da transação 1 e o início da transação 2, veremos a tabela A com uma referência a uma linha de B que não existe. Então, neste caso,a tabela temporal fornece uma visão inconsistente do banco de dados . Obviamente, essa não era a intenção do padrão SQL: 2011, que afirma,

As linhas históricas do sistema em uma tabela com versão do sistema formam instantâneos imutáveis ​​do passado. Quaisquer restrições que estavam em vigor quando uma linha histórica do sistema foi criada já teriam sido verificadas quando essa linha era uma linha atual do sistema, portanto, nunca há necessidade de impor restrições nas linhas históricas do sistema.

Chaves primárias não exclusivas : digamos que temos uma tabela com uma chave primária e duas transações, ambas no nível de isolamento READ COMMITTED, no qual ocorre o seguinte: Após o início da transação 1, mas antes de tocar nessa tabela, a transação 2 exclui um determinado linha da tabela e confirma. Em seguida, a transação 1 insere uma nova linha com a mesma chave primária que a que foi excluída. Isso ocorre muito bem, mas quando você olha para a tabela A PARTIR DE ENTREGAMENTE no momento em que a transação 1 começou e quando a transação 2 começou, veremos duas linhas com a mesma chave primária.

Erros em atualizações simultâneas : digamos que temos uma tabela e duas transações que atualizam a mesma linha nela, novamente em um nível de isolamento READ COMMITTED. A transação 1 começa primeiro, mas a transação 2 é a primeira a atualizar a linha. A transação 2 é confirmada e a transação 1 faz uma atualização diferente na linha e confirmada. Tudo bem, exceto que, se for uma tabela temporal, após a execução da atualização na transação 1, quando o sistema inserir a linha necessária na tabela de histórico, o SysStartTime gerado será o horário de início da transação 2, enquanto o SysEndTime será o horário de início da transação 1, que não é um intervalo de tempo válido, pois o SysEndTime seria anterior ao SysStartTime. Nesse caso, o SQL Server gera um erro e reverte a transação (por exemplo, consulteesta discussão ). Isso é muito desagradável, pois no nível de isolamento READ COMMITTED, não seria de esperar que os problemas de concorrência levassem a falhas definitivas, o que significa que os aplicativos não necessariamente serão preparados para fazer novas tentativas. Em particular, isso é contrário a uma "garantia" na documentação da Microsoft:

Esse comportamento garante que seus aplicativos herdados continuem funcionando quando você habilitar a versão do sistema em tabelas que se beneficiarão da versão. ( link )

Outras implementações de tabelas temporais lidaram com esse cenário (duas transações simultâneas atualizando a mesma linha) oferecendo uma opção para "ajustar" automaticamente os carimbos de data e hora, se eles forem inválidos (veja aqui e aqui ). Essa é uma solução feia, pois tem a conseqüência infeliz de interromper a atomicidade das transações, pois outras instruções nas mesmas transações geralmente não terão seus timestamps ajustados da mesma maneira; isto é, com esta solução alternativa, se visualizarmos o banco de dados "AS OF" em determinados momentos, poderemos ver transações parcialmente executadas.

Solução: Você já sugeriu a solução óbvia, que é a implementação de usar o horário de término da transação (ou seja, o horário de confirmação) em vez do horário de início. Sim, é verdade que, quando estamos executando uma instrução no meio de uma transação, é impossível saber qual será o tempo de consolidação (como é no futuro, ou talvez nem exista se a transação for rolada). de volta). Mas isso não significa que a solução não é implementável; só tem que ser feito de uma maneira diferente. Por exemplo, ao executar uma instrução UPDATE ou DELETE, ao criar a linha do histórico, o sistema pode simplesmente inserir o ID da transação atual em vez de uma hora de início e, em seguida, o ID pode ser convertido em um carimbo de data / hora posteriormente pelo sistema após a transação ser confirmada .

No contexto desse tipo de implementação, eu sugeriria que, antes da transação ser confirmada, todas as linhas adicionadas à tabela de histórico não deveriam ser visíveis ao usuário. Da perspectiva do usuário, deve parecer simplesmente que essas linhas foram adicionadas (com o carimbo de data / hora de confirmação) no momento da confirmação. Em particular, se a transação nunca for confirmada com êxito, nunca deverá aparecer no histórico. Obviamente, isso é inconsistente com o padrão SQL: 2011, que descreve as inserções no histórico (incluindo registros de data e hora) como ocorrendo no momento das instruções UPDATE e DELETE (em oposição ao horário da confirmação). Mas não acho que isso realmente importe, considerando que o padrão nunca foi implementado adequadamente (e possivelmente nunca pode ser) devido aos problemas descritos acima,

Do ponto de vista do desempenho, pode parecer indesejável que o sistema precise voltar e revisitar as linhas do histórico para preencher o carimbo de data / hora de confirmação. Mas, dependendo de como isso é feito, o custo pode ser bastante baixo. Não estou muito familiarizado com o funcionamento interno do SQL Server, mas o PostgreSQL, por exemplo, usa um write-ahead-log, o que faz com que, se várias atualizações forem executadas nas mesmas partes de uma tabela, essas atualizações sejam consolidadas para que o os dados precisam ser gravados apenas uma vez nas páginas da tabela física - e isso normalmente se aplica a esse cenário. Em qualquer caso,

É claro que, desde que (até onde eu sei) esse tipo de sistema nunca foi implementado, não posso dizer com certeza que funcionaria - talvez haja algo que esteja faltando - mas não vejo nenhuma razão por que não funcionou.


0

No momento em que você confirma sua transação, todos os dados devem ser gravados dentro das páginas de dados (na memória e no disco no arquivo de log). Incluindo SysStartTimee SysEndTimecolunas. Como você pode saber o horário de término da transação antes que ela seja realmente concluída?

A menos que você possa prever o futuro, usar a hora de início da transação é a única opção, mesmo que seja menos intuitiva.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.