在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被一个线程执行。在单机环境中,JAVA中其实提供很多并发处理相关的API,但是这些api在分布式场景中就无能为力了。也就是说单纯的java Api并不能提供分布式锁的能力。所以针对分布式锁的实现目前有多种方案
比较常见的有以下几种方案:
1、基于数据库实现的分布式锁
2、基于缓存实现分布式锁(常见)
3、基于zookeeper实现分布式锁(常见)
4、基于文件的分布式锁
详谈
基于数据库表
这是相对比较简单的一种方式,直接创建一张锁表,然后通过操作该表的数据来实现。当我们要锁住某个方法或者资源的时候,我们就在该表中增加一条记录,想要释放锁的时候就删除该记录。
表结构如下:
1 | create table 'methodLock' ( |
锁住方法是,执行sql:
1 | insert into methodLock(method_name,desc) values ('method_name','desc') |
对 method_name 字段做唯一性约束,如果有多个请求提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以任务操作成功的那个县城获得了该方法的锁,可以执行方法体内容。
方法执行完,释放锁,执行以下sql,
1 | delete from methodLock where method_name = 'method_name' |
弊端:
1、这把锁强依赖数据库的可用性,数据库是一个单点,,数据库一旦挂掉,导致业务系统不可用
2、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法获得该锁
3、这把只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,想再次获得所就要再次出发获得锁操作
解决:
1、可搞两个数据库,数据库之前双向同步,一旦挂掉快速切换到备库上
2、没失效时间?可以做个定时任务,每隔一定时间,清理超时数据
3、非阻塞? 可以做个while 循环,直到insert成功在返回成功。
基于数据库排他锁
我们还用刚刚创建的那张数据库表。可以通过数据库的排他锁来实现分布式锁。基于MySql的InnoDB引擎,可以使用以下方法来实现加锁操作:
1 | public boolean lock() { |
在查询语句后面添加 for update, 数据库在查询过程中给数据库表增加排它锁(这里再多提一句,InnoDB引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给method_name添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上。) 当某条记录被加上排它锁,其他线程无法再在该记录上增加排它锁
我们可以任务获得排它锁的线程既可以获得分布式锁,当获得所之后,可以执行方法的业务逻辑,执行完方法之后,再通过以下方法解锁
1 | public void unlock() { |
通过以上操作来释放锁。
比较:
相对于以上的方法,解决了无法释放锁和阻塞锁的问题
阻塞锁? for update 语句在执行成功之后立即返回,在执行失败时一直处于阻塞状态,直到成功
锁定之后服务宕机,无法释放?使用这种方式,服务宕机之后数据库会自己把锁释放掉。
注意点:
1、这里有个问题,虽然我们对 method_name 使用了唯一索引,并且显示的使用了for update 是使用行级锁。但是mysql 会对查询进行优化,几遍在条件中使用了索引字段,但是否使用索引来检索数据是由mysql 通过判断不同执行计划的代价来决定,如果mysql 认为全表扫效率更高。比如对一些很小的表,他就不会使用索引了。这种情况下 innodb 将使用表锁,而不是行锁,若果发生这种情况就悲剧了。。。对于for update 的用法下期会想讲!https://www.cnblogs.com/wangshiwen/p/9837408.html
2、当我们使用排它锁方法进行分布式锁的时候,那么一个排它锁如果长时间不提交的话,就会占用数据库连接,一旦类似的连接变多了,就可能把数据库连接池撑爆。
总结:
以上两种数据的分布式锁方式,都是依赖数据库的一张表。
通过数据库实现的分布式锁的优点:直接借助数据库,容易理解
缺点:存在各种各样的问题,需要各种方法来解决,导致整个方法变得非常复杂
操作数据库需要一定的开销,性能需要考虑。
使用数据库的行级锁的时候并不可靠,尤其当锁表并不大的时候
基于缓存实现分布式锁
相对基于数据库实现的分布式锁,基于缓存的分布式锁性能方面相对会好一些。
基于Zookeeper实现分布式锁
未完待续…