事先声明:本文为原创文章,禁止直接转载和抄袭,若要转载需经过本人同意。
概述
在上篇博文里讲过,锁的种类整体分为java内置锁、数据库锁和分布式锁,上篇博文里解述了 Lock的使用场景和原理,本篇博文笔者将对数据库锁机制进行详解,主要针对数据库锁类型和锁机制进行分析。
数据库锁类型
1、悲观锁
2、乐观锁
数据库锁从概念上讲分为悲观锁和乐观锁,悲观锁是数据库自身的锁机制,乐观锁是通过sql语句实现的,但是乐观锁需要依托于数据库的隔离级别为可重复读。
使用场景
数据库锁的使用场景和Lock锁类似,都是针对临界资源去使用,目的也是为了保证临界资源的安全性,只不过数据库的临界资源是数据库中的数据。
数据库锁分析
悲观锁
悲观锁是数据库锁机制,数据库锁分为表锁、行锁、间隙锁,下面一一介绍这几种锁的使用。
行锁
select * from table where id = 1 for update;
执行以上sql后就会开启行锁。在开启行锁后,当前事务未提交之前,其他事务不可以对该行数据进行操作。行锁会产生阻塞,所以比较耗费资源,粒度较大,会降低并发。
间隙锁
select * from table where id > 1and id <5 for update;
执行以上sql后就会开启间隙锁。间隙锁和行锁的不同之处就是间隙锁会占有范围内的资源,如上述sql,间隙锁会占有小于5且大于1的数据。同样,间隙所也会产生阻塞,但是其粒度比行锁更大。
表锁
lock tables table read;(读锁)
lock tables table write;(写锁)
unlock tables;(解锁)
执行以上sql任意之一就可以开启表锁,但是需要注意的是表锁分为读锁和写锁。如果执行第一行sql则会开启读锁,如果执行第二句sql则会开启写锁。读锁和写锁的区别就是,读锁不会阻塞读操作,只会阻塞写操作,而写锁则无论读操作还是写操作都会阻塞。表锁会锁住整张表,表里的数据都会产生阻塞,表锁粒度最大,并发最低。
临键锁
这里提一个临键锁的概念,数据库锁是加在索引上的,如果锁的作用字段是一个非索引字段,那么行锁可能会升级为表锁
乐观锁
乐观锁是sql的一种写法,它依赖于数据库的隔离级别为可重复读,下面做具体分析:
在使用乐观锁时,一般会有一个version字段来记录版本,在每次更新操作之前,先将version版本号查询出来,在更新数据时,以version版本号作为条件去更新,这样就可以保证当前事务所更新的数据是未被其他事务更新的,sql如下:
首先查询版本号:select version from table where id = 1;(假设查询到的version为1)
更新数据:update table set xx = xx, version = version + 1 where id = 1 and version = 1;
这就是乐观锁的sql写法,那么看到这里是否有个疑问,为什么这样写就可以实现乐观锁呢?还有上文之前提到的乐观锁依赖于数据库事务又是怎么回事呢?
分析乐观锁原理:
1、假设现有一张表table,有三个字段:id、status、version,其中version是版本字段。
2、表里有一行数据:id = 1、status = 0、version = 1。
3、现在有两个事务,事务A和事务B要对该行数据同事做出修改。
4、在修改该行数据之前,事务A先查询到version = 1,事务B也先查询到version = 1。
5、执行修改sql:
事务A:update table set status = 1,version = version +1 where id = 1 and version = 1;
事务B:update table set status = 2,version = version +1 where id = 1 and version = 1;
6、提交事务,假设A事务先于B事务提交,那么在A事务提交完事务后,version字段在表中的数据已经更新为2。而这时,B事务在A事务之后提交,因为version字段已经变为2,所以B事务更新失败。(这里B事务为什么会更新失败,就涉及到了事物的隔离级别)
以上就是乐观锁的原理。
总结
到这里,数据库锁的使用场景以及原理机制详解就讲完了,若是对文章中的一些点有疑惑,可在评论区留言或私信我。