Jump to content


Photo

Problema Com Transactions


  • Faça o login para participar
14 replies to this topic

#1 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 17/06/2011, 19:42

Boa Noite Pessoal,

Conseguem me ajudar no problema de conflito de transactions abaixo

Se duas transactions forem executadas ao mesmo tempo como mostra abaixo
levando em consideração que o valor de X no banco for 1 antes de executar a transaction;

Posted Image

as duas vão incrementar 1 transformando X em 2 e vão dar commit, estas informações são salvas na session entao
só depois aplicadas ao banco com o commit, como o banco de dados Mysql em InnoDB vai se comportar com esta situação?

Pelo que vejo parece que ao inves do resultado final ter que ficar 3, vai ser aplicado por 2 vezes o mesmo valor, neste
caso o valor 2 ficará como resultado final sendo aplicado pelos 2 usuários.

Obrigado desde já.

#2 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 17/06/2011, 22:24

Se não é pra isso acontecer, por que usar transação? Se for só UPDATEs como no exemplo aí, parece não ter necessidade.

#3 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 07/07/2011, 09:19

Se não é pra isso acontecer, por que usar transação? Se for só UPDATEs como no exemplo aí, parece não ter necessidade.



LeoB, da mesma forma, poderiam existir outras informações no bloco de instruções

como vai ser tratado?

#4 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 07/07/2011, 11:26

A única solução que vejo para esse caso é travar o registro antes do update, de modo que só uma transação faça a alteração por vez.

START TRANSACTION
...
SELECT 1 FROM tabela1 WHERE id=1 FOR UPDATE
...
UPDATE tabela1 SET x=x+1 WHERE id=1
...
COMMIT
Assim o registro só vai ser liberado no COMMIT. Se outra transação tentar mexer nele, vai ficar em espera até o fim da primeira.

#5 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 07/07/2011, 15:56

A única solução que vejo para esse caso é travar o registro antes do update, de modo que só uma transação faça a alteração por vez.

START TRANSACTION
...
SELECT 1 FROM tabela1 WHERE id=1 FOR UPDATE
...
UPDATE tabela1 SET x=x+1 WHERE id=1
...
COMMIT
Assim o registro só vai ser liberado no COMMIT. Se outra transação tentar mexer nele, vai ficar em espera até o fim da primeira.



aham...

isso significa que o update não da lock nos registros alterados até o fim da transaction na hora do commit ?

#6 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 07/07/2011, 16:34

Nem teria sentido.

#7 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 07/07/2011, 21:02

Entao consegue verifica o codigo abaixo ???

// inicia transaction para investir pontos em habilidades
		    $this->db->consql->query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; START TRANSACTION;");
		    $this->db->consql->query("SELECT id, cpontos FROM mecanismos WHERE id = ".$this->id." FOR UPDATE;");
		    $this->db->consql->query("SELECT * FROM habilidades WHERE meca_id = ".$this->id." FOR UPDATE;");		    
		    		    
		    // cria a query para remover os pontos a serem investidos
		    $Qr = "UPDATE mecanismos SET cpontos = cpontos - ".$Quantia." WHERE id = ".$this->id." AND cpontos >= ".$Quantia;
				    
		    // executa query     
		    $Sql = $this->db->consql->query($Qr);
	   
		    // verifica se retornou algo na query verificando se existem pontos suficientes
		    IF ($this->db->consql->affected_rows > 0)
		    {
			 // cria a query para investir o ponto na habilidade
			 $Qr = "INSERT INTO habilidades(meca_id,classe_id,valor) VALUES (".$this->id.",".$IdHabilidade.",".$Quantia.") ON DUPLICATE KEY UPDATE valor = valor + ".$Quantia;
		    
			 // executa query     
			 $Sql = $this->db->consql->query($Qr);
		    
			 // verifica se retornou algo na query para inserir os pontos
			 IF ($this->db->consql->affected_rows > 0)
			 {			      
			      // executa query para aplicar modificações feitas na transaction
			      $this->db->consql->query("COMMIT");
			      
			      // atualiza status de combate do mecanismo
			      $this->VerifyStatus();
			      
			      print "Ponto em habilidade investido com sucesso.";
			 }
			 ELSE
			 {
			      // executa query para cancelar a transaction
			      $this->db->consql->query("ROLLBACK");
			 
			      print "Falha ao investir ponto na habilidade.";
			 }
		    }
		    ELSE
		    {
			 // executa query para cancelar a transaction
			 $this->db->consql->query("ROLLBACK");
			 
			 print "Falha ao investir ponto na habilidade.";
		    }


#8 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 07/07/2011, 21:08

Pra mim parece bom. Está dando algum problema?

#9 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 07/07/2011, 21:13

Pra mim parece bom. Está dando algum problema?


Não não Leo.

Esta funcionando perfeitamente. testei ate com o timer.

Com isso consigo fazer com que outras pessoas não consigam alterar e nem mecher em nada em que os registros que esta transaction estão usando.

Tenho outro exemplo também que usei..pra remover o dinheiro e depois o item tendo um commit no final...

Entao vou precisar ficar usando os FOR UPDATE para dar lock nas linhas que precisar também..assim outras transactions vao aguardar a transaction atual ser concluida correto?

obrigado pela ajuda.

Edição feita por: DouglasDomi, 07/07/2011, 21:16.


#10 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 07/07/2011, 22:11

Isso sempre que um problema como o que você deu de exemplo puder acontecer. Dependendo da situação, travar o registro pode ser desnecessário.

#11 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 09/07/2011, 15:55

Isso sempre que um problema como o que você deu de exemplo puder acontecer. Dependendo da situação, travar o registro pode ser desnecessário.



LeoB.

Os DELETEs mesmo em transactions são auto commit, então preciso fazer selects de verificações e depois fazer uma multi_query executando os deletes.
Porem eu não quero que ninguem acesse as linhas de registros enquando eu fazer os selects de verificação na transaction.

Qual a solução? Se eu utilizar FOR UPDATE ele da lock de leitura de outros usuarios também ?, sei que o lock in share mode da lock apenas para remoção e edição de linhas.

#12 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 09/07/2011, 16:12

O FOR UPDATE vai travar tanto a leitura por outras transações quanto por outros usuários.

#13 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 09/07/2011, 16:19

O FOR UPDATE vai travar tanto a leitura por outras transações quanto por outros usuários.


ou seja, bloqueia qualquer operação??

veja o codigo

// captura dados do mecanismo que vai forjar o item e o item a ser forjado
			 $ArrayForja = $SqlForja->fetch_array(MYSQLI_ASSOC);
			 
			 // inicia transaction para remover os itens do inventario
			 $this->db->consql->query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; START TRANSACTION;");
			 $this->db->consql->query("SELECT * FROM inventario WHERE meca_id = ".$this->id." FOR UPDATE;");
			 
			 // cria variavel vazia que vai remover os materiais necessarios para criar o item
			 $QrMateriais = "";		 
			 
			 // verifica se o mecanismo do jogador tem todos os itens da forja no inventario
			 WHILE ($ArrayMateriais = $SqlMateriais->fetch_array(MYSQLI_ASSOC))
			 {
			      // cria query where de filtragem dos itens a serem pesquisados
			      $Where = "WHERE meca_id = ".$this->id." AND item_id = ".$ArrayMateriais['item_id']." AND equipado = 0 ORDER BY nivel LIMIT ".$ArrayMateriais['quantia'].";";
			     			     
			      // cria query que verifica se o mecanismo do jogador tem o material    
			      $Qr = "SELECT id FROM inventario ".$Where;			      
			      
			      // executa query
			      $this->db->consql->query($Qr);
			      
			      // verifica se o foi tirada a quantia necessaria do item se não da rollback
			      IF ($this->db->consql->affected_rows != $ArrayMateriais['quantia'])
			      {	       
				   print "Faltam itens para completar esta forja.";
			 
				   RETURN FALSE;
			      }
			      ELSE
			      {
				   // implementa query para remover material		    
				   $QrMateriais .= "DELETE FROM inventario ".$Where;
			      }
			 }
			 
			  // cria a query para adicionar o item forjado no inventario
			 $Qr = "INSERT INTO inventario(meca_id,item_id,drone_drop,drone_quantia,equipado) VALUES (".$this->id.",".$ArrayForja['item_id'].",0,0,0);";			 

			 // implementa na query de adicionar o item forjado as queries para remover os materias usados na forja
			 $Qr .= $QrMateriais;
			 
			 // executa query
			 $this->db->consql->multi_query($Qr);
			  
			 // executa query para aplicar modificações feitas na transaction
			 $this->db->consql->query("COMMIT");
			 
			 print "Item forjado com sucesso. Já disponivel no inventário.";
				   
			 RETURN TRUE;

veja que quando chamo a função
$this->db->consql->query("SELECT * FROM inventario WHERE meca_id = ".$this->id." FOR UPDATE;");
quero prevenir que ninguem delete,altere ou faça leitura desses registros no banco ate chegar no commit ou rollback do codigo

Edição feita por: DouglasDomi, 09/07/2011, 16:21.


#14 LeoB

LeoB

    Super Veterano

  • Usuários
  • 1876 posts
  • Sexo:Masculino
  • Interesses:Programação

Posted 09/07/2011, 16:40

Não tenho muita experiência com transações, mas até onde eu sei, é pra acontecer como você está querendo sim. Qualquer operação fora da sua transação que venha a envolver o registro travado será suspensa até que ele seja liberado.

#15 DouglasDomi

DouglasDomi

    Novato no fórum

  • Usuários
  • 13 posts
  • Sexo:Não informado

Posted 09/07/2011, 16:42

Não tenho muita experiência com transações, mas até onde eu sei, é pra acontecer como você está querendo sim. Qualquer operação fora da sua transação que venha a envolver o registro travado será suspensa até que ele seja liberado.


OK LeoB. Obrigado por toda ajuda.

Você nem sabe o quando pesquisei sobre concorrencias em MySQL.

RESOLVIDO =D




1 user(s) are reading this topic

0 membro(s), 1 visitante(s) e 0 membros anônimo(s)

IPB Skin By Virteq