Fork me on GitHub

MySQL中锁详解(行锁、表锁、页锁、悲观锁、乐观锁等)

悲观锁:

  顾名思义,很悲观,就是每次拿数据的时候都认为别的线程会修改数据,所以在每次拿的时候都会给数据上锁。上锁之后,当别的线程想要拿数据时,就会阻塞,直到给数据上锁的线程将事务提交或者回滚。传统的关系型数据库里就用到了很多这种锁机制,比如行锁,表锁,共享锁,排他锁等,都是在做操作之前先上锁。 
   
行锁:

  下面演示行锁,打开两个mysql命令行界面,两个线程分别执行如下操作:(左边先执行) 
   
  image_1b8uapk3h3pk1kmmpkg1av0v759.png-174kB
   
  左边的线程,在事务中通过select for update语句给sid = 1的数据行上了锁。右边的线程此时可以使用select语句读取数据,但是如果也使用select for update语句,就会阻塞,使用update,add,delete也会阻塞。 
  当左边的线程将事务提交(或者回滚),右边的线程就会获取锁,线程不再阻塞: 
   
  image_1b8ub488p128jhm911gka0hhhm.png-201.2kB
   
  此时,右边的线程获取锁,左边的线程如果执行类似操作,也会被阻塞: 
   
  image_1b8ub6ptr1i0n1j8hg681fir1fnb13.png-236.3kB
   
表锁: 
   
  上述例子中,如果使用如下语句就是使用的表锁:

select * from student for update;

页锁: 

   
  行锁锁指定行,表锁锁整张表,页锁是折中实现,即一次锁定相邻的一组记录。 
   
共享锁: 
   
  共享锁又称为读锁,一个线程给数据加上共享锁后,其他线程只能读数据,不能修改。 
   
排他锁: 
   
  排他锁又称为写锁,和共享锁的区别在于,其他线程既不能读也不能修改。 
   
乐观锁: 
   
  乐观锁其实不会上锁。顾名思义,很乐观,它默认别的线程不会修改数据,所以不会上锁。只是在更新前去判断别的线程在此期间有没有修改数据,如果修改了,会交给业务层去处理。 
  常用的实现方式是使用版本戳,例如在一张表中添加一个整型字段version,每更新version++,比如某个时刻version=1,线程A读取了此version=1,线程B也读取了此version=1,当线程A更新数据之前,判断version仍然为1,更新成功,version++变为2,但是当线程B再提交更新时,发现version变为2了,与之前读的version=1不一致,就知道有别的线程更新了数据,这个时候就会进行业务逻辑的处理。 
   
通常情况下,写操作较少时,使用乐观锁,写操作较多时,使用悲观锁。

posted @ 2017-12-18 16:11  薄荷加冰2060  阅读(365)  评论(0编辑  收藏  举报