Redis String 命令与实战

news2025/4/27 9:23:40

在前面的文章中,我们了解了 Redis 的基本功能,通过源码方式安装了 Redis,并搭建了 Redis 的源码运行环境。接下来将进入 “Redis 的实战应用篇”,在这部分我们将重点介绍 Redis 核心命令的使用,主要包括 Redis 中 StringListSetHash 以及 Sorted Set 五大核心数据结构的命令,同时还会分析这五大数据结构的使用场景。

本节作为 Redis 实战应用篇的第一篇,我们重点介绍 Redis 中 String 相关的命令以及应用场景。

文末有视频讲解

redis-cli 基础使用

在开始正式介绍 String 的相关命令之前,我们需要先了解一下本篇执行 Redis 命令的工具 —— redis-cli。它是 Redis 自带的命令行客户端,可以将命令发送到 Redis 服务端执行并接收命令执行结果。

在上一讲编译好的 Redis 的 src 目录下面,就可以看到 redis-cli 这个命令行客户端,如下图所示:

直接执行这个 redis-cli 命令,它就会默认连到前面启动的 Redis 服务上。要是连其他远程的 Redis 服务,我们可以用 -h 指定 Redis 服务的 IP 地址,然后 -p 指定 Redis 的端口就行了。

./redis-cli -h 127.0.0.1 -p 6379

视频讲解:

说透 Redis 7 - 杨四正 - 掘金小册核心原理剖析+源码解读+实践应用,全方位带你吃透 Redis 7。「说透 Redis 7」由杨四正撰写,979人购买https://s.juejin.cn/ds/kaY9xnj/

String 命令

连接上 Redis 之后,我们再来看 Redis 里面关于字符串的命令。我们后续的介绍会尽可能覆盖官网的文档给的这些命令,如下图所示:

这里对字符串命令做一个简单的分类,主要分成五大类,如下图,有:基础读写命令批量操作递增操作部分字符串操作以及复合操作的命令。这一节主要就是按照这个分类方式来介绍。

1. 读写命令

字符串儿最简单的命令就是 SET 和 GET 命令。SET 命令的话,就是设置一个键值对。如下截图,执行一下这个 SET 命令,这个 name 就是 Key,kouzhao 就是 Value 值。然后,GET 命令的话,就可以通过刚才设置的这个 Key 值,获取对应的 Value 值。

这个 SET 命令除了设置 Value 值之外,SET 还可以带其他参数。比如说:

  • EX 参数的话,就是给这个 Key 设置一个过期时间,单位是秒。
  • PX 参数的话,和 EX 参数类似,唯一的区别就是时间单位变成了毫秒。

来试一下下面这几条命令。下面把 name 设置成 kouzhaoxuejie,把它的过期时间设置成 20 秒,然后我们就可以通过 GET 命令拿到这个 name 的值,还可以通过 TTL 这个命令来看一下这个 name 的过期时间,可以看到这里,它还有几秒钟就过期了。在这几秒钟之内都可以用 GET 命令来拿到这个 name 的值。等一会儿,等这个值过期了,这个时候就会发现啊,TTL 命令返回了 -2 ,这就说明这个 Key 已经过期了;然后再用 GET 命令去拿的时候,就发现拿不到这个 name 的值,因为它已经过期,Redis 就会把它清理掉。

·

除了 EX 和 PX 两个之外,SET 命令还可以有下面几个参数。

  • EXAT 和 PXAT 是指定 Key 在某个时间点过期,后面跟的是一个明确的时间戳,意思是到了这个点 Key 就没了。

  • NX 是在目标 Key 不存在时候,才写入这个 Key;要是 Key 已存在的话,这个 Key 就写不进去。

  • XX 参数的功能正好是和 NX 反着的,XX 参数表示的是 Key 不存在的时候,写不进去。

先来看一下 NX 这个参数。先添加一个 name,Value 值设置成 kouzhao。然后,执行一下 SET name kouzhaoxuejie NX 命令,这个命令的意思就是,如果 name 这个 Key 不存在的话,我们就把它的值写成 kouzhaoxuejie。前面已经设置了 name 这个 Key,所以现在是写不进去的,再执行 GET 命令,看到 name 还是 kouzhao。

再来试一下 XX 参数。先查一下 age 这个 Key,发现没有 age 这个 Key。然后我们执行 SET age 25 XX 这条命令,意思就是如果 age 这个 Key 存在的话,才能把它改成 25,那么这个时候也是写不进去了,因为之前没有设置过 age 这个 Key。看到这个返回是 nil,也就是说那个命令没有执行成功,然后执行 GET age 也返回 nil。接下来我们把 age 的值设置为 0, 然后再执行这条 SET age 25 XX 命令,就能看到修改成功了。

至于 SETNX 命令,如果你感兴趣的话可以自己动手操作一下,这里就不再赘述了。

2. 批量操作

除了读写单个字符串,Redis 里面还提供了 MSET 和 MGET 两个命令,这两个命令是 SET、GET 命令的批量版本,可以一次操作多个 Key 值。

来看一个具体的例子。现在有很多个人的名字要存,name1、name2、name3,里边存的值分别是张三、李四、王五,我们用 MSET 一条命令全部存进去。然后用 GET 拿,如下图所示,都成功获取到了,这就说明 MSET 命令写三个 Key 都成功了。

但一次次 GET 是不是很麻烦呢?这里可以用 MGET 命令一次把三个 name 全部拿出来,看这个输出,输出的就是存进去的张三、李四、王五。

最后一个要说的字符串批量命令是 MSETNX,它是 SETNX 的批量版本,就是多个 Key 都不存在的时候,可以一把写入多个 Key

来看例子,name1 现在是存在的,然后执行 MSETNX name1 zhaoliu name4 sunqi,name1 和 name4 这两个 Key 是更新不了的。从下图中可以看出,name1 还是张三,name4 还是不存在。

再来尝试一下 MSETNX name4 zhaoliu name5 sunqi 这条命令,name4 和 name5 这两个 Key 是不存在的,所以能正常写进去。执行一下 MGET 命令,查询一下 name4和 name5 两个 Key,如下图所示,拿到 zhaoliu、sunqi,证明这两个 Key 写入成功了。

3. 递增操作

Redis 字符串里面除了放文本,还可以放个数字进去,这个和 Java 里面的字符串一样。比如说,在 Java 里面定一个 name 字符串,存“口罩学姐”:

String name = "kouzhaoxuejie";

也可以写一个 age 字符串,存 “25 ”这个字符串:

String age = "25";

这两行代码都能编译过的。

在 Redis 里面也是一样,我们可以用 SET age 25 把 age 这个 Key 的值设置成 25,这也是没问题的,Redis 高级的地方就在于它能自己感知到这个 age 是个数字,然后允许我们对这个 age 进行加减操作。比如说,可以用 INCR 命令和 DECR 命令 对这个 age 进行加一减一操作。

这里多说一句,INCR 是 increment 的缩写,就是“加”的意思;DECR 是 decrement 的缩写,是“减”的意思。

如下图,执行 INCR age 命令后,用 GET 命令再次查询 age 的时候它的值会加了 1;然后,DECR age 命令执行之后,age 值是会减了 1。

除了加 1 和减 1 外,Redis 还提供了 INCRBY 和 DECRBY 两个命令,这两条命令后面可以指定加多少、减多少。

举个例子,今天有 1000 个粉丝,然后又有 100 个小伙伴来关注了我,对应的命令就是 INCRBY fans 100;然后,有 2 个小伙伴取关了,对应的命令就是 DECRBY fans 2,最后还剩 1098。相应的命令如下图所示:

4. 操作部分字符串

Redis 还提供了一些操作部分字符串的命令,但在实际场景中用得不是特别多,了解一下就可以了

首先是 APPEND 命令,它是往一个 Key 里面追加一个字符串,和 Java 字符串的 append() 方法一个意思。在下图中,name 这个 Key 是 kouzhao,然后我们用 APPEND 命令把 xuejie 追加到 name 里面,name 就变成了 kouzhaoxuejie 了。

GETRANGE 命令是取字符串的一部分值返回,和 Java 里面 substring() 方法的功能一样。我们来看下图这个例子,需求是要获取 kouzhaoxuejie 中 xuejie 这部分的字符串,也就是需要获取 name 字符串里面下标在 7 到 12 的部分。

SETRANGE 命令,功能是指定一个下标,然后用传进去的字符串,替换这个下标后的内容。下图就是我们用 kz 来替换 kouzhaoxuejie 中的前两个字符,这个 SETRANGE 命令是从 name 里面的 0 这个位置开始替换,可以看到开头的 ko 两个字符被替换成了 kz,剩下的字符完全没有变。

5. 复合操作

复合操作其实就是上面几个命令的组合,常用的有三个命令:GETDEL、GETEX 和 GETSET。其实通过命令的名称,你估计就把命令的功能猜个差不多了。

GETSET 是 GET 和 SET 两个命令的组合。下图是一个使用 GETSET 命令的示例,GETSET 命令的意思是,返回当前这个 name 的值,也就是返回 kouzhaoxuejie,然后同时用 zhangsan 来覆盖 name 的值,这个时候再查 name 这个 Key,拿到的就是 zhangsan 了。

GETEX 命令的意思是,获取 Key 的值,同时给 Key 设置一个过期时间。来看下图的例子,我们这里获取 name 的同时,把 name 这个 Key 的过期时间设置成 10 秒。我们用 TTL 命令可以看到 name 剩余的过期时间不断在减少,最后返回一个 -2,就表示 name 过期了。

DEL 命令是删除一个 Key,GETDEL 命令 就是先获取一个 Key 的值,同时删除这个 Key 的值。如下图所示,这里我们用 GETDEL 命令读取 name 值的时候,就把它一并删除了,再用 GET 命令查,就查询不到了。

使用场景

介绍完了 Redis 字符串命令之后,我们接下来要介绍一下在什么场景下可以考虑使用 Redis 字符串,同时结合 Lettuce 客户端,看看如何在 Java 里面执行 Redis 字符串命令

我们会通过三个示例来介绍一下 Redis String 命令在实际场景中的应用,下图列举了这三个示例,分别是:用 Redis 的 String 结构缓存一个 Java 对象、实现分布式的效果以及实现限流的效果。

我们这里先创建一个 Maven 项目 lettuce-demo,然后 pom 文件里面加上 lettuce 的依赖,现在最新的版本已经到 6.1.8 了。后面还要用 JSON 序列化,还有 Junit 写单测,这里也加一下这两个依赖。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>lettuce-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <!-- lettuce依赖-->
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>6.1.8.RELEASE</version>
        </dependency>
        <!-- json依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.1</version>
        </dependency>
        <!-- junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
        </dependency>
    </dependencies>
</project>

1. 缓存对象

Redis 最常被用作缓存,那如果我要用 Redis 缓存一个 Java 对象,应该怎么做呢?我们常用的方式是把 Java 对象序列化,然后作为字符串存储到 Redis 里面。

我们就简单模拟一个商品信息缓存的场景,创建一个商品类 Product,里面定义商品的名称、价格以及商品的描述信息,然后生成一个 toString() 方法:

public class Product {
    private String name;
    
    private double price;
    
    private String desc;
    
    // 省略getter/setter方法
    
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Product{");
        sb.append("name='").append(name).append('\'');
        sb.append(", price=").append(price);
        sb.append(", desc='").append(desc).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

我们写个单元测试,该单元测试的名字叫 testCacheProduct(),具体代码如下:

public class RedisTest {

    @Test
    public void testCacheProduct() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        Product product = new Product(); // 创建Product对象
        product.setName("杯子");
        product.setPrice(100d);
        product.setDesc("这是一个杯子");
        String json = objectMapper.writeValueAsString(product);

        RedisClient redisClient = RedisClient.create("redis://127.0.0.1:6379/0");
        StatefulRedisConnection<String, String> connection = redisClient.connect();
        RedisAsyncCommands<String, String> asyncCommands = connection.async();
        asyncCommands.set("product", json).get(1, TimeUnit.SECONDS);
        
        // RedisCommands<String, String> syncCommands = connection.sync();
        // syncCommands.set("product", json);

        connection.close();
        redisClient.shutdown();
    }
}

其中会先创建一个 Product 对象,然后设置一下它的字段,再拿 ObjectMapper 把这个 Product 对象序列化成 Json 字符串。这里使用 Lettuce 这个 Redis 客户端连接前面启动 Redis 实例,连单个 Redis 实例的时候,我们需要用 create() 方法生成一个 RedisClient 对象,create() 方法里面填的是 Redis 的 URL,这个和我们用 JDBC 连数据库的时候类似,URL 里面指定了 Redis 的地址、端口,这样的话,RedisClient 就知道去哪里连 Redis 了。然后执行 connect() 方法,请求 Redis 实例创建连接。

连接完之后,就可以通过 async() 方法创建一个异步的 Command 对象,这个对象就是用来执行 Redis 命令的关键对象。我们能看到它有一个 set() 方法,这就对应我们前面说的 SET 命令,set() 方法返回的是一个 Future 对象,我们可以用 get() 方法设置一个等待的超时时间,等待 SET 命令执行完成。我们这里设置 1 秒,TimeUnit 用来指定时间的单位。

除了异步 Command 对象,Lettuce 还提供了一个同步的 Command 对象。在用这个同步的对象执行命令的时候,就不用再调 get() 方法,设置超时时间了,它会一直阻塞等待命令执行结束。一般在工作场景里面,都是用异步的客户端,然后设置一个合理的超时时间,就可以防止 Redis 长时间不返回把上游服务拖垮了。

当业务与 Redis 的交互结束之后,我们就把连接和 RedisClient 都关闭掉,这和 JDBC 连数据库一样,用来释放比较珍贵的网络资源。

执行完这个单测之后我们从命令行里面看一下 product 这个 Key,发现已经存进来了,名字、价格都有了哈:

127.0.0.1:6379> GET product
"{"name":"\xe6\x9d\xaf\xe5\xad\x90","price":100.0,"desc":"\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe4\xb8\xaa\xe6\x9d\xaf\xe5\xad\x90"}"

我们再加一个 GET 命令的单测,同时把单测类的结构调整一下。我们把建连和创建 Command 对象的逻辑放到 before() 方法里面,把释放连接的逻辑放到 after() 方法里面,然后加上 @Before 和 @After 注解,这样的话,每次跑单测的时候,就会先执行 before() 进行建连,再执行单测逻辑,最后执行 after() 关闭连接。

来看 GET 命令的单测,这里先执行一个 GET 方法,也就是发一条 GET 命令,去拿到 product 这个 Key 对应的 Value 值,也就是前面存进去的这个 json 字符串。然后,把它反序列化成一个 Product 对象,并打印到控制台。

public class RedisTest {

    private static RedisClient redisClient;

    private static StatefulRedisConnection<String, String> connection;

    private static RedisAsyncCommands<String, String> asyncCommands;

    @Before
    public void before(){
        redisClient = RedisClient.create("redis://127.0.0.1:6379/0");
        connection = redisClient.connect();
        asyncCommands = connection.async();
        // RedisCommands<String, String> syncCommands = connection.sync();
    }

    @After
    public void after(){
        connection.close();
        redisClient.shutdown();
    }

    @Test
    public void testCacheProduct() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        Product product = new Product();
        product.setName("杯子");
        product.setPrice(100d);
        product.setDesc("这是一个杯子");
        String json = objectMapper.writeValueAsString(product);

        asyncCommands.set("product", json).get(1, TimeUnit.SECONDS);
    }

    @Test
    public void testGetProduct() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        String json = asyncCommands.get("product").get(1, TimeUnit.SECONDS);
        Product product = objectMapper.readValue(json, new TypeReference<Product>() {
        });
        System.out.println(product);
    }
}

2. 分布式锁

Redis 字符串还有一个非常常用的场景就是分布式锁,大概的实现思路就是利用 Redis 命令的原子性和 SET...NX 命令(或者是 SETNX 命令)实现锁的效果,Redis 字符串的 Key 是锁的名称,Value 存的是当前锁的拥有者

下面就来简单模拟一下多个服务来争抢资源的场景,然后用 SET...NX 实现分布式锁,控制服务之间的并发。

@Test
public void testLock() throws Exception {
    int threadNum = 1;
    CountDownLatch countDownLatch = new CountDownLatch(threadNum);
    Runnable runnable = () -> {
        try {
            countDownLatch.await();
            while (true) {
                // 获取锁
                SetArgs setArgs = SetArgs.Builder.ex(5).nx();
                String succ = asyncCommands.set("update-product",
                        Thread.currentThread().getName(), setArgs).get(1, TimeUnit.SECONDS);
                // 加锁失败
                if (!"OK".equals(succ)) {
                    System.out.println(Thread.currentThread().getName() + "加锁失败,自选等待锁");
                    Thread.sleep(100);
                } else {
                    System.out.println(Thread.currentThread().getName() + "加锁成功");
                    break;
                }
            }
            // 加锁成功
            System.out.println(Thread.currentThread().getName() + "开始执行业务逻辑");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + "完成业务逻辑");
            // 释放锁
            asyncCommands.del("update-product").get(1, TimeUnit.SECONDS);
            System.out.println(Thread.currentThread().getName() + "释放锁");
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    Thread thread1 = new Thread(runnable);
    Thread thread2 = new Thread(runnable);
    Thread thread3 = new Thread(runnable);
    thread1.start();
    thread2.start();
    thread3.start();
    countDownLatch.countDown();

    Thread.sleep(TimeUnit.DAYS.toMillis(1));
}

这里先来写一个 Runnable 任务,它里面有一个 CountDownLatch,CountDownLatch 的值是 1,后面我们会启动三个线程来执行这个 Runnable 任务。前面两个线程启动的时候,都会阻塞在这个 CountDownLatch.await() 这里,然后第三个线程启动的时候,也走到 CountDownLatch.await() 的位置,然后三个线程会一起开始执行下面的业务逻辑。

业务逻辑的第一步就抢锁,这里会创建一个 SetArgs 参数,SET 命令后面的那些参数都可以在这里找到对应的方法,比如,SetArgs.Builder.ex() 方法,对标的就是 SET 命令的 EX 参数,SetArgs.Builder.nx() 方法就是 SET 命令的 NX 参数。这里我们用 ex(5) 方法告诉 Redis 需要这个 Key 五秒自动过期,用 nx() 方法告诉 Redis 这个 Key 不存在的时候,才能创建。

然后,执行 asyncCommands.set() 方法发送 SET 命令,其中第一个参数是 SET 命令的 Key,第二个是 Value,第三个的话就是 SetArgs 这些扩展参数,然后在这里阻塞一秒钟,等 SET 命令执行完。如果返回值不是 “OK”,那就是当前线程加锁失败了,当前线程会休眠一会,再继续这个 while 循环去抢锁。如果返回是 “OK” 的话,那证明这个 Key 写入成功了,也就加锁成功了,我们直接跳出 while 循环,就可以开始执行业务逻辑了。我们用这个 sleep 1 秒来表示执行业务逻辑的耗时。

执行完业务逻辑之后,就可以执行 DEL 命令,把这个 Key 删掉,表示释放锁,其他线程就可以抢锁成功了。

执行一下这个单测,看一下输出。先是线程 1 成功加锁,线程 2、3 会自旋等待,然后线程 1 执行完业务逻辑,释放锁,线程 3 加锁,最后是线程 2 加锁。

Thread-1加锁成功
Thread-3加锁失败,自旋等待锁
Thread-2加锁失败,自旋等待锁
Thread-1开始执行业务逻辑
Thread-3加锁失败,自旋等待锁
Thread-2加锁失败,自旋等待锁
...
Thread-1完成业务逻辑
Thread-1释放锁
Thread-3加锁成功
Thread-3开始执行业务逻辑
Thread-2加锁失败,自旋等待锁
Thread-2加锁失败,自旋等待锁
...
Thread-3完成业务逻辑
Thread-3释放锁
Thread-2加锁成功
Thread-2开始执行业务逻辑
Thread-2完成业务逻辑
Thread-2释放锁

3. 限流

有些高并发的场景,例如抢购、秒杀,流量峰值会比较高,后端业务资源是有限的,需要对业务进行限流才可以支持业务的正常运转。比如说,我们要求整个服务的 QPS 不能超过 1000,这个时候就可以用 Redis 的 INCR 命令,来实现限流的效果。

先介绍一下这个限流 Key 的结构:前缀是单个服务的名称,也可以是某个接口的名称。我们这里演示订单服务的限流,前缀定义为 order-service,后缀是秒级时间戳。

然后每次访问订单服务的时候,就会根据当前时间戳去算一下需要操作的限流 Key。然后,调用 incr() 方法对限流 Key 进行加一操作。要是加一之后超过了 10,就是下面指定的 maxQps 值,就表示 QPS 超过限流阈值了,会打印出“请求被限流”。

@Test
public void testLimit() throws Exception {
    String prefix = "order-service";
    long maxQps = 10;
    long nowSeconds = System.currentTimeMillis() / 1000;
    for (int i = 0; i < 15; i++) {
        Long result = asyncCommands.incr(prefix + nowSeconds).get(1, TimeUnit.SECONDS);
        if (result > maxQps) {
            System.out.println("请求被限流");
        }else{
            System.out.println("请求正常被处理");
        }
    }
}

执行一下上面这个单测,输出了 10 个“请求正常被处理”,然后就到达了 maxQps 阈值,开始输出“请求被限流”。等到下一秒的时候,会重新开始输出 10 个“请求被限流”,之后会再次被限流。

请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求正常被处理
请求被限流
请求被限流
请求被限流
请求被限流
请求被限流

总结

这一节主要介绍了 Redis 字符串的核心命令,主要就是读写命令、批量操作、递增操作,还有一些复合操作,这里就不再一一展开重复了,关键还是小伙伴们根据本节的命令演示自己动手练习,才能真正熟悉这些命令的含义。

然后,本节分析了 Redis 字符串的应用场景。这里首先介绍了在 Java 中使用 Lettuce 客户端连接访问 Redis 的基本代码模板,后续其他命令的实践示例都会使用这套模板,小伙伴们一定要亲自搭建一下这个 demo 项目。然后通过缓存对象、分布式锁或者服务限流三个实践示例,讲解了 Redis String 如何与实际项目需求相结合。

说透 Redis 7 - 杨四正 - 掘金小册核心原理剖析+源码解读+实践应用,全方位带你吃透 Redis 7。「说透 Redis 7」由杨四正撰写,979人购买https://s.juejin.cn/ds/kaY9xnj/

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

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

相关文章

磨金石教育摄影技能干货分享|摄影师镜头下生活的色彩

在生活中也有很多亮丽的色彩&#xff0c;生活在我们看起来是平凡的&#xff0c;但其实每个地方每个人都会有不同的风景。不同的文化&#xff0c;不同的地域&#xff0c;这些都足以构造出不同的色彩。下面就让我们跟随镜头看看世界各地人民多姿多彩的生活。1 生活的色彩这是西孟…

ASP .Net Core内置 Identity 简介 使用

一、简介 1、概况 ASP.NET Core Identity是一个成员身份系统&#xff0c;可将用户注册和登录功能添加到 ASP.NET Core Web UI。 成员身份系统处理身份验证和授权问题。 身份验证涉及你的身份。 授权涉及允许你进行的操作。 因此&#xff0c;身份验证是授权的先决条件。 ASP .N…

js判断 数组中是否存在形同元素

上例子 flag true; //假设不重复&#xff1b; let arr [{ settingName: 渠道客户订单, settingCode: A1 },{ settingName: 户订单, settingCode: A1 },];for(let i 0;i < arr.length-1;i){ //循环开始元素 for(let j i 1;j < arr.length;j){ //循环后续所有元素 …

Pandoc 多Markdown转单PDF

文章目录Pandoc 简介Pandoc 安装pandoc-latex-template字体安装Powershell 脚本Ubuntu PandocMarkdown 合并Pandoc 简介 Pandoc, 免费的文档转换器, 支持常见的各种文件格式的相互转换, 如Markdown, HTML, EPUB, LaTeX, docx, RTF, PDF 等. 本篇以Windows下的多Markdown转单P…

个性化脑机接口及应用

脑机接口&#xff08;BCI&#xff09;是一种变革传统人机交互的新型技术&#xff0c;用户的大脑是直接的控制信号源。在BCI转化为实际应用时&#xff0c;由于用户个体之间的感觉、知觉、表象与认知思维活动、脑结构与功能具有一定的差异&#xff0c;通用BCI难以满足不同个体的需…

国产linux操作系统——麒麟操作系统的来龙去脉(有下载地址,亲测可用)

文章目录1、linux操作系统2、国产操作系统3、麒麟操作系统4、引用1、linux操作系统 目前市场主流的linux操作系统分类大致如此&#xff0c;国产操作系统的麒麟操作系统&#xff0c;底层比较杂&#xff0c;所以单独一类。 2、国产操作系统 排名日期截止到2022.6。 这里提一下排…

基于java SSM的房屋租赁系统设计和实现

基于java SSM的房屋租赁系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文…

ASO优化的两个渠道之推广+老用户维系

要想自己的APP在应用商城里获得较高的排名&#xff0c;提高用户的下载量&#xff0c;就要通过ASO优化来辅助完成。 &#xff08;一&#xff09;&#xff0c;ASO优化的排名因素——元数据的优化&#xff1a;1&#xff0c;icon图标。图片要直观和清晰地突出APP的内容&#xff0c…

力扣刷题记录——434. 字符串中的单词数、448. 找到所有数组中消失的数字、455. 分发饼干

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《434. 字符串中的单词数、448. 找到所有数组中消失的数字…

Google colab-思腾云

文章目录Google colab具体操作过程问题1问题2AutoDL问题1 无卡开机思腾云使用基础信息如何租用服务器&#xff1f;如何上传代码以及运行程序&#xff1f;操作服务器的方式&#xff1f;pycharm方式Xshell的形式问题1 libSM.so.6 和 libXrender.so.1问题2&#xff1a;运行代码出现…

Dubbo概述-快速入门

Dubbo概念 ●Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架。 ●致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。 ●官网: http://ubbo.apache.orgo 节点角色说明: . ●Provider: 暴露服务的服务提供方 ●Contahier: 服务运行容器 ●Co…

YOLOv3论文精读: An Incremental Improvement-增量式的改进

Abstract 我们对 YOLO 进行了一系列更新&#xff01;它包含一堆小设计&#xff0c;可以使系统的性能得到更新。我们也训练了一个新的、比较大的神经网络。虽然比上一版更大一些&#xff0c;但是精度也提高了。不用担心&#xff0c;它的速度依然很快。YOLOv3 在 320320 输入图像…

【Django项目开发】部门管理模块的开发(九)

文章目录一、模型类设计二、视图设计1.都有哪些接口三、序列化器类设计四.分页操作1.utils工具中定义pagination.py2.视图类中使用五.路由配置一、模型类设计 一个部门下面可能会有很多子部门&#xff0c;一个子部门上面可能会有父部门&#xff1b;即部门内部之间进行关联&…

Grapecity DataViewsJS JavaScript Crack

Grapecity DataViewsJS一个完整的 React 数据展示和数据网格平台 通过从各种不同的演示视图中进行选择&#xff0c;包括树、卡片、砖石、网格、时间线、甘特图、日历和网格&#xff0c;超越传统的表格显示。采集 by Ω578867473 免费无限开发者许可&#xff01;只为部署付费 快…

Hexo博客搭建(简化版)

Hexo博客搭建&#xff08;简化版&#xff09; 一、环境搭建 1.1 Git Git官网-下载界面 > git --version1.2 Node.js 1.2.1 下载安装Node.js Node.js官网下载界面 > node --version1.2.2 Hexo与Node.js Hexo versionMinimum (Node.js version)Less than (Node.js …

2023年我花费数小时整理的Java常用类文档,建议收藏学习

推荐学习专栏&#xff1a;【Java 编程基础教程系列】&#xff0c;从入门到就业精通&#xff0c;买不了吃亏&#xff0c;买不了上当&#xff01;&#xff01; 文章目录1. 基本类型的包装类1.1 概念1.2 常用的属性1.3 常用的方法1.4 自动装箱和自动拆箱2. 字符串类2.1 String 类2…

二分模板:查找数的范围、数的三次方根

内容摘自ACWING&#xff0c;一个很好的算法学习平台。 二分模板 判断左边界 当q[mid]>x时&#xff0c;待查找元素只会在mid左边&#xff0c;令rmid。 while( l < r ) {mid l r >> 1;if(q[mid] > x) r mid;else l mid 1; }判断右边界 当q[mid]<x&…

Spring项目中自动打印执行SQL和耗时,这款神级插件你值得拥有

这里写自定义目录标题参考简介集成方式如何使用步骤一&#xff0c;引入P6Spy。步骤二&#xff0c;修改数据源的配置。步骤三&#xff0c;新建spy.properties文件&#xff0c;放在resources目录下。步骤四&#xff0c;新建一个类实现MessageFormattingStrategy&#xff0c;并重写…

spring cache @Cacheable的使用

Cacheable的使用1.Cacheable1.1 cacheNames value1.2 关联多个缓存名1.3 key 和 keygenerator1.4 CacheManager CacheResolver1.5 sync1.6 condition1.7 unless2.CachePut 放置缓存3.CacheEvict 删除缓存4.测试代码5.默认缓存和redis缓存6.过程中的问题1.Cacheable 注解在方法…

剩余系,欧拉定理,扩展欧拉定理

剩余类&#xff08;同余类&#xff09; 给定一个正整数n&#xff0c;以 r ∈[0, n -1]表示所有形如的整数组成的集合称为模n的剩余类。例n 5,r 3,则C3 5x &#xff0b;3为模5的一个剩余类。 完全剩余系&#xff08;完系&#xff09; 给定一个正整数n&#xff0c;在模n的剩…