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 / 72501
Accepted
JohnMerlino
JohnMerlino
Asked: 2014-07-20 08:42:02 +0800 CST2014-07-20 08:42:02 +0800 CST 2014-07-20 08:42:02 +0800 CST

a chave duplicada viola a restrição exclusiva ao usar multi-threading

  • 772

Estou usando o ExecutorService com um pool de threads fixo de 50 e um pool de conexão de banco de dados fixo de 50, usando HikariCP. Cada thread de trabalho processa um pacote (um "relatório"), verifica se ele é válido (onde cada relatório deve ter um unit_id, hora, latitude e longitude exclusivos), obtém uma conexão db do pool de conexões e, em seguida, insere o relatório no a tabela de relatórios. A restrição de exclusividade é criada com postgresql e chamada de "reports_uniqueness_index". Quando eu tenho alto volume, recebo uma tonelada do seguinte erro:

org.postgresql.util.PSQLException: ERROR: duplicate key value 
  violates unique constraint "reports_uniqueness_index"

Aqui está o que eu acredito que seja o problema. Antes da inserção do banco de dados, realizo uma verificação para determinar se já existe um relatório na tabela com o mesmo unit_id, hora, latitude e longitude. Caso contrário, o relatório é válido e eu realizo a inserção. Porém, acho que por estar usando simultaneidade tenho 50 threads cada verificando ao mesmo tempo se o relatório é válido e como nenhum deles foi inserido ainda, cada thread pensa que tem um relatório válido e quando vai inseri-los em mesmo momento, é quando o postgresql gera o erro.

Gostaria de uma solução que não gerasse latência com a simultaneidade. Tenho tentado evitar o uso de instrução sincronizada ou um bloqueio reentrante porque as inserções de banco de dados precisam ocorrer o mais rápido possível. Esta é a inserção aqui:

 private boolean save(){
        Connection conn=null;
        Statement stmt=null;
        int status=0;
        DbConnectionPool dbPool = DbConnectionPool.getInstance();
        String sql = = "INSERT INTO reports"
        sql += " (unit_id, time, time_secs, latitude, longitude, speed, created_at)";
        sql += " values (...)";
        try {
            conn = dbPool.getConnection();
            stmt = conn.createStatement();
            status = stmt.executeUpdate(sql);
        } catch (SQLException e) {
            return false;
        } finally {
            try {
                if (stmt != null)  
                {  
                    stmt.close();
                }  

                if (conn != null)  
                {  
                    conn.close();  
                }  
            } catch(SQLException e){}
        }

        if(status > 0){
            return true;            
        }

        return false;
}

Uma solução que pensei foi usar o próprio objeto Class como um objeto de bloqueio:

synchronized(Report.class) {
    status = stmt.executeUpdate(sql);
}

Mas isso atrasará a inserção de outros segmentos. Existe uma solução melhor?

postgresql java
  • 1 1 respostas
  • 18649 Views

1 respostas

  • Voted
  1. Best Answer
    harmic
    2014-07-20T17:35:07+08:002014-07-20T17:35:07+08:00

    Ao criar uma restrição única em sua tabela, você está dizendo ao banco de dados "cada vez que tento inserir uma linha nesta tabela, verifique se não há nenhuma linha existente com esta combinação de colunas igual. Ah, e certifique-se de fazer de forma atômica, de modo que, se alguém tentar inserir um ao mesmo tempo que eu, apenas um de nós conseguirá".

    Depois de fazer isso, na maioria dos casos, não há muito sentido em replicar a mesma lógica em seu aplicativo. Isso só tornará o processo menos eficiente. Seria melhor você aceitar a sugestão de @horse e pegar a exceção.

    No entanto, há algumas coisas a serem observadas. Uma é que, se você fizer isso dentro de uma transação, a transação será revertida automaticamente no erro. Se você fez qualquer outra coisa antes disso na transação, ela será desfeita. Para evitar isso, você precisa definir um SAVEPOINT antes da inserção e, em seguida, RELEASE ou ROLLBACK o ponto de salvamento conforme apropriado.

    O outro problema é que o log do postgresql ainda conterá esses erros, mesmo que sejam detectados e ignorados pelo código Java. Inundar seus logs com ruído sem sentido ajuda a esconder problemas verdadeiros que podem estar escondidos lá, então não é uma coisa boa.

    Possivelmente, uma solução melhor, que evita esses dois problemas, é criar um procedimento armazenado que insere o registro, manipula todas as exceções que ocorrem e retorna uma indicação ao seu aplicativo se o registro foi armazenado ou não.

    Por exemplo, se você tivesse uma tabela assim:

    CREATE TABLE test(a INTEGER, b TEXT, UNIQUE(A,B));
    

    Então você poderia ter uma função como esta:

    CREATE FUNCTION test_insert(pa INTEGER, pb TEXT) RETURNS INTEGER AS $$
    BEGIN
        INSERT INTO test(a,b) VALUES (pa, pb);
        RETURN 1;
    EXCEPTION
        WHEN unique_violation THEN
           RETURN -1;
    END;
    $$ LANGUAGE plpgsql;
    

    e você poderia usar assim:

    testdb=> SELECT test_insert(1,'foo');
     test_insert
    -------------
               1
    (1 row)
    
    testdb=> SELECT test_insert(1,'foo');
     test_insert
    -------------
              -1
    (1 row)
    

    Se for feita uma tentativa de inserir um registro que viole a restrição, nenhuma exceção é lançada ou registrada e, se executada dentro de uma transação, a transação não é afetada. A função retorna um valor que você pode usar para ver se o registro foi inserido ou não. É um exemplo muito básico, mas deve ilustrar o ponto.

    • 4

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

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