百度开源分布式id生成器集成--真香警告

news2025/1/15 7:56:47

百度开源分布式id生成器集成–真香警告

文章目录

    • @[toc]
  • 1.为什么需要分布式id生成器?
  • 2.常见id生成方案
    • 2.1 数据库表主键自增
    • 2.2 uuid
    • 2.3 雪花算法
      • 2.3.1 实现代码
      • 2.3.2 缺点的解决方案
        • 百度开源的分布式唯一ID生成器UidGenerator(本文重点讲解这个)
        • Leaf--美团点评分布式ID生成系统
        • 滴滴的tinyid
    • 2.4 使用redis生成分布式id
      • 2.4.1 因为Redis是单线的天生保证原子性,可以使用原子操作INCR和INCRBY来实现
      • 2.4.2 使用redis执行lua脚本
    • 2.5 使用ThreadLocal加时间和一个redis的一个自增的序列
  • 3.百度开源uid-generator的集成
    • 3.1 依赖
    • 3.2 数据库执行sql脚本
    • 3.3 nacos配置
    • 3.4 代码
    • 3.5 PostMan测试
  • 4.总结

1.为什么需要分布式id生成器?

    在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需求;特别一点的如订单、骑手、优惠券也都需要有唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。概括下来,那业务系统对ID号的要求有哪些呢?

  1. 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
  2. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
  3. 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
  4. 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。

    上述123对应三类不同的场景,3和4需求还是互斥的,无法使用同一个方案满足。

    同时除了对ID号码自身的要求,业务还对ID号生成系统的可用性要求极高,想象一下,如果ID生成系统瘫痪,整个美团点评支付、优惠券发券、骑手派单等关键动作都无法执行,这就会带来一场灾难。

    由此总结下一个ID生成系统应该做到如下几点:

    1. 平均延迟和TP999延迟都要尽可能低;

    2. 可用性5个9;

    3. 高QPS。

2.常见id生成方案

2.1 数据库表主键自增

    这个是数据库提供的能力,但是在数据量非常大的情况下,数据库表的id不够用了,那怎么办,凡是都会有一个尽头,物极必反都有一定的道理。

数据库自增主键实现原理:数据库自增id和replace into实现的

REPLACE INTO的含义是插入一条记录,如果表中唯一索引的值遇到冲突,则替换老数据

那数据库自增ID机制适合作分布式ID吗? 答案是不太适合

  1. 系统水平扩展比较困难,比如定义好了步长和机器台数之后,如果要添加机器该怎么做? 假设现在只有一台机器发号是1,2.3.4.5(步长是1),这个时候需要扩容机器一台。可以这样做,把第二台机器的初始值设置得比第一台超过很多,貌似还好,现在想象一下如果我们线上有100台机器,这个时候要扩容该怎么做? 简直是噩梦。所以系统水平扩展方案复杂难以实现。
  2. 数据库压力还是很大,每次获取ID都得读写一次数据库,非常影响性能,不符合分布式ID里面的延迟低和要高QPS的规则(在高并发下,如果都去数据库里面获取id,那是非常影响性能的)

2.2 uuid

    UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID,详情见IETF发布的UUID规范 A Universally Unique IDentifier (UUID) URN Namespace。

http://www.ietf.org/rfc/rfc4122.txt

优点:

  • 性能非常高:本地生成,没有网络消耗。

缺点:

  • 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。

  • 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。

  • ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:

  • 索引, B+树索引的分裂

    既然分布式id是主键,然后主键是包含索引的,然后mysql的索引是通过b+树来实现的,每一次新的UUID数据的插入,为了查询的优化,都会对索引底层的b+树进行修改,因为UUID数据是无序的。

    所以**每一次UUID数据的插入都会对主键地的b+树进行很大的修改,这一点很不好。**插入完全无序,不但会导致一些中间节点产生分裂,也会白白创造出很多不饱和的节点,这样大大降低了数据库插入的性能

① MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID不符合要求。

All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index.*** If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key***.

② 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。

2.3 雪花算法

图片

    41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。

这种方式的优缺点是:

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

2.3.1 实现代码

snowflake

这个是github上的实现,可以学习和参考:

https://github.com/twitter-archive/snowflake

下面是我在网上找的代码实现,可以学习和参考:

/**
 * Twitter_Snowflake<br>
 * SnowFlake的结构如下(每部分用-分开):<br>
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
 * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
 * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
 * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
 * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
 * 加起来刚好64位,为一个Long型。<br>
 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
 */
public class SnowflakeIdWorker {
    // ==============================Fields===========================================
    /** 开始时间截 (2020-08-28) */
    private final long twepoch = 1598598185157L;
 
    /** 机器id所占的位数 */
    private final long workerIdBits = 5L;
 
    /** 数据标识id所占的位数 */
    private final long datacenterIdBits = 5L;
 
    /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
 
    /** 支持的最大数据标识id,结果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 
    /** 序列在id中占的位数 */
    private final long sequenceBits = 12L;
 
    /** 机器ID向左移12位 */
    private final long workerIdShift = sequenceBits;
 
    /** 数据标识id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;
 
    /** 时间截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
 
    /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
 
    /** 工作机器ID(0~31) */
    private long workerId;
 
    /** 数据中心ID(0~31) */
    private long datacenterId;
 
    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;
 
    /** 上次生成ID的时间截 */
    private long lastTimestamp = -1L;
 
    //==============================Constructors=====================================
    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param datacenterId 数据中心ID (0~31) 此方法是判断传入的机房号和机器号是否超过了最大值,即31,或者小于0
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        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));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
 
    // ==============================Methods==========================================
    /*
     * 核心方法
     * 获得下一个ID (该方法是线程安全的)
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        //1.获取当前的系统时间
        long timestamp = timeGen();
 
        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
 
        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            // sequence 要增1, 但要预防sequence超过 最大值4095,所以要 与 SEQUENCE_MASK 按位求与 
            // 即如果此时sequence等于4095,加1后为4096,再和4095按位与后,结果为0
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //时间戳改变,毫秒内序列重置
        else {
            sequence = 0L;
        }
 
        //上次生成ID的时间截
        //把当前时间赋值给 lastTime, 以便下一次判断是否处在同一个毫秒内
        lastTimestamp = timestamp;
 
        //移位并通过或运算拼到一起组成64位的ID
         long id = ((timestamp - twepoch) << timestampLeftShift) // 时间戳减去默认时间 再左移22位 与运算
                | (datacenterId << datacenterIdShift) // 机房号 左移17位 与运算
                | (workerId << workerIdShift) // 机器号 左移12位 与运算
                | sequence; // 序列号无需左移 直接进行与运算
        return id;
    }
 
    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间截
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
 
    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
 
    //==============================Test=============================================
    /** 测试 */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(id);
        }
    }
}

2.3.2 缺点的解决方案

可以参照以下两个来进行机器时钟的同步

    对于并发数要求不高、期望长期使用的应用, 可增加timeBits位数, 减少seqBits位数. 例如节点采取用完即弃的WorkerIdAssigner策略, 重启频率为12次/天, 那么配置成{"workerBits":23,"timeBits":31,"seqBits":9}时, 可支持28个节点以整体并发量14400 UID/s的速度持续运行68年.

    对于节点重启频率频繁、期望长期使用的应用, 可增加workerBitstimeBits位数, 减少seqBits位数. 例如节点采取用完即弃的WorkerIdAssigner策略, 重启频率为24*12次/天, 那么配置成{"workerBits":27,"timeBits":30,"seqBits":6}时, 可支持37个节点以整体并发量2400 UID/s的速度持续运行34年.

  • Leaf–美团点评分布式ID生成系统
    There are no two identical leaves in the world.
    
    世界上没有两片完全相同的树叶。
    
    --莱布尼茨
    

    Leaf——美团点评分布式ID生成系统

    https://tech.meituan.com/2017/04/21/mt-leaf.htm
    

    Leaf:美团分布式ID生成服务开源

    https://tech.meituan.com/2019/03/07/open-source-project-leaf.html
    

    Leaf

    https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md
    

    Leaf有两种模式

    1.号段模式

    该模式依赖于数据库,所以受限于数据库的高可用和数据库的性能,数据库可能成为性能瓶颈。

2.Snowflake模式

    该模式依赖于zk,所以受限于zk的高可用和跟zk的交互网络稳定性也是有一定的影响。

使用leaf-starter注解来启动leaf

https://github.com/Meituan-Dianping/Leaf/blob/feature/spring-boot-starter/README_CN.md#使用leaf-starter注解来启动leaf

这是官方提供的一个start启动器,可以方便的使用Leaf

参看官方文档使用起来也很简单。

    tinyid依赖于数据库,所以性能跟数据库有很大的关系,实现类似于美团的短号模式,是client-server模式架构,server提供id生成服务,client通过网络请求去请求server的id生成,所以也受网络稳定性的影响,如果使用不当会有数据库慢sql的问题。

图片

上图是官方提供的架构图,架构可以参考如下的文章:

tinyid介绍 及架构分析

https://blog.csdn.net/qq_34687559/article/details/115932532

2.4 使用redis生成分布式id

2.4.1 因为Redis是单线的天生保证原子性,可以使用原子操作INCR和INCRBY来实现

注意:在Redis集群情况下,同样和MySQL一样需要设置不同的增长步长,同时key一定要设置有效期

可以使用Redis集群来获取更高的吞吐量。

假如一个集群中有5台Redis。可以初始化每台Redis的值分别是1,2,3,4,5,然后步长都是5。

各个Redis生成的ID为:

  • A: 1,6,11,16,21
  • B: 2,7,12,17,22
  • C: 3,8,13,18,23
  • D: 4,9,14,19,24
  • E: 5,10,15,20,25

2.4.2 使用redis执行lua脚本

生成的id的格式如下:yyyyMMddxxxxxxxxxxx

年月日+一个自增的序列号,实现省略

2.5 使用ThreadLocal加时间和一个redis的一个自增的序列

生成的格式如下:yyyyMMddxxxxxxxxxxx

年月日+一个自增的序列号,这里的年月日是系统时间,后面的自增序列号是调用redis的NCR和INCRBY来实现,然后外加一个定时任务需要今天凌晨12点整去清除前一天的生成的序列号,每天都有一个key是存储这个redis的自增的序列号的,这种方式是我认为比较low的方式,只是一个思路,不建议使用。

3.百度开源uid-generator的集成

3.1 依赖

uid-generator-spring-boot-starter

https://github.com/wujun234/uid-generator-spring-boot-starter
 <properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <uid-generator.version>1.0.3.RELEASE</uid-generator.version>
</properties>
<dependency>
        <groupId>com.github.wujun234</groupId>
        <artifactId>uid-generator-spring-boot-starter</artifactId>
          <version>${uid-generator.version}</version>
          <exclusions>
            <exclusion>
            <groupId>org.mybatis</groupId>
          <artifactId>mybatis</artifactId>
         </exclusion>
    </exclusions>
</dependency>

    这里如果项目中有使用mypatis-plus的依赖的话需要把这个mybatis的依赖排除,否则就会jar依赖冲突,项目启动不起来。

3.2 数据库执行sql脚本

DROP TABLE IF EXISTS WORKER_NODE;
CREATE TABLE WORKER_NODE
(
ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
PORT VARCHAR(64) NOT NULL COMMENT 'port',
TYPE INT NOT NULL COMMENT 'node type: CONTAINER(1), ACTUAL(2), FAKE(3)',
LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
CREATED TIMESTAMP NOT NULL COMMENT 'created time',
PRIMARY KEY(ID)
)
 COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;

项目启动后表里数据如下:

图片

3.3 nacos配置

# UidGenerator
# 初始时间, 默认:"2019-02-20"
uid:
  epochStr: 2019-02-20
  # 时间位, 默认:30
  timeBits: 41
  # 机器位, 默认:16
  workerBits: 10
  # ***, 默认:7
  seqBits: 12
  # 是否容忍时钟回拨, 默认:true
  enableBackward: true
  # RingBuffer size扩容参数, 可提高UID生成的吞吐量, 默认:3
  CachedUidGenerator:
    boostPower: 3
    # 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50
    paddingFactor: 50

3.4 代码

IdGenerator

package xxxx.utils;

import com.github.wujun234.uid.impl.CachedUidGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class IdGenerator {

    @Autowired
    private CachedUidGenerator cachedUidGenerator;

    /**
     * 获取uid
     *
     * @return
     */
    public long nextId() {
        return cachedUidGenerator.getUID();
    }

    /**
     * 格式化传入的uid,方便查看其实际含义
     *
     * @param uid
     * @return
     */
    public String parse(long uid) {
        return cachedUidGenerator.parseUID(uid);
    }

}

UidController

package xxxx.controller;

import xxx.utils.IdGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UidController {

    @Autowired
    private IdGenerator idGenerator;

    @GetMapping("/testGet")
    public Long testGet() {
        return idGenerator.nextId();
    }

    @GetMapping("/testParse/{uid}")
    public String testParse(@PathVariable("uid") Long uid) {
        return idGenerator.parse(uid);
    }

}

3.5 PostMan测试

id生成接口:

图片

id生成解析接口:

图片

4.总结

    通过本文的分享对分布式id生成的实现会有跟多的选择和思路,对开源的组件使用更加的娴熟,希望对你有帮助,请一键三连,么么么哒!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1108373.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gnome-terminal禁止关闭确认

当你想要关闭一个终端时&#xff0c;弹出“确认关闭&#xff1f;”多少有些烦。 比如当前为root下&#xff0c;要么一路exit&#xff0c;要么就点击确认&#xff1a; 解决方法&#xff1a; 安装一个配置编辑器来帮我们方便地编辑配置项 sudo apt install dconf-editor找到…

小程序搭建OA项目首页布局界面

首先让我们来学习以下Flex布局 一&#xff0c;Flex布局简介 布局的传统解决方案&#xff0c;基于盒状模型&#xff0c;依赖 display属性 position属性 float属性 Flex布局简介 Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”&#xff0c;用来为盒状模型提供最大的…

tomcat、nginx实现四层转发+七层代理+动静分离实验

实验环境&#xff1a; nginx1——20.0.0.11——客户端 静态页面&#xff1a; nginx2——20.0.0.21——代理服务器1 nginx3——20.0.0.31——代理服务器2 动态页面&#xff1a; tomcat1——20.0.0.12——后端服务器1 tomcat2——20.0.0.22——后端服务器2 实验步骤&…

什么是4K三路虚拟情景互动教学软件?

4K三路虚拟情景互动教学软件具备了AI对话&#xff0c;场景库丰富自定义选择&#xff0c;画面色差调节&#xff0c;人物滤镜调节&#xff0c;截图编辑&#xff0c;视频录制与编辑&#xff0c;视频直播&#xff0c;画中画控制功能&#xff0c;字幕&#xff0c;图片和特效录入功能…

sqlmap --os-shell选项原理解析

文章目录 sqlmap --os-shell选项原理解析原理解析总结 sqlmap --os-shell选项原理解析 以sqli第一关为例。 --os-shell 是 SQLMap 工具的一个参数&#xff0c;用于在成功注入数据库后&#xff0c;执行操作系统命令并获取其输出。 sqlmap -u "http://192.168.188.199/sq…

学信息系统项目管理师第4版系列31_信息系统工程

1. 信息系统战略三角突出了业务战略、信息系统和组织机制之间的必要一致性 1.1. 【高23上选07】 2. 软件工程 2.1. 软件工程方法是完成软件工程项目的技术手段&#xff0c;它支持整个软件生命周期 2.2. 软件工程使用的工具是人们在开发软件的活动中智力和体力的扩展与延伸 …

React合成事件

一、合成事件 event 是 SyntheticEvent &#xff0c;模拟出来 DOM 事件所有能力 event.nativeEvent 是原生事件对象 所有的事件&#xff0c;都被挂载到 document 上&#xff08;React ≤ 16&#xff09;&#xff0c;React17之后是挂载到root组件 和 DOM 事件不一样&#xff…

[1Panel]开源,现代化,新一代的 Linux 服务器运维管理面板

测评介绍 本期测评试用一下1Panel这款面板。1Panel是国内飞致云旗下开源产品。整个界面简洁清爽&#xff0c;后端使用GO开发&#xff0c;前端使用VUE的Element-Plus作为UI框架&#xff0c;整个面板的管理都是基于docker的&#xff0c;想法很先进。官方还提供了视频的使用教程&…

Spring Security配置多个数据源并添加登录验证码(7)

1、配置多个数据源 多个数据源是指在同一个系统中&#xff0c;用户数据来自不同的表&#xff0c;在认证时&#xff0c;如果第一张表没有查找到用户&#xff0c;那就去第二张表中査询&#xff0c;依次类推。 看了前面的分析&#xff0c;要实现这个需求就很容易了&#xff0c;认…

【计算机网络笔记】计算机网络体系结构概念

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

GO 语言的函数??

函数是什么&#xff1f; 学过编程的 xdm 对于函数自然不会陌生&#xff0c;那么函数是什么呢&#xff1f; 函数是一段可以重用的代码块&#xff0c;可以被多次调用&#xff0c;我们可以通过使用函数&#xff0c;提高咱们代码代码的模块化&#xff0c;提高程序的可读性和可维护…

渐进式编程之旅:探寻PHP函数的奇妙世界

目录 前言 一、函数的定义和调用 1.1 初识函数 1.1.1 函数分类 1.1.2 自定义函数 1.1.3 return关键字 1.2 参数设置 1.2.1 无参函数 1.2.2 按值传递参数 1.2.3 引用传参 1.2.4 设置参数默认值 1.2.5 指定参数类型&#xff08;弱&#xff09; 1.3 变量的作用域 1.3.1 变量分类 1…

使用bisect模块进行二分查找操作 bisect.bisect()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 使用bisect模块 进行二分查找操作 bisect.bisect() 选择题 请问bisect.bisect(c,2)的结果是&#xff1a; import bisect print("【执行】c [1,2,2,6,7]") c [1,2,2,6,7] print(c…

Linux性能优化--性能追踪3:系统级迟缓(prelink)

12.0 概述 本章包含的例子说明了如何用Linux性能工具寻找并修复影响整个系统而不是某个应用程序的性能问题。阅读本章后&#xff0c;你将能够&#xff1a; 追踪是哪一个进程导致了系统速度的降低。用strace调查一个不受CPU限制的进程的性能表现。用strace调查一个应用程序是如…

清除git历史敏感数据--bfg的应用

项目场景&#xff1a; 开源项目中的git历史中存在敏感 信息&#xff0c;如数据库地址&#xff0c;端口&#xff0c;密码&#xff0c;用户等 我们我们在主分支擦除密码&#xff0c;用户名等&#xff0c;git的历史记录里还是会有相关信息&#xff0c;并不能真正做到清除敏感信息…

前端自学需要把大量时间放在 HTML、CSS 吗?

前言 html和css其实不需要花费太多的时间&#xff0c;html暂且不说&#xff0c;css各类的属性太多了&#xff0c;平时如果只是简单做一些小网站根本不需要全部掌握&#xff0c;只需要掌握一些基础常用的即可&#xff0c;一般遇到不会的也可以直接查文档&#xff0c;就我个人来…

负载均衡、代理和动静分离的战略

一、Nginx简介 1.1 概述 Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理服务器,特点是占有内存少,并发能力强,能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数 。 1.2正向代理与反向代理 1.2.1正向代理 正向代理:如果把局域网外的 Internet 想象…

【数之道 06】神经网络模型中激活函数的选择

激活函数的选择 为什么使用激活函数激活函数的选择sigmoidtanhReLU(Rectified Linear Unit)Leaky ReLU&#xff08; ReLU 函数的变形体&#xff09;隐藏层的选择顺序 输出层的激活函数以业务要求为导向二分类问题多分类问题多标签问题线性回归问题 b站视频 为什么使用激活函数…

Linux内核8. Linux内核的经典调试方式

1 内核调试以及工具总结 内核总是那么捉摸不透, 内核也会犯错, 但是调试却不能像用户空间程序那样, 为此内核开发者为我们提供了一系列的工具和系统来支持内核的调试. 内核的调试, 其本质是内核空间与用户空间的数据交换, 内核开发者们提供了多样的形式来完成这一功能. 2 用户…

【一】Lambda表达式

1.Lambda表达式概述 ​ Lambda表达式是现代C在C 11和更高版本中的一个新的语法糖 &#xff0c;在C11、C14、C17和C20中Lambda表达的内容还在不断更新。 lambda表达式&#xff08;也称为lambda函数&#xff09;是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法。…