今天聊聊分布式锁 No.86

2024-05-15 08:38
文章标签 分布式 聊聊 今天 no.86

本文主要是介绍今天聊聊分布式锁 No.86,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先祝大家新年快乐,感谢大家过去一年的陪伴。大蕉在这里给大家拜年啦啦。新年快乐,我爱学习。



恭喜发财,红包拿来~

640?wx_fmt=jpeg





好了切入正题,一直在工作中会聊到很多锁的问题,今天跟大家一起闲聊一下,究竟什么是锁,为什么需要锁,以及分布式的情况下,怎么设计和实现锁。






什么是锁?


明·魏禧《大铁椎传》上是这样解释的:


锁:置于可启闭的器物上,以钥匙或暗码(如字码机构、时间机构、自动释放开关、磁性螺线管等)打开的扣件,例如:柄铁折叠环复,如锁上练,引之长丈许。


锁,就是要对一个可启闭的东西上,拥有者拥有着钥匙或者某些 Code , 用于打开的扣件。那么锁为什么要产生?为什么要用锁来将那个东西给加上锁,以便达到只有拥有者可以操作的效果呢?


历史上来看,锁几乎与私有制同时诞生。早在公元前3000年的中国仰韶文化遗址中,就留存有装在木结构框架建筑上的木锁。东汉时,中国铁制三簧锁的技术已具有相当高的水平。三簧锁前后沿用了1000多年。





那么在互联网,在软件中的锁是什么定义呢?在我看来,锁就是保证多线程在竞态条件下对共享资源操作的一致性。


怎么理解?


如果没有共享资源,那么锁并没有任何作用,每个业务每个线程都拥有自己的独占的资源,那么锁也就没有用武之地了。这些资源,任何其他业务其他线程都访问不了,那么这些资源对于本业务来说就是私有的,也就不需要加锁了。


那什么叫竞态条件呢?百科里是这样解释的:


竞态条件(race condition),从多进程间通信的角度来讲,是指两个或多个进程对共享的数据进行读或写的操作时,最终的结果取决于这些进程的执行顺序。


我们可以抓住三个关键字,多进程、共享数据、执行顺序。如果并没有多进程多线程,那么并不需要锁,因为不可能会出现竞态。如果所有的操作都是有序的,那么也不需要锁,因为顺序操作只要每个操作都是原子性的,那么基本不可能会出现竞态。


所以,锁的出现,是为了保证多线程在竞态条件下对共享资源操作的一致性。






经典传统应用环境下锁的使用机制是怎么样的?我们都知道数据库有很多种锁。乐观锁,悲观锁,排他锁,行锁,表锁... 诸如此类的定义。我们这里只稍微看乐观锁和悲观锁。


乐观锁:很乐观。认为大家的数据操作都是很守规矩的不会乱来,所以只在修改操作的时候会加锁。


悲观锁:很悲观。认为大家的数据操作都是不可估计而且可能带来严重影响的,所以在整个操作过程都会进行加锁。


当然,各种设计可能在这个层次之上会加上意向锁,意思就是你要获得乐观锁之前,要先获得意见乐观锁,意向排他锁与此类似。

也可能很变态,带上意向的意向锁。就好像,预约一下去预约去预约买车牌的预约。




传统的应用因为都是单机的,所以可以单起一个线程单独控制所有的操作即可,对数据进行锁定。很多的数据库都实现了相应的锁机制。


例如 Oracle ,根据保护的对象不同,Oracle实现的数据库锁可以分为以下几大类。    


1、DML锁(data locks,数据锁),用于保护数据的完整性。

2、DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义。

3、内部锁和闩(internal locks and latches),保护数据库的内部结构。



例如 MySQL ,不同的引擎支持的锁类型是不一样的,下面的表格可以一探究竟。至于乐观、悲观、意向乐观、意向悲观这些的设计跟 Oracle 如出一辙。



行锁表锁页锁
MyISAM


BDB


InnoDB






但是慢慢的,很多软件都运行在分布式的环境下,具体的套路可以看看我之前的文章。分布式架构的套路No.74


那为什么需要在分布式环境下使用锁呢?传统的应用在单机的情况下直接用一个统一的线程进行管控就可以了,但是在分布式环境下情况又不一样了。如果每个人都只持有自己的锁,对于其他人不可见,并不是全局唯一的锁,这样的锁是没有意义的。所以也就会有了分布式架构下的锁。


分布式锁有两层含义。第一层是在分布式的系统中用锁来保证业务的正确性。另外一层是用分布式的服务来保证锁的高可用性。




在分布式的环境中,分布式锁的实现方式大概有下面这么几种。


  1. 数据库。

  2. 缓存。

  3. 分布式一致性系统。


下面我们一一来聊他们的设计和实现。


数据库


现在 MySQL 和很多数据库都实现了分布式,但是也可以使用 MySQL 自己来实现分布式锁,实现方式是这样的。


1、在分布式操作之前,对数据库的定义了唯一键的表中插入一条数据。

2、操作之后,将这条数据删除掉。

3、启动一个定时 Job ,对已经过时的锁进行删除。


这样就能实现一个基于数据库的排他锁了。


缓存系统


缓存系统在实现的时候跟数据库的模式差不多,但是因为数据都是在缓存中,所以加锁和解锁都会比数据库快很多。


下面举例看看基于 Redis 的分布式锁实现。Redis 的分布式锁都是基于一个命令 -- SETNX,也就是 SET IF NOT EXIST,如果不存在就写入。从 Redis 2.6.12 版本开始,Redis 的 SET 命令直接直接设置 NX 和 EX 属性,NX 即附带了 SETNX 数据,key 存在就无法插入,EX 是过期属性,可以设置过期时间。这样一个命令就能原子的完成加锁和设置过期时间。



代码在这,自己看看吧。


pom文件是这样。


<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
   <version>2.9.0</version>
   <type>jar</type>
   <scope>compile</scope>
</dependency>


实现的代码是这样的。我就不讲解了,Code will talk 。


package lock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import utils.Printer;

import java.util.Collections;
import java.util.ResourceBundle;

/**
* @Author  大蕉
* @Since   2018.02.13
* @desc    基于redis的分布式锁实现
*/


public class RedisManager {
public static JedisPool jedisPool;
  private static final String LOCK_SUCCESS = "OK";
  private static final String SET_IF_NOT_EXIST = "NX";
  private static final String SET_WITH_EXPIRE_TIME = "PX";
  private static final Long RELEASE_SUCCESS = 1L;


  /**
   *
   * 过期时间设置
   * EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
   * PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
   *
   * 执行条件设置
   * NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
   * XX :只在键已经存在时,才对键进行设置操作。
   */


  static {

//读取相关的配置
     ResourceBundle resourceBundle = ResourceBundle.getBundle("redis");
     int maxActive = Integer.parseInt(resourceBundle.getString("redis.pool.maxActive"));
     int maxIdle = Integer.parseInt(resourceBundle.getString("redis.pool.maxIdle"));
     int maxWait = Integer.parseInt(resourceBundle.getString("redis.pool.maxWait"));

     String ip = resourceBundle.getString("redis.ip");
     int port = Integer.parseInt(resourceBundle.getString("redis.port"));

     JedisPoolConfig config = new JedisPoolConfig();
     //设置最大连接数
     config.setMaxTotal(maxActive);
     //设置最大空闲数
     config.setMaxIdle(maxIdle);
     //设置超时时间
     config.setMaxWaitMillis(maxWait);
     //初始化连接池
     jedisPool = new JedisPool(config, ip, port);
  }


public static boolean tryLock(String key,String value,int expireSecond){
Jedis jedis = jedisPool.getResource();
     if(jedis == null){
return false;
     }

String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireSecond);

     if (LOCK_SUCCESS.equals(result)) {
return true;
     }
return false;

  }

public static boolean releaseDistributedLock(String key,String value) {

Jedis jedis = jedisPool.getResource();
     if(jedis == null){
return false;
     }

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
     Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));

     if (RELEASE_SUCCESS.equals(result)) {
return true;
     }
return false;

  }


public static void main(String[] args){
Printer.println(tryLock("A","B",100));
     Printer.println(releaseDistributedLock("A","B"));
  }
}





除此之外,Redis 的作者还实现了一个分布式锁算法,叫Redlock,有兴趣的朋友自己 Google 。感兴趣的朋友多的话,我后面再聊聊这个算法的由来和因缘。




那么基于 Tair 的分布式锁是怎么实现的呢?


Tair 多了一个版本的概念,所以另外一种实现思路是用版本来控制锁。加锁的时候写一个默认的版本号,那么如果两次写入都指定了同一个版本的话,服务端会直接报错导致加锁失败。



分布式一致性系统


分布式一致性的系统,现在最流行的应该局势 Zookeeper 了。Zookeeper 实现了类 Paxos 的设计。用 Zookeeper 是使用新增子节点的模式来进行加锁。


比如 B 要对数据 A 进行加锁,可以这样操作。  create /locks/A "B"


其他节点在对 Zookeeper 进行加锁的时候,因为目录已经存在,会直接报错。解锁的时候直接    delete /locks/A  ,这样就好了。



Zookeeper 是怎么实现分布式一致性的呢?最最主要的设计就是 Zookeeper 实现了 leader 选举制以及 follower 转发制。follower 在接收到请求的时候,会直接转发给 leader,由 leader 进行数据的统一处理。




好了,今天的分布式锁就聊到这里啦,大家有什么想聊的可以留言评论告诉我,平时也会分享一些小玩意小感想,一起到我的小蜜圈来玩耍吧。新年快乐么么哒 ? 。



640?wx_fmt=jpeg



这篇关于今天聊聊分布式锁 No.86的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991345

相关文章

集中式版本控制与分布式版本控制——Git 学习笔记01

什么是版本控制 如果你用 Microsoft Word 写过东西,那你八成会有这样的经历: 想删除一段文字,又怕将来这段文字有用,怎么办呢?有一个办法,先把当前文件“另存为”一个文件,然后继续改,改到某个程度,再“另存为”一个文件。就这样改着、存着……最后你的 Word 文档变成了这样: 过了几天,你想找回被删除的文字,但是已经记不清保存在哪个文件了,只能挨个去找。真麻烦,眼睛都花了。看

开源分布式数据库中间件

转自:https://www.csdn.net/article/2015-07-16/2825228 MyCat:开源分布式数据库中间件 为什么需要MyCat? 虽然云计算时代,传统数据库存在着先天性的弊端,但是NoSQL数据库又无法将其替代。如果传统数据易于扩展,可切分,就可以避免单机(单库)的性能缺陷。 MyCat的目标就是:低成本地将现有的单机数据库和应用平滑迁移到“云”端

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl

基于MySQL实现的分布式锁

概述 在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行加锁,当使用完资源之后,再解锁,其它线程就可以接着使用了。例如,在JAVA中,甚至专门提供了一些处理锁机制的一些API(synchronize/Lock等)。 但是到了分布式系统的时代,这种

Kafka 分布式消息系统详细介绍

Kafka 分布式消息系统 一、Kafka 概述1.1 Kafka 定义1.2 Kafka 设计目标1.3 Kafka 特点 二、Kafka 架构设计2.1 基本架构2.2 Topic 和 Partition2.3 消费者和消费者组2.4 Replica 副本 三、Kafka 分布式集群搭建3.1 下载解压3.1.1 上传解压 3.2 修改 Kafka 配置文件3.2.1 修改zookeep

聊聊说话的习惯

1 在日常生活中,每个人都有固定的说话习惯。心理学研究表明,通过一个人的说话习惯,也可以分析出他的性格特点。对于每一个人来讲,说话习惯已经融为他们生活中的一部分。在社交活动中,一些不良的说话习惯很可能会给他们带来麻烦。因此,了解说话习惯对心理活动的影响是十分有必要的。 2 具有顺畅的说话习惯的人,大多思路清晰、语速适中、用词准确并且声声人耳,是典型的顺畅型说话方式这种类型的人要么不说话,要么

Spring Cloud整合Seata实现分布式事务

文章目录 1.Seata1.1 官网1.2 下载1.3 通过安装包运行seata1.3.1 解压seata-server-1.3.0.zip1.3.2 修改 conf/file.conf 配置文件1.3.3 修改conf/registry.conf配置文件1.3.4 添加seata配置信息到nacos1.3.5 配置seata服务端数据库表结构1.3.6 启动seata 2.Spring

ELK+Spring Cloud搭建分布式日志中心

ELK+Spring Cloud搭建分布式日志中心 1.ELK简介2.资源包下载3.Elasticsearch安装3.1 解压Elasticsearch3.2 修改Elasticsearch的配置文件3.3 修改系统配置3.4 启动Elasticsearch 4.ElasticSearch-head插件安装5.Logstash安装6.Kibana安装7.SpringCloud集成logsta

Redis进阶(七):分布式锁

在分布式系统下,涉及到多个节点访问同一个公共资源的情况,此时需要通过 锁 进行互斥控制:避免出现 线程安全问题。 1.分布式锁的基本实现 超卖问题: 解决: 采用redis实现分布式锁 可用采取:在购票的时候,操作过程中需要先加锁。在redis上设置一个key - value,完成上述买票操作,再把key - value 删掉。如果发现key - value 存在,就加锁失败,无法进

聊聊分布式,再讨论分布式解决方案

前言 最近很久没有写博客了,一方面是因为公司事情最近比较忙,另外一方面是因为在进行 CAP 的下一阶段的开发工作,不过目前已经告一段落了。 接下来还是开始我们今天的话题,说说分布式事务,或者说是我眼中的分布式事务,因为每个人可能对其的理解都不一样。 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免,本文就分布式事