Hello.


> BEGIN;

> SELECT id FROM ttt WHERE id=3D7 FOR UPDATE;

> INSERT INTO ttt(id) VALUES (7);

> DELETE FROM ttt WHERE id=3D7;

> COMMIT;





Maybe we have this scenario:



A, B - transactions.



A -> SELECT ... FOR UPDATE - is setting an X lock on index for id=8

(next key locking, it is preventing the insertion of record  with id=8)



B -> INSERT ... - is trying to get a lock and waits for A to release a  

        lock

A-> INSERT ... - A already has a lock (which is wasn't released by an

        UPDATE statement). A is trying to get another one,

        but B is already in the queue





In my opinion, UPDATE statement should be run immediately after

SELECT ... FOR UPDATE. BTW, your situation is a beat easier to

reproduce with this perl script (I put it here in case somebody has a

better scenario):



[EMAIL PROTECTED] pl]$ cat deadlock.pl

#!/usr/bin/perl

use strict;

use DBI;

my ($dbh,$sql,$dsn);

$dsn =

"DBI:mysql:database=test;host=localhost;mysql_socket=/home/gleb/mysqls/tmp/mysql.sock.gleb.d";

$dbh = DBI->connect

                                                        ($dsn,

                                                        "root","",

                                                        {RaiseError => 1})

 or die "connecting : $DBI::errstr\n";



                $sql = "create table if not exists ttt(

                   id int unsigned not null

                   auto_increment primary key)";



                $dbh->do($sql);

                $sql = "begin";

                $dbh->do($sql);

                $sql = "select id from ttt where id=7 for update";

                $dbh->do($sql);

                sleep(1);

                $sql = "insert into ttt set id=7";

                $dbh->do($sql);

                sleep(1);

                $sql = "delete from  ttt where id=7";

                $dbh->do($sql);

                $sql = "commit";

                $dbh->do($sql);

$dbh->disconnect;



Run it as

for i in 1 2 3 4 5 6 7 8 9; do ./deadlock.pl & done



The snip from the output of 'SHOW INNODB STATUS':



------------------------

051007 23:09:51

*** (1) TRANSACTION:

TRANSACTION 0 1976, ACTIVE 1 sec, process no 2119, OS thread id 2768907

insertin

g

mysql tables in use 1, locked 1

LOCK WAIT 3 lock struct(s), heap size 320

MySQL thread id 160, query id 848 localhost root update

insert into ttt set id=7

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table

`test/ttt`

trx id 0 1976 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info

bits 0

 0: len 8; hex 73757072656d756d; asc supremum;;



*** (2) TRANSACTION:

TRANSACTION 0 1984, ACTIVE 1 sec, process no 2132, OS thread id 2899987

insertin

g

mysql tables in use 1, locked 1

3 lock struct(s), heap size 320

MySQL thread id 168, query id 863 localhost root update

insert into ttt set id=7

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table

`test/ttt`

trx id 0 1984 lock_mode X

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info

bits 0

 0: len 8; hex 73757072656d756d; asc supremum;;



*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 9 page no 3 n bits 72 index `PRIMARY` of table

`test/ttt`

trx id 0 1984 lock_mode X insert intention waiting

Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info

bits 0

 0: len 8; hex 73757072656d756d; asc supremum;;





Pooly wrote:

> Hi,

> 

> I had that error for quiet a long time, and I usually restart the

> transaction, but sometimes I have to do it 2 or 3 times, and I don't

> really understand how it can happen.

> I've strip down an example, that does basically :

> 

> BEGIN;

> SELECT id FROM ttt WHERE id=3D7 FOR UPDATE;

> INSERT INTO ttt(id) VALUES (7);

> DELETE FROM ttt WHERE id=3D7;

> COMMIT;

> 

> I run 10 instances of the program in parallel and I get the error :

> Deadlock found when trying to get lock; try restarting transaction.

> The isolation level is the default one.

> My understanding of the SELECT ... FOR UPDATE is that I should not get

> that deadlock, all transaction should be waiting on this select. From

> the manual :

>  A SELECT ... FOR UPDATE reads the latest available data, setting

> exclusive locks on each row it reads.

> All instances should select the latest data, or wait until the lock is rele=

> ased.

> Does anyone have pointer for a better explanations ?

> 

> Here is my program which I run in parallel with :

> for i in 1 2 3 4 5 6 7 8 9; do ./test_mysql $i & done

> 

> 

> #include "mysql/mysql.h"

> #include <stdio.h>

> 

> int main(int argc, char **argv)

> {

>         MYSQL *mysql;

>         int insert =3D0;

> 

>         my_init();

> 

>         mysql =3D mysql_init((MYSQL*)NULL);

>         if(! mysql_real_connect( mysql,

>                                 "127.0.0.1",

>                                 "root",

>                                 "",

>                                 "test",

>                                 3306,

>                                 NULL,

>                                 CLIENT_COMPRESS) ) {

>                 printf("Connexion failed.\n");

>                 mysql_close(mysql);

>         } else {

>                 int ret;

>                 printf("%s : create table\n", argv[1]);

>                 ret =3D mysql_query(mysql,

>                                 "CREATE TABLE IF NOT EXISTS ttt "

>                                 "( id integer unsigned NOT NULL AUTO_INCREM=

> ENT,"

>                                 "PRIMARY KEY(id) "

>                                 ") Engine=3DInnoDB;");

>                 if ( ret ) {

>                         printf("%s : Creation failed %s\n", argv[1],

> mysql_error(mysql));

>                         return 1;

>                 }

>                 printf("%s : Begin\n", argv[1]);

>                 ret =3D mysql_query(mysql, "BEGIN");

>                 if (ret) {

>                         printf("%s : Begin failed %s\n", argv[1],

> mysql_error(mysql));

>                         return 1;

>                 }

>                 printf("%s : Begin ok\n", argv[1]);

>                 printf("%s : Select for update\n", argv[1]);

>                 ret =3D mysql_query(mysql,

>                                 "SELECT id FROM ttt WHERE id=3D7 FOR UPDATE=

> ");

>                 if ( ret ) {

>                         printf("%s : select failed : %s\n", argv[1],

> mysql_error(mysql));

>                         return 1;

>                 } else {

>                         MYSQL_RES *res;

>                         res =3D mysql_store_result(mysql);

>                         if ( res && mysql_num_rows(res) ) {

>                                 printf("%s : found a row\n", argv[1]);

>                                 insert =3D 0;

>                         } else {

>                                 printf("%s : found no row\n", argv[1]);

>                                 insert =3D 1;

>                         }

>                         if ( res )

>                                 mysql_free_result(res);

>                 }

>                 printf("%s : Select for udate OK\n", argv[1]);

>                 printf("%s : sleep\n");

>                 sleep(1);

>                 /* should be ok to check and not fire a timeout */

>                 if (insert ) {

>                         printf("%s : insertion \n", argv[1]);

>                         ret =3D mysql_query(mysql,

>                                 "INSERT INTO ttt(id) VALUES (7)");

>                         if ( ret ) {

>                                 printf("%s : insert failed : %s\n",

> argv[1], mysql_error(mysql));

>                                 return 1;

>                         }

>                         printf("%s : delete it \n", argv[1]);

>                         ret =3D mysql_query(mysql,

>                                         "DELETE FROM ttt WHERE id=3D7");

>                         if ( ret ) {

>                                 printf("%s : delete failed : %s\n",

> argv[1], mysql_error(mysql));

>                                 return 1;

>                         }

>                 }

>                 printf("%s : commit\n", argv[1]);

>                 ret =3D mysql_query(mysql, "COMMIT");

>                 if ( ret ) {

>                         printf("%s : commit failed : %s\n", argv[1],

> mysql_error(mysql));

>                         return 1;

>                 }

>                 printf("%s : Commit ok\n", argv[1]);

> 

>         }

>         return 0;

> }

> 

> 

> 

> --

> Pooly

> Webzine Rock : http://www.w-fenec.org/

> 



-- 
For technical support contracts, goto https://order.mysql.com/?ref=ensita
This email is sponsored by Ensita.NET http://www.ensita.net/
   __  ___     ___ ____  __
  /  |/  /_ __/ __/ __ \/ /    Gleb Paharenko
 / /|_/ / // /\ \/ /_/ / /__   [EMAIL PROTECTED]
/_/  /_/\_, /___/\___\_\___/   MySQL AB / Ensita.NET
       <___/   www.mysql.com




-- 
MySQL General Mailing List
For list archives: http://lists.mysql.com/mysql
To unsubscribe:    http://lists.mysql.com/[EMAIL PROTECTED]

Reply via email to