百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

互联网大厂面试系列-请简单介绍一下关于分布式锁相关?

mhr18 2024-11-25 10:45 23 浏览 0 评论

随着互联网技术的发展,对于很多应用系统来讲,单体结构已经完全不能满足互联网高并发的需求了,很多系统都在慢慢的转向分布式的发展方向,这也成为提升用户体验感的重要手段之一。因为引入了分布式系统,所以需要引入分布式锁来解决分布式应用之间的共享资源并发访问问题。那么下面我们就来看看关于分布式锁相关的内容。

分布式锁介绍

首先在传统的单体应用中,如果出现了并发访问共享资源的状况,我们可以操作Synchroinzed机制来实现锁。但是随着业务的不断增长,企业为了能够适应业务需求的发展不得不对原有的单体应用进行拆分成为分布式的应用。这种分布式部署在带来性能上的提升的同时,也带来了很多的问题,例如分布式事务、分布式锁等等。

就如之前提到的分布式应用是多个应用分别部署到不同的服务器上,并且这些应用在每台服务器上都有自己单独的JVM资源,而这些JVM资源不再是我们在单体应用中看到的那种共享资源了。为了解决在分布式环境下的共享资源线程安全问题,就出现了分布式锁的概念。

锁机制

在单体应用中,解决高并发访问的问题主要借助的就是JDK所提供的一些关键字以及支持并发操作的工具类。例如之前我们提到的Synchroinzed关键字、Lock类、RetreenLock类等等。这种访问机制就是JDK提供给单体应用的锁机制。而这些方式在解决高并发数据一致性问题上也确实起到了一定的作用,也成为了经得起考验的单体应用并发访问共享资源控制的实现方案之一。

这里我们通过一个模拟售票窗口的小例子来理解一下单体应用Java锁。

public class Ticket implements Runnable {
    //未设置票数,默认为100
    private int num = 100;

    public Ticket() {
    }
    //获取传入票数
    public Ticket(int num) {
        this.num = num;
    }
    public void run() {
        while (true) {
            //加锁,当一个进程进入时其他进程阻塞,防止数据出错
            synchronized (this) {
                try {
                    //延时1秒
                    TimeUnit.SECONDS.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (num > 0) {
                    System.out.printf("[%s] 售出一张票,剩余%d张票%n", Thread.currentThread().getName(), --num);
                } else {
                    System.out.printf("%n[%s] 票已售完,停止售票。", Thread.currentThread().getName());
                    break;
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

模拟四个站台同时买票

public class Test {

    public static void main(String[] args) {
        //传入票数
        Ticket ticket = new Ticket(20);
        //创建四个新线程并且更名
        Thread thread = new Thread(ticket, "北京西站");
        Thread thread1 = new Thread(ticket, "北京东站");
        Thread thread2 = new Thread(ticket, "北京南站");
        Thread thread3 = new Thread(ticket, "北京北站");
        //启动四个线程
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

这里我们就可以将车票看做是一个共享资源,所有的车站都在对其进行售票。而这里我们也看到了再单体应用中使用最多的锁机制Synchroinzed关键字来解决多个站台并发访问车票信息的导致超卖现象发生的问题。

加锁的关键,就是防止同一时间多个线程对共享资源进行访问修改,这个时候就会导致数据异常等问题的出现。当然这是在单体应用的架构下的实现方式,那么如果在分布式架构下这种方式是否起作用呢?

分布式锁

根据之前的分析,我们知道,无论是采用Synchroinzed关键字还是使用并发操作工具类Lock的方式控制并发线程对共享资源的访问都只是适用于单体应用或者是单一部署的应用程序。对于分布式系统来讲在一个分布式系统中存在多个应用实例,而这种方式只能锁定当前JVM中的共享资源,这就导致整个的分布式系统对于共享资源的控制就有些力不从心了。

分布式锁也是一种锁机制,只不过这种锁机制是专门为分布式环境而设计的。它并不是一种什么全新的理念,或者是某个中间件。而只是一种锁的分布式实现机制、实现方案。是用来解决在分布式环境下对于多个应用之间对共享资源访问进行安全性控制的解决方案。

在实际应用中,分布式锁需要结合具体的应用,具体的分布式系统设计来实现,才能保证将整体的系统效率发挥到最大。也就是说虽然分布式锁说起来很简单,看上去也很容易,但真要与实际的业务结合起来,部署到分布式环境下,要考虑的问题还是非常多的。

由于分布式系统底层大部分都是采用网络进行通信,众所周知的是,但凡是网络传输、通信,等场景就会有各种网络延迟,网络不稳定,甚至有的时候会出现网络中断的情况的出现。这个时候,如果分布式锁使用的不恰当,很有可能会出现死锁,或者是获取不到锁等情况。所以说分布式锁在分布式系统中的使用还是非常难落地的。

分布式锁的要求

根据上面的分析,在实现分布式锁的场景中,分布式锁应该满足如下的一些要求。

排他性

所谓的排他性就是说,在分布式场景下,对于共享的资源的控制只能是同一时间被被同一台机机器上的一个线程所访问。

避免死锁

避免死锁是指在当前服务器线程获取到锁之后,经过一段时间之后分布式锁一定要能被释放,这里的释放包括正常的业务逻辑执行成功之后的释放,还包括在整个业务逻辑执行失败之后也要能进行释放。

高可用

对于分布式锁来讲,一定要保证锁的高可用。不然就会导致系统出现严重的问题。

可重入

分布式锁最好是一把可重入的锁,因为在不同服务器的不同线程逻辑中可能会出现调用同一个业务的情况,这个时候如果不能试下重入的话就会导致业务进入到一个死循环中,无法正常执行,无法正常释放。

公平性

锁的公平性主要是要保证多服务器多线程在访问分布式锁的时候都是可以公平获取锁的,不会再因为某个线程调用次数多而它对应获取锁的操作就容易。

分布式锁的实现方式

根据以上的几点要求,在实际开发中也总结出了几种分布式锁实现方案。

基于数据库实现乐观分布式锁

主要是通过在查询、操作共享记录的时候带上一个版本标记字段,通过版本标记字段来控制对于共享数据的更新操作。有点类似于CAS。

基于数据库实现悲观分布式锁

这里以InnoDB引擎为例,可以通过在操作共享数据的记录上增加for update 关键字,标记当前数据已经被当前线程锁定,只有当当前线程执行并且提交完事务之后,才会释放对应的锁。其他线程才可以正常访问该记录。

基于Redis原子性操作实现分布式锁

主要是通过Redis的原子性操作SETNX和EXPIRE来实现。利用的就是Redis的存储结构。SETNX表示只有当KEY不存在的时候才会创建,而EXPIRE则是用来操作释放锁。

基于Zookeeper实现互斥排它锁

主要是通过Zookeeper的临时节点来维护分布式锁,保证了同一时刻只能有一个线程访问共享资源,并且也可以进行释放操作。

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: