自动化高可用:基于数据库的分布式锁

栏目: 后端 · 发布时间: 6年前

内容简介:分布式锁的技术背景

分布式锁的机制原理

分布式锁,是控制分布式系统之间同步访问共享资源的一种方式。 在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

分布式锁的技术背景

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。在单机环境中, Java 中其实提供了很多并发处理相关的 API ,但是这些 API 在分布式场景中就无能为力了。也就是说单纯的 Java Api 并不能提供分布式锁的能力。所以针对分 布式锁的实现目前有多种方案。

其中比较常用的有以下几种方式:

1、  基于数据库实现分布式锁

2、  基于缓存(  redis memcached tair  )实现分布式锁

3、  基于 Zookeeper 实现分布式锁

本文着重介绍相对轻量级的,易于实现的基于 Mysql 数据库的分布式锁,此方案也是在自动化作业项目中经过实际应用的方案。

基于数据库表

要实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现了 当我们要锁住某个方法或资源时,我们就在该表中增加一条记录,想要释放锁的时候就删除这条记录。

创建这样一张数据库表:

自动化高可用:基于数据库的分布式锁  

当我们想要锁住某个方法时,执行以下 SQL

自动化高可用:基于数据库的分布式锁

上面这种简单的实现有以下几个问题:

1、  这把锁强依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用;

2、  这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在数据库中,其他线程无法再获得到锁;

3、  这把锁只能是非阻塞的,因为数据的 insert 操作,一旦插入失败就会直接报错。没有获得锁的线程并不会进入排队队列,要想再次获得锁就要再次触发获得锁操作;

4、  这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了;

当然,我们也可以有其他方式解决上面的问题。

数据库是单点?搞两个数据库,数据之前双向同步。一旦挂掉快速切换到备库上;

没有失效时间?只要做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍;

非阻塞的?搞一个 while 循环,直到 insert 成功再返回成功;

非重入的?在数据库表中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取锁的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查到的话,直接把锁分配给他就可以了;

基于数据库排他锁

除了可以通过增删操作数据表中的记录以外,其实还可以借助数据中自带的锁来实现分布式的锁;

我们还用刚刚创建的那张数据库表。可以通过数据库的排他锁来实现分布式锁。 基于 MySql InnoDB 引擎,可以使用以下方法来实现加锁操作:

自动化高可用:基于数据库的分布式锁  

在查询语句后面增加 for update ,数据库会在查询过程中给数据库表增加排他锁(这里再多提一句, InnoDB 引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给 method_name 添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上)当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁。

我们可以认为获得排它锁的线程即可获得分布式锁,当获取到锁之后,可以执行方法的业务逻辑,执行完方法之后,再通过以下方法解锁:

自动化高可用:基于数据库的分布式锁  

通过 connection.commit() 操作来释放锁

这种方法可以有效的解决上面提到的无法释放锁和阻塞锁的问题。

阻塞锁? for update 语句会在执行成功后立即返回,在执行失败时一直处于阻塞状态,直到成功;

锁定之后服务宕机,无法释放?使用这种方式,服务宕机之后数据库会自己把锁释放掉;

但是还是无法直接解决数据库单点和可重入问题。

这里还可能存在另外一个问题,虽然我们对 method_name   使用了唯一索引,并且显示使用 for update 来使用行级锁。但是, MySql会对查询进行优化,即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。如果发生这种情况就悲剧了

还有一个问题,就是我们要使用排他锁来进行分布式锁的 lock,那么一个排他锁长时间不提交,就会占用数据库连接。一旦类似的连接变得多了,就可能把数据库连接池撑爆。

总结一下使用数据库来实现分布式锁的方式,这两种方式都是依赖数据库的一张表,一种是通过表中的记录的存在情况确定当前是否有锁存在,另外一种是通过数据库的排他锁来实现分布式锁。

数据库实现分布式锁的优点:

直接借助数据库,容易理解。

数据库实现分布式锁的缺点:

会有各种各样的问题,在解决问题的过程中会使整个方案变得越来越复杂。

操作数据库需要一定的开销,性能问题需要考虑。

使用数据库的行级锁并不一定靠谱,尤其是当我们的锁表并不大的时候。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ajax开发精要

Ajax开发精要

柯自聪 / 电子工业出版社 / 2006 / 45.00

书籍目录: 概念篇 第1章 Ajax介绍 2 1.1 Ajax的由来 2 1.2 Ajax的定义 3 1.3 Web应用程序的解决方案 5 1.4 Ajax的工作方式 7 1.5 小结 8 第2章 B/S请求响应机制与Web开发模式 9 2.1 HTTP请求响应模型 9 2.2 B/S架构的请求响应机......一起来看看 《Ajax开发精要》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换