【Zookeeper】 面试题总结

news2024/11/26 16:47:31

Zookeeper

  • 1、工作中 Zookeeper 有什么用途吗
  • 2、zookeeper 数据模型是什么样的
  • 3、那你知道 znode 有几种类型呢
  • 4、你知道 znode 节点里面存储什么吗
  • 5、每个节点数据最大不能超过多少呢
  • 6、你知道 znode 节点上监听机制嘛
  • 7、那你讲下 Zookeeper 特性吧
  • 8、你刚提到顺序一致性,那 zookeeper 如何保证事务顺序一致性呢
  • 9、你知道 Zookeeper 服务器有几种角色嘛
  • 10、Zookeeper 下 Server 工作状态又有几种
  • 11、ZooKeeper 集群部署图
  • 12、ZooKeeper 如何保证主从节点数据一致性
  • 13、你讲一下 ZooKeeper 选举机制吧
  • 14、讲一下 zk 分布式锁实现原理吧
  • 15、ZooKeeper 集群为啥最好奇数台

1、工作中 Zookeeper 有什么用途吗

  1. 注册中心
    • 什么是注册中心
      • 注册中心是只有在分布式架构中才有的概念,注册中心是分布式架构中所有服务器注册信息等相关功能的专用服务器,注册的地址是服务器的名称与对应IP地址。分布式环境下的应用在启动时候都会向这个地方来注册自己的网络地址和名称。用一句话来概括,注册中心的作用就是存放和调度服务,实现服务和注册中心,服务与服务之间的通信。
      • 在分布式架构中,服务会注册到注册中心,应用 a 访问应用 b 时,则应用 a 首先通过应用 b 的应用名称向注册中心获取最新的 ip 地址,再根据 ip 地址访问应用 b。
    • 为什么需要注册中心
      • ① 在不用服务注册之前,去维护这种关系网络呢就是写死 IP 地址。将其他模块的 ip 和 port 写死在自己的配置文件里,甚至写死在代码里,每次要去新增或者移除1个服务的实例的时候,就得去通知其他所有相关联的服务去修改。随之而来的就是各个项目的配置文件的反复更新、每隔一段时间大规模的 ip 修改和机器裁撤,非常的痛苦。
      • ② 有了注册中心之后,每个服务在调用别人的时候只需要服务的名称就好,调用时会通过注册中心根据服务编码进行具体服务地址进行调用。
  2. 分布式锁
    • 分布式锁的实现方式有很多种,比如 Redis 、数据库 、zookeeper 等。个人认为 zookeeper 在实现分布式锁这方面是非常非常简单的。
  3. 命名服务
    • 命名服务指通过指定名字来获取资源或者服务地址。Zookeeper 可以创一个全局唯一路径,这个路径就可以作为一个名字。被命名实体可以集群中机器,服务地址,或者远程对象等。
  4. 集群管理
    • 集群管理包括集群监控和集群控制,其实就监控集群机器状态,剔除机器和加入机器。zookeeper 可以方便集群机器管理,它可以实时监控 znode 节点变化,一旦发现有机器挂了,该机器就会与 zk 断开连接,对用临时目录节点会被删除,其他所有机器都收到通知。

2、zookeeper 数据模型是什么样的

ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二级制序列。并且。每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表。每个数据节点在ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。并且,每个 znode 都一个唯一的路径标识。

从下图可以更直观地看出:ZooKeeper 节点路径标识方式和 Unix 文件系统路径非常相似,都是由一系列使用斜杠"/"进行分割的路径表示,开发人员可以向这个节点中写入数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。

在这里插入图片描述

3、那你知道 znode 有几种类型呢

我们通常是将 znode 分为 4 大类:

  • 持久(PERSISTENT)节点 :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
  • 临时(EPHEMERAL)节点 :临时节点的生命周期是与 客户端会话(session) 绑定的,会话消失则节点消失 。并且,临时节点只能做叶子节点 ,不能创建子节点。
  • 持久顺序(PERSISTENT_SEQUENTIAL)节点 :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 /node1/app0000000001 、/node1/app0000000002 。
  • 临时顺序(EPHEMERAL_SEQUENTIAL)节点 :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性

4、你知道 znode 节点里面存储什么吗

  • stat :状态信息。

Stat 类中包含了一个数据节点的所有状态信息的字段,包括事务 ID(cZxid)、节点创建时间(ctime) 和子节点个数(numChildren) 等等,如下:

在这里插入图片描述

  • data : znode 存储业务数据信息。
  • acl : 记录客户端对 znode 节点访问权限,如 IP 等。
    • ZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。对于 znode 操作的权限,ZooKeeper 提供了以下 5 种:
      • CREATE : 能创建子节点
      • READ :能获取节点数据和列出其子节点
      • WRITE : 能设置/更新节点数据
      • DELETE : 能删除子节点
      • ADMIN : 能设置节点 ACL 的权限
    • 其中尤其需要注意的是,CREATE 和 DELETE 这两种权限都是针对 子节点 的权限控制。对于身份认证,提供了以下几种方式:
      • world : 默认方式,所有用户都可无条件访问。
      • auth :不使用任何 id,代表任何已认证的用户。
      • digest :用户名:密码认证方式: username:password 。
      • ip : 对指定 ip 进行限制
  • child : 当前节点子节点引用。

5、每个节点数据最大不能超过多少呢

为了保证高吞吐和低延迟,以及数据一致性,znode 只适合存储非常小数据,不能超过 1M,最好都小于 1K。

6、你知道 znode 节点上监听机制嘛

Watcher 为事件监听器,是 zk 非常重要的一个特性,很多功能都依赖于它,它有点类似于订阅的方式,即客户端向服务端注册指定的 watcher ,当服务端符合了 watcher 的某些事件或要求则会向客户端发送事件通知 ,客户端收到通知后找到自己定义的 Watcher 然后执行相应的回调方法 。

在这里插入图片描述

可以把 Watcher 理解成客户端注册在某个 Znode 上触发器,当这个 Znode 节点发生变化时(增删改查),就会触发 Znode 对应注册事件,注册客户端就会收到异步通知,然后做出业务改变。

zookeeper 监听原理

zookeeper的监听事件有四种

  • nodedatachanged 节点数据改变
  • nodecreate 节点创建事件
  • nodedelete 节点删除事件
  • nodechildrenchanged 子节点改变事件

在这里插入图片描述

ZooKeeper Watcher 机制主要包括客户端线程、客户端WatcherManager、Zookeeper 服务器三部分。

  1. 客户端向 ZooKeeper 服务器注册 Watcher 同时,会将 Watcher 对象存储在客户端 WatchManager 中。
  2. 当 zookeeper 服务器触发 watcher 事件后,会向客户端发送通知, 客户端线程从 WatcherManager 中取出对应Watcher 对象来执行回调逻辑。

7、那你讲下 Zookeeper 特性吧

  1. 全局数据一致:集群中每个服务器保存一份相同的数据副本,client无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征。
  2. 可靠性:如果消息被其中一台服务器接受,那么将被所有的服务器接受。
  3. 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。
  4. 数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态。
  5. 实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。

8、你刚提到顺序一致性,那 zookeeper 如何保证事务顺序一致性呢

9、你知道 Zookeeper 服务器有几种角色嘛

在这里插入图片描述

10、Zookeeper 下 Server 工作状态又有几种

  1. LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
  2. FOLLOWING:跟随者状态。表明当前服务器角色✁ Follower。
  3. LEADING:领导者状态。表明当前服务器角色✁ Leader。
  4. OBSERVING:观察者状态。表明当前服务器角色✁ Observer。

11、ZooKeeper 集群部署图

为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么 ZooKeeper 本身仍然是可用的。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。ZooKeeper 官方提供的架构图就是一个 ZooKeeper 集群整体对外提供服务。

在这里插入图片描述
上图中每一个 Server 代表一个安装 ZooKeeper 服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 ZAB 协议(ZooKeeper Atomic Broadcast)来保持数据的一致性。

最典型集群模式: Master/Slave 模式(主备模式)。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。

12、ZooKeeper 如何保证主从节点数据一致性

ZooKeeper 主要提供数据的一致性服务,其实现分布式系统的状态一致性依赖一个叫 Paxos 的算法。Paxos 算法在多台服务器通过内部的投票表决机制决定一个数据的更新与写入。

但Paxos 算法有点过于复杂、实现难度也比较高,所以 ZooKeeper 在编程实现的时候将其简化成了一种叫做 ZAB 的算法(Zookeeper Atomic Broadcast, Zookeeper 原子广播)。

在这里插入图片描述

ZAB 算法的目的,同样是在多台服务器之间达成一致,保证这些服务器上存储的数据是一致的。ZAB 算法的主要特点在于:需要在这些服务器中选举一个 Leader,所有的写请求都必须提交给 Leader。由 Leader 服务器向其他服务器(Follower)发起 Propose,通知所有服务器:我们要完成一个写操作请求,大家检查自己的数据状态,是否有问题。

如果所有 Follower 服务器都回复 Leader 服务器 ACK,即没有问题,那么 Leader 服务器会向所有 Follower 发送 Commit 命令,要求所有服务器完成写操作。这样包括 Leader 服务器在内的所有 ZooKeeper 集群服务器的数据,就都更新并保持一致了。如果有两个客户端程序同时请求修改同一个数据,因为必须要经过 Leader 的审核,而 Leader 只接受其中一个请求,数据也会保持一致。

在实际应用中,客户端程序可以连接任意一个 Follower,进行数据读写操作。如果是写操作,那么这个请求会被这个 Follower 发送给 Leader,进行如上所述的处理;如果是读操作,因为所有服务器的数据都是一致的,那么这个 Follower 直接返回自己本地的数据给客户端就可以了。

13、你讲一下 ZooKeeper 选举机制吧

服务器启动或者服务器运行期间(Leader 挂了),都会进入 Leader 选举,我们来看一下~假设现在 ZooKeeper 集群有五台服务器。

服务器启动 Leader 选举

在这里插入图片描述

服务器运行期间 Leader 选举

在这里插入图片描述

选举Leader规则:①EPOCH大的直接胜出 ②EPOCH相同,事务id大的胜出 ③事务id相同,服务器id大的胜出

总结:择优选取,保证leader是zk集群中数据最完整、最可靠的一台服务器。

14、讲一下 zk 分布式锁实现原理吧

ZooKeeper 分布式锁是基于临时顺序节点和Watcher(事件监听器) 实现的。

获取锁:

  1. 首先我们要有一个持久节点/locks,客户端获取锁就是在locks下创建临时顺序节点。
  2. 假设客户端 1 创建了/locks/lock1节点,创建成功之后,会判断 lock1是否是 /locks 下最小的子节点。
  3. 如果 lock1是最小的子节点,则获取锁成功。否则,获取锁失败。
  4. 如果获取锁失败,则说明有其他的客户端已经成功获取锁。客户端 1 并不会不停地循环去尝试加锁,而是在前一个节点比如/locks/lock0上注册一个事件监听器。这个监听器的作用是当前一个节点释放锁之后通知客户端 1(避免无效自旋),这样客户端 1 就加锁成功了。

释放锁:

  1. 成功获取锁的客户端在执行完业务流程之后,会将对应的子节点删除。
  2. 成功获取锁的客户端在出现故障之后,对应的子节点由于是临时顺序节点,也会被自动删除,避免了锁无法被释放。
  3. 我们前面说的事件监听器其实监听的就是这个子节点删除事件,子节点删除就意味着锁被释放。

在这里插入图片描述

分布式锁

package com.example.canal.zk;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class DistributedLock {

    private final String connectionString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
    private final int sessionTimeout = 2000;
    private final ZooKeeper zk;
    private CountDownLatch countDownLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);
    private String waitPath;
    private String currentMode;

    public DistributedLock() throws IOException, InterruptedException, KeeperException {

        // 获取连接
        zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // connectLatch 如果连接上zk 可以释放
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    countDownLatch.countDown();
                }
                // waitLatch 需要释放
                if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)) {
                    waitLatch.countDown();
                }
            }
        });
        // 等待zk正常连接后,往下走程序
        countDownLatch.await();

        // 判断根节点/locks是否存在
        Stat stat = zk.exists("/locks", false);

        if (stat == null) {
            // 创建根节点,这是⼀个完全开放的ACL,持久节点
            zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }

    }

    // 对zk加锁
    public void zkLock() {

        try {
            // 创建对应的临时顺序节点
            currentMode =
                zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

            // 判断创建的节点是否是序号最小的节点,如果是获取到锁,如果不是,监听他序号前一个节点
            List<String> children = zk.getChildren("/locks", false);
            if (children.size() == 1) {
                return;
            } else {

                //[seq-0000000016, seq-0000000017]
                Collections.sort(children);

                // 获取节点名称 /locks/seq-0000000017 -> seq-0000000017
                String thisNode = currentMode.substring("/locks/".length());

                // 通过seq-0000000017获取该节点在children集合的位置
                int index = children.indexOf(thisNode);

                // 判断
                if (index == -1) {
                    System.out.println("数据异常");
                } else if (index == 0) {
                    // 就一个节点,可以获取锁了
                    return;
                } else {
                    // 需要监听 他前一个结点的变化
                    waitPath = "/locks/" + children.get(index - 1);
                    zk.getData(waitPath, true, null);
                    // 等待监听
                    waitLatch.await();
                }

            }

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    // 解锁
    public void unZkLock() {
        // 删除节点
        try {
            zk.delete(currentMode, -1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}

测试类

package com.example.canal.zk;

import org.apache.zookeeper.KeeperException;

import java.io.IOException;

public class DistributedLockTest {

    public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
        final DistributedLock lock1 = new DistributedLock();
        final DistributedLock lock2 = new DistributedLock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.zkLock();
                    System.out.println("线程1 启动,获取到锁");
                    Thread.sleep(5 * 1000);
                    lock1.unZkLock();
                    System.out.println("线程1 释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.zkLock();
                    System.out.println("线程2 启动,获取到锁");
                    Thread.sleep(5 * 1000);
                    lock2.unZkLock();
                    System.out.println("线程2 释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

为什么要用临时顺序节点

临时节点相比持久节点,最主要的是对会话失效的情况处理不一样,临时节点会话消失则对应的节点消失。这样的话,如果客户端发生异常导致没来得及释放锁也没关系,会话失效节点自动被删除,不会发生死锁的问题。

使用 Redis 实现分布式锁的时候,我们是通过过期时间来避免锁无法被释放导致死锁问题的,而 ZooKeeper 直接利用临时节点的特性即可。

假设不适用顺序节点的话,所有尝试获取锁的客户端都会对持有锁的子节点加监听器。当该锁被释放之后,势必会造成所有尝试获取锁的客户端来争夺锁,这样对性能不友好。使用顺序节点之后,只需要监听前一个节点就好了,对性能更友好。

为什么要设置对前一个节点的监听

同一时间段内,可能会有很多客户端同时获取锁,但只有一个可以获取成功。如果获取锁失败,则说明有其他的客户端已经成功获取锁。获取锁失败的客户端并不会不停地循环去尝试加锁,而是在前一个节点注册一个事件监听器。

这个事件监听器的作用是: 当前一个节点对应的客户端释放锁之后(也就是前一个节点被删除之后,监听的是删除事件),通知获取锁失败的客户端(唤醒等待的线程,Java 中的 wait/notifyAll ),让它尝试去获取锁,然后就成功获取锁了。

15、ZooKeeper 集群为啥最好奇数台

ZooKeeper 集群在宕掉几个 ZooKeeper 服务器之后,如果剩下的 ZooKeeper 服务器个数大于宕掉的个数的话整个 ZooKeeper 才依然可用。假如我们的集群中有 n 台 ZooKeeper 服务器,那么也就是剩下的服务数必须大于 n/2。先说一下结论,2n 和 2n-1 的容忍度是一样的,都是 n-1,大家可以先自己仔细想一想,这应该是一个很简单的数学问题了。

比如假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台。 假如我们有 5 台,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的的时候也同样只允许宕掉 2 台。

综上,何必增加那一个不必要的 ZooKeeper 呢?

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

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

相关文章

计算机网络-基础编程实验(JAVA\Python3)

计算机网络-网络基础编程实验(JAVA\Python3) 一.实验目的 通过本实验&#xff0c;学习采用Socket&#xff08;套接字&#xff09;设计简单的网络数据收发程序&#xff0c;理解应用数据包是如何通过传输层进行传送的。 二.实验内容 学习套接字编程&#xff0c;完成以下的网络数…

海尔牵头IEEE P2786国际标准通过Sponsor投票并连任工作组主席

01 海尔牵头IEEE P2786国际标准 通过Sponsor投票 并连任工作组主席 海尔牵头制定的全球首个服装物联网国际标准IEEE P2786《Standard for General Requirements and Interoperability for Internet of Clothing》通过Sponsor投票&#xff0c;标志着该国际标准草案得到了行业…

ThingsBoard教程(三六):规则节点解析 检查关系节点 check relation,实体类型节点 entity type

前言 本篇文章和大家一起来学习两个节点,检查关系节点 check relation和实体类型节点 entity type。 check relation 检查消息的发起者与其他实体之间是否存在关系。如果选择了“check relation to specific entity(检查与特定实体的关系)”,则必须指定相关实体。否则,…

程序员的4个真相(看了有点想乱搞)

最近在知乎看到一个很有意思的问题&#xff0c;热度还挺高的。 “程序员的通病是什么&#xff1f;” 很多人第一印象就是&#xff1a; - 加班 - 脱发 - 单身直男 - 亚历山大 - 需求魔幻 其实这些标签在其他行业领域也普遍存在&#xff0c;也并不是程序员真正的悲哀。 真正的…

3 程序流程控制-函数、包、常用函数【Go语言教程】

3 程序流程控制-函数、包【Go语言教程】 一、 程序流程控制 顺序控制分支控制switch分支for循环 Go 语言没有 while 和 do…while 语法&#xff0c;这一点需要同学们注意一下&#xff0c;如果我们需要使用类似其它语言(比如 java / c 的 while 和 do…while )&#xff0c;可以…

CVE-2023-28432 MiniO信息泄露漏洞复现

CVE-2023-28432 MiniO信息泄露漏洞 MiniO 是一个基于 Apache License v2.0 开源协议的对象存储服务。它兼容亚马逊 S3 云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等 在集群部署的 Minio…

计算机网络学习05(HTTP vs HTTPS)

1、HTTP 协议介绍 HTTP 协议&#xff0c;全称超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff09;。顾名思义&#xff0c;HTTP 协议就是用来规范超文本的传输&#xff0c;超文本&#xff0c;也就是网络上的包括文本在内的各式各样的消息&#xff0c;具体来说&…

【干货分享】一文说透分布式一致性协议(上)

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;大熊老师 | 慕课网讲师 在常见的分布式系统中&#xff0c;总会发生诸如机器宕机或网络异常&#xff08;包括消息的延迟…

ChatGPT实现多语种翻译

语言翻译 多语种翻译是 NLP 领域的经典话题&#xff0c;也是过去很多 AI 研究的热门领域。一般来说&#xff0c;我们认为主流语种的互译一定程度上属于传统 AI 已经能较好完成的任务。比如谷歌翻译所采用的的神经机器翻译(NMT, Neural Machine Translation)技术就一度让世人惊…

【JavaEE进阶】——第四节.Spring更简单的实现Bean对象的存取(利用注解储存和注入Bean对象)

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;JavaEE进阶 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录 前…

Linux安装flutter

在Linux上安装flutter 1.使用快照安装flutter 安装snapd sudo apt update sudo apt install snapd安装flutter sudo snap install flutter --classic显示flutter sdk-path flutter sdk-path2.手动安装flutter 在flutter官网上查看安装流程 https://docs.flutter.dev/get…

现代微服务中缓存的分类及缓存一致性设计原则

引言 大部分面向公众的互联网系统&#xff0c;其并发请求数量与在线用户数量都是正相关的&#xff0c;而 MySQL能够承担的并发读写量是有一定上限的&#xff0c;当系统的访问量超过一定程度的时候&#xff0c;纯MySQL就很难应付了。 绝大多数互联网系统都是采用MySQLRedis这对…

MongoDB【索引-index】

目录 1&#xff1a;概述 2&#xff1a;索引的类型 2.1&#xff1a;单字段索引 2.2&#xff1a;复合索引 2.3&#xff1a;其他索引 3&#xff1a;索引的管理操作 3.1&#xff1a;索引的查看 3.2&#xff1a;索引的创建 3.3&#xff1a;索引的移除 4&#xff1a;索引的…

【福利赠书】有人说,测试驱动开发已死?(文末赠书3本)

友情推荐一本测试领域的教科书&#xff1a;&#xff08;文末送3本&#xff09; 《 测试驱动开发&#xff1a;入门、实战与进阶》&#xff0c;英文原书名为《Learning Test-Driven Development 》&#xff0c;是一本实用且有趣的TDD实践教程。如果你想开始做测试驱动开发&#x…

【WPF动画】简单构造一个相册轮播图淡化切换特效

效果图 简单的定时器结合DoubleAnimation使用示例&#xff0c;实现轮播图淡化切入特效 代码部分 <Image x:Name"carouselImage" Margin"10" HorizontalAlignment"Center" VerticalAlignment"Center" Stretch"UniformToFill&…

倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析

倾斜摄影超大场景的三维模型的顶层合并的纹理压缩与抽稀处理技术分析 倾斜摄影超大场景的三维模型的顶层合并需要对纹理进行压缩和抽稀处理&#xff0c;以减小数据量和提高数据的传输和展示性能。以下是一种常用的纹理压缩和抽稀处理技术&#xff1a; 1、纹理图集 纹理瓦片化…

网络安全漏洞分析之远程代码执行

介绍 Apache Flume 是一个分布式的&#xff0c;可靠的&#xff0c;并且可用于高效地收集&#xff0c;汇总和移动大量日志数据的软件。它具有基于流数据流的简单而灵活的体系结构。它具有可调的可靠性机制以及许多故障转移和恢复机制&#xff0c;并且具有健壮性和容错性。它使用…

国民技术N32G430开发笔记(9)- IAP升级 Bootloader的制作

IAP升级 Bootloader的制作 1、上节提到Flash的分区&#xff0c;0x8000000-0x8004000为Boot分区&#xff0c;我们的bootloader就烧录到此分区。 Bootloader很简单&#xff0c;新建一个普通的工程&#xff0c; 也不用初始化外部设备&#xff0c;开机后&#xff0c;直接跳转到 A…

【方法】 如何批量将RAR或其他压缩格式转换成ZIP?

压缩文件的格式有很多种&#xff0c;比如RAR、ZIP、7-Zip、CAB、ARJ、ACE、TAR、BZ2等等。因为需求不同&#xff0c;或者不同平台对上传的压缩包格式要求不同&#xff0c;我们往往需要把压缩文件进行格式转换&#xff0c;那压缩文件不同格式之间如何进行转换呢&#xff1f; 如…

AlgoC++第八课:手写BP

目录 手写BP前言1. 数据加载2. 前向传播3. 反向传播总结 手写BP 前言 手写AI推出的全新面向AI算法的C课程 Algo C&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考。 本次课程主要是手写 BP 代码 课程大纲可看下面的思维导图 1. 数据加载 我们首先来实现下MNIST…