• 共享锁:由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写
  • 排它锁:由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁,典型是mysql事务中
start transaction;
select * from user where userId = 1 for update;
执行完这句以后
  1)当其他事务想要获取共享锁,比如事务隔离级别为SERIALIZABLE的事务,执行
  select * from user;
   将会被挂起,因为SERIALIZABLE的select语句需要获取共享锁
  2)当其他事务执行
  select * from user where userId = 1 for update;
  update user set userAge = 100 where userId = 1; 
  也会被挂起,因为for update会获取这一行数据的排它锁,需要等到前一个事务释放该排它锁才可以继续进行
锁的范围:
  • 行锁: 对某行记录加上锁
  • 表锁: 对整个表加上锁 这样组合起来就有,行级共享锁,表级共享锁,行级排他锁,表级排他锁。

行锁

行锁有3种实现算法。

记录锁

Record Lock,字面意思,行记录的锁,锁定一个记录上的索引,而不是记录本身。

比如执行语句select * from user where age=10 for update,将会锁住user表所有age=10的行记录,所有对age=10的记录的操作都会被阻塞。

间隙锁

Gap Lock,它用于锁定的索引之间的间隙,但是不会包含记录本身。 间隙锁是可重复读RR隔离级别下特有的,另外还有几种场景也会不使用间隙锁。

  • 事务隔离级别设置为不可重复读RC ,这样肯定没有间隙锁了。
  • Innodb_locks_unsafe_for_binlog设置为1。
  • 另外一种情况适用于主键索引或者唯一索引的等值查询条件,比如select * from user where id=1,id是主键索引,这样只使用Record Lock就可以了,因为能唯一锁定一条记录,所以没有必要再加间隙锁了,这是锁降级的过程。

Next-Key Lock

实际上就是相当于Record Lock+Gap Lock的组合。

pic

摘自网络

实战

SELECT … FOR UPDATE

锁住数据,等待其它事务数据被提交(Commit)后才会执行。FOR UPDATE 仅适用于InnoDB,且必须在事务区块(BEGIN/COMMIT)中才能生效。不过锁定(Lock)的数据是判别就得要注意一下了。由于InnoDB 预设是Row-Level Lock,位于索引上,所以只有「明确」的指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

更新丢失

当有两个并发执行的事务,更新同一行数据,那么有可能一个事务会把另一个事务的更新覆盖掉。 当数据库没有加任何锁操作的情况下会发生。

executeUpdate 如果只是插入一条新数据,则返回1;如果是insert on duplicate update更新已有数据,则返回2。

举例

update,delete,select for update:

  • 索引不存在:所有行加行锁,加GAP锁,无法进行任何update,insert,delete操作
  • 索引存在且唯一:指定行加行锁,无GAP锁,不影响其他行的update,delete,insert
  • 所以存在且不唯一:指定行加行锁,加GAP锁,无法insert,可delete,区间内数据无法update,区间外数据可以update