Redis学习——高级篇⑨

Redis学习——高级篇⑨

    • = = = = = = = Redis7高级之Redlock算法和Redisson的使用(十) = = = = = = =
    • 10.1 Redlock 红锁算法
      • 1.解决手写分布式锁的单点故障问题
      • 2.设计理念
      • 3. 解决方案
    • 10.2 Redisson进行代码改造
    • 10.3 多机案例(解决单点故障)
    • 10.4 Redis 的缓存淘汰策略
      • 1.Redis 内存满了怎么办?
      • 2.Redis 过期键的删除策略
      • 3.redis 缓存淘汰策略
        • 3.1 LRU 和 LFU
        • 3.2 8种缓存淘汰策略
      • 4.性能配置建议

在这里插入图片描述

在这里插入图片描述
lock加锁关键逻辑

  1. 加锁的Lua脚本,通过redis 里面的hash数据模型,加锁和可重入性都要保证
  2. 加锁不成,需要while进行重试并自旋.
  3. 自动续期,加个钟

在这里插入图片描述

unlock解锁关键逻辑

  1. 考虑可重入性的递减,加锁几次就要减锁几次
  2. 最后到零了,直接del删除

在这里插入图片描述
在这里插入图片描述

= = = = = = = Redis7高级之Redlock算法和Redisson的使用(十) = = = = = = =

在这里插入图片描述

10.1 Redlock 红锁算法

1.解决手写分布式锁的单点故障问题

  • Redis 提供了 Redlock 算法,用来实现基于多个实例的分布式锁
  • 锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作
  • Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用

红锁算法

在算法的分布式版本中,我们假设我们有N个Redis主节点。这些节点是完全独立的,所以我们不使用复制或任何其他隐式协调系统。我们已经描述了如何在单个实例中安全地获取和释放锁。我们想当然地认为算会使用这个方法在单个实例中获取和释放锁。在我们的示例中,我们设置N=5,这是一个合理的值,因此我们需要在不同的计算机或虚拟机上运行5个Redis master,以确保它们以几乎独立的方式发生故障

为了获取锁,客户端执行以下操作:

  1. 它以毫秒为单位获取当前时间。
  2. 它尝试在所有N个实例中顺序获取锁,在所有实例中使用相同的键名和随机值。在步骤2中,在每个实例中设置锁时,客户端使用与总锁自动释放时间相比较小的超时来获取锁。例如,如果自动释放时间为10秒,则超时可能在5-50毫秒范围内。这可以防止客户端在尝试与已关闭的Redis节点通信时长时间处于阻塞状态:如果一个实例不可用, 我们应该尽快尝试与下一个实例通信。
  3. 客户端通过从当前时间减去步骤1中获得的时间戳来计算获得锁所用的时间。当且仅当客户端能够在大多数实例(至少3个)中获得锁时,并且获得锁的总时间小于锁有效期,则认为获得了锁。
  4. 如果获得了锁,则其有效时间被认为是初始有效时间减去经过的时间,如步骤3中计算的那样。
  5. 如果客户端由于某种原因未能获得锁(要么无法锁定 N / 2 + 1 N/2+1 N/2+1个实例,要么有效期为负),它将尝试解锁所有实例(即使是它认为不能锁定的实例)可以锁定)

2.设计理念

假设我们有N个Redis主节点,例如 N = 5 N = 5 N=5这些节点是完全独立的,我们不使用复制或任何其他隐式协调系统,为了取到锁客户端执行以下操作:

1获取当前时间,以毫秒为单位;
2依次尝试从5个实例,使用相同的 key 和随机值(例如 UUID)获取锁。当向Redis 请求获取锁时,客户端应该设置一个超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为 10 秒,则超时时间应该在 5-50 毫秒之间。这样可以防止客户端在试图与一个宕机的 Redis 节点对话时长时间处于阻塞状态。如果一个实例不可用,客户端应该尽快尝试去另外一个 Redis 实例请求获取锁;
3客户端通过当前时间减去步骤 1 记录的时间来计算获取锁使用的时间。当且仅当从大多数(N/2+1,这里是 3 个节点)的 Redis 节点都取到锁,并且获取锁使用的时间小于锁失效时间时,锁才算获取成功;
4如果取到了锁,其真正有效时间等于初始有效时间减去获取锁所使用的时间(步骤 3 计算的结果)。
5如果由于某些原因未能获得锁(无法在至少 N / 2 + 1 N/2 + 1 N/2+1 个 Redis 实例获取锁、或获取锁的时间超过了有效时间),客户端应该在所有的 Redis 实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。

该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用 master 节点,同时由于舍弃了 slave,为了保证可用性,引入了 N 个节点,官方建议是 5。

客户端只有在满足下面的这两个条件时,才能认为是加锁成功。

条件1:客户端从超过半数(大于等于N/2+1)的Redis实例上成功获取到了锁;

条件2:客户端获取锁的总耗时没有超过锁的有效时间。

3. 解决方案

在这里插入图片描述

为什么是奇数? N = 2X + 1 (N是最终部署机器数,X是容错机器数)

1 先知道什么是容错

失败了多少个机器实例后我还是可以容忍的,所谓的容忍就是数据一致性还是可以Ok的,CP数据一致性还是可以满足

加入在集群环境中,redis失败1台,可接受。2X+1 = 2 * 1+1 =3,部署3台,死了1个剩下2个可以正常工作,那就部署3台。

加入在集群环境中,redis失败2台,可接受。2X+1 = 2 * 2+1 =5,部署5台,死了2个剩下3个可以正常工作,那就部署5台。

2 为什么是奇数?

最少的机器,最多的产出效果

​ 加入在集群环境中,redis失败1台,可接受。2N+2= 2 * 1+2 =4,部署4台

​ 加入在集群环境中,redis失败2台,可接受。2N+2 = 2 * 2+2 =6,部署6台

10.2 Redisson进行代码改造

Redisson 就是 Redlock算法 的实现

  • POM
<!--redisson-->
<dependency>
	 <groupId>org.redisson</groupId>
	 <artifactId>redisson</artifactId>
	 <version>3.13.4</version>
</dependency>
  • RedisConfig
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory)
    {
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        //设置key序列化方式string
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式json
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

//    v 8.0 引入 redisson
    @Bean
    public Redisson redisson() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.238.111:6379")
                .setDatabase(0)
                .setPassword("123456");
        return (Redisson) Redisson.create(config);
    }
}
  • InventoryController
import com.xfcy.service.InventoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@Api(tags = "redis分布式锁测试")
public class InvetoryController {

    @Autowired
    private InventoryService inventoryService;

    @ApiOperation("扣减库存sale,一次卖一个")
    @GetMapping(value = "/inventory/sale")
    public String sale()
    {
        return inventoryService.sale();
    }


    @ApiOperation("扣减库存saleByRedisson,一次卖一个")
    @GetMapping(value = "/inventory/saleByRedisson")
    public String saleByRedisson()
    {
        return inventoryService.saleByRedisson();
    }
}
  • InventoryService
/**
 * v 9.0    引入Redisson对应的官网推荐RedLock算法实现
 *
 * @return
 */
@Autowired
private Redisson redisson;

public String saleByRedisson() {
    String retMessage = "";

    RLock redissonLock = redisson.getLock("xfcyRedisLock");
    redissonLock.lock();
    try {
        //1 查询库存信息
        String result = stringRedisTemplate.opsForValue().get("inventory001");
        //2 判断库存是否足够
        Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
        //3 扣减库存
        if (inventoryNumber > 0) {
            stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
            retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;

        } else {
            retMessage = "商品卖完了,o(╥﹏╥)o";
        }
        System.out.println(retMessage);
    } finally {
        // 改进点,只能删除属于自己的 key,不能删除别人的
        if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){
            redissonLock.unlock();
        }
    }
    return retMessage + "\t" + "服务端口号:" + port;
}

10.3 多机案例(解决单点故障)

使用 Redisson 的 MultiLock 多重锁

  • 使用docker 启动 3 台redis master ,3台master 并无从属关系
docker run -p 6381:6379 --name redis-master-1 -d redis
docker run -p 6382:6379 --name redis-master-2 -d redis
docker run -p 6383:6379 --name redis-master-3 -d redis
  • 进入到redis容器实例
docker exec -it redis-master-1 redis-cli
docker exec -it redis-master-2 /bin/bash 
docker exec -it redis-master-3 /bin/bash  
  • 建 Module redis_redlock

  • POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.10.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.atguigu.redis.redlock</groupId>
    <artifactId>redis_redlock</artifactId>
    <version>0.0.1-SNAPSHOT</version>


    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.19.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.14</version>
        </dependency>
        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--swagger-ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  • YML
server.port=9090
spring.application.name=redlock


spring.swagger2.enabled=true


spring.redis.database=0
#没配置密码
spring.redis.password=
spring.redis.timeout=3000
spring.redis.mode=single

spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10

spring.redis.single.address1=192.168.238.111:6381
spring.redis.single.address2=192.168.238.111:6382
spring.redis.single.address3=192.168.238.111:6383
  • 主启动
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RedisRedlockApplication {

    public static void main(String[] args) {
        SpringApplication.run(RedisRedlockApplication.class, args);
    }
}
  • 配置类
  • CacheConfiguration
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {

    @Autowired
    RedisProperties redisProperties;

    @Bean
    RedissonClient redissonClient1() {
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress1();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            serverConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }

    @Bean
    RedissonClient redissonClient2() {
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress2();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            serverConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }

    @Bean
    RedissonClient redissonClient3() {
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress3();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            serverConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }


    /**
     * 单机
     * @return
     */
    /*@Bean
    public Redisson redisson()
    {
        Config config = new Config();

        config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);

        return (Redisson) Redisson.create(config);
    }*/
}
  • RedisPoolProperties
import lombok.Data;

@Data
public class RedisPoolProperties {
    private int maxIdle;

    private int minIdle;

    private int maxActive;

    private int maxWait;

    private int connTimeout;

    private int soTimeout;

    /*
      池大小
     */
    private  int size;
}
  • RedisProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;


@ConfigurationProperties(prefix = "spring.redis", ignoreUnknownFields = false)
@Data
public class RedisProperties {

    private int database;

    /*
    等待节点回复命令的时间。 该时间从命令发送成功时开始计时
     */
    private int timeout = 3000;

    private String password;

    private String mode;

    /*
        池配置
     */
    private RedisPoolProperties pool;

    /*
        单机信息配置
     */
    private RedisSingleProperties single;

}
  • RedisSingleProperties
import lombok.Data;

@Data
public class RedisSingleProperties {

    private String address1;

    private String address2;

    private String address3;
}
  • 业务类
  • RedLockController
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@RestController
@Slf4j
public class RedLockController {

    public static final String CACHE_KEY_REDLOCK = "ATGUIGU_REDLOCK";

    @Autowired
    RedissonClient redissonClient1;

    @Autowired
    RedissonClient redissonClient2;

    @Autowired
    RedissonClient redissonClient3;

    boolean isLockBoolean;

    @GetMapping(value = "/multiLock")
    public String getMultiLock() throws InterruptedException
    {
        String uuid =  IdUtil.simpleUUID();
        String uuidValue = uuid+":"+Thread.currentThread().getId();

        RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);
        RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);
        RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);

        RedissonMultiLock redLock = new RedissonMultiLock(lock1, lock2, lock3);
        redLock.lock();
        try
        {
            System.out.println(uuidValue+"\t"+"---come in biz multiLock");
            try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(uuidValue+"\t"+"---task is over multiLock");
        } catch (Exception e) {
            e.printStackTrace();
            log.error("multiLock exception ",e);
        } finally {
            redLock.unlock();
            log.info("释放分布式锁成功key:{}", CACHE_KEY_REDLOCK);
        }
        return "multiLock task is over  "+uuidValue;
    }
}
  • 测试

    • 就是在发送请求时,模拟一台机器挂掉,查看系统是否还能运行

10.4 Redis 的缓存淘汰策略

1.Redis 内存满了怎么办?

  • 查看 Redis 最大占用内存

在这里插入图片描述

  • redis默认内存多少

    • 如果在 64位操作系统, maxmemory 设置0或者不设置最大内存大小表示不限制Redis内存使用
  • 一般生产上如何配置

    • 推荐Redis设置为最大物理内存的四分之三

如何修改redis内存设置

  • 通过配置文件修改 (单位是 byte)

    在这里插入图片描述

  • 通过命令修改

在这里插入图片描述

查看redis内存使用情况

  • info memory
  • config get maxmemory

设置了maxmemory的选项,假如redis 内存使用达到上限,没有加上过期时间就会导致数据写满 maxmemory,这就需要内存淘汰策略

2.Redis 过期键的删除策略

  1. 立即删除

    • 对CPU不友好,用处理器性能换取存储空间(拿时间换空间)
  2. 惰性删除

    • 对memory 不友好,用存储空间换取处理器性能(拿空间换时间)

    • 开启惰性删除, lazyfree-lazy-eviction=yes

在这里插入图片描述

  1. 定期删除

    • 每隔一段时间执行一次删除过期键操作并通过限制删除操作执行时长和频率来减少删除操作对CPU时间的影响。
    • 定期抽样key,判断是否过期
    • 容易出现漏网之鱼

3.redis 缓存淘汰策略

在这里插入图片描述

3.1 LRU 和 LFU
  • LRU
    • 最近最少使用的页面置换算法,淘汰最长时间未被使用的页面,看页面最后一次被使用到发生调度的时间长短,首先淘汰最长时间未被使用的页面
  • LFU
  • 最近最不常用页面置换算法,淘汰一定时期内被访问次数最少的页面,看一定时间段内被访问次数最少的页,看一定时间段内页面被使用的频率,淘汰一定时期内被访问次数最少的页
3.2 8种缓存淘汰策略
  1. noevication : 不会驱逐任何key,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都返回 error
  2. allkeys-lru: 对所有key使用 LRU算法进行删除,优先删除掉最近不经常使用的key,用以保存新数据
  3. volatie-lru : 对所有设置了过期时间的key使用LRU 算法删除
  4. allkeys-random :对所有key随机删除
  5. volatie-random : 对所有设置了过期时间的key随机删除
  6. volatie-ttl :对所有设置了过期时间的key随即删除
  7. allkeys-lfu:对所有key使用LFU算法进行删除
  8. volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除

推荐使用

  • 在所有的 key 都是最近经常使用的,那么就需要选择 allkeys-lru 进行置换最近最不经常使用的key,如果不确定使用哪种策略,那么推荐使用 allkeys-lru
  • 如果所有的key的访问概率都是差不多的,那么可以选用 allkeys-random 策略去置换数据
  • 如果对数据有足够的了解,能够为key指定hint(expire/ttl指定),那么可以选择 volatile-ttl 进行置换(不大推荐,要求过高)

4.性能配置建议

  • 避免存储 bigkey
  • 开启惰性淘汰 lazyfree-lazy-eviction=yes

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3使用is动态切换组件报错Vue received a Component which was made a reactive object.

vue3使用is动态切换组件&#xff0c;activeComponent用ref定义报错 Vue received a Component which was made a reactive object. This can lead to unnecessary performance overhead, and should be avoided by marking the component with markRaw or using shallowRef ins…

代码随想录算法训练营29期|day36任务以及具体安排

第八章 贪心算法 part05 435. 无重叠区间 class Solution {public int eraseOverlapIntervals(int[][] intervals) {Arrays.sort(intervals, (a,b)-> {return Integer.compare(a[0],b[0]);});if(intervals.length 1) return 0;int result 0;for(int i 1 ; i < interva…

结构体与共用体——共用体——C语言——day16

昨天介绍了下结构体&#xff0c;今天主要介绍共用体&#xff0c;枚举 共用体 概念&#xff1a;有时需要使几种不同类型的变量存放到同一段内存单元中。 例如&#xff0c;可把一个整型变量、一个字符型变量、一个浮点型变量放在同一个地址开始的内存单元中 。以上三个变量在内…

Python系列-字典

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” ​ 目录 ​ 字典是什么 创建字典 查找key 新增/修改元素 删除元素 遍历字典元素 取出所有的key和value 合成的key类型 ​编辑 小结 字典是什么 字典是一种存储键值对的结…

云打印怎么收费?云打印需要付费吗?

随着云打印概念的火热发展&#xff0c;很多有打印需求的App或者个人用户都想使用易绘创云打印服务。那么易绘创云打印怎么收费&#xff1f;云打印需要付费吗&#xff1f;今天就带大家来了解一下。 云打印怎么收费&#xff1f;云打印需要付费吗&#xff1f; 很多有打印需求的小…

Linux网络状态查看与防火墙管理

网络状态查看 netstat [选项] Netstat是一款命令行工具&#xff0c;用于显示Linux系统中网络的状态信息&#xff0c;可以显示网络连接、路由表、连接的数据统计等信息。 使用 选项 -a&#xff1a;显示所有选项&#xff0c;包括监听和未监听的端口。 -t&#xff1a;仅显示tc…

IS-IS的LSP分片扩展

原理 IS-IS通过泛洪LSP来宣告链路状态信息,由于一个LSP能够承载的信息量有限,IS-IS将对LSP进行分片。每个LSP分片由产生该LSP的结点或伪结点的SystemID、PseudnodeID(普通LSP中该值为0,Pseudonode LSP中该值为非0)、LSPNumber(LSP分片号)组合起来唯一标识,由于LSPNumb…

微信小程序(二十四)简易的双向绑定

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.双向绑定实例 2.双向绑定的局限性 源码&#xff1a; index.wxml <!-- 1.placeholder:输入框为空时的占位提示语2.model:value 双向绑定&#xff08;其实就是在原先基础上加上了model:&#xff09; 3.目前双…

小白水平理解面试经典题目LeetCode 118 Pascal‘s Triangle【Java实现】

LeetCode 118 生成杨辉三角&#xff08;Pascal’s Triangle&#xff09; 小白渣翻译 给定一个非负整数 numRows&#xff0c;生成杨辉三角的前 numRows 行。 在杨辉三角中&#xff0c;每个数是它左上方和右上方的数的和。 例子 这里是小白理解 那么这种题目一上来看&#xf…

C++进阶(九)哈希概念哈希函数哈希冲突

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、哈希概念1、哈希介绍2、哈希与哈希表 二、哈希冲突三、哈希函数四、 哈希冲突解决 一、哈…

ElementUI Form:Checkbox 多选框

ElementUI安装与使用指南 Checkbox 多选框 点击下载learnelementuispringboot项目源码 效果图 el-checkbox.vue &#xff08;Checkbox 多选框&#xff09;页面效果图 项目里el-checkbox.vue代码 <script> const cityOptions [上海, 北京, 广州, 深圳] export def…

react 之 UseMemo

useMemo 看个场景 下面我们的本来的用意是想基于count的变化计算斐波那契数列之和&#xff0c;但是当我们修改num状态的时候&#xff0c;斐波那契求和函数也会被执行&#xff0c;显然是一种浪费 // useMemo // 作用&#xff1a;在组件渲染时缓存计算的结果import { useState …

【C++干货铺】哈希结构在C++中的应用

目录 unordered系列关联式容器 unordered_map unordered_map的接口说明 1.unordered_map的构造 2. unordered_map的容量 3. unordered_map的迭代器 4. unordered_map的元素访问 5. unordered_map的查询 6. unordered_map的修改操作 7. unordered_map的桶操作 底层结构 …

Nijijourney V6版本动漫图像生成模型发布

简介 这是一个最先进的AI&#xff0c;可以绘制任何二次元风格的绘画&#xff01;这是一个由 Spellbrush 与 Midjourney 所共同设计开发的魔法般工具。无论您是在寻找可爱的Q版角色还是充满动感的动作场景&#xff0c;niji・journey 都能将您的想象变为现实。 功能介绍 - 增强…

深度解析:i++ 与 ++i,探究其性能差异与使用技巧

在编程世界中&#xff0c;经常会遇到对变量进行递增操作&#xff0c;而i和i这两个递增操作符就是我们常用的两种方式。这两者看似简单&#xff0c;但却有着微妙的性能区别和使用差异。 1. 性能差异的探究 首先&#xff0c;我们来研究i和i在性能上的微妙差异。这对于编写高效的…

搭建 idea 插件仓库私服

正常情况下&#xff0c;我们开发的 idea 插件会发布到 idea 官方商城中&#xff0c;这样用户就可以在 idea 的 Marketplace 中搜索安装。 但是在企业内部&#xff0c;有可能我们开发了很多内部插件&#xff0c;而不能发布到公共市场中&#xff0c;这种情况下我们就需要搭建一个…

子查询练习1

数据表 链接&#xff1a;https://pan.baidu.com/s/1dPitBSxLznogqsbfwmih2Q 提取码&#xff1a;b0rp --来自百度网盘超级会员V5的分享 子查询举例 子查询的分类 单行子查询 > > < < ! <> 单行子查询 WHERE中的子查询 查询工资大于149号…

智源成立5年,高层大变动!黄铁军不再担任院长,张宏江、唐杰、刘江均已离任

大家好&#xff0c;我是二狗。 就在刚刚&#xff0c;北京智源人工智能研究院官方微信公众号宣布&#xff1a;王仲远博士加入智源研究院&#xff0c;接任院长一职&#xff0c;全面负责研究院各项工作&#xff0c;黄铁军不再兼任智源研究院院长。 智源表示&#xff0c;黄铁军于2…

Dubbo之架构源码公共知识

1.ScopeModel 抽象这三个能力是为了实现 Dubbo 的多实例支持&#xff0c;FrameworkModel 是实现类似 JVM 租户级别的隔离&#xff0c;ApplicationModel 是为了实现一个机器上发布多个应用&#xff08;如 demo-application1 和 demo-application2 一起发布&#xff09;&#xff…

11. UE5 RPG使用GameplayEffect修改角色属性(二)

上一篇写了一下GameplayEffect的基础操作&#xff0c;这一篇进阶一下&#xff0c;讲解一下GameplayEffect堆叠功能&#xff0c;以及能够基于这个堆叠能够实现一些怎样的效果。 经过几天的查看&#xff0c;发现新版的更新的真不错&#xff0c;而且最上面竟然直接显示编译的错误…