Redis【实践篇】之RedisTemplate基本操作

Redis 从入门到精通【应用篇】之RedisTemplate详解

文章目录

  • Redis 从入门到精通【应用篇】之RedisTemplate详解
  • 0. 前言
  • 1. RedisTemplate 方法
    • 1. 设置RedisTemplate的序列化方式
    • 2. RedisTemplate的基本操作
  • 2. 源码浅析
    • 2.1. 构造方法
    • 2.2. 序列化方式
    • 2.3. RedisTemplate的操作方法
    • 2.4. RedisTemplate的事务
    • 2.5. RedisTemplate的执行方法
    • 2.6. RedisTemplate的回调方法
      • 代码示例
  • 3.总结
    • 3.1. 项目中如何使用 RedisTemplate 支持多个 Redis 数据库?
    • 3.2. 如何使用 RedisTemplate 支持 Redis 集群?
    • 3.3. 如何使用 RedisTemplate 实现 Redis 事务的乐观锁?
    • 3.4. 如何使用 RedisTemplate 实现 Redis 的分布式锁重入?
    • 3.5. 如何使用 RedisTemplate 实现 Redis 的分布式事务?
    • 3.6. 如何使用 RedisTemplate 实现 Redis 的分布式限速?
    • 3.7. 如何使用 RedisTemplate 实现 Redis 的分布式锁可重入性?
    • 3.8. 如何使用 RedisTemplate 实现 Redis 的分布式信号量?
    • 3.9. 如何使用 RedisTemplate 实现 Redis 的分布式缓存穿透?
    • 3.10. 如何使用 RedisTemplate 实现 Redis 的分布式缓存击穿?
  • 4. Redis从入门到精通系列文章

在这里插入图片描述

0. 前言

在SpringBoot中,可以使用RedisTemplate来操作Redis数据库。RedisTemplate是Spring Data Redis提供的一个强大的Redis客户端,它支持各种Redis数据结构,并提供了许多方便的方法来操作这些数据结构。下面是一些RedisTemplate的用法示例:

1. RedisTemplate 方法

1. 设置RedisTemplate的序列化方式

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 设置key和value的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));

        // 设置hash key和value的序列化方式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));

        return redisTemplate;
    }
}

在此示例中,创建了一个RedisTemplate对象,并设置了key和value的序列化方式为StringRedisSerializer和Jackson2JsonRedisSerializer。同时,还设置了hash key和value的序列化方式。

2. RedisTemplate的基本操作

其实在项目中我们通常会将RedisTemplate 再封装一层,作为一个Redis操作类处理,相当于提供了一层语法糖。

@Service
public class RedisService {

    // RedisTemplate是Spring提供的对Redis的操作模板类,使用泛型限定key和value的类型为String和Object
    private final RedisTemplate<String, Object> redisTemplate;

    // 构造函数,注入RedisTemplate对象
    public RedisService(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 设置key-value键值对
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    // 根据key获取value
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    // 根据key删除键值对
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    // 判断key是否存在
    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }

    // 将key对应的value增加delta
    public long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    // 获取hash结构中所有键值对
    public Map<Object, Object> hashGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    // 向hash结构中添加键值对
    public void hashPut(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    // 根据hash结构中的key获取对应的value
    public Object hashGet(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    // 根据hash结构中的key删除对应的hashKey
    public void hashDelete(String key, Object... hashKeys) {
        redisTemplate.opsForHash().delete(key, hashKeys);
    }

    // 判断hash结构中是否存在hashKey
    public boolean hashExists(String key, Object hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    // 获取set结构中所有元素
    public Set<Object> setGetAll(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // 向set结构中添加元素
    public void setAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    // 判断set结构中是否存在某个元素
    public boolean setExists(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    // 根据value删除set结构中的元素
    public void setDelete(String key, Object... values) {
        redisTemplate.opsForSet().remove(key, values);
    }

    // 获取list结构中所有元素
    public List<Object> listGetAll(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    // 向list结构中左侧插入元素
    public void listPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    // 从list结构中左侧弹出元素
    public Object listPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    // 获取list结构中元素的数量
    public long listSize(String key) {
        return redisTemplate.opsForList().size(key);
    }
}

2. 源码浅析

2.1. 构造方法

RedisTemplate的构造方法需要一个RedisConnectionFactory对象作为参数,它通过这个对象来获取Redis连接。RedisConnectionFactory 是 Spring Data Redis 提供的一个接口,用于创建和管理 Redis 连接。它是将 Redis 连接池(连接 Redis 数据库的客户端)与 Spring 应用程序集成的关键。

public RedisTemplate() {
    RedisConnectionFactory redisConnectionFactory = RedisConnectionConfiguration.determineConnectionFactory(redisSentinelConfiguration, redisClusterConfiguration, connectionFactory, jedisConnectionFactory);
    setConnectionFactory(redisConnectionFactory);
    afterPropertiesSet();
}

2.2. 序列化方式

RedisTemplate支持各种数据类型的序列化和反序列化,它提供了以下四种序列化方式:

  • keySerializer:key的序列化方式。
  • valueSerializer:value的序列化方式。
  • hashKeySerializer:hash key的序列化方式。
  • hashValueSerializer:hash value的序列化方式。

RedisTemplate默认使用JdkSerializationRedisSerializer作为序列化方式,但是在实际使用中,通常需要根据实际情况选择更加高效的序列化方式,如StringRedisSerializer、Jackson2JsonRedisSerializer等。

public void setKeySerializer(RedisSerializer<?> keySerializer) {
    Assert.notNull(keySerializer, "RedisSerializer must not be null!");
    this.keySerializer = keySerializer;
}

public void setValueSerializer(RedisSerializer<?> valueSerializer) {
    Assert.notNull(valueSerializer, "RedisSerializer must not be null!");
    this.valueSerializer = valueSerializer;
}

public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) {
    Assert.notNull(hashKeySerializer, "RedisSerializer must not be null!");
    this.hashKeySerializer = hashKeySerializer;
}

public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) {
    Assert.notNull(hashValueSerializer, "RedisSerializer must not be null!");
    this.hashValueSerializer = hashValueSerializer;
}

2.3. RedisTemplate的操作方法

RedisTemplate提供了各种操作方法,如opsForValue()、opsForList()、opsForSet()、opsForZSet()、opsForHash()等,它们返回的是具体数据结构的操作对象,如ValueOperations、ListOperations、SetOperations、ZSetOperations、HashOperations等。这些操作对象提供了各种操作方法,如get()、set()、push()、pop()、add()、remove()、score()、range()、increment()等,它们对应了Redis的各种操作。

public ValueOperations<K, V> opsForValue() {
    if (valueOps == null) {
        valueOps = new DefaultValueOperations<>(this);
    }
    return valueOps;
}

public ListOperations<K, V> opsForList() {
    if (listOps == null) {
        listOps = new DefaultListOperations<>(this);
    }
    return listOps;
}

public SetOperations<K, V> opsForSet() {
    if (setOps == null) {
        setOps = new DefaultSetOperations<>(this);
    }
    return setOps;
}

public ZSetOperations<K, V> opsForZSet() {
    if (zSetOps == null) {
        zSetOps = new DefaultZSetOperations<>(this);
    }
    return zSetOps;
}

public HashOperations<K, HK, HV> opsForHash() {
    if (hashOps == null) {
        hashOps = new DefaultHashOperations<>(this);
    }
    return hashOps;
}

2.4. RedisTemplate的事务

RedisTemplate支持事务,它提供了multi()、exec()和discard()三个方法来实现事务。multi()方法用于开启事务,exec()方法用于提交事务,discard()方法用于回滚事务。

public void multi() {
    RedisConnectionUtils.bindConnection(getRequiredConnectionFactory(), true);
    try {
        RedisConnectionUtils.getRequiredConnection(getConnectionFactory()).multi();
    } catch (RuntimeException ex) {
        RedisConnectionUtils.unbindConnection(getRequiredConnectionFactory());
        throw ex;
    }
}

public List<Object> exec() {
    RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
    return execute((RedisCallback<List<Object>>) connection -> {
        List<Object> results = connection.exec();
        return results != null ? results : Collections.emptyList();
    }, true);
}

public void discard() {
    RedisConnectionUtils.unbindConnectionIfPossible(getConnectionFactory());
    execute(RedisConnectionUtils::discard, true);
}

2.5. RedisTemplate的执行方法

RedisTemplate提供了execute()方法来执行Redis操作,它需要传入一个RedisCallback对象作为参数,RedisCallback是一个函数式接口,它定义了一个回调函数,用于执行具体的Redis操作。execute()方法会获取一个Redis连接,执行RedisCallback对象的回调函数,并返回回调函数的结果。

public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
    Assert.notNull(action, "Callback object must not be null");

    RedisConnection conn = null;
    try {
        conn = getConnection(exposeConnection);
        boolean existingConnection = TransactionSynchronizationManager.hasResource(getConnectionFactory());
        RedisConnection connToUse = preProcessConnection(conn, existingConnection);
        T result = action.doInRedis(connToUse);
        return postProcessResult(result, conn, existingConnection);
    } catch (RuntimeException ex) {
        releaseConnection(conn, existingConnection);
        throw ex;
    } finally {
        if (!exposeConnection) {
            RedisConnectionUtils.releaseConnection(conn, getConnectionFactory(), false);
        }
    }
}

在execute()方法中,首先获取Redis连接,然后调用preProcessConnection()方法进行预处理,接着执行RedisCallback对象的回调函数,最后调用postProcessResult()方法进行后处理,并返回结果。如果执行过程中发生异常,会调用releaseConnection()方法释放Redis连接。

2.6. RedisTemplate的回调方法

RedisTemplate的回调方法主要有以下三个:

这里有三个方法:

  1. execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline):执行Redis操作的核心方法。接受一个RedisCallback对象,该对象封装了要在Redis上执行的操作。还有两个布尔类型的参数,一个表示是否暴露连接(exposeConnection),另一个表示是否启用pipeline(pipeline)。如果启用pipeline,将使用Redis连接对象的openPipeline()和closePipeline()方法执行操作。

  2. execute(SessionCallback<T> session):执行Redis事务的方法。接受一个SessionCallback对象,该对象封装了在 Redis 事务中执行的操作。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,最后解除绑定。

  3. executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer):执行 Redis pipeline 的方法。接受一个SessionCallback对象,该对象封装了要在 Redis pipeline 中执行的操作,以及一个可选的 RedisSerializer 对象,用于反序列化结果。该方法会绑定 Redis 连接并执行 SessionCallback 对象的 execute() 方法,在执行期间使用 Redis 连接对象的 openPipeline() 和 closePipeline() 方法开启和关闭 Redis pipeline。

常用场景:第一个方法执行单个Redis操作,第二个方法执行Redis事务,第三个方法执行Redis
pipeline。此外,第二个方法是为了执行多个 Redis 操作而设计的,而第一个方法和第三个方法只执行单个 Redis 操作。第三个方法需要额外的参数,用于反序列化 Redis pipeline 的结果。


    @Nullable
    public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(action, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnection conn = RedisConnectionUtils.getConnection(factory, this.enableTransactionSupport);

        Object var11;
        try {
            boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
            RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
            boolean pipelineStatus = connToUse.isPipelined();
            if (pipeline && !pipelineStatus) {
                connToUse.openPipeline();
            }

            RedisConnection connToExpose = exposeConnection ? connToUse : this.createRedisConnectionProxy(connToUse);
            T result = action.doInRedis(connToExpose);
            if (pipeline && !pipelineStatus) {
                connToUse.closePipeline();
            }

            var11 = this.postProcessResult(result, connToUse, existingConnection);
        } finally {
            RedisConnectionUtils.releaseConnection(conn, factory, this.enableTransactionSupport);
        }

        return var11;
    }

    public <T> T execute(SessionCallback<T> session) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        Object var3;
        try {
            var3 = session.execute(this);
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var3;
    }

    public List<Object> executePipelined(SessionCallback<?> session) {
        return this.executePipelined(session, this.valueSerializer);
    }

    public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) {
        Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
        Assert.notNull(session, "Callback object must not be null");
        RedisConnectionFactory factory = this.getRequiredConnectionFactory();
        RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);

        List var4;
        try {
            var4 = (List)this.execute((connection) -> {
                connection.openPipeline();
                boolean pipelinedClosed = false;

                List var7;
                try {
                    Object result = this.executeSession(session);
                    if (result != null) {
                        throw new InvalidDataAccessApiUsageException("Callback cannot return a non-null value as it gets overwritten by the pipeline");
                    }

                    List<Object> closePipeline = connection.closePipeline();
                    pipelinedClosed = true;
                    var7 = this.deserializeMixedResults(closePipeline, resultSerializer, this.hashKeySerializer, this.hashValueSerializer);
                } finally {
                    if (!pipelinedClosed) {
                        connection.closePipeline();
                    }

                }

                return var7;
            });
        } finally {
            RedisConnectionUtils.unbindConnection(factory);
        }

        return var4;
    }

代码示例

  1. 使用 execute(RedisCallback<T> action) 执行 Redis 命令
 
// 使用 execute() 方法执行 Redis 命令
String key = "myKey";
String value = redisTemplate.execute((RedisCallback<String>) connection -> {
    connection.set(redisTemplate.getStringSerializer().serialize(key), redisTemplate.getStringSerializer().serialize("Hello"));
    return redisTemplate.getStringSerializer().deserialize(connection.get(redisTemplate.getStringSerializer().serialize(key)));
});

// 输出 Redis 命令执行结果
System.out.println(value); // 输出 "Hello"

在上面的示例中, 我们使用 execute(RedisCallback<T> action) 方法将 Redis 命令封装在一个 RedisCallback 对象中,并将其传递给 execute() 方法。该命令使用 set() 方法将一个 key-value 对写入 Redis 中,然后使用 get() 方法从 Redis 中读取该 key 对应的值。

  1. 使用 execute(SessionCallback<T> session) 执行 Redis 事务
// 使用 execute(SessionCallback<T> session) 方法执行 Redis 事务
String key1 = "myKey1";
String key2 = "myKey2";
String value1 = "myValue1";
String value2 = "myValue2";
List<Object> results = redisTemplate.execute((SessionCallback<List<Object>>) session -> {
    session.multi();
    session.opsForValue().set(key1, value1);
    session.opsForValue().set(key2, value2);
    return session.exec();
});

// 输出 Redis 事务的结果
System.out.println(results); // 输出 "[true, true]"

使用 execute(SessionCallback<T> session) 方法将 Redis 事务封装在一个 SessionCallback<T> 对象中,并将其传递给 execute() 方法。该事务使用 multi() 方法开启事务,在事务中使用 opsForValue() 对象的 set() 方法将两个 key-value 对写入 Redis 中,最后使用 exec() 方法提交事务。事务执行完成后,我们可以通过 execute() 方法返回的结果列表查看每个 Redis 命令的执行结果。在上面的示例中,我们可以看到结果列表为 [true, true],表示两个 Redis 命令都成功执行。

  1. 使用 executePipelined(SessionCallback<?> session) 执行 Redis pipeline
 

// 使用 executePipelined(SessionCallback<?> session) 方法执行 Redis pipeline
String key1 = "myKey1";
String key2 = "myKey2";
List<Object> results = redisTemplate.executePipelined((SessionCallback<List<Object>>) session -> {
    session.opsForValue().get(key1);
    session.opsForValue().get(key2);
    return null;
});

// 输出 Redis pipeline 的结果
System.out.println(results); // 输出 "[Hello1, Hello2]"

使用 executePipelined(SessionCallback<?> session) 方法将 Redis pipeline 封装在一个 SessionCallback<?> 对象中,并将其传递给 executePipelined() 方法。该 pipeline 使用 opsForValue() 对象的 get() 方法获取两个 key 的值,并返回一个结果列表。在执行期间,该方法将使用 Redis 连接对象的 openPipeline()closePipeline() 方法开启和关闭 Redis pipeline,以便可以批量执行多个命令。

3.总结

看完这些基本上行只是学会了增删查看和批量操作。也就是只学会了RedisTemplate 的皮毛。其实在项目中还有更多的复杂需求,需要重新实现。

比如以下这些问题,也是很常见的,需要我们处理实际的问题。我大概做一个简答,后面将详细输出示例

3.1. 项目中如何使用 RedisTemplate 支持多个 Redis 数据库?

可以通过配置 RedisConnectionFactory 来支持多个 Redis 数据库,其中可以使用 JedisConnectionFactory 或 LettuceConnectionFactory 来创建不同的 RedisConnectionFactory 实例。详细教程可以参考我的其他博客《SpringBoot 项目配置多数据源》

3.2. 如何使用 RedisTemplate 支持 Redis 集群?

可以使用 RedisTemplate 的 ClusterRedisConnectionFactory 来支持 Redis 集群,通过配置 Redis 集群中的多个节点来实现高可用性和负载均衡。详细教程参考我的其他博客《SpringBoot 项目配置 Redis 集群》

3.3. 如何使用 RedisTemplate 实现 Redis 事务的乐观锁?

可以使用 RedisTemplate 的 watch() 方法和 multi() 方法来实现 Redis 事务的乐观锁,通过在事务开始前使用 watch() 方法监视 Redis 中的某个 key,然后在事务中使用 multi() 方法执行多个 Redis 命令,并使用 exec() 方法提交事务,如果在事务执行期间,被监视的 key 被修改,则事务会失败,从而实现乐观锁。
详细教程可以参考我的其他博客《SpringBoot 项目配置 Redis 集群》

3.4. 如何使用 RedisTemplate 实现 Redis 的分布式锁重入?

可以使用 RedisTemplate 的 ThreadLocal 方式来实现 Redis 的分布式锁重入,即在每个线程中保存一个 Redis 分布式锁的状态,并在需要重入时,检查当前线程是否已经获取了分布式锁。

3.5. 如何使用 RedisTemplate 实现 Redis 的分布式事务?

可以使用 RedisTemplate 的 execute(SessionCallback session) 方法来实现 Redis 的分布式事务,其中 SessionCallback 接口可以用来执行多个 Redis 命令,并保证这些命令以原子方式执行。

3.6. 如何使用 RedisTemplate 实现 Redis 的分布式限速?

可以使用 RedisTemplate 的 incr() 方法和 expire() 方法来实现 Redis 的分布式限速,通过在 Redis 中设置一个计数器和过期时间来实现分布式限速。

3.7. 如何使用 RedisTemplate 实现 Redis 的分布式锁可重入性?

可以使用 RedisTemplate 的 ReentrantRedisLock 类来实现 Redis 的分布式锁可重入性,该类可以在 Redis 中保存一个计数器来记录锁的重入次数,并在释放锁时,检查当前线程是否已经完全释放了锁,从而实现分布式锁的可重入性。

3.8. 如何使用 RedisTemplate 实现 Redis 的分布式信号量?

可以使用 RedisTemplate 的 RedisSemaphore 类来实现 Redis 的分布式信号量,该类可以在 Redis 中保存一个计数器来记录当前已经获得信号量的数量,并在释放信号量时,将计数器减一。

3.9. 如何使用 RedisTemplate 实现 Redis 的分布式缓存穿透?

可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和布隆过滤器来实现 Redis 的分布式缓存穿透,其中布隆过滤器可以用来过滤掉不存在的 key,从而避免缓存穿透的问题。

3.10. 如何使用 RedisTemplate 实现 Redis 的分布式缓存击穿?

可以使用 RedisTemplate 的缓存注解(例如 @Cacheable、@CachePut、@CacheEvict)和 Redis 分布式锁来实现 Redis 的分布式缓存击穿,其中 Redis 分布式锁可以用来防止缓存击穿,即在缓存不存在的情况下,使用 Redis 分布式锁来避免多个线程同时访问数据库。

4. Redis从入门到精通系列文章

  • 《Redis【应用篇】之RedisTemplate基本操作》
  • 《Redis 从入门到精通【实践篇】之SpringBoot配置Redis多数据源》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis HyperLogLog 数据结构》
  • 《Redis 从入门到精通【进阶篇】之三分钟了解Redis地理位置数据结构GeoHash》
  • 《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
  • 《Redis 从入门到精通【进阶篇】之redis主从复制详解》
  • 《Redis 从入门到精通【进阶篇】之Redis事务详解》
  • 《Redis从入门到精通【进阶篇】之对象机制详解》
  • 《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
  • 《Redis从入门到精通【进阶篇】之持久化 AOF详解》
  • 《Redis从入门到精通【进阶篇】之持久化RDB详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
  • 《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
  • 《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
    在这里插入图片描述大家好,我是冰点,今天的Redis【实践篇】之RedisTemplate基本操作详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

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

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

相关文章

PGembedding 代码分析

pgembedding 存储结构 pg embedding 数据是存在共享内存中的&#xff0c;pg down 之后索引数据就没了&#xff0c;但索引对象本身还在&#xff0c;第一次访问时会重新创建。 数据以 plain 的形式存储&#xff0c;其中每个点是这样的结构&#xff1a; idx_size&#xff1a; 表…

继承-菱形继承

继承 继承是类设计层次的复用 继承方式与访问限定符 限定了啥&#xff1f; 1.根据表中我们可以看到 基类的私有成员在子类不可见&#xff0c;但还是被继承了下来 2.根据继承方式和成员在基类的访问限定符小的那个来决定了子类访问基类成员的访问方式 例如如果是public继承&a…

Linux工具——vim

安装vim yum -y install vim 如果安装失败&#xff0c;提示Could not resolve host:mirrorlist.centos.org: Unkown error的问题&#xff0c;需要替换yum源&#xff0c;可以参考这个文章 配置vim root的vim配置文件在 /etc/vimrc 普通用户的vim配置文件在用户对应家目录下&a…

图像处理之canny边缘检测(非极大值抑制和高低阈值)

Canny 边缘检测方法 Canny算子是John F.Canny 大佬在1986年在其发表的论文 《Canny J. A computational approach to edge detection [J]. IEEE Transactions on Pattern Analysis and Machine Intelligence, 1986 (6): 679-698.》提出来的。 检测目标&#xff1a; 低错误率…

Python实现抽象工厂模式

抽象工厂模式是一种创建型设计模式&#xff0c;用于创建一系列相关或依赖对象的家族&#xff0c;而无需指定具体类。在Python中&#xff0c;可以通过类和接口的组合来实现抽象工厂模式。 下面是一个简单的Python实现抽象工厂模式的示例&#xff1a; # 抽象产品接口 class Abs…

【NLP】视觉变压器与卷积神经网络

一、说明 本篇是 变压器因其计算效率和可扩展性而成为NLP的首选模型。在计算机视觉中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;架构仍然占主导地位&#xff0c;但一些研究人员已经尝试将CNN与自我注意相结合。作者尝试将标准变压器直接应用于图像&#xff0c;发现在…

自学网络安全(黑客)全笔记

一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

区块链实验室(11) - PBFT耗时与流量特征

以前面仿真程序为例&#xff0c;分析PBFT的耗时与流量特征。实验如下&#xff0c;100个节点构成1个无标度网络&#xff0c;节点最小度为5&#xff0c;最大度为38. 从每个节点发起1次交易共识。统计每次交易的耗时以及流量。本文所述的流量见前述仿真程序的说明:区块链实验室(3)…

python暴力破解wifi密码

python破解WiFi密码 无图形化界面版图形化界面版 刚开始怀着是以为可以自动生成并匹配密码进行破解&#xff0c;然后才知道都需要使用密码本&#xff0c;破解都不知道要猴年马月去了。。。。。但可以研究理解一下代码过程 使用pycharm需要调试一下&#xff0c;比较麻烦&#xf…

mac使用教程【快速从windows切换为mac,mac快捷键合集】

mac使用教程 1. 安装brew并通过brew安装git 1.1 安装brew 打开终端输入如下命令&#xff1a; % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"选择对应的镜像进行安装 # 例如&#xff1a;输入5&#xff…

华为云零代码平台AstroZero新手操作指南-3分钟体验创建培训报名表

华为云Astro轻应用Astro Zero是华为云为行业客户、合作伙伴、开发者量身打造的低代码/零代码应用开发平台&#xff0c;提供全场景可视化开发能力和端到端部署能力&#xff0c;可快速搭建行业和大型企业级应用并沉淀复用行业资产&#xff0c;加速行业数字化。 在AstroZero上&am…

在Microsoft Excel中如何快速合并表格

在 Excel 中分析数据时&#xff0c;在一个工作表中收集所有必要信息的频率是多少&#xff1f;几乎从来没有&#xff01;当不同的数据分散在许多工作表和工作簿中时&#xff0c;这是一种非常常见的情况。幸运的是&#xff0c;有几种不同的方法可以将多个表中的数据组合成一个表&…

css - Media Query

使用bootstrap的grid system可以在一个较为粗糙的范围得到较好的响应性&#xff0c;但是通过viewport可以看到网站在具体哪个像素点处变得丑陋&#xff0c;再通过css media query来精细调整网页布局。 可以通过media query来提高网页移动响应能力。

【Kubernetes资源篇】ingress-nginx最佳实践详解

文章目录 一、Ingress Controller理论知识1、Ingress Controller、Ingress简介2、四层代理与七层代理的区别3、Ingress Controller中封装Nginx&#xff0c;为什么不直接用Nginx呢&#xff1f;4、Ingress Controller代理K8S内部Pod流程 二、实践&#xff1a;部署Ingress Control…

reset master

1 reset master 执行 reset master; 后 变化1 &#xff1a;位点被重置 变化2 binlog日志被清空 原来的binlog被删除 从 mysql-bin.000001 开始记录。

12.(开发工具篇vscode+git)vscode 不能识别npm命令

1&#xff1a;vscode 不能识别npm命令 问题描述&#xff1a; 解决方式&#xff1a; &#xff08;1&#xff09;右击VSCode图标&#xff0c;选择以管理员身份运行&#xff1b; &#xff08;2&#xff09;在终端中执行get-ExecutionPolicy&#xff0c;显示Restricted&#xff…

Linux内核bridge浅析

Linux网桥模型&#xff1a; Linux内核通过一个虚拟的网桥设备来实现桥接的&#xff0c;这个设备可以绑定若干个以太网接口设备&#xff0c;从而将它们桥接起来。如下图所示&#xff1a; 网桥设备br0绑定了eth0和eth1。对于网络协议栈的上层来说&#xff0c;只看得到br0&#xf…

富文本CKEditor5简易入门,包括自定义上传图片(html版+vue.js)

一、安装及引入 官网&#xff1a;https://ckeditor.com/ckeditor-5/download/ 我这边使用的是自定义构建&#xff0c;然后下载下来。 二、简单使用 引入js <script src"../../../assets/plugins/ckeditor5/ckeditor.js"></script>html&#xff1a;…

能直接运营的校园跑腿代买拿寄取小程序开发

​说到做跑腿生意&#xff0c;除了做同城跑腿配送外&#xff0c;校园跑腿可是即成本又低又好操作的一个项目。 一般省会城市大学大专基本都是有好几所的&#xff0c;学校的特点是人员密集&#xff0c;跑腿配送周期短&#xff0c;且配送人员好招募&#xff0c;推广人员好招募。…

【Postman】- 基本用法

一、用例集 1.1 用例集 Collections&#xff1a;用例集。目录下可以创建子目录。 1.2 导出用例集 1.3 导入用例集 二、Postman断言 断言&#xff1a;让程序判断预期结果和实际结果是否一致 2.1 特点 Postman的断言是使用JavaScript语言编写的&#xff0c;写在"Tests&…
最新文章