每次锁定的是一行数据的锁机制就是行级别锁定(row-level)。行级锁定不是MySQL本身实现的锁定方式,而是由其余存储引擎本身所实现的mysql
优势sql
缺点并发
使用行级锁定的主要有InnoDB存储引擎,以及MySQL的分布式存储引擎NDBCluster分布式
InnoDB的行级锁定一样分为两种类型:共享锁和排他锁,而在锁定机制的实现过程当中为了让行级锁定和表级锁定共存,InnoDB也一样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。性能
意向锁的做用就是当一个事务在须要获取资源锁定的时候,若是遇到本身须要的资源已经被排他锁占用的时候,该事务能够须要锁定行的表上面添加一个合适的意向锁。若是本身须要一个共享锁,那么就在表上面添加一个意向共享锁。而若是本身须要的是某行(或者某些行)上面添加一个排他锁的话,则先在表上面添加一个意向排他锁。优化
意向共享锁能够同时并存多个,可是意向排他锁同时只能有一个存在。因此,能够说InnoDB的锁定模式实际上能够分为四种:共享锁(S),排他锁(X),意向共享锁(IS)和意向排他锁(IX)spa
锁模式的兼容性:.net
InnoDB行锁是经过给索引上的索引项加锁来实现的。因此,只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁。其余注意事项:线程
隐式加锁:设计
显示加锁:
用SELECT … IN SHARE MODE得到共享锁,主要用在须要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操做。
可是若是当前事务也须要对该记录进行更新操做,则颇有可能形成死锁,对于锁定行记录后须要进行更新操做的应用,应该使用SELECT… FOR UPDATE方式得到排他锁。
InnoDB如何加表锁:
在用 LOCK TABLES对InnoDB表加锁时要注意,要将AUTOCOMMIT设为0,不然MySQL不会给表加锁;事务结束前,不要用UNLOCK TABLES释放表锁,由于UNLOCK TABLES会隐含地提交事务;COMMIT或ROLLBACK并不能释放用LOCK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁。
SET AUTOCOMMIT=0; LOCK TABLES t1 WRITE, t2 READ, ...; [do something with tables t1 and t2 here]; COMMIT; UNLOCK TABLES;
既然都用表锁了,怎么不选择MyISAM引擎呢!
nnodb的锁定规则是经过在指向数据记录的第一个索引键以前和最后一个索引键以后的空域空间上标记锁定信息而实现的。 Innodb的这种锁定实现方式被称为“ NEXT-KEY locking” (间隙锁),由于Query执行过程当中经过范围查找的话,它会锁定整个范围内全部的索引键值,即便这个键值并不存在。
例:假如emp表中只有101条记录,其empid的值分别是 1,2,…,100,101,下面的SQL:
mysql> select * from emp where empid > 100 for update;
是一个范围条件的检索,InnoDB不只会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
间隙锁有一个比较致命的弱点,就是当锁定一个范围键值以后,即便某些不存在的键值也会被无辜的锁定,而形成在锁定的时候没法插入锁定键值范围内的任何数据。在某些场景下这可能会对性能形成很大的危害
* 当Query没法利用索引的时候, Innodb会放弃使用行级别锁定而改用表级别的锁定,形成并发性能的下降; * 当Quuery使用的索引并不包含全部过滤条件的时候,数据检索使用到的索引键所指向的数据可能有部分并不属于该Query的结果集的行列,可是也会被锁定,由于间隙锁锁定的是一个范围,而不是具体的索引键; * 当Query在使用索引定位数据的时候,若是使用的索引键同样但访问的数据行不一样的时候(索引只是过滤条件的一部分),同样会被锁定
防止幻读,以知足相关隔离级别的要求。
为了数据恢复和复制的须要。
在实际应用开发中,尤为是并发插入比较多的应用,咱们要尽可能优化业务逻辑,尽可能使用相等条件来访问更新数据,避免使用范围条件。
InnoDB除了经过范围条件加锁时使用间隙锁外,若是使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁。
执行SQL:mysql> show status like 'InnoDB_row_lock%';
mysql> show status like 'InnoDB_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | InnoDB_row_lock_current_waits | 0 | | InnoDB_row_lock_time | 0 | | InnoDB_row_lock_time_avg | 0 | | InnoDB_row_lock_time_max | 0 | | InnoDB_row_lock_waits | 0 | +-------------------------------+-------+
若是发现锁争用比较严重,还能够经过设置InnoDB Monitors 来进一步观察发生锁冲突的表、数据行等,并分析锁争用的缘由。如:
设置监视器:mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
查看:mysql> show engine InnoDB status;
中止查看:mysql> drop table InnoDB_monitor;
如何发现死锁: 在InnoDB的事务管理和锁定机制中,有专门检测死锁的机制,会在系统中产生死锁以后的很短期内就检测到该死锁的存在
回滚较小的那个事务
在REPEATABLE-READ隔离级别下,若是两个线程同时对相同条件记录用SELECT…FOR UPDATE加排他锁,在没有符合该条件记录状况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条新记录,若是两个线程都这么作,就会出现死锁。这种状况下,将隔离级别改为READ COMMITTED,就可避免问题。
判断事务大小:事务各自插入、更新或者删除的数据量
当产生死锁的场景中涉及到不止InnoDB存储引擎的时候,InnoDB是没办法检测到该死锁的,这时候就只能经过锁定超时限制参数InnoDB_lock_wait_timeout来解决。
InnoDB存储引擎因为实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,可是在总体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,InnoDB的总体性能和MyISAM相比就会有比较明显的优点了。可是,InnoDB的行级锁定一样也有其脆弱的一面,当咱们使用不当的时候,可能会让InnoDB的总体性能表现不只不能比MyISAM高,甚至可能会更差。
(1)要想合理利用InnoDB的行级锁定,作到扬长避短,咱们必须作好如下工做:
(2)因为InnoDB的行级锁定和事务性,因此确定会产生死锁,下面是一些比较经常使用的减小死锁产生几率的小建议: