redis的5种数据类型

redis的5种数据类型:


1.1string 字符串(可以为整形、浮点型和字符串,统称为元素)
1.2list 列表(实现队列,元素不唯一,先入先出原则)
1.3set 集合(各不相同的元素)
1.4hash hash散列值(hash的key必须是唯一的)
1.5sort set 有序集合

1.String类型

1.1使用场景:

场景一:“某某综艺”,启动海选投票,只能通过微信投票,每个微信号每4个小时只能投1票。

场景二:电商商家开启热门商品推荐,热门商品不能一直处于热门期,每种商品热门期维持3天,3天后自动取消热门

场景三:新闻网站会出现热点新闻,热点新闻最大的特征是对时效性,如何自动控制热点新闻的时效性

1.2数据操作成功与否的反馈

1.2.1表示运行结果是否成功
(integer)0 –> false 失败
(integer)1 –> true 成功

1.2.2表示运行结果值
(integer)3 –> 3 3个
(integer)1 –> 1 1个
数据未获取到
(nil)等同于null
 

1.3String类型操作实践

字符串类型是redis中最简单的数据类型,它存储的值可以是字符串,其最大字符串长度支持到512M。基于此类型,可以实现博客的字数统计,将日志不断追加到指定key,实现一个分布式自增id,实现一个博客的的点赞操作等

incr/incrby

当存储的字符串是整数时,redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。如果num不存在,则自动会创建,如果存在自动+1。

INCR key

127.0.0.1:6379> set num 1
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> keys *
1) "num"    
127.0.0.1:6379> incr num
127.0.0.1:6379>

 INCRBY key increment可以指定增长数据

127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
127.0.0.1:6379> incrby num 2
(integer) 9
127.0.0.1:6379>

 decr/decrby

当存储的字符串是整数时,redis提供了一个实用的命令DECR,其作用是让当前键值递减,并返回递增后的值。如果num不存在,则自动会创建,如果存在自动-1。

DECR key 按照默认步长(默认为1)进行递减

 DECRBY key decrement 按照指定步长进行递减

append

向尾部追加值。如果键不存在则创建该键,其值为写的value,即相当于SET key value。返回值是追加后字符串的总长度。
语法:APPEND key value

strlen

字符串长度,返回数据的长度,如果键不存在则返回0。注意,如果键值为空串,返回也是0。
语法:STRLEN key

127.0.0.1:6379> get test
"123abc"
127.0.0.1:6379> strlen test
(integer) 6
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> set tnt ""
OK
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> exists tnt
(integer) 1
127.0.0.1:6379>

mset/mget

同时设置/获取多个键值
语法:MSET key value [key value …]
MGET key [key …]

127.0.0.1:6379> flushall
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> mset a 1 b 2 c 3
OK
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"
127.0.0.1:6379>

  • 博客的字数统计如何实现?(strlen)
  • 如何将审计日志不断追加到指定key?(append)
  • 你如何实现一个分布式自增id?(incr-雪花算法)
  • 如何实现一个博客的的点赞操作?(incr,decr)

2.string应用案例demo

1.创建工程

创建maven父工程,例如03-sca-redis,并在此工程下创建两个子工程,一个为sca-jedis,一个为sca-tempate,例如:

1.2在sca-jedis添加项目依赖


<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.2</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

 1.3在sca-template添加项目依赖

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

分布式id
业务描述

在分布式系统中,数据量将越来越大时,就需要对数据进行分表操作,但是,分表后,每个表中的数据都会按自己的节奏进行自增,很有可能出现ID冲突。这时就需要一个单独的机制来负责生成唯一ID,生成出来的ID也可以叫做 分布式ID,这里我们借助redis实现一个简易的分布式id进行实现,当然还有一些第三方的系统,可以帮你生成这样的id,可以自己进行拓展学习.
 

关键代码实现

package com.jt.redis;

import redis.clients.jedis.Jedis;

import java.time.zone.ZoneOffsetTransitionRule;

/**
 * 需求:生成一个分布递增的id
 * 多张表基于这个方法中生成的id作为主键id值(分布式环境不会采用数据库
 * 表中自带的自增策略-auto_increment)
 */
public class IdGenerator {

    //虚拟机IP地址
    private static final String IP ="192.168.126.129";
    //redis端口号
    private static final int port =6379;
    public static Long getId(){
        Jedis jedis = new Jedis(IP,port);
        //jedis.auth("123456");//数据库密码没有密码就不用写
        Long id = jedis.incr("id");
        jedis.close();//关闭连接
        return id;
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(){
                @Override
                public void run() {
                   Long id = IdGenerator.getId();
                    System.out.println("id = "+id);
                }
            }.start();
        }
    }
}

2.hash hash散列值(hash的key必须是唯一的)

String类型存储的困惑
对象类数据的存储如果具有较为频繁的更新需求,操作会显得笨重,存容易,改麻烦。
为了区别与Redis中的键值对的称呼,hash中的键成为field,而key特征Redis的键。

2.1hash类型

新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
需要的内存结构:一个存储空间保存多少个键值对数据
hash类型:底层使用哈希表结构实现数据存储

2.2hash类型数据操作的注意事项

    hash类型下的value只能存储字符串,不允许存储其他类型数据,不存在嵌套现象。如果数据未获取到,对应的值为(nil)
    每个hash可以存储232-1个键值对
    hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但hash设计不是为了存储大量对象的,切记不可滥用,更不可以将hash作为对象列表使用
    hgetall操作可以获取全部属性,如果内部key过多,遍历整体数据效率就会很低,有可能成为数据访问瓶颈。

2.3Hash语法实践

hset/hget

HSET key field value
HGET key field
HMSET key field value [field value…]
HMGET key field [field]
HGETALL key

127.0.0.1:6379> hset user username chenchen
(integer) 1
127.0.0.1:6379> hget user username
"chenchen"
127.0.0.1:6379> hset user username chen
(integer) 0
127.0.0.1:6379> keys user
1) "user"
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
127.0.0.1:6379>
127.0.0.1:6379> hset user age 18
(integer) 1
127.0.0.1:6379> hset user address "xi'an"
(integer) 1
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
3) "age"
4) "18"
3) "address"
4) "xi'an"
127.0.0.1:6379>

HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。

hincrby

127.0.0.1:6379> hdecrby article total 1        #执行会出错
127.0.0.1:6379> hincrby article total -1        #没有hdecrby自减命令
(integer) 1
127.0.0.1:6379> hget article total            #获取值

hmset/hmget

HMSET和HMGET设置和获取对象属性

127.0.0.1:6379> hmset person username tony age 18
OK
127.0.0.1:6379> hmget person age username
1) "18"
2) "tony"
127.0.0.1:6379> hgetall person
1) "username"
2) "tony"
3) "age"
4) "18"
127.0.0.1:6379>

hexists

属性是否存在
127.0.0.1:6379> hexists killer
(error) ERR wrong number of arguments for 'hexists' command
127.0.0.1:6379> hexists killer a
(integer) 0
127.0.0.1:6379> hexists user username
(integer) 1
127.0.0.1:6379> hexists person age
(integer) 1
127.0.0.1:6379>

hdel 

127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hgetall user
1) "username"
2) "chen"
127.0.0.1:6379> hgetall person
1) "username"
2) "tony"
3) "age"
4) "18"
127.0.0.1:6379>

hkeys/hvals

只获取字段名HKEYS或字段值HVALS

127.0.0.1:6379> hkeys person
1) "username"
2) "age"
127.0.0.1:6379> hvals person
1) "tony"
2) "18"
2.3.8    hlen
元素个数
127.0.0.1:6379> hlen user
(integer) 1
127.0.0.1:6379> hlen person
(integer) 2
127.0.0.1:6379>

小节面试分析

  • 发布一篇博客需要写内存吗?(需要,hmset)
  • 浏览博客内容会怎么做?(hmget)
  • 如何判定一篇博客是否存在?(hexists)
  • 删除一篇博客如何实现?(hdel)
  • 分布式系统中你登录成功以后是如何存储用户信息的?(hmset)

 hash应用场景

单点登陆
业务描述

在分布式系统中,通过会有多个服务,我们登录了一个服务以后,再访问其它服务时,不想再登录,就需要有一套单独的认证系统,我们通常会称之为单点登录系统,在这套系统中提供一个认证服务器,服务完成用户身份认证,在一些中小型分布式系统中中,我们通常会借助redis存储用户的认证信息

 在这里插入图片描述

 关键代码实现

package com.jt.redis;

import redis.clients.jedis.Jedis;

import java.util.Map;
import java.util.UUID;

/**
 * SSO(单点登录系统) 案例演示:
 * 1)访问资源(假如没有登录,要提示先登录,如何判定是否登录了)
 * 2)执行登录(登录成功,存储用户登录信息)
 * 3)访问资源(已登录)
 * 解决方案
 * 1)方案1:SpringSecurity+jwt+oauth2 (并发比较大)
 * 2)方案2:SpringSecurity+redis+oauth2 (中小型并发)
 *
 */
public class SSODemo02 {
    /**认证中心的登录设计*/
    static String doLogin(String username,String password){
     //1.执行用户身份校验
      if(!"jack".equals(username))//将来这个jack来自数据库
          throw new IllegalArgumentException("这个用户不存在");
     //2.用户存在并且密码正确,表示用户是系统的合法用户
      if(!"123456".equals(password))
          throw new IllegalArgumentException("密码不正确");
     //3.将合法用户信息存储到redis
        Jedis jedis=new Jedis("192.168.126.129", 6379);
        String token=UUID.randomUUID().toString();
        jedis.hset(token,"username",username);
        jedis.hset(token, "status", "1");
        //....
        jedis.expire(token, 2);
        jedis.close();
        return token;//token
    }
    /**获取资源服务中的资源*/
    static Object doGetResource(String token){
      //1.检查用户是否已经登录
       if(token==null||"".equals(token))
           throw new IllegalArgumentException("请先登录");
       Jedis jedis=new Jedis("192.168.126.129", 6379);
       Map<String,String> map=jedis.hgetAll(token);
       jedis.close();
      //2.假如没有登录,则提示先登录
        System.out.println("map="+map);
       if(map.isEmpty())
           throw new RuntimeException("登录超时或token无效,请重新登录");
      //3.已登录则可以访问资源
        System.out.println("继续访问资源");
        //.....
        return "the resource of user";
    }
    //假设这里的main方法为客户端
    public static void main(String[] args) throws InterruptedException {
        String token=null;
       //第一次访问资源
       // doGetResource(token);
       //执行登录操作(将来认证要在认证中心实现)
        token=doLogin("jack", "123456");
       //第二次访问资源
        Thread.sleep(2000);
        doGetResource(token);
    }
}

购物车简易实现Demo

业务描述

简易购物车业务设计如图所示:

在这里插入图片描述

 基础指令操作,例如:

1)向购物车添加商品
hset  cart:101  2001  1
hset  cart:101  2002  1
hset  cart:101  2003  2
2)查看购物车商品
hgetall cart:101
3)删除购物车商品
hdel cart:101 2003
4)改变购物车某个商品的购买数量
hincrby cart:101 2002 2

 购物车简易代码Demo

package com.jt.redis;

import redis.clients.jedis.Jedis;

import java.util.Map;

/**
 * 购物车系统简易实现
 * 需求:
 * 1)向购物车添加商品
 * 2)修改购物车商品数量
 * 3)查看购物车商品
 * 4)清空购车商品信息
 * 存储设计:
 * 1)mysql (优势是持久性,读写速度会差一些)
 * 2)redis (优势是性能,持久性相对MySQL会差一下,
 * 假如性能要求高于数据的存储安全,可以存储到redis)
 */
public class CartDemo01 {
    static final String IP="192.168.126.129";
    static final int PORT=6379;

    /**
     * 向购物车添加商品
     * @param userId
     * @param productId
     * @param num
     */
    public static void doAddCart(String userId,
                                 String productId,
                                 int num){
        //1.创建jedis对象
        Jedis jedis=new Jedis(IP,PORT);
        //2.基于hash结构存储商品信息
        //jedis.hset()
        jedis.hincrBy("cart:"+userId, productId, num);
        //3.释放资源
        jedis.close();
    }

    /**
     * 查看购物车商品信息
     * @param userId
     */
    public static  Map<String, String> doViewCart(
                          String userId){
        //1.创建jedis对象
        Jedis jedis=new Jedis(IP,PORT);
        //2.查看购物车信息
        Map<String, String> cart =
                jedis.hgetAll("cart:" + userId);
        //3.释放资源
        jedis.close();
        //4.返回购物车商品信息
        return cart;
    }

    /**
     * 删除购车某个商品
     * @param userId
     * @param productId
     */
    public static void doClearCart(String userId,
                                 String... productId){
        //1.创建jedis对象
        Jedis jedis=new Jedis(IP,PORT);
        //2.清除指定商品
        jedis.hdel("cart:"+userId, productId);
        //3.释放资源
        jedis.close();
    }

    public static void main(String[] args){
        String userId="1001";
        //1.购买商品时,将商品信息添加到购物车
        doAddCart(userId,"201",1);
        doAddCart(userId,"202",1);
        doAddCart(userId,"203",1);
        //2.修改购车商品的数量
        doAddCart(userId,"202",1);
        doAddCart(userId,"203",2);
        //3.查看购物车商品
        Map<String, String> cart =
                doViewCart(userId);
        System.out.println(cart);
        //4.清空购物车
        doClearCart(userId, "201","202","203");
        cart = doViewCart(userId);
        System.out.println(cart);
    }
}


版权声明:本文为weixin_59868842原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。