一 概述
InnoDB与MyISAM有两处不同:
1)InnoDB支持事务;
2)默认采用行级锁(也可以支持表级锁)
对于更新操作(UPDATE、INSERT、DELETE),InnoDB会自动给涉及到的数据集加排他锁(X);对于普通的SELECT语句,InnoDB不加任何锁(所以即使有一个线程的写操作在占用锁,不影响其他线程的读,但是如果某个线程试图加共享锁则不行)。
InnoDB的行锁模式及加锁方法
InnoDB实现了以下两种类型的行锁。
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁;
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务获得相同数据集的共享读锁和排他写锁。
另外,为了允许行锁和表锁共存,InnoDB还有两张内部使用的意向锁,都是表锁:
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前先必须取得该表的意向共享锁;
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的意向排他锁。
上述几种锁的兼容性如下:
表20-6 InnoDB行锁模式兼容性列表
如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。
意向锁是InnoDB自动加的,不需用户干预。
对于更新操作(UPDATE、INSERT、DELETE),InnoDB会自动给涉及到的数据集加排他锁(X);对于普通的SELECT语句,InnoDB不加任何锁(所以即使有一个线程的写操作在占用锁,不影响其他线程的读,但是如果某个线程试图加共享锁则不行)。
显式的给记录集加共享锁:
共享锁: SELECT * FROM tableName WHERE …. LOCK IN SHARE MODE 排他锁: SELECT * FROM tableName WHERE …. FOR UPDATE
二、 共享锁中执行update操作容易导致死锁
注意:**用共享锁然后执行了update操作,则有可能和别的线程的update操作发生锁冲突,从而死锁。死锁后Mysql会自动关闭一个线程的事务操作,让锁被一个线程使用。**如下所示:
1)线程A和线程B对同一行记录使用了共享锁,两个线程读都没有问题(读不需要加锁,不管当前记录加了共享锁还是排他锁,都不影响单独的读操作);
2)线程A进行更新操作,因为更新操作需要加独占锁,而线程B还对当前记录保留了共享锁,故线程A无法获得当前线程的独占锁,要等待线程B释放共享锁;
3)线程B也进行了更新操作,它也要对当前记录加独占锁。那么显然它也无法获得到该记录的独占锁,两个线程都会等待下去,也就是死锁。
4)此时Mysql会自动根据一定规则把锁交给某个线程,另一个线程失去锁重新启动事务。
另外,注意,默认情况下单行执行后就会自动提交事务,此时锁也就被自动释放了。需要关闭事务的自动提交。
set autocommit = 0;
对于需要更新的操作,应当直接使用排他锁。这种情况下,因为线程A已经占有了排他锁,线程B无法获得共享锁和排他锁,只能等待。但是注意,InnoDB的读操作不需要加锁,所以可以照常的读。
当使用SELECT…FOR UPDATE加锁后再更新记录,出现如表20-8所示的情况。
表20-8 InnoDB存储引擎的排他锁例子