分库分表订单全局ID

全局唯一订单号生成(分布式ID) 其中系统标识码用于区分具体是什么业务(如交易订单、库存、积分等),表下标为了兼容分库分表的场景,序列号采用雪花算法生成。这里的雪花算法,采用IdUtil.getSnowflake实现的。为了生成一个这样一个全局的订单号,我自定义了一个分布式 ID 生成器,其中包含了三部分内容,分别是系统标识码、表下标以及序列化。业务码用来区分具体的业务,如交易订单、支付单等,然后雪花算法用来保证唯一性,分表信息用来实现分表后的订单号查询。在我们的项目中,我们需要定义一些全局唯一的 ID,比如订单号,支付单号等等。 阅读详情

概述

分库分表后涉及到的另一个问题就是主键如何保证唯一且自增。以前单库单表的时候只需要利用数据库特性进行自增即可,现在因为是各自独立的库表,数据库之间的主键自增无法进行交互,比如数据库1的订单明细表主键自增到了1001,数据库2的订单明细表主键现在是1000,如果现在往数据库2的订单明细表中插入一条数据,这个时候获取到的主键ID会是1001,这样就会造成业务上的主键冲突。

全局ID

为了解决订单明细表主键的重复问题。靠数据库的主键自增是无法做到了。如何解决这个问题呢?可以给项目中引入一个全局唯一的ID服务,这个服务就是用来生成全局唯一ID的,每次生成的ID都不一样,可以保证主键的唯一性。

全局ID算法

全局ID需要保证如下的特性:

  • 全局唯一:必须保证ID是全局性唯一的,基本要求

  • 高性能:高可用低延时,ID生成响应要块,否则反倒会成为业务瓶颈

  • 高可用:100%的可用性是骗人的,但是也要无限接近于100%的可用性

  • 好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单

  • 趋势递增:最好趋势递增,这个要求就得看具体业务场景了,一般不严格要求

常用的全局ID算法如下:

  • 雪花算法Snowflake
  • 百度uid-generator
  • 美团Leaf
  • 滴滴Tinyid

雪花算法

雪花算法的结构:

主要分为 4 个部分:

  1. 是 1 个 bit:0,这个是无意义的。
  2. 是 41 个 bit:表示的是时间戳。
  3. 是 10 个 bit:表示的是机房 id,0000000000,因为我传进去的就是0。
  4. 是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,0000 0000 0000。

1 bit,是无意义的:

因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。

41 bit:表示的是时间戳,单位是毫秒。

41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成>年就是表示 69 年的时间。

10 bit:记录工作机器 id,代表的是这个服务最多可以部署在 2^10 台机器上,也就是 1024 台机器。

但是 10 bit 里 5 个 bit 代表机房 id,5 个 bit 代表机器 id。意思就是最多代表 2 ^ 5 个机房(32 个机房),每个机房里可以代表 2 ^ 5 个机器(32 台机器),这里可以随意拆分,比如拿出4位标识业务号,其他6位作为机器号。可以随意组合。

12 bit:这个是用来记录同一个毫秒内产生的不同 id。

12 bit 可以代表的最大正整数是 2 ^ 12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。也就是同一毫秒内同一台机器所生成的最大ID数量为4096

简单来说,你的某个服务假设要生成一个全局唯一 id,那么就可以发送一个请求给部署了 SnowFlake 算法的系统,由这个 SnowFlake 算法系统来生成唯一 id。这个 SnowFlake 算法系统首先肯定是知道自己所在的机器号,(这里姑且讲10bit全部作为工作机器ID)接着 SnowFlake 算法系统接收到这个请求之后,首先就会用二进制位运算的方式生成一个 64 bit 的 long 型 id,64 个 bit 中的第一个 bit 是无意义的。接着用当前时间戳(单位到毫秒)占用41 个 bit,然后接着 10 个 bit 设置机器 id。最后再判断一下,当前这台机房的这台机器上这一毫秒内,这是第几个请求,给这次生成 id 的请求累加一个序号,作为最后的 12 个 bit。

/**
 * 雪花算法解析 结构 snowflake的结构如下(每部分用-分开):
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
 * 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10
 * 位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)
 * <p>
 * 一共加起来刚好64位,为一个Long型。(转换成字符串长度为18)
 */
public class IdWorkerUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(IdWorkerUtils.class);

    /**
     * 工作机器ID(0~31)
     */
    private long workerId;

    /**
     * 数据中心ID(0~31)
     */
    private long datacenterId;

    /**
     * 毫秒内序列(0~4095)
     */
    private long sequence;

    /**
     * 开始时间截 (2015-01-01)
     */
    private long twepoch = 1288834974657L;

    /**
     * 机器id所占的位数
     */
    private long workerIdBits = 5L;

    /**
     * 数据标识id所占的位数
     */
    private long datacenterIdBits = 5L;

    /**
     * 这个是二进制运算,就是 5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
     */
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 这个是二进制运算,就是 5 bit最多只能有31个数字,机房id最多只能是32以内
     */
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 序列在id中占的位数
     */
    private long sequenceBits = 12L;

    /**
     * 机器ID向左移12位
     */
    private long workerIdShift = sequenceBits;

    /**
     * 数据标识id向左移17位(12+5)
     */
    private long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 时间截向左移22位(5+5+12)
     */
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
     */
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 上次生成ID的时间截
     */
    private long lastTimestamp = -1L;

    public IdWorkerUtils(long workerId, long datacenterId, long sequence) {
        // sanity check for workerId
        // 这儿不就检查了一下,要求就是你传递进来的机房id和机器id不能超过32,不能小于0
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }

        LOGGER.info(
                "worker starting. timestamp left shift {}, datacenter id bits {}, worker id bits {}, sequence bits {}, workerid {}",
                timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);

        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    public long getWorkerId() {
        return workerId;
    }

    public long getDatacenterId() {
        return datacenterId;
    }

    public long getTimestamp() {
        return System.currentTimeMillis();
    }

    public synchronized long nextId() {
        // 这儿就是获取当前时间戳,单位是毫秒
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            LOGGER.error("clock is moving backwards.  Rejecting requests until {}.", lastTimestamp);
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 这个意思是说一个毫秒内最多只能有4096个数字
            // 无论你传递多少进来,这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        // 这儿记录一下最近一次生成id的时间戳,单位是毫秒
        lastTimestamp = timestamp;

        // 这儿就是将时间戳左移,放到 41 bit那儿;
        // 将机房 id左移放到 5 bit那儿;
        // 将机器id左移放到5 bit那儿;将序号放最后12 bit;
        // 最后拼接起来成一个 64 bit的二进制数字,转换成 10 进制就是个 long 型
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    // ---------------测试---------------
    /*public static void main(String[] args) {
        IdWorkerUtils worker = new IdWorkerUtils(1, 1, 1);
        for (int i = 0; i < 30; i++) {
            System.out.println(worker.nextId());
        }
    }*/
}

  • 时钟回拨

因为雪花算法中包含时间戳,因此依赖系统的时间,如果系统的时间由于某一些原因回到了过去的某个时间,比如现在的系统时间是2022年8月7日09时01分10秒,但是下次获获取到的时间是2022年8月7日09时00分10秒,那么就可能导致生成的全局ID与过去的某一个ID重复,这就是时钟回拨问题。

为了解决时钟回拨问题可以把之前的系统获取到哦啊的时间戳缓存起来,每次获取时间戳和上次的进行比较,如果本次获取的时间小于上一次的时间,就证明时钟回拨了,就可以取上次时间戳+1来解决。

总结

其实对于分布式ID的生成策略。无论是我们上述提到的哪一种。无非需要具有以下两种特点。 自增的、不重复的 ,而对于不重复且是自增的,那么很容易想到的是时间,而雪花算法就是基于时间戳。但是毫秒级的并发下如果直接拿来用,显然是不合理的。那么就要在这个时间戳上面做一些文章。至于怎么能让这个东西保持唯一且自增。就要打开自己的脑洞了。可以看到雪花算法中是基于 synchronized 锁进行实现的。

5问分库分表:如何确保ID的全局唯一性? 🔥关注墨瑾轩,带你探索编程的奥秘!🚀🔥超萌技术攻略,轻松晋级编程高手🚀🔥技术宝库已备好,就等你来挖掘🚀🔥订阅墨瑾轩,智趣学习不孤单🚀🔥即刻启航,编程之旅更有趣🚀嘿,大家好!今天我们要聊聊一个在分布式系统中非常关键的问题——如何保证分库分表后ID的全局唯一性。想象一下,如果你有一个庞大的数据库,里面的数据被分散存储在多个数据库和表中,那么怎样才能确保每个记录都有一个独一无二的身份标识呢?别担心,接下来我会带你一步步揭开这个谜题,让你轻松掌握解决之道。 阅读详情

相关推荐

MySQL分库分表后唯一ID如何生成

在选择唯一ID生成方案时,应综合考虑系统的并发需求、性能要求、数据规模、扩展性、可用性以及现有技术栈等因素。对于高并发、大数据量且对ID有序性有一定要求的分库分表场景,推荐采用专门的分布式ID生成服务或算法,如Snowflake、Leaf等,以实现高效、可靠的全局唯一ID生成。如果系统对ID的生成速度要求不是极高,且愿意接受一定的运维复杂性,可以考虑使用数据库自增ID结合分库分表规则或中心化表的方式来生成唯一ID。这些服务或算法通常结合时间戳、机器ID、序列号等元素生成全局唯一且有序的ID。

java专栏 1369

10 | 发号器:如何保证分库分表后ID的全局唯一性?

在前面两节课程中,了解了分布式存储两个核心问题:数据冗余和数据分片,以及在传统关系型数据库中是如何解决的。当我们面临高并发的查询数据请求时,可以使用主从读写分离的方式,部署多个从库分摊读压力;当存储的数据量达到瓶颈时,我们可以将数据分片存储在多个节点上,降低单个存储节点的存储压力,此时我们的架构变成了下面这个样子:你可以看到,我们通过分库分表和主从读写分离的方式解决了数据库的扩展性问题,但是在 09 讲我也提到过,数据库在分库分表之后,我们在使用数据库时存在的许多限制,比方说查询的时候必须带着分区键;

qq_37756660的博客 875

java 分库分表后,如何确保ID的全局唯一性?3种方法大比拼!

UUID(Universally Unique Identifier),即通用唯一识别码,是一种标准格式的128位长整数。它通过特定算法生成,理论上可以保证在全球范围内不重复。因此,UUID成为了许多开发者解决ID唯一性问题的首选方案。

java专栏 1947

分库分表后如何解决唯一主键问题?雪花算法、自增id详解

在单库单表时,业务 ID 可以依赖数据库的自增主键实现,现在我们把存储拆分到了多处,如果还是用数据库的自增主键,势必会导致主键重复。但要是利用加锁来处理,又会严重拖垮效率。实际开发中主要有三种方式。

pige666的博客 1708

高速PCB设计中串扰的成因、影响与实战控制策略

在高速数字电路与硬件系统设计中,信号完整性是确保系统稳定可靠的核心基础。其核心原理在于,当信号在传输线上传播时,会伴随产生电场与磁场,若相邻导线距离过近,便会通过电磁场耦合产生不期望的能量转移,即串扰。这一现象会直接导致信号波形畸变、时序偏移和噪声裕量降低,严重影响系统性能。从技术价值看,深入理解并有效控制串扰,是提升数据传输速率、保障系统稳定性和降低误码率的关键。其应用场景广泛覆盖了从高速SerDes链路、DDR内存接口到高精度模拟电路等几乎所有现代电子系统。本文聚焦于高速PCB设计中的串扰问题,系统剖析

weixin_27625589的博客 577

Axure RP 9在Windows 11环境下的高效安装与配置指南

本文提供Axure RP 9在Windows 11系统下的详细安装步骤与配置指南。针对Win11安全特性,从获取官方安装资源、调整系统权限,到定制化安装路径、精准汉化及性能优化,系统性地解决了安装闪退、汉化失败、运行卡顿等常见问题,确保用户获得稳定流畅的原型设计体验。

mango的博客 480

订单分库分表怎么设计?一次讲清拆分维度、全局 ID、分页查询与跨库问题

本文系统探讨了订单系统的分库分表设计策略。作者指出分库分表不是简单的表拆分,而是需要综合考虑业务场景、查询路径和运维成本。文章分析了分库分表的适用场景、订单特性、拆分维度选择(用户/时间/组合维度)、全局ID设计(推荐雪花算法或分布式发号方案)等核心问题,特别强调了跨分片查询、聚合统计和运维复杂度等容易被低估的挑战。作者建议设计时应围绕主查询路径进行,避免为了拆而拆,并提供了面试中的回答思路。最后指出订单分库分表的关键在于平衡业务需求和长期运维成本。

weixin_46026202的博客 191

用Multisim和AD2复刻经典:手把手教你搭建μA741三角波-方波发生器(附完整避坑指南)

本文详细介绍了如何使用Multisim和AD2搭建基于μA741运算放大器的三角波-方波发生器,包括电路原理、Multisim仿真流程、AD2硬件搭建及调试技巧。通过对比理论计算、仿真和实测数据,深入分析器件非理想特性的影响,并提供16个典型问题的解决方案,帮助读者掌握波形发生器的设计原理和工程调试经验。

weixin_28744423的博客 285

分库分表 路由策略与全局ID

分库分表支持的场景 用户的角度 : 查询订单信息 商家角度: 获取商家订单的角度. 用户下单后, 可能查询多个表. 根据一定的规则, 让同一个用户, 下单都落在同一个表中. 路由key: 根据用户id , 作为路由的key , 对表的数量进行取模. 多库多表 先找库, 再找表. 先根据用户的id , 对数据库的数量进行取模, 再去找对应的表. 在找对应表的时候, 除以表的数量, 再对表的数量进行取模, 防止根据库进行路由后, 只插入到某一个表中去. 商户的路由策略: 买家下完单之后, 发送订

Java持续实践 1069

别再只看读写速度了!聊聊手机闪存eMMC、UFS3.1和SSD背后的功耗与体积玄学

本文深入探讨手机闪存技术eMMC、UFS3.1和SSD在功耗与体积设计上的关键差异。通过对比接口技术、封装工艺和温度管理,揭示移动存储如何在有限空间内平衡性能与能效,为消费者提供选购参考。重点关注UFS3.1的差分信号设计和全双工架构带来的显著优势。

weixin_27061475的博客 449

【项目分享】RailTracker: 火车票务数据采集与分析

从12306使用爬虫爬取火车站及车次信息、火车票价 通过本项目,我们将带领访问者手把手完成火车票数据采集、数据清洗、数据集成、可视化的一整个流程!

zhushatong的博客 3345

项目中分库分表的分布式ID如何生成

分库分表通常通过中间件(如 ShardingSphere)或手动实现,常见的策略包括按范围、哈希、模块划分等。分布式 ID 生成推荐使用 Snowflake 算法,它能够保证全局唯一且具有较高的性能,适用于高并发场景。Redis 和 UUID 也是常见的方案,但性能和空间效率不如 Snowflake。

有时间就写写代码 595

ANSYS——模态分析的理论基础

ANSYS模态分析理论基础

python_AI_fans的博客 9098

mysql分库分表分页查询语句_MySQL分库分表的实现过程详解介绍

MySQL分库分表对于许多的开发者来讲这个只是听说地自己从来没做过MySQL分库分表的安案了其实小编也只做过mysql分表的例子没做过分库的案例了,今天我们来看一篇关于MySQL分库分表的例子吧,具体如下所示。MySQL分库分表基础表介绍表基本模型结构这里我们模拟一个商城的基本的表结。此结构由(用户、门店、导购、门店商品、订单、订单对应的商品)。其中,导购也是一个用户,门店是只属于一个店主的,同时...

weixin_32666923的博客 1072

热力学仿真软件:SimScale_(1).SimScale入门与基础操作

导航栏:位于页面顶部,包含项目管理、仿真设置、结果分析等主要功能。项目列表:展示用户创建的所有项目。工作区:用于进行具体的操作,如几何建模、网格划分、边界条件设置等。控制台:显示操作日志和仿真进度。网格划分是将几何模型离散化为一系列小单元的过程,这些单元用于求解热力学方程。SimScale提供了多种网格划分方法,包括结构化网格和非结构化网格。SimScale提供了丰富的API接口,用户可以通过编程语言进行二次开发,实现自动化仿真和结果分析。项目管理:创建、删除和管理项目。几何建模:创建、导入和修复几何模型。

kkchenjj的博客 1870

YOLOv4目标检测算法——通俗易懂的解析

YOLOv4目标检测算法——通俗易懂的解析

chase 7136

MySQL实现分库分表代码实战(Mango中间件)

本篇主要介绍Java版使用Mango框架实现Mysql分库分表实战,分库分表中间件也可以使用类似于ShardingJDBC,或者自研。以上分库分表数量仅供演示参考,实际工作中分表数量、分库数量、是根据公司实际业。

流楚丶格念的博客 2184

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

分布式 ID 生成方案实战指南:从选型到落地的全场景避坑手册(二)

摘要:分库分表场景下的数据库分段ID生成方案 本方案针对电商订单系统分库分表场景(3库10表),提出数据库分段ID生成方法。通过设置步长和偏移量(如3个库步长=3,偏移量1/2/3),实现多库自增ID不重复且全局唯一。核心配置包括MySQL自增步长设置和MyBatis分库路由插件实现。进一步优化采用号段模式,预申请ID段缓存到本地,减少80%数据库访问压力。该方案既保证ID有序性,又能支持高并发场景,通过异步申请机制避免ID耗尽风险。

七夜zippoe的博客 1619

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

(项目随笔)关于订单系统的思考

订单系统几个特点: 1、历史信息多(每一条订单完成后,生成一条历史信息,并且一般情况下,久远的订单信息不回被经常查看) 2、并发量高(特别是秒杀系统生成的订单,每秒会有成百上千条订单) 3、订单id必须全局唯一 一、分库分表 随着订单量的增加,数据库发展如下: 单一的数据库 一主一从 双主多从,读写分离 分表分库,提高并发 已知Mysql单表性能超过千万级别会严重下降,按照千万级...

喻小清的博客 945

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

[转]每秒处理10万订单乐视集团支付架构

转自:http://blog.youkuaiyun.com/clypm/article/details/54969438 随着乐视硬件抢购的不断升级,乐视集团支付面临的请求压力百倍乃至千倍的暴增。作为商品购买的最后一环,保证用户快速稳定的完成支付尤为重要。所以在15年11月,我们对整个支付系统进行了全面的架构升级,使之具备了每秒稳定处理10万订单的能力。为乐视生态各种形式的抢购秒杀活动提供了强有力...

avm8523362的博客 319

分库分表(四)

真实互联网公司常见的订单分库分表架构设计。这个设计基本是很多大厂(电商、外卖、支付系统)使用的模式。🚀我会从 设计目标 → 分片键选择 → 分库分表结构 → 表结构设计 → 查询优化 → 扩容方案 一步一步讲。假设一个电商系统:如果只有一张表: order问题:表太大(几十亿行)索引巨大查询慢MySQL 扛不住所以必须: 分库 + 分表典型架构: Application │ ShardingSphere │ ┌───────────┼───────────┐ │

极客on之路 566

每秒处理10万订单乐视集团支付架构

随着乐视硬件抢购的不断升级,乐视集团支付面临的请求压力百倍乃至千倍的暴增。作为商品购买的最后一环,保证用户快速稳定的完成支付尤为重要。所以在15年11月,我们对整个支付系统进行了全面的架构升级,使之具备了每秒稳定处理10万订单的能力。为乐视生态各种形式的抢购秒杀活动提供了强有力的支撑。 一...

chuichou2194的博客 268

分库分表核心理念

分库分表核心理念

weixin_46619605的博客 959

【架构设计】高并发订单系统分库分表方案

订单系统分库分表设计方案摘要:核心采用用户ID分片策略,通过哈希取模实现64库1024表结构,确保用户数据集中存储。全局ID使用雪花算法结合分片信息嵌入,解决跨分片查询需建立订单号-用户ID索引表。针对热点问题采用冷热数据分离和秒杀订单独立分片方案。架构上通过ShardingSphere中间件实现路由,配合读写分离提升性能。实施时需注意避免无分片键查询,预先设计扩容方案。推荐主流方案为用户ID分库+时间分表,超高并发场景可采用订单号基因分片,多角色查询则需要数据冗余同步。(149字)

weixin_42548337的博客 1354

自增 ID 存在的问题及分布式 ID 解决方案(Redis 实战)

MySQL 自增 ID 不适合高并发、分布式场景存在: 安全风险 扩展性问题 分库分表冲突分布式 ID 是秒杀、订单系统的必备组件使用Redis + 时间戳 + 序列号: 高性能 全局唯一 趋势递增 实现简单,可控性强。

2301_80383368的博客 205
上一篇: 强大的图片处理工具GraphicsMagick
下一篇: 阿里P9大神熬夜整合最全PDF:深入实践Mybatis技术原理与实战
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值