Redis笔记

1,Redis介绍

Redis是使用C语言编写的一个key-value 数据库,key是字符串,value包括以下5中类型:

结构类型

存储的数据

针对该结构能执行的操作

string

字符串、整数或浮点数

对整个字符串或其中的一部分执行操作;

对整数和浮点数执行自增或自减操作。

list

字符串列表,可以重复

从链表的两端压入或弹出元素;

对链表进行修剪(trim);

获取单个或多个元素;

根据值查找或移除元素。

set

字符串的无序集合,每个元素都是唯一的

添加、获取、移除单个元素;

检查一个元素是否存在于集合中;

计算两个集合的交集、并集、差集。

hash

多个key-value的集合,key不能重复的散列表

添加、获取、移除单个键值对;

获取所有键值对。HashMap、JavaBean、JSON

sorted set

字符串的有序集合,每个元素都有一个浮点数分值,元素的排列顺序由分值大小决定

添加、获取、删除单个元素;

根据分值范围获取元素

2,下载安装包 

redis 官方网站 :Redis

3,安装redis

1,安装C语言的编译环境

yum -y install gcc automake autoconf libtool make


2,解压并安装

默认在/usr/local/bin目录下安装redis服务程序redis-server和客户端程序redis-cli

tar -xzf redis-xxx.tar.gz

cd redis-xxx

make

make install

3,启动redis服务

将redis-xxx/redis.conf拷贝到/etc目录下,启动时要指定配置文件的路径。

./redis-server  /etc/redis.conf

 4,启动客户端并测试

默认连接本机的6379端口,ping 命令用于验证 redis 服务是否正常,quit命令关闭连接退出客户端。启动客户端时可指定主机地址和端口:redis-cli  -h 127.0.0.1  -p 6379 

./redis-cli

127.0.0.1:6379>ping

PONG

127.0.0.1:6379>quit

 4,redis配置

在etc/redis.conf下设置

1, bind

默认情况bind=127.0.0.1只能接受本机的访问请求,不写的情况下,无限制接受任何ip地址的访问

2,protected-mode

将本机访问保护模式设置 no

3,port

端口号,默认 6379

4,timeout

一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。

5,cp-keepalive

对访问客户端的一种心跳检测,每个n秒检测一次。

单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60

6,daemonize

是否为后台进程,设置为yes

守护进程,后台启动

7,requirepass

永久设置密码,需要再配置文件中进行设置

5,redis常用操作命令

quit -------------- 退出客户端连接

shutdown --------- 在客户端关闭redis服务

ping/pong ------- 测试redis连接

select index ------ 选择数据库,Redis默认有16个数据库index从0~15,默认连接的是0号数据库

keys * --------- 查看当前数据库中所有的key

expire key 秒 --------- 设置key的有效时间(秒),过期后会自动删除key

ttl key ---------- 查看key的剩余时间

del key ---------- 删除key

dbsize ---------- 返回当前数据库中key的数量

flushdb ------- 清空当前数据库中所有的key

type key ------ 返回key的类型

rename oldKey newKey --------- 重命名key

exists key------确认某个key是否存在

 1,String类型

设置值:set key value

获取值:get key

自增:incr key (适用于整数类型的value)

自减:decr key(适用于整数类型的value)

返回旧值设置新值:getset key value (原子性)

设置多值:mset k1 v1 k2 v2 ...

获取多值:mget k1 k2 ....

2,hash类型

设置hash的单个属性和值:hset key field value

获取hash单个属性值:hget key field

设置hash的多个属性和值hmset key f1 v1 f2 v2 ...

获取hash多个属性值hmget key f1 f2 ...

返回hash中所有的field:hkyes key

返回hash中所有的value:hvals key

返回hash中所有的field和value:hgetall key

3, list类型

头部添加:lpush key value...

尾部添加:rpush key value...

头部弹出(删除):lpop key

尾部弹出(删除):rpop key

返回列表的元素:lrange key start stop (lrange key 0 -1 返回列表的所有元素)  

4,set类型

添加元素:sadd key value ...

返回集合元素个数:scard key

返回集合的所有元素:smembers key  (无序)

5,zset类型

添加:zadd key score value

升序返回所有元素 zrange key 0 -1 (按分值升序)

降序返回所有元素 zrevrange key 0 -1 (按分值降序)

返回集合中元素个数:zcard key

6,Java连接Redis——Jedis 

1,连接准备

1,注释IP绑定:在/etc/redis.conf配置文件

# bind 127.0.0.1

2,关闭保护模式

protected-mode no

3,Linux防火墙开启6379端口

firewall-cmd --zone=public --add-port=6379/tcp --permanent

firewall-cmd --reload

4,配置完重启redis服务器

2,添加Jedis依赖

<dependency>

      <groupId>redis.clients</groupId>

      <artifactId>jedis</artifactId>

      <version>3.3.0</version>

</dependency>

3,使用Jedis操作Linux上的Redis

public class App

{

    public static void main( String[] args )

    {

        Jedis jedis=new Jedis("192.168.40.128",6379);

        jedis.set("name","张三");

        System.out.println(jedis.get("name"));

    }

}

4,使用Jedis操作Java对象

        不管是Redis中哪种数据类型进行存储时,jedis的方法只接受String和byte[]类型的参数,所以需要在Redis中保存Java对象的思路:Java对象——>String或byte[]。

1,将对象转换为Json字符串进行存储

可以利用gson、fastjson、jackson等工具进行json格式转换以fastjson为例:

1,添加fastjson和lombok依赖

    <dependency>

      <groupId>org.projectlombok</groupId>

      <artifactId>lombok</artifactId>

      <version>1.18.28</version>

    </dependency>

    <dependency>

      <groupId>com.alibaba</groupId>

      <artifactId>fastjson</artifactId>

      <version>1.2.70</version>

    </dependency>

 2,使用jedis存取对象
public class App 
{
    public static void main( String[] args )
    {   //连接redis
        Jedis jedis = new Jedis("192.168.40.128", 6379);
        //创建对象Student
        Student student = new Student("张三", 23);
        //转换成Jjson字符串
        String jsonString = JSON.toJSONString(student);
        //添加到redis
        jedis.set("student", jsonString);
        //获取key值为student的json字符串
        String s = jedis.get("student");
        System.out.println(s);
        //将json字符串转换成对象
        Student student1 = JSON.parseObject(s, Student.class);
        System.out.println(student1);
    }
}

2,将对象转换为byte[]存储

1,对象实现序列化接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
    private Integer id;
    private String name;
    private Integer age;
}
2,对象的序列化和反序列化
public static void main( String[] args ) throws Exception
    {
        Jedis jedis=new Jedis("192.168.138.128",6379);
        Student stu=new Student(2,"李四",24);
        //序列化对象
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(baos);
        oos.writeObject(stu);
        byte[] bytes = baos.toByteArray();
        //保存redis
        jedis.set("stu".getBytes(),bytes);
        //从redis中获取
        byte[] data = jedis.get("stu".getBytes());
        //反序列化操作
        ByteArrayInputStream bais=new ByteArrayInputStream(data);
        ObjectInputStream ois=new ObjectInputStream(bais);
        Student student=(Student) ois.readObject();
        System.out.println(student);
    }

7,Redis的发布和订阅机制

举例:电商的主要应用程序负责产生订单——>订单成功后立刻返回订单信息,进行消息的发布——>让其他应用完成产生订单后的相关任务:

  1. 发送短信应用:给用户发送确认短信,或者发邮件
  2. 发送发货信息应用:给仓库人员/商家/物流发送订单信息

频道:bjpn.com

1,使用客户端进行发布和订阅

1,订阅者订阅相关频道

2, 发布者在频道发送消息

3,订阅者收到消息

2,使用Java进行发布和订阅

1,编写订阅类 MyPubSub继承JedisPubSub

public class MyPubSub extends JedisPubSub {
    @Override
    public void onMessage(String channel, String message) {
        System.out.println("收到"+channel+"的消息:"+message);
    }
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        System.out.println(channel+"频道订阅成功");
    }
}

2,开启订阅,并模拟另外一个应用发布消息

public class AppTest 
{
    @Test
    //开启订阅
    public void start()
    {
        Jedis jedis = new Jedis("192.168.40.128", 6379);
        MyPubSub myPubSub=new MyPubSub();
        jedis.subscribe(myPubSub,"bjpn.com");
    }
    @Test
    //模拟另外一个应用发布消息
    public void end()
    {
        Jedis jedis = new Jedis("192.168.40.128", 6379);
        jedis.publish("bjpn.com","你好!");
    }
}

8,Redis的事务

        multi命令用于开始一个事务;此后发送的多个命令会被Redis入队,不会马上执行;最后发送 exec命令让Redis执行队列中的所有命令。取消事务:discard 命令,其实就是清空命令队列并退出事务

1,关于乐观锁和悲观锁

        在涉及到多个线程操作共享资源的情况下,为了确保多个线程有序的执行修改操作,在修改过程中不被其他的线程打断,这个时候我们就可以采用“锁”机制。

1,悲观锁

        当在操作共享资源之前,先加锁,然后执行操作,操作完成后再释放锁。在Java中的同步代码块和同步方法、以及MySQL都采用了悲观锁来避免多个线程操作同时操作共享数据。

2,乐观锁

        在修改数据之前,先读取数据,修改时再判断此刻的值是否和之前读取到的值一致,如果一致则说明该数据未被其他的线程修改,则直接修改;如果数据不一致了,则说明该数据已经被其他线程修改,则回到开始重复该过程——“自旋锁”。

2,使用watch命令来实现乐观锁

        watch命令用来实现乐观锁,使用watch 命令监视某个key,当exec时如果该key值有变化,则事务会失败。在multi 命令之前使用 watch 命令监控某些键,然后使用multi命令开启事务,将多条命令入队。当执行 exec 命令时,Redis会比对watch命令监控的值有没有发生变化,如果没有变化就执行队列中的命令;如果发生了变化,那么它不会执行任何命令,而是返回nil。

提示:不要在multi和exec之间执行watch命令。

9,Redis持久化机制

Redis支持两种备份方式,一种是快照,这也是默认方式;另一种是Append-only File(AOF)方式。

1,快照方式

save 900 1        # 超过900秒且超过1个键被更改则进行快照

save 300 10      #超过 300秒且超过10个键被更改则进行快照

save 60 10000   # 超过60秒且超过10000个键被更改则进行快照

        上面的三个条件之间是“”的关系,只要其中一个条件满足,就会进行快照。每次保存快照都是将内存数据完整写入到磁盘,并不是保存增量数据。Redis会创建一个子进程,将内存数据写入一个临时文件,写入完成后,使用该文件替换之前的快照文件。客户端可以发送savebgsave命令通知Redis进行一次快照。save是在Redis Server的主进程中执行快照,因此会阻塞所有客户端的请求,所以不推荐使用。bgsave命令则是在子进程中执行快照。使用shutdwon停止redis的服务,则redis会进行一次快照操作。

2,AOF方式

         AOF备份方式是将Redis收到的每一个写命令都追加到一个appendonly.aof文件中,当Redis故障后重启时会重新执行该文件中的所有命令,就能在内存中重建整个数据库的内容。要启用AOF备份方式,需要配置如下:

每秒钟刷新一次缓冲区,在性能和持久化方面做了折中,推荐!!!

10,Redis主从复制集群模式

主从复制其主要的目的:为了提高Redis服务的高可用(HA),采用集群部署保证高可用(HA),采用多台服务器,分布在不同的机房或机架上,通过网络进行连接,让它们进行数据的同步,保持数据的一致性。即使一台服务器宕机,其他的服务器仍然可以提供服务,保证了高可用性。

master为主节点:可读可写

slave为从节点:只读

1,Redis主从复制的工作流程:

1,创建独立的配置文件

Master:redis.conf

Slave1:redis6380.conf

Slave2:redi6381.conf

2,修改配置文件

3,启动主节点和分节点服务器

在/usr/local/bin下执行命令

 ./redis-server /etc/redis.conf

 ./redis-server /etc/redis6380.conf

 ./redis-server /etc/redis6381.conf

4, 客户端连接

主:./redis-cli -p 6379

从:./redis-cli -p 6380

从:./redis-cli -p 6381

5,实现主从复制 

1,命令实现

在分节点的服务器下执行命令:slaveof IP 主端口号

slaveof 192.168.40.128 6379

2,配置实现

在分界点的配置文件下修改

11, Spring Data Redis

        Spring Data Redis(SDR)是Spring提供的用来访问Redis的组件, 它基于lettuce来连接Redis服务,SDR中的核心类是RedisTemplate,使用该类的实例可方便的和Redis进行交互。SpringBoot创建了StringRedisTemplate<String,String>和RedisTemplate<Object,Object>,可以直接利用这两个对象来访问Redis,只需要注入即可使用。

1, 添加spring-data-redis启动器

<dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

也开在创建项目页面添加

2,在yml配置redis

spring:

  redis:

    host: 192.168.138.128         主节点IP

    port: 6379                            主节点端口号

    database: 0                          数据库编号

    timeout: 3000                       服务器超时时间

    lettuce:                                 Lettuce 连接池配置

      pool:

        max-active: 8                   最大活动连接数

        max-idle: 8                       最大空闲连接数

        max-wait: 3000ms          连接池中等待连接的最长时间

        min-idle: 0                        连接池中最小空闲连接数

3,注入StringRedisTemplate或RedisTemplate操作Redis

@SpringBootTest
class Demo2ApplicationTests {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads1() {
        //StringRedisTemplate中的K,V都是String
        stringRedisTemplate.opsForValue().set("name","张三");
        String name = stringRedisTemplate.opsForValue().get("name");
        System.out.println(name);
    }
    @Test
    void contextLoads2() {
        //RedisTemplate中的K,V都是Object,它会将字符串序列化成字节数组
        redisTemplate.opsForValue().set("username","李四");
        Object username = redisTemplate.opsForValue().get("username");
        System.out.println(username);
    }
}

存在的问题:

  1. RedisTemplate对象将key也序列化成了字节数组,给操作带来了不便。
  2. StringRedisTemplate的key和value都只能是字符串类型,
  3. 如果需要<String,Object>格式存储Reids,则需要自己编写配置类(设置序列化器)

4,编写配置类,设置对象的序列化器

@Configuration
public class RedisConfig {
    @Bean("myRedisTemplate")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //将key序列化为string
        redisTemplate.setKeySerializer(RedisSerializer.string());
        //将value序列化为json
        redisTemplate.setValueSerializer(RedisSerializer.json());
        return redisTemplate;
    }
}

5,通过“myRedisTemplate”操作Redis

@SpringBootTest
class Demo2ApplicationTests {
    @Autowired
    private RedisTemplate<String,Object> myRedisTemplate;
    @Test
    void contextLoads3() {
        User user=new User("齐天大圣",500);
        myRedisTemplate.opsForValue().set("user",user);
        User user1 =(User) myRedisTemplate.opsForValue().get("user");
        System.out.println(user1);
    }
}

6,使用RedisTemplate操作其他Redis的数据类型

        RedisTemplate提供了boundValueOps(key),boundListOps(key),boundSetOps(key),boundHashOps(key)等方法,该方法返回BoundXxxOperations对象:

  1. BoundValueOperations:字符串类型操作
  2. BoundListOperations:列表类型操作
  3. BoundSetOperations:集合类型操作
  4. BoundZSetOperations:有序集合类型操作
  5. BoundHashOperations:散列操作

1,字符串操作

@Test
    void string1() {
        BoundValueOperations<String, Object> bvo = myRedisTemplate.boundValueOps("name");
        bvo.set("张三");
        System.out.println(bvo.get());
    }

@Test
    void string2() {
        BoundValueOperations<String, Object> bvo = myRedisTemplate.boundValueOps("user");
        bvo.set(new User("悟空",500));
        System.out.println(bvo.get());
    }

2,链表操作

@Test
    void list1() {
        BoundListOperations<String, Object> blo = myRedisTemplate.boundListOps("list1");
        blo.leftPush("张三");
        blo.leftPush("李四");
        blo.leftPush("王五");
        List<Object> list = blo.range(0, -1);
        System.out.println(list);
    }

@Test
    void list2() {
        BoundListOperations<String, Object> blo = myRedisTemplate.boundListOps("list2");
        blo.rightPush(new User("张三",23));
        blo.rightPush(new User("李四",24));
        blo.rightPush(new User("王五",25));
        List<Object> list = blo.range(0, -1);
        System.out.println(list);
    }

3,集合操作

@Test
    void set1() {
        BoundSetOperations<String, Object> bso = myRedisTemplate.boundSetOps("set1");
        bso.add("悟空","八戒","沙僧","悟空");
        Set<Object> set = bso.members();
        System.out.println(set);
    }

@Test
    void set2() {
        BoundSetOperations<String, Object> bso = myRedisTemplate.boundSetOps("set2");
        bso.add(new User("悟空",500),new User("八戒",300),new User("沙僧",200));
        Set<Object> set = bso.members();
        System.out.println(set);
    }

4,Hash操作

@Test
    void hash() {
        BoundHashOperations<String, Object, Object> bho = myRedisTemplate.boundHashOps("user1");
        bho.put("username","齐天大圣");
        bho.put("age",500);
        bho.put("address","花果山");

        System.out.println(bho.get("username"));
        System.out.println(bho.get("age"));
        System.out.println(bho.get("address"));

        List<Object> values = bho.values();
        System.out.println(values);
    }

5,有序链表操作

  @Test
    void Zset() {
        BoundZSetOperations<String, Object> bzso = myRedisTemplate.boundZSetOps("zest1");
        bzso.add("张三",100);
        bzso.add("李四",90);
        bzso.add("王五",60);
        Set<Object> zset1 = bzso.range(0, -1);
        System.out.println(zset1);
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = bzso.rangeWithScores(0, -1);
        System.out.println(typedTuples);
    }

12,使用Redis作为缓存服务器

1,编写缓存配置类

@Configuration
public class RedisConfig {
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
        //初始化一个RedisCacheWriter
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //序列化方式为json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jsonSerializer);
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair);
        //设置默认超过期时间是30分钟
        // cacheConfig =cacheConfig.entryTtl(Duration.ofMinutes(30));
        //初始化RedisCacheManager
        return new RedisCacheManager(cacheWriter, cacheConfig);
    }
}

2,启用缓存

@SpringBootApplication
@EnableCaching
public class Demo3Application {
    public static void main(String[] args) {
        SpringApplication.run(Demo3Application.class, args);
    }
}

3,使用缓存的注解

        @Cacheable标识需要缓存的方法,该方法的返回值会被Redis进行缓存(缓存的key=cacheNames+key);@CacheEvict用于清空缓存(key=cacheNames+key),该注解经常添加在修改和删除方法上。

@Service
public class UserService {
    @Cacheable(cacheNames = "user",key = "#id")
    public User findById(Integer id){
        System.out.println("从数据库MySQL中查询用户...");
        return new User(id,"用户"+id,23);
    }
    @CacheEvict(cacheNames = "user",key ="#user.id" )
    public int update(User user){
        System.out.println("修改数据库MySQL中的用户..."+user.getId());
        return 1;
    }
}

4,编写测试类

@SpringBootTest
class Demo3ApplicationTests {
    @Autowired
    private UserService userService;
    @Test
    void contextLoads() {
        User user1=userService.findById(1);
        System.out.println(user1);
        userService.update(new User(1,null,null));
        user1=userService.findById(1);
        System.out.println(user1);
    }
}

13,Spring Session

        Spring Session主要用于分布式应用中实现Session共享。比如:微服务架构的应用程序中,有专门负责进行用户登录的模块,当用户登录后,访问别的模块时,就不再需要进行登录认证了。这也就是常说的单点登录

高并发的解决方案:

  1. 集群部署:将相应的应用部署到多台服务器上,形成服务器的集群。
  2. 微服务:将一个项目拆分成若干个独立的模块,分别部署到不同的服务器上。集群和微服务都会带来Session的共享问题,Spring Session主要用于分布式应用上实现Session的共享。

 

        当客户端向A模块发送登录请求,登录成功后将用户的session信息保存到Redis中,当客户端向B、C模块发送订单请求,该模块会从Redis中获取用户的session信息,从而实现了A、B和C的session共享。

1,session共享实现案例

1,添加依赖

<dependency>

            <groupId>org.springframework.session</groupId>

            <artifactId>spring-session-data-redis</artifactId>

</dependency>

2,配置yml 文件

spring:

  redis:

    host: 192.168.138.128

  session:

    store-type: redis

server:

  port: 8081

spring:

  redis:

    host: 192.168.138.128

  session:

    store-type: redis

server:

  port: 8082

3,创建登录controller 

@RestController
public class LoginController {
    @RequestMapping("login")
    public String login(HttpSession session){
        //模拟登录成功后保存用户信息到session
        session.setAttribute("loginUser","admin");
        return "success";
    }
}

4,创建订单controller

@RestController
public class OrderController {
    @RequestMapping("order")
    public String order(HttpSession session){
        //模拟判断用户是否登录
        return "登录用户=====>"+session.getAttribute("loginUser");
    }
}

5,访问测试

2, Spring Session的工作过程

        在SpringBoot的应用中,如果添加了Spring Session的依赖,就会自动注册一个名为SpringSessionRespositoryFilter过滤器,这个过滤器过滤客户端的所有请求,该过滤器会将HttpServletRequest对象进行包装产生了一个代理对象SessionRepositoryRequestWrapper,代理对象,SessionRepositoryRequestWrapper重写了HttpServletRequest的getSession()方法,从Redis中读写对应的Session信息。

当发送第一次登录请求时,没有提交任何Cookie信息,Spring Session会创建一个新的Session存入到Redis中,该Session对象有一个唯一的uuid作为key,在响应时将uuid进行Base64编码后再通过cookie发送给客户端。

 当客户端发送订单请求时,客户端浏览器会通过Cookie再将base64编码后的uuid发送给服务器。

 

  Spring Session根据Base64解码后的uuid(即key值)从Redis获取对应的session信息

 Redis中保存session信息

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
狂神在他的Redis笔记中可能会涉及到一些内容,但是根据提供的引用内容,无法确定具体是哪些内容。可以使用自定义的配置文件来启动Redis服务,使用systemctl命令来启动和关闭服务。\[1\]在复杂的情况下,可以使用专业的消息中间件来进行订阅,但是需要注意如果订阅方的读取消息速度不够快,可能会导致消息积压,影响Redis的性能甚至导致崩溃。\[2\]Redis和Memcached一样,数据都是缓存在内存中,但是Redis会周期性地将更新的数据写入磁盘或记录文件,并通过主从复制实现数据同步。\[3\]如果你有具体的问题或需要更详细的信息,请提供更多的上下文。 #### 引用[.reference_title] - *1* *2* [Redis详细笔记](https://blog.csdn.net/qq_40087648/article/details/109808727)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [狂神说 Redis笔记](https://blog.csdn.net/DDDDeng_/article/details/108118544)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hbb123654

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值