AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 56487
Accepted
crokusek
crokusek
Asked: 2014-01-10 12:33:00 +0800 CST2014-01-10 12:33:00 +0800 CST 2014-01-10 12:33:00 +0800 CST

Como usar dicas adequadas para Upsert envolvendo uma subconsulta top 1

  • 772

Para este caso específico (rastreamento de switches do Load Balancer), desejamos otimizar um Upsert para que

  • não apresenta nenhuma condição de corrida,
  • causar qualquer violação PK, ou
  • adquirir quaisquer bloqueios de tamanho excessivo.

Eu entendo que bloqueios maiores (página) podem ser mais eficientes, mas para fins de questão, o objetivo é mínimo (linha). Existem vários links sobre o assunto upsert/lock, mas as respostas são um tanto inconsistentes (esp. updlock e multi-statements ) e este caso particular envolve uma subconsulta incorporada.

Definição da tabela:

create table [User].[SessionWebServerLog] (
  [SessionId] bigint not null,
  [IsSSL] bit not null default ((0)),
  [LastRequestUtc] datetime2(7) not null default (sysutcdatetime()),
  [WebServerProcessInstanceId] bigint not null,
  [RequestCount] int not null default ((1)),
  [FirstRequestUtc] datetime2(7) not null default (sysutcdatetime()),
  foreign key ([SessionId]) references [User].[Session] ( [SessionId] ) on delete cascade,
  primary key clustered ([SessionId] asc, [IsSSL] asc, [LastRequestUtc] desc, [WebServerProcessInstanceId] asc)
  with ( 
    allow_row_locks = on,
    allow_page_locks = off,  -- Needed else page locks were taken
  )
)

O SP deve inserir apenas se a combinação Session+IsSsl tiver alterado os IDs do servidor desde a solicitação mais recente para essa Session+IsSsl:

create proc [User].[usp_LogSessionWebServerRequest]    
    @pSessionId                   bigint, 
    @pWebServerProcessInstanceId  bigint,    
    @pIsSsl                       bit,        -- True for https, false for http
    @pDebug                       bit = 0     -- debug flag for print statements
as    
begin try        
    set xact_abort on;
    begin transaction;

        update l
           set RequestCount = RequestCount + 1,
               LastRequestUtc = sysutcdatetime()
          from [User].SessionWebServerLog l             
          with (rowlock, xlock, serializable) -- row level, exclusively held, until end of xact
         cross apply
             (
               select top(1) WebServerProcessInstanceId, LastRequestUtc
                 from [User].SessionWebServerLog 
                 with (rowlock, xlock, serializable) -- row level, exclusively held, until end of xact
                   -- PK supports this join:  SessionId, IsSsl, LastRequestUtc (desc), WebServerProcessId                       
                where SessionId = @pSessionId
                  and IsSSL = @pIsSsl
                order by LastRequestUtc desc 
             ) prev -- previous request
         where SessionId = @pSessionId
           and IsSSL = @pIsSsl
           and prev.WebServerProcessInstanceId = @pWebServerProcessInstanceId
           and l.WebServerProcessInstanceId = @pWebServerProcessInstanceId
           and l.LastRequestUtc = prev.LastRequestUtc;

        if (@@rowcount = 0) -- if no update occurred, insert new
        begin
            insert into [user].SessionWebServerLog
                 ( SessionId, WebServerProcessInstanceId, IsSSL )
            values 
                 ( @pSessionId, @pWebServerProcessInstanceId, @pIsSsl );                
        end            

    commit;            
end try
begin catch    
   if (xact_state() = -1 or @@trancount > 0)
    rollback;
   -- log, etc.
end catch

Essa rotina parece funcionar para casos simples, testando usando duas janelas e executando a primeira metade da transação dentro de cada janela e verificando o bloqueio.

Q1: Ele bloqueia quando a atualização não corresponde a nenhuma linha de nenhuma das janelas, mas ainda assim são chaves diferentes. O bloqueio ocorre porque o bloqueio do intervalo de chaves é mantido apenas nas chaves existentes ?

Vitória1:

declare
    @pSessionId                   bigint = 3, -- does not exist in table
    @pWebServerProcessInstanceId  bigint = 100,    
    @pIsSsl                       bit = 0;

 sp_lock 72:

spid  dbid  ObjId      IndId   Type       Resource  Mode      Status
  72    16      0          0     DB                 S          GRANT 
  72    16  388964512      1    KEY (6c2787a590a2)  RangeX-X   GRANT
  72    16  388964512      0    TAB                 IX         GRANT

Vitória2:

 declare
        @pSessionId                   bigint = 4,  -- does not exist in table
        @pWebServerProcessInstanceId  bigint = 100,    
        @pIsSsl                       bit = 0;

    sp_lock 92:

    spid  dbid      ObjId   IndId   Type  Resource         Mode   Status
    92      16          0       0     DB                    S      GRANT
    92      16  388964512       1    KEY  (6c2787a590a2) RangeX-X   WAIT
    92      16  388964512       0    TAB                    IX     GRANT

declare @pSessionId bigint = 4, @pWebServerProcessInstanceId bigint = 100,
@pIsSsl bit = 0;

Q2: Se eu permitir bloqueios de página (o padrão) no PK, por que um bloqueio de página é retirado mesmo que a dica de bloqueio de linha tenha sido especificada.

spid  dbid      ObjId   IndId   Type     Resource      Mode      Status
72      16          0       0      DB                    S       GRANT
72      16  388964512       1     PAG       1:444       IX       GRANT
72      16  388964512       1     KEY (6c2787a590a2)  RangeX-X   GRANT
72      16  388964512       0     TAB                   IX       GRANT

O nível de isolamento da transação é o padrão "leitura confirmada". Eu escolhi não alterar para este específico porque restaurá-lo parece mais confuso (para sucesso e falha e assumir/determinar o padrão) do que apenas usar bloqueios de tabela (imo).

Plano de consulta para o caso zero:

Quando não há linhas correspondentes para atualizar

Plano de consulta quando existem várias linhas para WebSession+Ssl com datas diferentes (exatamente uma linha da ramificação para o topo, perfeita, aparentemente usando o PK por data):

insira a descrição da imagem aqui

Q3: Isso é um exagero - existem outras dicas que atingirão os objetivos? (Por favor, não reorganize a consulta ou tente converter em declaração de mesclagem para fins desta pergunta).

sql-server locking
  • 1 1 respostas
  • 644 Views

1 respostas

  • Voted
  1. Best Answer
    Thomas Kejser
    2014-01-10T16:39:38+08:002014-01-10T16:39:38+08:00

    Q1: O bloqueio de intervalo provavelmente foi mantido porque SessionId,IsSSL,LastRequestUtcnão foi declarado exclusivo. Isso está correto? Isso significa que o valor anterior e posterior ao que você está procurando deve ser bloqueado (pois você está solicitando um XLOCK na tabela) para evitar que o intervalo seja modificado durante a leitura. Se você declarasse a combinação única, acredito que esse problema deveria desaparecer. Isso também pode fazer com que o INSERT trave.

    Q2: Em primeiro lugar, um esclarecimento. O bloqueio de página NÃO é o padrão, o bloqueio de linha é (os bloqueios de página eram padrão no SQL Server 7.0). Dito isto, o servidor SQL ainda removerá um bloqueio de intenção Exclusivo (IX) na página. Isso não é um problema, pois esta fechadura é compatível com outras fechaduras IX. Na verdade, você pode tornar o bloqueio mais barato usando ALLOW_PAGE_LOCKS = OFFseus índices (isso tem efeitos colaterais se você fizer varreduras de tabela frequentes, portanto, tenha cuidado com isso).

    Q3 : Você não precisa forçar o rowlock ou forçar o xlock no CROSS APPLY. Você PODE precisar forçar a serialização se espera que várias instruções sejam inseridas na mesma sessão e intervalos. Caso contrário, você também pode se livrar disso (por exemplo, se cada insersor inserir apenas para a mesma sessão).

    • 2

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve