ProjectDay14

续Redis基本使用

Redis的用途

上次课我们使用了Redis来保存一个字符串

但是实际开发中,基本上保存的都是集合或对象

我们可以保存json格式的字符串来实现

在实际开发中,我们可能会将标签\分类\秒杀这样或类似的数据保存在Redis中,以应对频繁的访问

实际上,还有一种比较多的使用的缓存就是某一条信息的浏览量\评论数\点赞数等,以及秒杀商品时的库存数等,都可以利用Redis提高并发量,高效访问

我们就来使用一下加减数字的命令

127.0.0.1:6379> set num "2"
OK
127.0.0.1:6379> get num
"2"
127.0.0.1:6379> incr num
(integer) 3
127.0.0.1:6379> get num
"3"
127.0.0.1:6379> decr num
(integer) 2

笔记末尾有Redis操作其它类型的演示操作,同学们可以自己运行测试

Redis线程安全问题

Redis底层操作数据的线程只有一条,即使有并发请求,也不会有线程安全问题,因为足够快,所以一条线程也能快速处理数据,请求不会有明显的等待

SpringBoot操作Redis

添加依赖

转到knows-faq模块

在pom.xml文件中添加如下依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>

Redis也是数据库,java操作Redis和java操作mysql有很多相似

java是通过jdbc来操作mysql数据库的

java是通过jedis来操作redis数据库的

jdbc和jedis都是比较繁琐的操作数据库的代码

所以我们使用mybatis操作mysql更简单

我们使用Spring-data-redis也可以更简单的操作redis

在操作之前我们还要配置一下application.properties文件,指定Redis的位置

knows-faq模块的application.properties文件

# 配置Redis的ip地址和端口号,以便SpringDataRedis连接
spring.redis.host=localhost
spring.redis.port=6379

基本操作测试

我们配置完SpringDataRedis

最好先在测试类中进行一下测试,保证运行状况良好

测试代码如下

// 下面代码时要连接Redis来进行新增和获取的操作
// 注解标记的对象可以操作Redis
// 是SpringDataRedis框架自动向Spring容器中保存的对象
// RedisTemplate<[key类型],[value的类型]>
@Resource
RedisTemplate<String,String> redisTemplate;
// 新增数据
@Test
public void add(){
    redisTemplate.opsForValue().set("myname","诸葛亮");
    System.out.println("ok");
}
// 获取数据
@Test
public void get(){
    String name=redisTemplate.opsForValue().get("myname");
    System.out.println(name);
}

Redis缓存标签列表

继续在knows-faq模块

我们的目标是TagServiceImpl类中查询出所有标签保存在Redis中

而现在我们在使用List<Tag>和Map<String,Tag>类型的属性来充当缓存需要进行改进,所有要删除之前的缓存属性和实现的代码

TagServiceImpl代码修改后结果如下

@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements ITagService {
    @Autowired
    private TagMapper tagMapper;
    // 获得操作Redis的对象
    @Resource
    private RedisTemplate<String,List<Tag>> redisTemplate;
    @Override
    public List<Tag> getTags() {
        // 先编写代码,从Redis中获得所有标签
        List<Tag> tags=redisTemplate.opsForValue().get("tags");
        // 判断从Redis中获得的标签是否为空
        if(tags==null){
            // 如果为空,证明是第一次访问,需要连接数据库查询所有标签
            tags=tagMapper.selectList(null);
            // 将查询出的所有标签保存在Redis中,以便下次获取
            redisTemplate.opsForValue().set("tags",tags);
            System.out.println("Redis加载所有标签完毕");
        }
        // 返回所有标签
        return tags;
    }
    @Override
    public Map<String, Tag> getTagMap() {
        // 实例化一个map对象
        Map<String,Tag> tagMap=new HashMap<>();
        // 遍历上面获得List的方法,将所有标签保存在map中
        for(Tag t: getTags()){
            tagMap.put(t.getName(),t);
        }
        // 返回tagMap
        return tagMap;
    }
}

重启faq项目

启动Nacos\gateway\knows-client

再访问学生首页,第一次访问时控制台会输出"Redis加载所有标签完毕",但是之后的刷新就不会再输出这个信息了,甚至重启faq模块之后访问学生首页也不会输出这个信息了,原因是Redis中保存着这个信息,只要Redis不重启,就不会出现这个信息!

Ribbon实现微服务间调用

什么是Ribbon

Ribbon也是SpringCloud提供的组件

它的功能是实现微服务之间的相互调用的

因为微服务项目每个业务都是项目整体的多个分支

分支之间一定会有交互,微服务之间的互相调用是普遍存在的

由此可知,Ribbon的使用时非常频繁和普遍呃,所以我们添加的

spring-cloud-starter-alibaba-nacos-discovery依赖已经包含了Ribbon,

也就是说Ribbon不需要单独添加配置和依赖,直接使用即可

Ribbon使用示例

在使用Ribbon之前

我们需要先明确在指定的业务流程中,Ribbon作用的多个微服务项目中哪个是调用的发起者,哪个是被调用的

被调用的一方称之为服务的提供者,也叫"生产者"

发起调用的一方称之为服务的调用者,也叫"消费者"

Ribbon可以调用什么样的方法

Ribbon本质上就是向目标服务器发送了一次请求,它能调用到的方法就是目标服务器编写的控制器的方法,调用的依据就是控制器方法的url

我们将用于Ribbon调用的控制器方法称之为"Rest接口"

  1. 定义一个生产者的方法(Controller类的方法)

  2. 添加Ribbon调用的支持(每个微服务项目编写一次)

  3. 消费者发起调用(一般在业务逻辑层中发起调用)

步骤1:

定义生产者

任何已经定义好的控制器方法,都可以被Ribbon调用

我们现在将knows-sys模块的AuthController类中的demo方法作为生产者,也就是调用目标

调用它的路径就是:/v1/auth/demo

步骤2:

添加Ribbon的支持

我们需要在将要发起Ribbon请求的项目中添加Ribbon的支持

我们一般会在SpringBoot启动类中将能够调用Ribbon请求的对象保存在Spring容器中,以备项目使用

我们本次测试使用faq模块调用sys模块中的方法

knows-faq模块的SpringBoot启动类中添加支持Ribbon的配置

@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("cn.tedu.knows.faq.mapper")
public class KnowsFaqApplication {

    public static void main(String[] args) {
        SpringApplication.run(KnowsFaqApplication.class, args);
    }

    // @Bean表示会将下面方法的返回值保存在Spring容器中
    @Bean
    // LoadBalanced是负载均衡的意思
    // 微服务模块间的调用是不经过网关的,所以网关中设置的负载均衡无效
    // 导致Ribbon请求需要单独的配置负载均衡的注解,完成高效调用
    @LoadBalanced
    // 向Spring容器中保存一个RestTemplate类型的对象,支持Ribbon调用
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

RestTemplate就是能够执行Ribbon请求的对象

这个步骤只需要配置一次,如果在其他业务中knows-faq项目又需要调用其他项目的方法,就不需要再次配置了

步骤3:

发起调用

我们应该已经在发起调用的一方中配置了RestTemplate对象

实际开发中,都是在业务逻辑层中发起Ribbon调用

现在我们是测试,写在测试类中即可

调用目标:/v1/auth/demo

测试代码如下

// 从Spring容器中获得发起Ribbon请求的对象
@Resource
RestTemplate restTemplate;
@Test
public void ribbon(){
    // 发送ribbon请求,先定义url
    // url=[协议]+[服务器名称]+[控制器路径]
    // 服务器名称必须是Nacos服务列表中存在的名称
    // 控制器路径就是从/v1开始的路径
    String url="http://sys-service/v1/auth/demo";
    // 发起Ribbon请求
    String str=restTemplate.getForObject(url,String.class);
    System.out.println(str);

}

测试必须在Nacos和sys启动的情况下运行

faq模块会在测试运行时启动

Ribbon调用示意图
在这里插入图片描述

Ribbon请求用户信息

上面章节完成了第一个Ribbon程序

在实际开发中,需要更有意义的控制器方法作为调用目标,而不是返回一个"helloworld"

下面我们就来实现根据用户名获得用户对象的实际业务

这个业务在faq模块中,很多方法都需要使用

sys模块仍然是生产者

faq模块仍然是消费者

sys模块要定义一个根据用户名返回用户对象的控制层方法

因为没有这个方法,所以我们要

转到knows-sys模块,编写出这个方法

先编写业务逻辑层IUserService添加方法如下

// 根据用户名获得用户对象
User getUserByUsername(String username);

UserServiceImpl实现类方法

@Override
public User getUserByUsername(String username) {
    return userMapper.findUserByUsername(username);
}

AuthController类中编写方法调用上面业务逻辑层

@Resource
private IUserService userService;
// 当前控制器方法用于Ribbon请求,路径设计为/v1/auth/user
// 参数是用户名,返回值为用户对象,Ribbon请求对应GetMapping
@GetMapping("/user")
public User getUser(String username){
    return userService.getUserByUsername(username);
}

sys模块Rest接口定义完成

也就是生产者定义完成下面要开始编写消费者的Ribbon的支持

上次课已经配置完毕,直接跳过

所以直接开始在knows-faq模块编写Ribbon调用即可

仍然使用测试类来进行测试

// 根据用户名获得用户对象的Ribbon调用
@Test
public void getUser(){
    // url路径中?之后的内容就是Ribbon请求的参数
    // 参数名称必须和控制器方法参数名称一致
    // 参数的值不直接赋值,使用{1}来占位
    // 调用时有既定的赋值方式
    String url="http://sys-service/v1/auth/user?username={1}";
    // 调用时,前两个参数意义不变,第三参数向{1}中复制
    User user=restTemplate
            .getForObject(url,User.class,"st2");
    System.out.println(user);
}

启动Nacos\重启Sys后再运行测试

测试结果中包含我们查询的用户信息,表示一切正常

微服务的会话保持

会话和会话保持

会话就是Session

指多次请求和响应的过程,只要浏览器打开之后不关闭,不超时就是同一次会话,在本次会话中登录成功时,会将用户信息保存在会话中,只要是同一次会话,当前登录的用户信息,就能一直保存在当前服务器内存中

我们达内知道单体项目portal实际上也是有会话支持的,只是所有会话的操作都封装在了Spring-Security框架,Spring-Security底层也是依靠session实现会话保持的

单体项目只需要一个服务器来保存当前登录用户信息,就可以实现会话保持

但是微服务项目是由多个服务器构成的,我们登录之后如何让所有微服务都知道我们的身份就成为了问题

微服务中的会话保持

每个服务器都有自己的内存,它们的内存中的数据不会自动共享

当一个用户登录一台服务器后,相当于把自己的用户信息保存在了这台服务器的内存中,当访问其它服务器时,新的服务器并没有保存当前用户的信息,所以会话保持是失败的

在这里插入图片描述

所有微服务项目都面临这个问题

那么想要在微服务架构下实现登录后还能会话保持,就需要特殊的解决方案

这个方案的名称叫"单点登录"

单点登录的实现思路

现在业界实现单点登录的思路主要有两种,都能够实现微服务的会话保持

1.Session共享

2.Token(令牌)

方案一:Session共享

核心思想就是将登录成功的用户信息共享给所有模块

在这里插入图片描述

实现思路

用户在登录服务器(sys)模块登录成功,同时会将用户信息保存在Redis中(保存的key为当前用户的sessionId)

该用户访问其它模块时,这个模块会到Redis中寻找这个用户的信息(依据也是当前用户的sessionId),这样就能实现会话保持

优点:

  • 支持Session共享功能的框架比较成熟,仅需要简单配置就可以实现功能
  • 结构相对简单,不需要大范围修改代码和更改程序结构,成本比较低

缺点:

  • 因为用户信息要共享到Redis中,各个微服务模块需要时还需要从Redis中获取,内存使用开销较大,影响服务器性能
  • 只能在当前微服务项目中实现会话保持,比较难以实现跨项目的信息共享

方案二:Token令牌

在这里插入图片描述

登录成功时,由登录服务器向客户端响应一个Token(令牌),客户端来保存这个Token,这个Token就是一个加密的字符串,其中包含当前登录用户的信息

当前项目的所有微服务都可以解析这个Token

最终客户端需要表名自己用户的身份时,只需要将Token和自己的请求信息一起发送给服务器,任何模块的微服务都可以知道这个用户的身份信息

优点:

  • 服务器内存不需要再保存用户信息,减少内存开销,运行效率更高
  • 客户端保存令牌,只要是可以解析这个令牌的项目都可以知道用户身份,方便跨应用(app)登录,也方便功能的扩展

缺点:

  • 一般需要一个单独的授权服务器来生成和解析令牌,配置内容较多,开发成本高
  • 因为加密和解密需要CPU的参与,所以需要CPU的算力,消耗的额外的算力,CPU的运行效率可能受影响

达内知道项目使用Token令牌的方式实现微服务架构的单点登录功能

Oauth2概述

什么是Oauth2

实现Token单点登录的解决方案现在业界是有明确标准的

Oauth(Open Auth:开放授权)是一套授权标准,是业界都在使用的一套完整的授权解决方案的格式

我们所使用的Oauth2.0就是Oauth1.0的升级.但是Oauth1.0基本没有被使用

现在很多大公司和企业都使用Oauth2作为开发授权标准

Oauth2支持的部分常用授权模式

  • 扫码登录(专业名称:授权码登录)

  • 用户名密码登录

  • 客户端登录(手机内部的app授权)

Spring Cloud Security

Spring Cloud Security就是微服务版的Spring-Security框架

在原有的基础上添加了微服务结构下用户的登录和权限管理的支持

最终我们要使用Spring Cloud Security和Oauth2结合实现微服务架构下的Token单点登录

最终实现项目结构如下图

在这里插入图片描述

微服务项目结构从功能上分为两大类

  • 授权服务器:接收用户名和密码,验证登录,返回令牌
  • 资源服务器:当用户请求当前服务器时,解析令牌,获得用户信息

Oauth2标准主要使用在授权服务器中

我们会创建授权服务器项目,这个项目会添加Oauth2的支持

这个依赖中包含了很多标准的方法

主要是包含了很多控制器方法,也就是说,我们创建的auth模块是不需要编写控制器方法的,都是Oauth2提供的,我们需要做的就是对这个项目进行各种配置

创建Auth授权服务器项目

创建knows-auth项目

在这里插入图片描述

父子相认

<module>knows-auth</module>

knows-auth的pom.xml

<?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>cn.tedu</groupId>
        <artifactId>knows</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>knows-auth</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>knows-auth</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>knows-commons</artifactId>
        </dependency>
        <!--  Spring Cloud Security结合Oauth2会在数据库中
              保存一些临时信息,需要jdbc依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--
            SpringCloudSecurity 依赖
            让我们的项目支持微服务结构下的用户授权和管理
        -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <!--
        Oauth2依赖
        让我们的项目支持Oauth2标准,这个依赖中自带很多控制器方法
        令牌的生成和解析等
        -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <!--
        JWT依赖
        让我们的项目执行对用户信息的JWT加密
        以保存到客户端
        -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>
    </dependencies>

</project>

application.properties文件配置如下

spring.datasource.url=jdbc:mysql://localhost:3306/knows?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root

logging.level.cn.tedu.knows.auth=debug

server.port=8010

spring.application.name=auth-service

spring.cloud.nacos.discovery.server-addr=localhost:8848

# 下面的配置是允许Spring容器中已经存在的对象被新对象覆盖
# 意思就是两个相同id的对象保存到Spring容器时会不会报错
# 设置完true之后,相同id的后一个出现的对象会覆盖掉之前的对象
# 当前我们的auth项目内部,会有我们注入的对象覆盖系统原有对象的情况
spring.main.allow-bean-definition-overriding=true

SpringBoot启动类

@SpringBootApplication
@EnableDiscoveryClient
public class KnowsAuthApplication {

    public static void main(String[] args) {
        SpringApplication.run(KnowsAuthApplication.class, args);
    }

    // Ribbon的支持
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

授权服务器配置准备

auth项目会配置很多信息

我们先按步骤进行准备工作

先配置Spring-Security放行

创建security包,包中创建SecurityConfig

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 当前auth项目也是设置Spring-Security全部放行
    // 因为登录验证交给了Oauth2,Spring-Security不在负责验证
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()   // 关闭防跨域攻击
                .authorizeRequests()   // 设计访问权限
                .anyRequest().permitAll()   // 任何请求全部放行
                .and().formLogin();         // 支持表单登录
    }
    // 我们在Spring容器中保存一个加密对象
    // 之后有配置需要加密,就可以取出使用
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    // 我们在后面的配置中,需要Spring-Security框架中的授权管理器
    // 授权管理器是登录功能的重要组成部分,现在是Oauth2需要它
    // 我们需要将这个授权管理器保存到Spring容器中,以便Oauth2使用它
    @Bean
    public AuthenticationManager
                    authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

在授权过程中,令牌的生成方式和保存方式也是非常重要的部分

我们要编写一个类,专门配置令牌的保存

我们最终的目标效果是生成一个令牌保存到客户端

但是分步骤完成,先做一个简单的版本,暂时还是保存在内存中

创建TokenConfig类,来编写保存令牌的配置信息

// 只要是Spring的配置,就需要添加这个注解
@Configuration
public class TokenConfig {
    // 配置保存令牌的策略对象到Spring容器
    // 1.保存在内存中
    // 2.生成令牌保存在客户端
    // 先暂时保存在内存中
    @Bean
    public TokenStore tokenStore(){
        return new InMemoryTokenStore();
    }

}

我们当前微服务版本的登录,仍然是SpringSecurity的实现思路,只是搭配了Oauth2的标准使用

就是portal项目中UserDetailsServiceImpl类中的登录配置类似

根据UserDetailsServiceImpl类中的业务,我们需要下面3个方法

1.根据用户名获得用户对象

2.根据用户id获得用户的所有角色

3.根据用户id获得用户的所有权限

因为这些方法都是应该有sys用户管理模块提供的

所以转到knows-sys模块编写上面3个方法

我们编写过1方法

2.3方法没有编写

从业务逻辑层开始

IUserService接口添加方法如下

// 根据用户id获得所有权限
List<Permission> getPermissionsById(Integer id);
// 根据用户id获得所有角色
List<Role> getRolesById(Integer id);

UserServiceImpl实现类

@Override
public List<Permission> getPermissionsById(Integer id) {
    return userMapper.findUserPermissionsById(id);
}
@Override
public List<Role> getRolesById(Integer id) {
    return userMapper.findUserRolesById(id);
}

AuthController类添加Rest接口

// 根据用户id获得所有权限
@GetMapping("/permissions")
public List<Permission> permissions(Integer id){
    return userService.getPermissionsById(id);
}
// 根据用户id获得所有角色
@GetMapping("/roles")
public List<Role> roles(Integer id){
    return userService.getRolesById(id);
}

英文

incr\increment:增长

随笔

Redis 其它类型操作参考

List 列表
常用命令: lpush,rpush,lpop,rpop,lrange等 
Redis的list在底层实现上并不是数组而是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。
Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。
lists的常用操作包括LPUSH、RPUSH、LRANGE、RPOP等。可以用LPUSH在lists的左侧插入一个新元素,用RPUSH在lists的右侧插入一个新元素,用LRANGE命令从lists中指定一个范围来提取元素,RPOP从右侧弹出数据。来看几个例子::
//新建一个list叫做mylist,并在列表头部插入元素"Tom"
127.0.0.1:6379> lpush mylist "Tom" 
//返回当前mylist中的元素个数
(integer) 1 
//在mylist右侧插入元素"Jerry"
127.0.0.1:6379> rpush mylist "Jerry" 
(integer) 2
//在mylist左侧插入元素"Andy"
127.0.0.1:6379> lpush mylist "Andy" 
(integer) 3
//列出mylist中从编号0到编号1的元素
127.0.0.1:6379> lrange mylist 0 1 
1) "Andy"
2) "Tom"
//列出mylist中从编号0到倒数第一个元素
127.0.0.1:6379> lrange mylist 0 -1 
1) "Andy"
2) "Tom"
3) "Jerry"
//从右侧取出最后一个数据
127.0.0.1:6379> rpop mylist
"Jerry"
//再次列出mylist中从编号0到倒数第一个元素
127.0.0.1:6379> lrange mylist 0 -1 
1) "Andy"
2) "Tom"
Set 集合 
常用命令: sadd,smembers,sunion 等 
set 是无序不重复集合,list是有序可以重复集合,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要功能,这个也是list所不能提供的。
可以基于 set 轻易实现交集、并集、差集的操作。比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能,也就是求交集的过程。set具体命令如下:
//向集合myset中加入一个新元素"Tom"
127.0.0.1:6379> sadd myset "Tom" 
(integer) 1
127.0.0.1:6379> sadd myset "Jerry"
(integer) 1
//列出集合myset中的所有元素
127.0.0.1:6379> smembers myset 
1) "Jerry"
2) "Tom"
//判断元素Tom是否在集合myset中,返回1表示存在
127.0.0.1:6379> sismember myset "Tom" 
(integer) 1
//判断元素3是否在集合myset中,返回0表示不存在
127.0.0.1:6379> sismember myset "Andy" 
(integer) 0
//新建一个新的集合yourset
127.0.0.1:6379> sadd yourset "Tom" 
(integer) 1
127.0.0.1:6379> sadd yourset "John"
(integer) 1
127.0.0.1:6379> smembers yourset
1) "Tom"
2) "John"
//对两个集合求并集
127.0.0.1:6379> sunion myset yourset 
1) "Tom"
2) "Jerry"
3) "John"
Sorted Set 有序集合
常用命令: zadd,zrange,zrem,zcard等 
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 SortedSet 结构进行存储。
很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等
来看几个生动的例子:
//新增一个有序集合hostset,加入一个元素baidu.com,给它赋予score:1
127.0.0.1:6379> zadd hostset 1 baidu.com 
(integer) 1
//向hostset中新增一个元素bing.com,赋予它的score是30
127.0.0.1:6379> zadd hostset 3 bing.com 
(integer) 1
//向hostset中新增一个元素google.com,赋予它的score是22
127.0.0.1:6379> zadd hostset 22 google.com 
(integer) 1
//列出hostset的所有元素,同时列出其score,可以看出myzset已经是有序的了。
127.0.0.1:6379> zrange hostset 0 -1 with scores 
1) "baidu.com"
2) "1"
3) "google.com"
4) "22"
5) "bing.com"
6) "30"
//只列出hostset的元素
127.0.0.1:6379> zrange hostset 0 -1 
1) "baidu.com"
2) "google.com"
3) "bing.com"
Hash
常用命令: hget,hset,hgetall 等。 
Hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。比如下面我就用 hash 类型存放了我本人的一些信息:
//建立哈希,并赋值
127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 
OK
//列出哈希的内容
127.0.0.1:6379> HGETALL user:001 
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
//更改哈希中的某一个值
127.0.0.1:6379> HSET user:001 password 12345 
(integer) 0
//再次列出哈希的内容
127.0.0.1:6379> HGETALL user:001 
1) "username"
2) "antirez"
3) "password"
4) "12345"
5) "age"
6) "34"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值