redstyle 7 Denunciar post Postado Outubro 22, 2012 Estou com um problema com mysql junto com php que é o seguinte. Minha aplicação recebe callbacks em forma de posts de um outro sistema. Acontece que este sistema manda muitos callbacks de um mesmo pedido. No sistema eu verifica se o id do pedido já existe, se não existir ele insere e se existir ele atualiza. Acontece que mesmo fazendo uma verificação com uma consulta do tipo COUNT ele está duplicando pedidos no banco de dados. A estrutura de minha tabela (de forma simplificada) é a seguinte: Tabela: Pedidos id - Id do meu pedido em meu sistema, auto increment e chave primária order_id - id que vem do callback outros campos... O que faço quando recebo um callback é: 1 - verifico dando um select count se o order_id vindo do callback já existe na tabela pedidos. 2 - se não existir insere 3 - se existir atualiza O problema é que parece que ele ignora o count e acaba duplicando os order_id. Será que isso está acontecendo por conta de receber um callback atrás do outro e não dar tempo de inserir o primeiro e ai no segundo ele acaba nem achando o da primeira inserção? Se for isso eu pensei em usar LOCK TABLE mas não sei se é a melhor solução e se o comportamento do mysql irá criar uma espécie de fila de querys. Tipo recebe um callback, o script da lock table e verifica se já tem o pedido, se não tiver insere e se tiver atualiza, desbloqueia a tabela, ai sim pega a próxima query que está esperando já que ela encontrou a tabela travada por conta do primeiro callback. Compartilhar este post Link para o post Compartilhar em outros sites
Prog 183 Denunciar post Postado Outubro 22, 2012 Neste processo: 1 - verifico dando um select count se o order_id vindo do callback já existe na tabela pedidos.2 - se não existir insere 3 - se existir atualiza Os passos 2 ou 3 são executados no mesmo script de callback do passo 1? Questiono isto pois, se há a duplicação, pode estar havendo algum gargalo de rede, imagino que isto seja devido a ficar aguardando mais de 1 callback. Você pode criar um indice UNIQUE, com os campos, sei lá, order_id e product_id. Caso o procedimento de INSERT falhe devido a duplicidade, execute o UPDATE. LOCK TABLE pode criar filas, mas isto requer um controle maior. Pode gerar mais problemas que soluções, causar gargalos na aplicação e outras coisas inesperadas. Lembrando que, o HTTP vai aguardar o response e caso ele não envie, vai dar timeout. O comando enviado vai continuar na fila, mas caso algum problema aconteça, você não tem mais o callback, vai ter que recorrer aos logs. O comando INSERT do MySQL possui uma clausula interessante, é a ON DUPLICATE KEY UPDATE que funciona como uma trigger, mas isto apenas quanto a chave primária é duplicada, no seu caso, que esta usando auto incremento, não deve servir, a não ser que esteja disposto a mudar o modelo, não acredito que este seja o melhor caminho. Compartilhar este post Link para o post Compartilhar em outros sites
redstyle 7 Denunciar post Postado Outubro 22, 2012 Prog, obrigado pela resposta. Sobre sua pergunta sim o script recebe um callback e faz o processamento das informações enviadas por ele em um mesmo script. A tabela que guarda as informações de callback é como uma espécie de fila que criei para processar os dados já que eu tenho que fazer uma consulta na API para pegar as informarções do pedido de forma completa. Na tabela de callbacks estou fazendo um teste para deixar somente um callback por order_id e fazendo a atualização dessa tabela ao invés de ter callbacks duplicados para processamento da fila. Bom se isso não resolver acho que o melhor a fazer é usar sua dica do ON DUPLICATE KEY já que estaria disposto a mudar a primary key para a coluna order_id. Agora sobre o problema de gargalo de rede eu pensei aqui. O MySQL trabalha de forma assincrona? Ou ele executa uma query por vez? Compartilhar este post Link para o post Compartilhar em outros sites
Prog 183 Denunciar post Postado Outubro 22, 2012 O MySQL tem um conceito de fila, desta forma, podemos dizer sim, que ele é assíncrono e multi-thread e que não necessariamente esta fila será executada em "ordem de chegada", alguns processos tem prioridade sobre outros. O MySQL da prioridade maior aos processos de escrita do que os processos de leitura, exemplo: se ele tiver um SELECT, um INSERT e um UPDATE na fila, o MySQL vai dar prioridade ao INSERT e o UPDATE antes do SELECT, independente da ordem de chegada. Outra técnica que você pode usar para contornar este problema são trigger BEFORE INSERT. Compartilhar este post Link para o post Compartilhar em outros sites
redstyle 7 Denunciar post Postado Outubro 22, 2012 Entendi, então em um esquema onde as seguintes querys fossem enviadas para o mysql ele poderia duplicar order_id? SELECT COUNT(1) AS total FROM callbacks WHERE order_id = 1; Verifica o valor retornado. Se valor igual a zero então: INSERT INTO callbacks (order_id, valor1, valor2, valorN) VALUES (1, 'valor 1', 'valor 2', 'valor n'); Se não: UPDATE callbacks SET valor_1 = 'novo 1', valor_2 = 'novo 2', valor_n = 'novo n' WHERE order_id = 1 Ai o callback envia dois pedidos ao mesmo tempo com o mesmo order_id. Poderia neste caso duplicar? Compartilhar este post Link para o post Compartilhar em outros sites
Motta 645 Denunciar post Postado Outubro 22, 2012 É feito uso de transactions ? Se não forem bem pensadas elas podem gerar locks. O MySQL tem o conceito de BIND VARIABLES como no Oracle ? Compartilhar este post Link para o post Compartilhar em outros sites
Prog 183 Denunciar post Postado Outubro 22, 2012 O MySQL tem o conceito de BIND VARIABLES como no Oracle ? Há um conceito equivalente, mas não sei dizer precisamente se funcionam como o BIND VARIABLES do Oracle. Compartilhar este post Link para o post Compartilhar em outros sites
Motta 645 Denunciar post Postado Outubro 22, 2012 Quando se manda um comando DML ao Oracle ele verifica se a "query" já foi compilada no Banco, se foi compilada ele só executa senão ele compila e executa. Se temos algo assim INSERT INTO TABELA (CAMPO1) VALUES (1); INSERT INTO TABELA (CAMPO1) VALUES (2); INSERT INTO TABELA (CAMPO1) VALUES (3); INSERT INTO TABELA (CAMPO1) VALUES (4); A query será compilada 4 vezes (e executada 4 vezes) mas se algo assim for feito vcampo1:=1 INSERT INTO TABELA (CAMPO1) VALUES (:vcampo1); vcampo1:=2 INSERT INTO TABELA (CAMPO1) VALUES (:vcampo1); vcampo1:=3 INSERT INTO TABELA (CAMPO1) VALUES (:vcampo1); vcampo1:=4 INSERT INTO TABELA (CAMPO1) VALUES (:vcampo1); A query será compilada 1 vez (e executada 4 vezes) Isto dimunui um eventual gargalo Imagine uma insersão de 100 mil registros. Compartilhar este post Link para o post Compartilhar em outros sites
Prog 183 Denunciar post Postado Outubro 22, 2012 Como eu disse, há um conceito equivalente, mas não sei dizer... como no Oracle. :) Eu faria algo mais ou menos assim: <?php $mysqli = new mysqli("127.0.0.1:3307", "root", "usbw", "teste"); $stmt = $mysqli->prepare("SELECT order_id FROM callback WHERE order_id = 1"); $stmt->execute(); $stmt->store_result(); if ($stmt->num_rows == 0) { if(!$mysqli->query("insert into callback (order_id) values (1)")) { if($mysqli->errno == 1062) { //INSERT TEST UPDATE } } } else { //RESULT TEST UPDATE } ?> Use o recurso que achar melhor. Compartilhar este post Link para o post Compartilhar em outros sites
redstyle 7 Denunciar post Postado Outubro 22, 2012 Então de acordo com as explicações eu não poderia confiar em um script assim: $mysqli = new mysqli("127.0.0.1:3307", "root", "usbw", "teste"); $stmt = $mysqli->prepare("SELECT order_id FROM callback WHERE order_id = $order_id"); $stmt->execute(); $stmt->store_result(); if ($stmt->num_rows == 0){ $mysqli->query("insert into callback (order_id) values ($order_id)"); } else { // update } Isso contando que order_id não é chave primária eu poderia ter esse problema? Compartilhar este post Link para o post Compartilhar em outros sites
Motta 645 Denunciar post Postado Outubro 22, 2012 Prog : Pelo que entendi é por aí. Exemplo Isto não garante que resolve o problema acima mas é uma boa prática. Compartilhar este post Link para o post Compartilhar em outros sites
Prog 183 Denunciar post Postado Outubro 22, 2012 Qual é o número real de callbacks? Compartilhar este post Link para o post Compartilhar em outros sites
redstyle 7 Denunciar post Postado Outubro 22, 2012 Pessoal acabei achando isso aqui no próprio imasters http://imasters.com.br/artigo/10540/mysql/mysql-locking Todo comando do mysql recebe o lock table automaticamente. Fiz um teste aqui com um loop e inseriu e fez update corretamente em todas as tabelas então acredito não ser gargalo. Então acho que achei um bug no framework usado aqui na empresa. Obrigado pelas respostas. Compartilhar este post Link para o post Compartilhar em outros sites
redstyle 7 Denunciar post Postado Novembro 1, 2012 Reativando o tópico pessoal, voltei a mexer nessa parte do script e fiz um script sem usar o framework. E eu vou ter que rever isso e queria pedir a ajuda de vocês. Seguinte eu tinha uma tabela assim: Tabela: Pedidos Campos: id chave primária order_id inteiro O script que faz a verificação está assim: $sql_callback = "SELECT COUNT(1) AS tem_callback FROM ml_callbacks WHERE order_id = '".$order_id."'"; $exe_callback = mysql_query($sql_callback, $con) or die(mysql_error()); $reg_callback = mysql_fetch_array($exe_callback, MYSQL_ASSOC); if ($reg_callback['tem_callback'] == 0){ // adicionando o callback addCallBack($order_id); } O callback tem horas que envia dois ao mesmo tempo e aconteceu de adicionar dois registros com order_id iguais. Então eu não posso confiar num esquema de select count? No momento estou usando o esquema "ON DUPLICATE KEY UPDATE" mas queria realmente saber se não posso confiar num esquema do exemplo acima. Compartilhar este post Link para o post Compartilhar em outros sites