为什么要加锁?
为了保证在并发地存取时的数据一致性。
锁有哪些分类?
加锁机制
乐观锁:先修改,保存时判断是否被更新过,应用级别。
悲观锁:先获取锁,再修改,数据库级别。
锁粒度
表级锁:开销小,加锁快,粒度大,锁冲突概率大,并发度低,适用于读多写少的情况。MyISAM存储引擎。
页级锁:bdb存储引擎。
行级锁:Innodb存储引擎,默认选项。
兼容性:
S锁:也叫读锁、共享锁,对应于 select * from users where id =1 lock in share mode
X锁:也叫写锁、排它锁、独占锁、互斥锁,对应于 select * from users where id =1 for update (记录锁、间隙锁、临键锁都是排它锁)
锁模式:
记录锁(Record Locks):封锁一个索引记录,也叫行锁,其他事务只能读被锁的记录,不能insert、update、delete。
间隙锁(Gap Locks):封锁索引记录中的间隔,或者查询到的第一条索引记录之前的范围,又或者查询到的最后一条索引记录之后的范围。
(1)多个事务可以获得同一个gap的间隙锁,间隙锁的唯一目的就是防止在间隙中insert数据。
(2)共享间隙锁与排他间隙锁没有区别。
临键锁(Next-key Locks):是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。
(1)临键锁主要是为了避免幻读。如果把事务的隔离级别降级为RC,临键锁则会失效。
意向锁(Intention Locks):简单来说是帮助更快地判断当前表加了什么样的行锁。如果另一个任务试图在该表级别上应用S或X锁,则受到由第一个任务控制的表级别意向锁的阻塞。第二个任务在锁定该表前不必检查各个页或行锁,而只需检查表上的意向锁。(两S兼容,两I兼容)
插入意向锁(Insert Intention Locks):该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待(允许并发insert)。 插入意向锁是一种间隙锁,但它不妨碍多个事务同时insert,多个插入意向锁可以兼容。但此时如果有事务要进行加间隙锁的查询,需要等待,不是因为间隙锁和插入意向锁冲突,而是因为insert操作和间隙锁是冲突的。
例1:假设已有数据4和7,有A、B线程分别要插入5、6。A、B线程先分别获得4~7这个间隙的插入意向锁,然后各自插入数据,A线程获得数据5的记录锁,B线程获得数据6的记录锁。
例2:A线程查询数据并获得4~7的间隙锁,此时B线程想插入数据6,B线程可以获得4~7的插入意向锁,但B线程需要等待A释放间隙锁,才可以insert加记录锁。
不同SQL语句加的锁(有待研究)
(1)普通select
在4个事务隔离级别中,除了在串行化(Serializable)时会加共享锁,其他的都不加锁。
因为MySQL(InnoDB)支持MVCC,对普通select语句能够进行快照读,很好地支持了读写并发。
(2)显式加锁select
select ... for update(加互斥锁)
select ... in share mode(加共享锁)
(要在事务中使用这些锁,否则不在事务中是没有意义的)
聚集索引等值查询
聚集索引上:记录锁 select * from user where id=10 for update
唯一索引(非聚集索引)等值查询
聚集索引上:记录锁
辅助索引(唯一索引)上:记录锁
非唯一索引等值查询
聚集索引:记录锁
辅助索引:临键锁(记录锁 + 间隙锁)
select * from user where name=‘b’ for update
无索引查询
表级锁
记录不存在
相应索引:间隙锁
要保证没有其他人可以插入,所以锁住间隙
(3)隐式加互斥锁update和delete
(4)普通insert
(插入区间)聚集索引:插入意向锁
(插入记录)聚集索引:记录锁
insert into child (id) values (90),(102);
(5)先select后insert
(插入区间)聚集索引:插入意向锁
(插入记录)聚集索引:记录锁
(select的源表)聚集索引:共享的临键锁。这是为了防止主从同步出问题。
insert into target_table select * from source_table ...
create target_table select * from source_table ...
#将select查询的结果集,插入到另一张表中,或者使用结果集,创建一个新表。