读锁,写锁
读锁:共享锁、Shared Locks、S锁。
写锁:排他锁、Exclusive Locks、X锁。
读操作
对于普通 SELECT 语句,InnoDB 不会加任何锁
select...lock in share mode
将查找到的数据加上一个S锁,允许其他事务继续获取这些记录的S锁,不能获取这些记录的X锁(会阻塞)
select...for update
将查找到的数据加上一个X锁,不允许其他事务获取这些记录的S锁和X锁。
写操作
DELETE:删除一条数据时,先对记录加X锁,再执行删除操作。
INSERT:插入一条记录时,会先加隐隐式式锁锁来保护这条新插入的记录在本事务提交前不被别的事务访问到。
UPDATE
如果被更新的列,修改前后没有导致存储空间变化,那么会先给记录加X锁,再直接对记录进行修改。
如果被更新的列,修改前后导致存储空间发生了变化,那么会先给记录加X锁,然后将记录删掉,再Insert一条新记录。
行锁与表锁
行锁
LOCK_REC_NOT_GAP:单个行记录上的锁。
LOCK_GAP:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
LOCK_ORDINARY:锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要 目的是解决幻读的问题。
Innodb,默认会加行锁
select * from t1 where a= 1 for update;
查询使用的是主键时,只需要在主键值对应的那一个条数据加锁即可。
select * from t1 where a = 1 for update;
查询使用的是普通索引时,会对满足条件的索引记录都加上锁,同时对这些索引记录对应的聚集索引上的项也加锁。
select * from t1 where c= '1 'for update;
Innodb,默认会加行锁,查询的时候没有索引的时候,也只会对表记录加锁。在已提交级别下,会锁住满足条件的结果,在可重复读的级别下会锁住整张表
表锁
在对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,InnoDB存储引擎是不会为这个表添加表级别的S锁或者X锁的。
在对某个表执行ALTER TABLE、DROP TABLE这些DDL语句时,其他事务对这个表执行SELECT、INSERT、DELETE、UPDATE的语句会发生阻塞,或者,某个事务对某个表执行SELECT、INSERT、DELETE、UPDATE语 句时,其他事务对这个表执行DDL语句也会发生阻塞。这个过程是通过使用的元数据锁(英文名:Metadata Locks,简称MDL)来实现的,并不是使用的表级别的S锁和X锁。
- LOCK TABLES t1 READ:对表t1加表级别的S锁。
- LOCK TABLES t1 WRITE:对表t1加表级别的X锁。
IS锁,IX锁
IS锁:意向共享锁、Intention Shared Lock。当事务准备在某条记录上加S锁时,需要先在表级别加一个IS锁。
IX锁,意向排他锁、Intention Exclusive Lock。当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。
IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。
悲观锁,乐观锁
悲观锁
悲观锁用的就是数据库的行锁,认为数据库会发生并发冲突,直接上来就把数据锁住,其他事务不能修改,直至 提交了当前事务。
乐观锁
乐观锁其实是一种思想,认为不会锁定的情况下去更新数据,如果发现不对劲,才不更新(回滚)。在数据库中往往添加一个version字段来实现。