分布式锁的实现与应用

2023-10-26

为什么需要锁

在多任务环境下解决并发场景的数据竞争问题

Java常见锁

我们可以根据锁是否包含某一特性来进行分组归类

  • 从线程是否对资源加锁,可以将锁分为乐观锁和悲观锁
  • 从资源已被锁定时,线程是否阻塞,可以分为自旋锁(JUC下的atomic家族)和阻塞锁(synchronized、ReentrantLock)
  • 从多个线程并发访问资源,可以分为无锁、偏向锁、轻量级锁和重量级锁(jdk1.6开始进行锁优化)
  • 从锁的公平性进行区分,分为公平锁和非公平锁
  • 从锁是否可以重复获取可以分为可重入锁和不可重入锁
  • 从多线程能否获得同一把锁分为共享锁和排他锁

在这里插入图片描述

为什么需要分布式锁

在单机应用的环境下,所有线程运行在同一个jvm进程中,使用Java中自带的锁足以控制并发;但是在分布式场景下,多个线程运行在不同的机器(jvm进程)上,就需要分布式锁来解决问题了

什么是分布式锁

分布式锁是控制分布式系统不同进程并发访问共享资源的一种锁的实现。如果不同主机之间共享了某个临界资源(例如数据库中的数据),往往需要互斥来防止彼此干扰,以保证一致性。

作用:分布式集群中多个服务请求同一方法或者同一个业务操作(比如秒杀)的情况下,对应的业务逻辑只能被一台机器上的一个线程执行,避免出现并发安全问题。

基于数据库实现的分布式锁

利用select...for update,数据库行锁来实现悲观锁。注意:如果查询条件用了索引/主键,那么select … for update就会进行行锁;如果是普通字段(没有索引/主键),那么select … for update就会进行锁表。

悲观锁实现

获取锁方法需要声明事务,加数据行锁,事务结束则释放行锁,释放锁的操作应放在finally中

伪代码如下:

//整体流程
try {
    if(lock(keyResource)){//加锁
        process();//业务逻辑处理
     }
} finally {
    unlock(keyResource);//释放
}

//锁方法实现
//获取锁
public boolean lock(String keyResource){
    resLock = 'select * from resource_lock where key_resource = '#{keySource}' for update';
    if(resLock != null && resLock.getLockFlag == 1){
        return false;
    }
    resLock.setLockFlag(1);//上锁
    insertOrUpdate(resLock);//提交
    return true;
}

//释放锁
public void unlock(String keyResource){
    resourceLock.setLockFlag(0);//解锁
    update(resourceLock);//提交
}
乐观锁实现

基于CAS思想,在数据库表中添加version字段。

使用时,带着条件去更新(判断version)

mybatis-plus已支持自动配置

特点
  1. 由于数据库本身的性能瓶颈,基于数据库实现的分布式锁主要应用于并发不高的场景
  2. 实习方式简单,稳定可靠

基于Redis实现的分布式锁

原始方案

使用SETNX命令,SETNXSET if Not eXists (对应 Java 中的 setIfAbsent 方法),如果 key 不存在的话,才会设置 key 的值,返回1。如果 key 已经存在, SETNX 啥也不做,返回0。

expire KEY seconds设置key的过期时间,如果key已过期,将会被自动删除。

del KEY删除key

伪代码如下:

//setnx加锁
if(jedis.setnx(key,lock_value) == 1){
    //设定锁过期时间
    expire(key,10);
    try{
        //业务处理
        do();
    } catch(){
        
    }finally{
        //释放锁
        jedis.del(key);
    }
}

在这个原始方案中,setnx和expire是两个分开的操作而不是原子操作。如果执行完setnx操作后,在执行expire设置过期时间之前进程挂了,那这个锁就无法释放,其他线程也获取不到锁了。

SET拓展命令(Redis2.6.12版本之后)

使用redis拓展命令SET key value[EX seconds][PX milliseconds][NX|XX]

其中各个参数的含义如下:

  • key: 要设置的键名。
  • value: 要设置的值。
  • EX seconds: 可选参数,表示设置键的过期时间(以秒为单位)。
  • PX milliseconds: 可选参数,表示设置键的过期时间(以毫秒为单位)。
  • NX: 可选参数,表示只在键不存在时才设置值。
  • XX: 可选参数,表示只在键已经存在时才设置值。

举例如下:

  • 设置一个键值对,不带任何选项:
SET username alice

这将设置键名为 “username” 的值为 “alice”。

  • 设置一个带有过期时间的键值对:
SET session_token 123456 EX 3600

这将设置键名为 “session_token” 的值为 “123456”,并且该键将在 3600 秒(1 小时)后过期。

  • 设置一个带有过期时间的键值对(以毫秒为单位):
SET cache_key data123 PX 5000

这将设置键名为 “cache_key” 的值为 “data123”,并且该键将在 5000 毫秒(5 秒)后过期。

  • 只在键不存在时设置值:
SET order_status pending NX

如果键名 “order_status” 不存在,那么它将被设置为 “pending”。如果键名已经存在,则不进行任何操作。

  • 只在键已经存在时设置值:
SET login_attempts 3 XX

如果键名 “login_attempts” 已经存在,它的值将被设置为 “3”。如果键名不存在,则不进行任何操作。

了解完这条命令,我们就可以用它来构建分布式锁了

伪代码如下:

if(jedis.set(key,lock_value,"NX","EX",10s) == 1){
    try{
        do();
    }catch(){
  
    }finally{
        jedis.del(key);
    }
}

这种操作保证了set和expire的原子性,但是仍有其他问题:

  1. 锁过期释放了,但是业务还没有执行完(后续会提到解决方法:看门狗机制)
  2. 锁被其他线程误删(后续会提到解决方法:Lua脚本):线程1的锁过期释放后,被其他线程(线程2)获取,但是之前的线程(线程1)在执行结束后又del了锁(即释放了线程2的锁),在高并发情况下这种场景等同于没有加锁
锁误删问题

有的同学就要问了,既然锁可以被其他线程误删,那我们给他加一个唯一标识可以吗?总的来说思路上是没有问题的,但是不能简单的在Java中进行处理

如果我们在Java中进行判断,伪代码如下:

//加锁时设置一个随机id来作为标识,如果释放锁时还是这个id即证明释放了自己的锁(实际上是有逻辑错误的)
if(jedis.set(key,randomId,"NX","EX",10s) == 1){
    try{
        do();
    }catch(){
  
    }finally{
        //从redis获取randomId,如果是期望值则释放
        if(randomId.equals(jedis.get(key))){
        	jedis.del(key);
        }
    }
}

看起来好像没什么问题,但是这里又会出现不是原子操作导致的问题:如果在刚判断完randomId是期望值后,锁过期了,第二个线程创建了自己的锁,这时由于第一个线程已经通过了randomId的判断,那么它还是会释放线程二刚刚创建的锁,锁误删的问题仍然存在…

好消息是,我们还有其他解决方案。

在redis 2.6版本后,允许开发者使用Lua编写脚本来传到redis执行,这样做的好处如下:

  1. 减少网络开销:本来多次网络请求的操作,可以用一个请求完成,原先多次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延;
  2. 原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入/打断;
  3. 替代redis的事务功能:Redis的lua脚本几乎实现了常规的事务功能,支持报错回滚操作,官方推荐如果要用redis事务功能可以用redis lua脚本替代。

Redis Eval 命令基本语法如下

EVAL script numkeys key [key ...] arg [arg ...] 

#实例			   eval  引号中是脚本内容                         key的个数 key[...]  arg[...]  
127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username age jack 20
1) "username"
2) "age"
3) "jack"
4) "20"

这时候我们就可以用Lua脚本来保证操作的原子性了

lua脚本:

if redis.call('get',KEYS[1])==ARGV[1] then
    return redis.call('del',KEYS[1])
else
    return 0
end;

在redis中:

EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 key value

在Java中:

String key = "key";
String value = "value";
// 定义 Lua 脚本
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
// 执行 Lua 脚本
Object result = jedis.eval(luaScript, 1, key, value);

到这里,我们就可以解决锁误删的情况了,但是还有另外的一个问题没有解决,锁过期释放了但是业务还没有执行完怎么办?

Redisson的看门狗机制

Redisson和Jedis类似,是Java操作Redis的客户端,他在解决分布式场景问题比Jedis更加好用,提供了各种分布式对象、分布式锁、分布式同步器、分布式服务等等

Redission分布式锁的实现流程如下

Redisson实现自动续约的实现思路即源码如下:

private void renewExpiration() {
  // 获取当前锁的过期时间续约条目
  RedissonBaseLock.ExpirationEntry ee = (RedissonBaseLock.ExpirationEntry) EXPIRATION_RENEWAL_MAP.get(this.getEntryName());

  // 如果存在续约条目
  if (ee != null) {
    // 创建定时任务,定时执行续约操作
    Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
      public void run(Timeout timeout) throws Exception {
        // 获取续约条目
        RedissonBaseLock.ExpirationEntry ent = (RedissonBaseLock.ExpirationEntry) EXPIRATION_RENEWAL_MAP.get(RedissonBaseLock.this.getEntryName());

        // 如果续约条目存在
        if (ent != null) {
          Long threadId = ent.getFirstThreadId();

          // 如果存在线程ID
          if (threadId != null) {
            // 异步执行续约操作
            CompletionStage<Boolean> future = RedissonBaseLock.this.renewExpirationAsync(threadId);

            // 当异步操作完成时
            future.whenComplete((res, e) -> {
              if (e != null) {
                // 发生错误时,记录日志并移除续约条目
                RedissonBaseLock.log.error("Can't update lock " + RedissonBaseLock.this.getRawName() + " expiration", e);
                RedissonBaseLock.EXPIRATION_RENEWAL_MAP.remove(RedissonBaseLock.this.getEntryName());
              } else {
                // 如果续约成功,递归调用续约操作
                if (res) {
                  RedissonBaseLock.this.renewExpiration();
                } else {
                  // 如果无法续约,取消续约操作
                  RedissonBaseLock.this.cancelExpirationRenewal((Long)null);
                }
              }
            });
          }
        }
      }
    }, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);

    // 设置定时任务到续约条目
    ee.setTimeout(task);
  }
}

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
  // 使用 evalWriteAsync 方法执行 Lua 脚本
  // 这个脚本会检查锁是否仍然由给定的线程持有,如果是则更新锁的过期时间
  return this.evalWriteAsync(
    this.getRawName(),                       // 锁的键名
    LongCodec.INSTANCE,                      // 键的编码器
    RedisCommands.EVAL_BOOLEAN,              // 使用 EVAL 命令并返回布尔值
    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;",
    Collections.singletonList(this.getRawName()),     // 键名作为 KEYS[1]
    this.internalLockLeaseTime,              // 锁的过期时间(毫秒)
    this.getLockName(threadId)               // 获取锁的名称,用于验证锁的持有者
  );
}

总结:

  1. 只有在未指定锁超时时间时,才会使用看门狗
  2. 如果Redisson实例挂了,看门狗也会跟着crash,那么达到失效时间的这个key会被redis清除,锁也就被释放了,不会出现锁被永久占用的问题。

Redisson的RLock接口继承了JUC的lock接口,所以他是符合Java中的Lock接口规范的,同时Redisson还提供了多种分布式锁的实现类(例如:RedissonFairLock、RedissonRedLock等)可供大家选择

Redis集群数据不一致问题

在部署redis时,为了避免单点问题,我们通常会采用集群方式部署,由于redis集群的数据同步是异步操作,在主节点加锁后就会返回加锁成功;如果一个线程在master节点上拿到了锁,但是加锁的key还没同步到slave节点时master节点就发生了故障,一个slave节点就会升级成master节点,其他线程就也可以获取同个key的锁,又一次相当于没加锁

redis的作者提出了一种高级的分布式锁算法:Redlock,来解决这个问题

Redlock核心思想

多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是在与Redis单实例使用相同方法来获取和释放锁。

在这里插入图片描述

Redlock流程步骤

1、按顺序向多个master节点(如上图5个)请求加锁

2、根据设置的超时时间来判断,是不是要跳过该master节点;

3、如果有半数以上节点加锁成功(右图3个成功即可),并且使用的时间小于锁的有效期(设置单个节点超时时间),即可认定加锁成功;

4、如果获取锁失败,给所有的master节点解锁

基于ZooKeeper实现的分布式锁

这个之前已经写过博客辣,大家直接点击链接跳转即可~

基于ZooKeeper临时顺序节点的分布式锁实现_❀always❀的博客-CSDN博客

实现思路如下:

在这里插入图片描述

分布式锁实现方案比较

方案 思路 优点 缺点 典型场景
mysql 悲观锁、乐观锁 实现简单、稳定可靠 性能差,不适合高并发 分布式定时任务
redis 基于SETNX和Lua脚本保证缓存操作原子化 性能好(AP) 实现相对复杂,不是100%可靠 秒杀、抢购、大型抽奖
zookeeper 基于ZK的节点特性和Watcher机制 可靠性高(CP) 实现相对复杂,性能略差 秒杀、抢购、大型抽奖

分布式锁与高并发

从设计角度来看,分布式锁和高并发本身是矛盾的:分布式锁实际是将并行代码串行化来解决并发问题,对性能是有影响的,但是可以进行优化。

主要方案有:

  1. 锁粒度最小:尽可能地将最小粒度的有并发安全问题的代码放在锁里面,其他代码都放到锁外面去,这是锁的基本优化原则
  2. 数据分片:例如ConcurrentHashMap使用分段锁机制提高并发能力,MySQL分库分表(将压力分摊到不同DB上)等

业务场景中分布式锁的应用

  • 某事件发生后需要发短信提醒用户,且两小时之内多次发生该事件只在第一次提醒用户

    实现思路:在每次发短信之前先获取分布式锁,设定过期时间为2h,若2h内事件再次发生则无法获取到相同的分布式锁,自动跳过发送短信的流程即可

  • 保证某表中以 id+当日时间 为唯一标识的数据只有一条

    实现思路:在插入或更新时,先获取到分布式锁,成功插入后解锁

  • 抢购某总量限定的奖品

    实现思路:每个线程抢占分布式锁,抢占成功后判断剩余数量是否满足所需数量,若满足,则抢购成功并释放锁

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

分布式锁的实现与应用 的相关文章

  • 面试官让我讲讲分布式系统容错架构,结果。。。

    V xin ruyuanhadeng获得600 页原创精品文章汇总PDF 目录 TB级数据放在一台机器上 难啊 到底啥是分布式存储 啥又是分布式存储系统 某台机器宕机了咋办 Master节点如何感知到数据副本消失 如何复制副本保持足够副本数
  • RabbitMQ教程-重要参数&&API解释

    RabbitMQ的工作原理 下图是RabbitMQ的基本结构 生产者发送消息流程 1 生产者和Broker建立TCP连接 2 生产者和Broker建立通道 3 生产者通过通道消息发送给Broker 由Exchange将消息进行转发 4 Ex
  • 区块链学习笔记(六)——区块链的分类

    文章目录 一 强调 二 公有链 联盟链 私有链 1 公有链 2 联盟链 3 私有链 总结 一 强调 先做一下重复强调 区块链技术是集分布式存储 点对点传输 共识机制 加密算法 数据区块等概念于一体的新兴技术集合 二 公有链 联盟链 私有链
  • 2021春招已正式开启,阿里巴巴企业智能事业部内推,有意者看下文!

    前言 说一说已经拿到内推的两个朋友的面试经验 你们可以看一下准备一下 同事A阿里巴巴一面 55分钟 先介绍一下自己吧 说一下自己的优缺点 具体讲一下之前做过的项目 你觉得项目里给里最大的挑战是什么 Hashmap为什么不用平衡树 AQS知道
  • java脚本引擎Groovy实战

    前言 互联网时代随着业务的飞速发展 不仅产品迭代 更新的速度越来越快 个性化需求也是越来越多 如何快速的满足各种业务的个性化需求是我们要重点思考的问题 我们开发的系统如何才能做到热部署 不重启服务就能适应各种规则变化呢 实现业务和规则的解耦
  • 安装并配置HBase集群(5个节点)

    安装并配置HBase 集群规划 HBase2 2 5安装 将安装包拷贝到5台机器上并解压缩 配置环境变量 配置HBase 时间同步 修改 usr local src hbase 2 2 5 conf hbase env sh 文件 修改 h
  • Kafka 权威指南

    Kafka 权威指南 这本书于 2021 年看完 2022 年又看了一遍 感觉书读百遍 其义自现 这本书侧重于 Kafka 的理论知识 虽然书有点老 但是其中关于 Kafka 的基础知识的章节讲得确实不错 适合学习 Kafka 的新手以及
  • Springboot结合Redis实现分布式定时任务

    一 背景 之前分享过分布式定时任务的技术选型方案 分布式定时任务技术选型方案 个人青睐xxl job 分享了搭建接入流程 xxl job搭建方案 本次项目需求较为简单 同时时间紧张 下面介绍利用Redis锁实现分布式定时任务的方案 二 Sc
  • 快速部署Ceph分布式高可用集群

    快速部署Ceph分布式高可用集群 Ceph简介 Ceph是一个PB EB级别的分布式存储系统 可以提供文件存储 对象存储 和块存储 它可靠性高 易扩展 管理简便 其中对象存储和块存储可以和其他云平台集成 一个Ceph集群中有Monitor节
  • 基于一致性理论的孤岛微电网分布式控制策略研究(Simulink仿真实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 2 1 仿真搭建 2 2 优化控制
  • 基于一致性理论的孤岛微电网分布式控制策略研究(Simulink仿真实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 2 1 仿真搭建 2 2 优化控制
  • MQ - KAFKA 基础篇

    1 KAFKA的核心组件 API Producer API 它允许应用程序向一个或多个 topics 上发送消息记录 Consumer API 允许应用程序订阅一个或多个 topics 并处理为其生成的记录流 Streams API 它允许
  • RabbitMQ避免重复消费

    在Java中 可以使用消息队列来实现消息的异步处理 其中常用的消息队列有 RabbitMQ ActiveMQ Kafka 等 什么是幂等性 幂等性是指无论操作执行多少次 都是得到相同的结果 而不会产生其他副作用 在rabbitMQ中 什么是
  • 什么是自动化测试,看完你就懂了!

    随着互联网技术的飞速发展 软件本身的规模和复杂度也是逐步增加 为了保证软件项目能够保质保量交付到客户手中 软件测试环节就显得非常重要了 它可以看作是软件项目交付给客户最后一道安全保证 今天给大家聊聊软件测试当中自动化测试相关的知识 希望对大
  • GoLong的学习之路,进阶,微服务之使用,RPC包(包括源码分析)

    今天这篇是接上上篇RPC原理之后这篇是讲如何使用go本身自带的标准库RPC 这篇篇幅会比较短 重点在于上一章对的补充 文章目录 RPC包的概念 使用RPC包 服务器代码分析 如何实现的 总结 Server还提供了两个注册服务的方法
  • 【分布式算法】Gossip协议详解

    一 为什么需要 Gossip 协议 为了实现 BASE 理论中的 最终一致性原则 两阶段提交协议和 Raft 算法需要满足 大多数服务节点正常运行 原则 如果希望系统在少数服务节点正常运行的情况下 仍能对外提供稳定服务 这时就需要实现最终一
  • 各种不同语言分别整理的拿来开箱即用的8个开源免费单点登录(SSO)系统

    各种不同语言分别整理的拿来开箱即用的8个开源免费单点登录 SSO 系统 单点登录 SSO 是一个登录服务层 通过一次登录访问多个应用 使用SSO服务可以提高多系统使用的用户体验和安全性 用户不必记忆多个密码 不必多次登录浪费时间 下面推荐一
  • 终于找到了最新版的Zookeeper入门级教程,建议收藏!

    小熊学Java https javaxiaobear cn 1 分布式一致性 1 CAP 理论 CAP 理论指出对于一个分布式计算系统来说 不可能同时满足以下三点 一致性 在分布式环境中 一致性是指数据在多个副本之间是否能够保持一致的特性
  • 考虑极端天气线路脆弱性的配电网分布式电源配置优化模型【IEEE33节点】(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现
  • CAP与BASE理论

    CAP与BASE理论 CAP 一个分布式系统最多只能同时满足一致性 Consistency 可用性 Availability 和分区容错性 Partition tolerance 这三项中的两项 C一致性 状态的一致性 缓存 数据库 集群等

随机推荐

  • jdk的安装

    由于要使用jmeter进行接口自动化测试 而jmeter的使用 必要要安装jdk 因此就在网上随便找了一篇jdk的安装教程 由于我喜欢自定义安装 结果费了半天时间也没安装成功 前期后后安装与卸载不下5次 最后决得可能是jdk不能自定义安装的
  • 接口设计说明书模板

    目 录 1 引言 1 1 1 设计目标 1 1 2 适用范围 1 1 3 相关术语 1 1 4 参考资料 1 2 分析设计 1 2 1 设计思想 1 2 2 接口结构 1 2 3 数据流分析 2 3 中间库接口 2 3 1 中间库说明 2
  • java面试官如何面试别人

    java面试官如何面试别人 一 java面试官的 面试心得 在公司当技术面试官几年间 从应届生到工作十几年的应聘者都遇到过 先表达一下我自己对面试的观点 1 笔试 面试去评价一个人肯定是不够准确的 了解一个人最准确的方式就是 路遥知马力 日
  • OCR-字体颜色与背景颜色区分不明显的调研

    需解决的问题 对于一些图片 字体和颜色区分度不高 白色字体的图片进行图像增强时容易使字体更加 模型识别时存在漏检现象 图像增强后有些文字将会变得不清晰甚至消失 解决的方法 图像的二值化 以下使用了七种方法进行测试 Python代码 impo
  • shell筛选log

    服务器跑 pbs 脚本的时候 会分配一个 job ID 并相应地创建两个文件
  • anaconda和python版本的对应关系

    https docs anaconda com anaconda packages oldpkglists Anaconda与Python版本对应关系 官方最新
  • KernelSU: 内核 ROOT 方案, KernelSU KernelSU KernelSU 新的隐藏root防止检测 封号方案

    大约一年多以前 我在一篇讲Android 上 ROOT 的过去 现在和未来https mp weixin qq com s biz MjM5Njg5ODU2NA mid 2257499009 idx 1 sn 3cfce1ea7deb6e0
  • 年终总结——旧的收获与新的开始

    引言 收获本身分为俩部分 一个就是旧经历的收获 另外就是新路程的规划 概述 旧的一年已然逝去 那我们是否颗粒归仓 新的一年即将开始 我们是否规划好新的旅程 内容 颗粒归仓 从没有思考过一年的时光竟如此之短 不知不觉之间又到了颗粒归仓的瞬间
  • 爬取美女图片保存本地与入MySQL库(宅男福利)

    本文详细记录如何爬取美女图片 并将图片下载保存在本地 同时将图片url进行入库 保存在本地肯定是为了没事能拿出来养养眼啊 那入库就是为了定位图片啊 要懂点技术的话 还能搬运搬运做个小图片网站 不为别的 就是养眼和学习 本文主要讲思路和方法
  • 【Linux】用fdisk进行磁盘分区(图文过程)

    目录 0 环境 1 思路概述 2 详细介绍 2 1查看磁盘现有分区 fdisk l 2 2对分区设置具体参数 0 环境 linux 新版整理于2023 6 29 内容未变 增加了思路概述部分 排版看起来比以前好些 1 思路概述 1 查看磁盘
  • 财务数据分析?奥威BI数据可视化工具很擅长

    BI数据可视化工具通常是可以用户各行各业 用于不同主题的数据可视化分析 但面对财务数据分析这块难啃的骨头 能够好好地完成的 还真不多 接下来要介绍的这款BI数据可视化工具不仅拥有内存行列计算模型这样的智能财务指标计算功能 还拥有一套标准化
  • spark报错OutOfMemory

    最近在使用spark进行分析的时候 几千万的数据量感觉不多 但是跑起来非常慢 内存溢出OutOfMemory 1 然后在有使用map的地方 在map之前进行分区repartition 2 join会有shuffle产生 shuffle也会产
  • java-PDF读取一页某一区域内容

    首先还是我的风格 先看效果 在某一页pdf中获取某一块区域的内容和图片 这样就能获取想要的东西了 上代码 这里需要注意maven 是两部分
  • 电路交换,报文交换和分组交换的原理、区别、优缺点

    电路交换 电路交换技术是在通信两端设备间 通过一个一个交换设备中线路的连接 实际建立了一条专用的物理线路 在该连接被拆除前 这两端的设备单独占用该线路进行数据传输 电话系统就是采用了线路交换技术 通过一个一个交换机中的输入线与输出线的物理连
  • word2003脚注问题

    问题分析 在题目上插入脚注的时候 脚注放在文件结尾 然后正文拆开了 不能续前节 解决办法 word2003中 工具 gt 选项 gt 兼容性
  • Java中的byte详解

    Java中的byte详解 介绍 byte 即字节 由8位的二进制组成 在Java中 byte类型的数据是8位带符号的二进制数 在计算机中 8位带符号二进制数的取值范围是 128 127 所以在Java中 byte类型的取值范围也是 128
  • OpenGL 红宝书 反走样 雾 点参数 多边形偏移

    6 2 反走样 void glHint GLenum target GLenum hint 控制OpenGL的某些行为 参数target是要控制的行为 参数hint可以是GL FASTEST GL NICEST GL DONT CARE 效
  • 组成原理---中断

    文章目录 中断的基本概念 中断请求与判优 中断响应 中断服务与返回 中断系统是计算机中实现中断功能的软 硬件总称 一般在 CPU 中配置中断机构 在外设接口中配置中断控制器 在软件上设计相应的中断初始化程序和中断服务程序 中断的基本概念 在
  • python从入门到精通 第一节 数据类型

    Python 中的变量不需要声明 每个变量在使用前都必须赋值 变量赋值以后该变量才会被创建 在 Python 中 变量就是变量 它没有类型 我们所说的 类型 是变量所指的内存中对象的类型 等号 用来给变量赋值 等号 运算符左边是一个变量名
  • 分布式锁的实现与应用

    为什么需要锁 在多任务环境下解决并发场景的数据竞争问题 Java常见锁 我们可以根据锁是否包含某一特性来进行分组归类 从线程是否对资源加锁 可以将锁分为乐观锁和悲观锁 从资源已被锁定时 线程是否阻塞 可以分为自旋锁 JUC下的atomic家