面试系列 - Redis分布式锁3种实现方式对比分析

Redis实战—分布式锁(setnx、redisson) 本文详细介绍了Redis分布式锁的实现,同时对 “分布式锁误删” 与 “分布式锁原子性” 问题进行了讨论和解决。 阅读详情

目录

一、SETNX命令的方式:

二、SETNX实现的分布式锁 

三、Redlock分布式锁


在分布式系统中,实现分布式锁是一个常见的问题,Redis 是一个常用的工具,用于实现分布式锁。

一、SETNX命令的方式:

  • 实现原理: 该方式使用 Redis 的 SETNX 命令(SET if Not eXists)来尝试设置一个锁键。如果该键不存在,它会被设置,并且返回成功。如果键已存在,表示锁已被其他客户端持有,则获取锁失败。

  • 优点: 实现简单,易于理解。适用于基本的锁需求。

  • 缺点: 存在死锁风险,如果持有锁的客户端发生崩溃或其他故障,锁可能会一直被占用。没有续约机制,可能导致锁超时。适用于单节点 Redis 环境。

SETNX 是 Redis 中的一个命令,用于设置一个键的值,但仅当该键不存在时。你可以使用 SETNX 命令来实现基本的分布式锁。

返回值:

  • 1 设置key成功

  • 0 设置key失败

以下是使用 Java 和 Jedis 客户端,演示如何使用 SETNX 来实现分布式锁:

import redis.clients.jedis.Jedis;

public class DistributedLock {

    private static final String LOCK_KEY = "mylock";
    private static final int LOCK_EXPIRE = 10000; // 锁的过期时间(毫秒)
    private static final int ACQUIRE_TIMEOUT = 2000; // 获取锁的超时时间(毫秒)

    public static boolean acquireLock(Jedis jedis, String lockValue) {
        long startTime = System.currentTimeMillis();
        try {
            while (System.currentTimeMillis() - startTime < ACQUIRE_TIMEOUT) {
                Long result = jedis.setnx(LOCK_KEY, lockValue); // 尝试获取锁
                if (result == 1) {
                    // 锁获取成功
                    jedis.pexpire(LOCK_KEY, LOCK_EXPIRE); // 设置锁的过期时间
                    return true;
                } else {
                    // 锁获取失败,等待一段时间后重试
                    Thread.sleep(50); // 等待一段时间后重试
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false; // 获取锁失败
    }

    public static void releaseLock(Jedis jedis, String lockValue) {
        try {
            String currentValue = jedis.get(LOCK_KEY);
            if (currentValue != null && currentValue.equals(lockValue)) {
                jedis.del(LOCK_KEY); // 释放锁
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String lockValue = "unique-lock-value";

        if (acquireLock(jedis, lockValue)) {
            // 获取锁成功,执行业务逻辑
            System.out.println("Lock acquired. Executing business logic...");

            // 模拟业务逻辑执行
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            releaseLock(jedis, lockValue); // 释放锁
            System.out.println("Lock released.");
        } else {
            System.out.println("Failed to acquire lock.");
        }

        jedis.close();
    }
}

在上面的示例中,acquireLock 方法尝试使用 SETNX 命令来获取锁,如果获取成功,它将设置锁的过期时间,然后返回 true。如果获取锁失败,它会等待一段时间后重试,直到达到获取锁的超时时间为止。

releaseLock 方法用于释放锁,它会首先检查当前锁是否属于当前客户端,然后删除锁。

二、SETNX实现的分布式锁 

  • 实现原理: 与 SETNX 不同,该方式在设置锁键时加入了过期时间。通过 SET 命令设置锁键,并为其设置一个过期时间。如果持有锁的客户端在指定时间内未续约锁,锁会自动过期释放。

  • 优点: 有自动过期机制,降低死锁风险。适用于单节点 Redis 环境。

  • 缺点: 仍然存在死锁风险,例如客户端在获取锁后崩溃,未能续约锁,锁可能会过早释放。不适用于多节点 Redis 环境。

以下是使用 Java 和 Jedis 客户端来实现基于 SETNX 的分布式锁的示例:

import redis.clients.jedis.Jedis;

public class DistributedLock {
    private static final String LOCK_KEY = "mylock";
    private static final String LOCK_VALUE = "locked";
    private static final int LOCK_EXPIRE_TIME = 10000; // 锁的过期时间,单位毫秒

    public static boolean acquireLock(Jedis jedis) {
        // 使用 SETNX 命令尝试获取锁
        Long result = jedis.setnx(LOCK_KEY, LOCK_VALUE);
        if (result == 1) {
            // 获取锁成功,设置锁的过期时间
            jedis.pexpire(LOCK_KEY, LOCK_EXPIRE_TIME);
            return true;
        }
        // 获取锁失败
        return false;
    }

    public static void releaseLock(Jedis jedis) {
        // 释放锁
        jedis.del(LOCK_KEY);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        try {
            if (acquireLock(jedis)) {
                // 获取锁成功,执行业务逻辑
                System.out.println("Lock acquired. Executing business logic...");
                Thread.sleep(5000); // 模拟业务逻辑执行
            } else {
                System.out.println("Failed to acquire lock.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            releaseLock(jedis);
            System.out.println("Lock released.");
            jedis.close();
        }
    }
}

在这个示例中,acquireLock 方法使用 setnx 命令尝试获取锁,如果返回值是 1,表示获取锁成功,然后设置锁的过期时间(通过 pexpire 命令)。releaseLock 方法用于释放锁,它使用 del 命令来删除锁键。

三、Redlock分布式锁

Redlock 是一个用于实现分布式锁的算法,由 Redis 的作者 Antirez 在一篇博客中提出。它的目标是提供高可用性和分布式环境下的锁定机制,以防止多个客户端同时访问共享资源。Redlock 是一种基于多个 Redis 节点的互斥锁算法。

  • 实现原理:

  1. 多个 Redis 节点: Redlock 需要至少 N 个 Redis 节点来工作。这些节点可以分布在不同的机器上,以确保高可用性。

  2. 锁的获取: 客户端尝试在 N 个 Redis 节点上获取锁,使用 SETNX 命令(SET if Not eXists)。

  3. 设置过期时间: 为了避免死锁,锁必须设置一个过期时间,确保锁在一段时间后自动释放。

  4. 成功获取锁: 如果客户端成功在至少 N/2 + 1 个节点上获取锁,并且锁设置了相同的唯一标识,那么锁被视为成功获取。

  5. 续约锁: 客户端可以定期续约锁,即在锁过期前延长锁的过期时间。这有助于防止锁过早释放。

  6. 释放锁: 客户端可以释放锁,通过删除在至少 N/2 + 1 个节点上设置的锁

  • 优点: 提供更高的锁可用性,适用于多节点 Redis 环境。具备容错性,可以应对 Redis 节点故障。

  • 缺点: 实现相对复杂,引入了网络通信的延迟。需要精心配置和调整以确保性能和可用性。可能需要较长的锁定和续约时间,导致锁等待时间较长。

简单的 Java 示例,演示如何使用 Jedis 客户端库来实现 Redlock 分布式锁:

import redis.clients.jedis.Jedis;

public class RedlockDemo {
    private static final String LOCK_KEY = "mylock";
    private static final int LOCK_EXPIRE_TIME = 10000; // 锁的过期时间,单位毫秒

    public static void main(String[] args) {
        Jedis jedis1 = new Jedis("redis-node-1", 6379);
        Jedis jedis2 = new Jedis("redis-node-2", 6379);
        Jedis jedis3 = new Jedis("redis-node-3", 6379);

        Redlock redlock = new Redlock(jedis1, jedis2, jedis3);

        try {
            Redlock.Lock lock = redlock.lock(LOCK_KEY, LOCK_EXPIRE_TIME);
            if (lock != null) {
                // 获取锁成功,执行业务逻辑
                System.out.println("Lock acquired. Executing business logic...");
                Thread.sleep(5000); // 模拟业务逻辑执行
            } else {
                System.out.println("Failed to acquire lock.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redlock.unlock(LOCK_KEY);
            System.out.println("Lock released.");
            jedis1.close();
            jedis2.close();
            jedis3.close();
        }
    }
}

这个示例中,我们使用 Jedis 客户端创建了三个 Redis 节点,并使用 Redlock 来获取和释放分布式锁。请注意,Redlock 是一个相对复杂的算法,实际使用中需要更多的容错和错误处理机制。此外,确保你的 Redis 节点是高可用的,以减少故障的影响。

redis分布式锁的原理与实现【分布式】 分布式锁是指在分布式系统中,为了实现协调和同步访问共享资源,而对分布式环境下的多个进程或线程进行同步的一种机制。它可以保证在分布式环境下各进程访问共享资源的时序一致性和互斥性,避免不同进程之间发生冲突。基于数据库的分布式锁:使用数据库的事务机制来实现分布式锁,通过在数据库插入一个唯一的记录来实现锁定,其他进程尝试并发获取锁时会阻塞等待。基于缓存的分布式锁。 阅读详情

相关推荐

说说Redis分布式锁是如何实现的。含Redis面试专题及答案

前言 近来,分布式的问题被广泛提及,比如分布式事务、分布式框架、ZooKeeper、SpringCloud等等。本文先回顾锁的概念,再介绍分布式锁,以及如何用Redis来实现分布式锁。 一、锁的基本了解 首先,回顾一下我们工作学习中的锁的概念。 为什么要先讲锁再讲分布式锁呢? 我们都清楚,锁的作用是要解决多线程对共享资源的访问而产生的线程安全问题,而在平时生活中用到锁的情况其实并不多,可能有些朋友对锁的概念和一些基本的使用不是很清楚,所以我们先看锁,再深入介绍分布式锁。 通过一个卖票的小案例来看

m0_67322837的博客 1878

redis的setNx方法

使用redis解决多线程问题

weixin_42855525的博客 4万+

【五斗米】实现分布式锁的三种方式

在单击环境下,就是只有当前JVM虚拟机A中的多个线程对同一个Object(共享资源)进行操作,可以采用同步互斥(就是只有同一时间只有一个线程对共享资源进行事务操作)的方式,我们常用的方式就是采用加锁的方式解决,虽然不能保证数据的强一致性,但是能够保证数据的最终一致性。........................

m0_64210833的博客 9576

SpringCloud 分布式锁的多种实现

面试必备:聊聊分布式锁的多种实现! 文章目录面试必备:聊聊分布式锁的多种实现!前言1. 分布式锁概述2. 基于数据库的分布式锁2.1 数据库悲观锁实现的分布式锁2.2 数据库乐观锁实现的分布式锁3.基于Redis实现的分布式锁3.1 setnx + expire3.2 setnx + value值是过期时间3.3 set的扩展命令(set ex px nx)3.4 set ex px nx + 校验唯一随机值,再删除3.5 Redisson3.6 Redisson + RedLock4. Zookeepe

猫头虎技术团队:授渔优于赠鱼,兴趣引领智慧,探索之乐尤显珍贵。商务合作+:Libin9iOak ,万粉变现+:优快云WF,猫头虎承诺每年免费为100名C站创作者做账号流量诊断服务!全网搜:猫头虎技术团队,点击文章底部名片或直接私信我一切皆可谈,快找虎哥! 5748

Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)

Redisson是一个基于Redis的Java驻内存数据网格(In-Memory Data Grid)和分布式锁服务的框架,提供了丰富的分布式对象和服务支持。它封装了Redis的分布式对象和服务,简化了Java应用程序对Redis的操作。Redisson是一个基于Redis的Java驻内存数据网格(In-Memory Data Grid)和分布式锁服务的框架,提供了丰富的分布式对象和服务支持。它封装了Redis的分布式对象和服务,简化了Java应用程序对Redis的操作。

私聊前往站内信:https://i.youkuaiyun.com/#/msg/chat/weixin_44976692 2万+

分享一些基于微信小程序开发的小游戏源码

小游戏小程序是运行在微信生态系统(或其他支持小程序的平台)中的轻量级应用程序,专为休闲游戏体验而设计的小程序子类别,小游戏小程序通常体积较小,加载速度快,用户无需等待长时间即可开始游戏。用户无需下载或安装单独的游戏应用程序,即可通过小程序入口直接进入游戏,同时小游戏小程序通常包含排行榜、分享功能等社交元素,可以增强游戏体验并促进用户参与。game.js:游戏页面的逻辑文件,包含页面数据、生命周期函数、事件处理函数等。game.wxml:游戏页面的结构文件,定义了页面的布局和组件。

xinshangdian的博客 6226

redis分布式锁原理及实现

一、写在前面 现在面试,一般都会聊聊分布式系统这块的东西。通常面试官都会从服务框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事务、分布式锁、ZooKeeper等知识。 所以咱们这篇文章就来聊聊分布式锁这块知识,具体的来看看Redis分布式锁的实现原理。 说实话,如果在公司里落地生产环境用分布式锁的时候,一定是会用开源类库的,比如Redis分布式锁,一般就是...

shuangyueliao的专栏 15万+

Qt Coordinate System

Qt 坐标系统Qt 坐标系统 数学基础 Qt坐标系统 视口 平移变换 缩放变换 setWindow 旋转 总结数学基础 矩阵与线性变换坐标系变换Qt坐标系统 视口和OpenGL一样,视口确定了画面在窗口(QWidget)中显示的区域,Qt的绘图坐标会完全映射到视口(View port)上,就像图示一样。默认情况下,窗口和视口是重合的,视口的大小覆盖了整个窗口。和OpenGL一样,Qt的QPa

unclerunning的博客 1151

Java面试-分布式锁的实现方式:Redis、ZooKeeper、数据库

《Java分布式锁实战:Redis、ZooKeeper与数据库对比》摘要: 本文系统解析分布式锁三大实现方案:1)Redis锁基于SETNX原子操作,推荐使用Redisson解决自动续期与主从问题;2)ZooKeeper锁依赖临时顺序节点实现强一致性,需注意性能开销;3)数据库锁通过唯一索引或SELECT FOR UPDATE保证互斥,适合简单场景。文章对比了三者优缺点,提供Java代码示例,并针对秒杀等典型场景给出选型建议,最后附上面试高频问题集,帮助开发者掌握分布式锁核心原理与实践技巧。(149字)

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

从轴承润滑到代码实现:手把手教你用Python FDM求解稳态雷诺方程

本文详细介绍了如何用Python FDM(有限差分法)求解稳态雷诺方程,从轴承润滑理论到代码实现的全过程。通过构建无量纲方程、有限差分格式和迭代求解算法,开发者可以快速实现专业级润滑分析,应用于涡轮发电机、汽车曲轴等场景。文章还提供了完整的Python代码示例和结果可视化方法,帮助工程师优化轴承设计。

weixin_28689507的博客 119

关于分布式锁的面试题都在这里了|Reids分布式锁|ZooKeeper分布式锁

我今天班儿都没上,就为了赶紧把这篇文章分布式锁早点写完。我真的不能再贴心了。 边喝茶边构思,你们可不要白嫖了! 三连来一遍? 引言 为什么要学习分布式锁? 最简单的理由就是作为一个社招程序员,面试的时候一定被面啦,你看怎么多公众号都翻来覆去的发分布式锁的主题,可见它很重要啦,在高考里这就是送分题,不要怪可惜的。 那应届生也会问吗?这就不一定了,但是,如果你会,面试官肯定会多给你那点分(钱) 第三...

6019

华为OD新系统真题-最小极差分组(C/C++/Py/Java/Js/Go)

华为OD机试 新系统真题【最小极差分组】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【最小极差分组】多语言题解。

qq_45776114的博客 587

Redis分布式锁的实现原理(面试)

如果聊到了分布式系统这块的东西。通常面试官都会从服务框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事务、分布式锁、ZooKeeper等知识。 如果在公司里落地生产环境用分布式锁的时候,一定是会用开源类库的,比如Redis分布式锁,一般就是用Redisson框架就好了,非常的简便易用。 可以去看看Redisson的官网,看看如何在项目中引入Redisson的依赖,然后基于Redis实现分布式锁的加锁与释放锁。下面是一个简单得Redisson分布式锁的实现伪代码: ...

qq_37286668的博客 1万+

动态时间规整(Dynamic Time Warping,DTW):让时间序列分析不再枯燥和代码全分析

动态时间规整(DTW)是一种用于比较时间序列相似性的算法,特别适用于处理长度不同或时间轴不对齐的序列。本文介绍了DTW的核心概念,包括其通过非线性对齐解决时间扭曲问题的能力,以及在语音识别、金融分析等领域的应用。通过Python代码示例,演示了如何计算两个模拟时间序列的DTW距离,并可视化最优对齐路径。结果显示,DTW能够有效捕捉序列间的相似模式,即使它们在时间轴上存在偏移或伸缩。该算法通过最小化累积距离来评估相似性,得分越低表明序列间相似度越高。

此星光明博客 162

Openclaw错误排查及解决方案之:Previous run is still shutting down. Please try again in a moment.

Openclaw错误排查及解决方案之:Previous run is still shutting down. Please try again in a moment.

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 232

Redis面试-两种方式实现分布式锁

介绍Redis的两种方式实现分布式锁、缓存更新、超卖问题

weixin_49429082的博客 1175

YOLOv13优化Faster_Block:卷积(Partial Convolution)、多层感知机(MLP)结构和残差连接

Faster_Block 代表了现代深度学习中效率与性能平衡的一个典型案例。它通过部分卷积技术巧妙地减少了计算开销,同时通过精心设计的架构保持了强大的特征学习能力。这种设计思想对于移动端部署、实时应用和大规模模型训练都具有重要意义。 未来的发展方向可能包括更智能的动态分割策略、与新兴硬件的深度适配,以及与其他先进技术(如自注意力机制、神经架构搜索等)的结合。Faster_Block 的成功展示了在深度学习领域,创新往往来自于对问题本质的深刻理解和巧妙的工程实现。

FJN110的博客 427

Redisson实现Redis分布式锁的原理分析

目录 一、写在前面 二、Redisson实现Redis分布式锁的底层原理 1、加锁机制 2、锁互斥机制 3、watch dog自动延期机制 4、可重入加锁机制 一、写在前面 现在面试,一般都会聊聊分布式系统这块的东西。通常面试官都会从服务框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事务、分布式锁、ZooKeeper等知识。 所以咱们这篇文章就来聊聊分布式锁这块知识,具体的来看看Redis分布式锁的实现原理。 说实话,如果在公司里落地生产环境用分布式锁的时候,一定是会用

骏马逸动,心随你动的博客 9079

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

38、分布式锁是啥?对比下redis和zk两种分布式锁的优劣?

1、面试题 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高? 2、面试官心里分析 其实一般问问题,都是这么问的,先问问你zk,然后其实是要过度的zk关联的一些问题里去,比如分布式锁。因为在分布式系统开发中,分布式锁的使用场景还是很常见的。 3、面试题剖析 (1)redis分布式锁 官方叫做RedLock...

一诺 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

关于分布式锁的面试题都在这里了

「我今天班儿都没上,就为了赶紧把这篇文章分布式锁早点写完。我真的不能再贴心了。」「边喝茶边构思,你们可不要白嫖了!三连来一遍?」引言为什么要学习分布式锁?最简单的理由就是作为一个社招程序...

6993

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

【Redis经典面试题五】实现分布式锁有几种方式?

分布式锁有多种实现方式,比较常见的实现是通过数据库、Redis或者Zookeeper实现。其中数据库的实现可以依赖悲观锁以及数据库表记录来实现,通过Redis的实现可以考虑使用setnx、redission以及redlock实现。使用zk主要是依赖他提供的临时有序节点来实现。

qq_38196449的博客 3385

Redis面试精讲 Day 15:Redis分布式锁实现与挑战

互斥性:同一时刻只有一个客户端能持有锁防死锁:锁必须能自动释放(正常或异常情况)容错性:部分节点故障不影响锁的可用性高性能:获取和释放锁的操作要高效分布式锁需要满足互斥、防死锁、容错等特性Redis锁基础实现基于SET NX PX命令Redlock算法通过多实例提高可靠性锁续期和原子释放是关键实现细节不同业务场景需要不同的锁策略。

在未来等你的专栏 970

Redis分布式锁原理与实现

在分布式系统中,数据的一致性和并发控制是至关重要的。随着微服务架构的普及,多个服务实例可能同时访问同一份数据,这就需要一种机制来确保数据的一致性和避免并发冲突。Redis分布式锁正是为了解决这一问题而诞生的。想象一个在线支付系统,当用户发起支付请求时,系统需要确保同一时间只有一个服务实例处理这笔交易,以防止重复扣款或数据不一致。这种情况下,分布式锁就扮演了至关重要的角色。Redis分布式锁是一种基于Redis的锁机制,它利用Redis的原子操作来保证锁的互斥性和可见性。

我是Java程序员廖志伟,感谢朋友们的支持! 2148

Redisson实现Redis分布式锁的原理

Redisson实现Redis分布式锁的原理 一、写在前面 现在面试,一般都会聊聊分布式系统这块的东西。通常面试官都会从服务框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事务、分布式锁、ZooKeeper等知识。 所以咱们这篇文章就来聊聊分布式锁这块知识,具体的来看看Redis分布式锁的实现原理。 说实话,如果在公司里落地生产环境用分布式锁的时候,一定是会用开源类库的,比如Redis分布式锁,一般就是用Redisson框架就好了,非常的简便易用。 大家如果...

MayMatrix 的博客 4636

Redis实现分布式锁原理(面试重点)

Redis实现分布式锁原理(面试重点)简素主要从为什么如何实现分布锁加锁以及解锁,解锁的几种方式,然后过期时间存在的问题,看门狗机制的处理,守护进程机制的实现

可乐要加冰!!! 7418
上一篇: 使用Java解压和压缩文件
下一篇: 一篇文章教你学会SpringBatch使用分区读取数据库
境里婆娑
境里婆娑 新星创作者: Java技术领域 新星创作者: Java技术领域 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄10年 1万+粉丝 300原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值