关于MySQL InnoDB锁
Innodb中的锁
共享锁和排它锁(Shared and Exclusive Locks)
共享锁和排它锁是行级锁,有两种类型的行级锁
共享锁(s lock)允许持有锁的事务对行进行读取操作
排它锁(x lock)允许持有锁的事务对行进行更新和删除操作
事务a在行r上拥有共享锁,则其他事务可以获得r的共享锁,无法获得r的排它锁,即可读不可写
事务a在行r上拥有排它锁,则其他事务既不能获得共享锁,也不能获得排它锁,即不可读也不可写而必须等待当前事务完成
意向锁(Intention Locks)
意向锁是表级锁,用来表示将会有哪种类型的行级锁即将被使用,有两种类型的意向锁
意向共享锁(IS Locks):表明即将在表中的某一行上设置共享锁(s lock)
意向排它锁(Ix Locks): 表明即将在表中的某一行上设置排它锁(x lock)
一个事务如果要获得一个共享锁(s lock)必须首先获得一个意向共享锁(is lock),一个事务如果要获得一个排它锁(x lock)则必须先获得一个意向排它锁(ix lock)
各种锁级别的兼容性
X | IX | S | IS | |
---|---|---|---|---|
X | Conflict | Conflict | Conflict | Conflict |
IX | Conflict | Compatible | Conflict | Compatible |
S | Conflict | Conflict | Compatible | Compatible |
IS | Conflict | Compatible | Compatible | Compatible |
行级锁(Record Locks)
行级锁总是锁住索引记录(index records),即便表没有定义索引,innodb也会创建一个隐藏的聚簇索引,然后用这个索引来锁住这一行。
间隙锁(Gap Locks)
间隙锁是用于索引之间或第一个索引之前或最后一个索引之后的锁, 当语句中有一个确定的索引值的时候,间隙锁会降级为行级锁。例如这个
select * from child where id =100;
Next-Key Locks
不知道怎么翻译了。这是行级锁和间隙锁的组合,也就是说这种类型的锁会做作用于一条记录上和它之前的间隙,不允许插入。
自增锁(AUTO-INC Locks)
一个特殊的表级锁,用于带有auto-increment选项的列上,它会让你获得连续的主键值。这个锁的释放在它的sql语句执行完成之时,而非事务结束之时,这样其他的事务可以不必等到事务结束才获得自增锁。innodb_autoinc_lock_mode是用来配置这个锁的。
Predicate Locks
谓词锁(不知道是不是这个意思),作用于空间索引(spatial indexes)。
常见sql语句的锁级别
innodb默认的隔离级别是可重复读(repeatable read),默认的读取方式是一致性读(consistent read),所谓一致性读就是在你开启事务的时候,innodb会保存一个时间戳,在此之后的改变都是不可见的。由于innodb是一个多版本存储引擎(multi-versioned storage engine),它会保存改变的行之前的旧的数据以支持一些事务特性,例如并发和回滚。这些数据被存储在表空间(tablespace)的一个叫rollback segment的数据结构里。这里的数据会被用来重建之前的值来保证一致性读(consistent read)。
select ... from : 一致性读,不需要锁,除非隔离级别被设为串行化(serializable)。
select ... from ... lock in share mode :在搜索条件里的每条记录设置共享的next-key lock, 如果有特定的索引值则为行级锁。
select ... from ... for update :在搜索条件里的每条记录设置排它的next-key lock, 如果有特定的索引值则为行级锁。
update ... where... 在搜索条件里的每条记录设置排它的next-key lock, 如果有特定的索引值则为行级锁。
delete from ... where ... : 在搜索条件里的每条记录设置排它的next-key lock, 如果有特定的索引值则为行级锁。
insert 语句设置一个排它锁在插入的行上,为行级锁。当有多个会话尝试插入相同主键发生主键冲突时,可能会发生死锁,因为在发生主键冲突时他们各自获得了共享锁,而都不能获得排它锁。
insert ... on duplicate key update : 和之前的insert语句不同的是当发生主键冲突时,他们会获得排它锁而非共享锁,这样就避免了多个会话获得共享锁而发生死锁。
手动加锁
select ... from ... lock in share mode
select ... from ... for update
lock tables tb_name [AS alias {read [local] | [low_priority] write }
[,tb_name [AS alias {read [local] | [low_priority] write }...]
unlock tables;
read local允许在锁定被保持时,执行非冲突性insert语句
low_priority write锁定来允许其它线程在该线程正在等待write锁时获得read锁