锁,很少人为操作,一般有: for update
====一堆名词,一个个分析吧============================
Innodb聚簇索引是给索引加锁
- 按照锁的粒度划分:行锁、表锁、页锁,记录锁,间隙锁,临键锁、
表锁:粒度大,简单,容易冲突,不会出现死锁,MyISAM和InnoDB都支持。DDL语句会自动加表锁,AlterTable 这种。也可以手动lock unlock
行锁:最细粒度,锁一行或者多行数据。粒度小,麻烦(一行一行加锁),不容易冲突,会出现死锁。
Innodb是聚簇索引,二级索引查询时需要回表,那就需要两把锁
下面以两条 SQL 的执行为例,讲解一下 InnoDB 对于单行数据的加锁原理。
update user set age = 10 where id = 49;//第一条 SQL 使用主键索引来查询,则只需要在 id = 49 这个主键索引上加上写锁; update user set age = 10 where name = 'Tom';//第二条 SQL 则使用二级索引来查询,则首先在 name = Tom 这个索引上加写锁,然后由于使用 InnoDB 二级索引还需再次根据主键索引查询,所以还需要在 id = 49 这个主键索引上加写锁,
也就是说使用主键索引需要加一把锁,使用二级索引需要在二级索引和主键索引上各加一把锁。
页锁:介于行和表锁之间,也会死锁
记录锁:属于行锁,但是只锁一行,必须是唯一索引。 解决脏读重复读
当 SQL 语句无法使用索引时,会进行全表扫描,这个时候 MySQL 会给整张表的所有数据行加记录锁,再由 MySQL Server 层进行过滤。但是,在 MySQL Server 层进行过滤的时候,如果发现不满足 WHERE 条件,会释放对应记录的锁。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。
所以更新操作必须要根据索引进行操作,没有索引时,不仅会消耗大量的锁资源,增加数据库的开销,还会极大的降低了数据库的并发性能。
selct * from user where id=1 for update 命中一条数据,自动触发记录锁
间隙锁:属于行锁,锁区间的,左开右闭
selct * from user where id>5 and id
其他事务,无法操作5-9区间的数据,被间隙锁锁住了
问题:selct * from user where id>15 for update 会怎么加锁,updaete 14的时候会成功吗,不会。
正常人理解,
其实:因为间隙锁是根据现有数据划分的锁,11到无穷大,大于11都是需要锁的哦。
临建锁(Next-Key-Lock):属于行锁,Innodb默认的,是间隙锁和记录锁的结合
selct * from user where id>5 and id
- 按照锁的属性:共享锁、排它锁(悲观锁的一种实现)
Innodb会自动在delete/update/insert加上锁,select* from a for update就是排他锁
共享锁(属于行锁):读锁,让别人只能读,不能改
排他锁(属于行锁):写锁,不能与其他锁共存
- 锁的状态分类:意向共享锁,意向排他锁
意向锁属于(表锁)
意向锁是记录行锁和表锁的。比如像alert table的时候,需要看看表里面有表锁和行锁没,就用意向锁来记录。
监控意向锁就可以知道,表中到底有没有锁了
由于表锁和行锁虽然锁定范围不同,但是会相互冲突。所以当你要加表锁时,势必要先遍历该表的所有记录,判断是否加有排他锁。这种遍历检查的方式显然是一种低效的方式,MySQL 引入了意向锁,来检测表锁和行锁的冲突。
意向锁也是表级锁,也可分为读意向锁(IS 锁)和写意向锁(IX 锁)。当事务要在记录上加上读锁或写锁时,要首先在表上加上意向锁。这样判断表中是否有记录加锁就很简单了,只要看下表上是否有意向锁就行了。
意向锁之间是不会产生冲突的,也不和 AUTO_INC 表锁冲突,它只会阻塞表级读锁或表级写锁,另外,意向锁也不会和行锁冲突,行锁只会和行锁冲突。
意向锁是InnoDB自动加的,不需要用户干预。**对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);
对于一般的Select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。