Redis面试整理:Redis几种数据类型的用法和应用场景重新梳理了一下

代码鄙视员 2020-11-16 10:09:27 ⋅ 1066 阅读

1、字符串

1.1 介绍

string 字符串类型是Redis中最为常用和基础的存储类型,是一个由字节组成的序列,他在Redis中是二进制安全的,也可以认为string字符串数据类型能够接收任何格式的数据,像JPEG图像数、Json等,是标准的key-value,一般来存字符串,整数和浮点数。

  • 数据长度:string字符串类型最大能容纳的512MB的数据长度。
面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 


1.2 使用场景

最常见的使用场景就是对于网站访问量的统计,例如在线人数、点赞数量和访问人数等。

总体来说Redis的string字符串类型主要有以下几种应用场景:

  • 计数器

string类型的incr和decr命令可以将key中存储的数字进行+1、-1,这两种操作具有原子性,所以是安全的。所以在记录评论数、网站访问次数、商品销量等场景中可以用到。

  • 分布式锁

string类型中可以使用setnx 来判断“当key不存在时,设置数值并返回1,当key存在时,返回0”。整个操作过程是原子性的执行,所以可以用在分布式锁的场景中,返回1获得锁,返回0没有获得所。

jedisCommands.set("key", "1", "NX", "EX", 60);//如果"key"不存在,则将其设值为1,同时设置60秒的自动过期时间
  • 存储对象

大家都知道JSON具有强大的兼容性、易用性和可读性,可以在对象和JSON字符串间相互转换。所以可以将用户信息、商品信息等存储在string类型中。

1.3 更多特性

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

// string
async function stringFun() {
    const [key1, key2, key3, key4, key5] = ["key1", "key2", "key3", "key4", "key5"];
    const [v1, v2, v3, v4, v5] = ["v1", "v2", "v3", "v4", "v5"];
    // 1. set get
    await redis.set(key1, v1);
    // 2. 过期时间
    await redis.setex(key2, 10, v2);
    // 3. 自增
    await redis.incr(key3);
    // 4. 加减
    await redis.incrby(key3, 100);
    // 5. 是否存在
    const judgeKey4 = await redis.exists(key4);
    // 6. value length
    const keyLen = await redis.strlen(key1);

    const getValue = await redis.get(key1);
    const hgetVaule = await redis.mget(key1, key2, key3);
    console.log("getValue : ", getValue);
    console.log("hgetVaule : ", hgetVaule);
    console.log("judgeKey4 : ", judgeKey4);

    // 6. 删除
    await redis.del(key1, key2, key3, key4);
    process.exit(1);
};

2、Hash

2.1 介绍

在Redis中Hash数据类型可以看成具有string key和value的map容器,hash支持可以将多个key/value 存储到一个key 中。

  • 数据长度:每个 hash 可以存储 232 - 1 键值对(40多亿)。

2.2 使用场景

  • 购物车

以用户id为key,商品id为field,商品数量为value,恰好构成了购物车的3个要素,如下图所示。

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

  • 存储对象

hash类型是key、field和value结构,与常用的对象id、属性和值相似,所以也可以用来存储对象。

2.3 更多特性

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

/**
 * Hash
 * 应用场景:
 * 1. 【存-取-改】用户信息【ID、name、age..】,无需set\get string 序列化。
 * 2. 初始化,缓存用户信息
 * 3. 购物车,用户ID:物品ID、数量增减、删除
 * 4. 商品的价格、销量、关注数、评价数等可能经常发生变化的属性,就适合存储在hash类型里
 */
async function hashFun() {
    const userObj = { id: 1, name: 'zhangsan', age: 18 }
    const userKey = 'user_1';
    // 1. hset key filed value
    await redis.hset(userKey, userObj);
    // 2. 所有的key
    const hkeys = await redis.hkeys(userKey);
    // 3. 整个value
    const hgetall = await redis.hgetall(userKey);
    console.log('hkeys: ', hkeys)
    console.log('hgetall: ', hgetall)
    // 4. 更改单个数值
    await redis.hset(userKey, 'age', 100);
    // 5. 字段加减
    await redis.hincrby(userKey, 'age', -90);
    const hgetall1 = await redis.hgetall(userKey);
    console.log('hgetall--: ', hgetall1)
    // 6. hsetnx 如果存在key,则不更新
    await redis.hsetnx(userKey, 'name', 'lisi');
    // !7. expire 设置过期时间
    await redis.expire(userKey, 2);
    process.exit(1);
    /** 
    
    % ts-node base.ts
    hkeys:  [ 'id', 'name', 'age' ]
    hgetall:  { id: '1', name: 'zhangsan', age: '18' }
    hgetall--:  { id: '1', name: 'zhangsan', age: '10' }
    
    */
}

3、List

3.1 介绍

可以将Redis的List类型看作是简单的字符串列表,由于Redis的List数据类型允许用户从序列的两端推入或弹出元素,并且List列表也是由多个字符串值组成的有序、可重复的序列链表结构,所以在获取元素时越接近两端速度就越快。即使存储了上千万的列表元素,在获取头部或者尾部的时候也是飞快的。

  • 数据长度:List中可以包含的最大元素数量是4294967295。

3.2 使用场景

  • 消息队列

可以使用list类型的lpop和rpush功能来实现队列功能,因此可以时间简单的点对点的消息队列。在具体的开发中不建议使用,可以采用市场上比较成熟的中间件Kafak、RabbitMQ和RabbitMQ等。

  • 排行榜

List数据类型的lrange命令可以分页查看队列中的数据,在实际应用场景中可以把销量、积分和成绩等排名信息每隔一段时间存储到Redis的List类型中。

只有定时计算的数据才适合使用list类型存储,不支持实时计算。

  • 最新列表

上面提到使用lpop和rpush来实现消息队列功能,那么也同样的可以实现最新列表功能,可以通过lpush命令将新数据写入列表中,接着用lrange命令去读取最新的元素列表。

3.3 更多特性

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

/**
 * 列表
 * 1. list类型是用来存储多个【有序】的字符串的,支持存储【2^32次方-1】个元素。
 * 2. 应用场景
 *      1. 栈: lpush + lpop
 *      2. 队列:lpush + rpop
 *      3. 定长队列:lpush + ltrim
 *      4. 消息队列:lpush + brpop
 */
async function listFun(){
    const bookKey = 'books';
    const bookValue = ['C', 'C++', 'Java', 'OC', 'Node'];
    // 1. 栈【左进左出】
    // C->Node 左:进入队列。
    await redis.lpush(bookKey, bookValue);
    const llen = await redis.llen(bookKey);
    let i = 0;
    while(i < llen) {
        // 左出
        const item = await redis.lpop(bookKey);
        console.log('出栈:', item);
        i++;
    }
    console.log('------');
    // 2. 左进右出
    await redis.lpush(bookKey, bookValue);
    const len = await redis.llen(bookKey);
    let index = 0;
    while(index < len) {
        // 左出
        const item = await redis.rpop(bookKey);
        console.log('出队列-:', item);
        index++;
    }
    console.log('------');    
    /** 
     * 3. 固定长度的队列
     *      1. 队列结合 ltrim,  【保留起止位置内,删除其他】:start_index和end_index定义了一个区间内的值将被保留下来
    */
    await redis.lpush(bookKey, bookValue);
    // 保留【前三个】
    await redis.ltrim(bookKey, 0, 2);
    // 获取【队列全部信息】
    const firstThree = await redis.lrange(bookKey, 0, -1);
    console.log('队列全部信息', firstThree);
    process.exit(1);
}

4、Set

4.1 介绍

set类型是string类型的集合,其特点是集合元素无序且不重复,和列表一样,在执行插入、删除和判断是否存在某元素时,效率是很高的。

  • 数据长度:Set可包含的最大元素数量是4294967295。

4.2 使用场景

  • 好友/关注/粉丝/感兴趣的人集合

Set类型唯一的特点是适合用于存储好友/关注/粉丝/感兴趣的人集合,集合中的元素数量可能很多,那么如果每次都全部取出来会耗费不小的成本,Set类型刚好是一个非常实用的命令用于直接操作这些集合,如

a. sinter命令可以获得A和B两个用户的共同好友

b. sismember命令可以判断A是否是B的好友

c. scard命令可以获取好友数量

c. 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合

  • 随机元素展示

例如应用的首页展示的区域是有先的,但是不能每个用户展示的内容是固定的,将会让用户感觉到视觉疲劳。那么可以先将一批可以展示的元素写入Set集合中,再从中随机获取,用户每次刷新也可以随机看到这些元素。

set类型适合存放所有需要展示的内容,而srandmember命令则可以从中随机获取几个。

  • 黑/白名单

在秒杀、抢购等业务场景中,在设计安全性的时候需要考虑设置用户黑名单、ip黑名单和设备黑名单等,Set数据类型适合存储这些黑名单数据,sismember命令可用于判断用户、ip、设备是否处于黑名单之中。

4.3 更多特性

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

/**
 * 集合
 * 1. 定义: 集合类型 (Set) 是一个【无序】并【唯一】的键值集合。
 * !2. 场景:【以某个条件为权重】
 *      1. 关注的人
 *      2. 中奖人
 *      3. 例子
 */
async function setFun() {
    const [key1, key2, key3, key4, key5] = ["set01", "set02", "set03", "set04", "set05"];
    const [v1, v2, v3, v4, v5] = ["v1", "2", "v3", "v4", "v5"];
    const [v10, v20, v30, v40, v50] = ["v1", "v2", "v3", "40", "50"];

    const setV1 = [v1, v2, v3, v4, v5];
    // 1. 添加元素
    await redis.sadd(key1, setV1);
    await redis.sadd(key2, [1, 2, 3, 4, 5]);
    // 2. 获取全部
    const allValue = await redis.smembers(key1);
    // 3. 是否存在
    const sismember = await redis.sismember(key1, 'v1');
    // 4. 随机获取几个数据
    const srandmember = await redis.srandmember(key1, 2);
    // 5. 随机删除几个数据
    await redis.spop(key1, 1);
    console.log('allvalue: ', allValue);
    console.log('sismember: ', sismember);
    console.log('srandmember: ', srandmember);
    const key1Values = await redis.smembers(key1);
    const key2Values = await redis.smembers(key2);
    console.log('key1---Values: ', key1Values);
    console.log('key2---Values: ', key2Values);

    // 6. 将 set1 中的数据 转移到 set2 中: 把set1中的“v1”转移到set2中; 
    await redis.smove(key1, key2, v10);
    // !7. 差集, 以前者为基准
    const sdiff = await redis.sdiff(key1, key2);
    // 8. 交集,
    const sinter = await redis.sinter(key1, key2);
    // 9. 并集
    const sunion = await redis.sunion(key1, key2);
    console.log('sdiff', sdiff)
    console.log('sinter', sinter)
    console.log('sunion', sunion)
    const Vkey1 = await redis.smembers(key1);
    const Vkey2 = await redis.smembers(key2);
    console.log('key1---Values: ', Vkey1);
    console.log('key2---Values: ', Vkey2);

    /** 
allvalue:  [ '2', 'v1', 'v4', 'v5', 'v2', 'v3' ]
sismember:  1
srandmember:  [ '2', 'v3' ]
key1---Values:  [ '2', 'v1', 'v4', 'v5', 'v3' ]
key2---Values:  [ '2', 'v1', '1', '3', '5', '4' ]


sdiff [ 'v5', 'v4', 'v3' ]
sinter [ '2' ]
sunion [ '2', '1', 'v1', '3', 'v4', 'v5', '5', 'v3', '4' ]

key1---Values:  [ '2', 'v4', 'v5', 'v3' ]
key2---Values:  [ '2', 'v1', '1', '3', '5', '4' ]
    */

    // string + set 求交集
    await redis.set("book:1:name", "shujujiegou");
    await redis.set("book:2:name", "suanfa");
    await redis.set("book:3:name", "C");
    // set
    await redis.sadd("tag:jichu", 1);
    await redis.sadd("tag:jichu", 2);
    await redis.sadd("tag:yuyan", 3);
    // 属于【语言】不属于【基础】
    const lang = await redis.sdiff("tag:yuyan", "tag:jichu");
    console.log("lang: ", lang);
    /** 
    lang:  [ '3' ]
    */
}

5、Sorted Set(zset)

5.1 介绍

Sorted Set 和set很像,同样是字符串集合,同一个集合中都不允许元素重复。区别在于有序集合中每一个元素都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的元素进行从小到大的排序。虽然在有序集合中的元素必须是唯一的,但是分数(score)却可以重复。

5.2 使用场景

可以用在在线游戏积分排行榜中,每当用户的积分发生变化时,可以执行zadd来update用户分数(score),然后用zrange获取top的用户信息。

5.3 更多特性

面试官经常问的Redis几种数据类型的用法和应用场景整理好了

 

/**
 * 有序集合
 * 1. 相比于集合类型多了一个排序属性 score(权重),单个存储元素有两个值【元素值、排序值】
 * !2. 场景
 *      1. 游戏排名
 *      2. 微博热点话题
 *      3. 学生成绩:汇总、排名、百分制、
 */
async function zsetFun() {
    const key = "z:student:math";
    const key1 = "z:student:english";
    const key2 = 'z:student:sum';

    const Ykey = "y:student:math";
    // const Ykey1 = "y:student:english";
    // const Ykey2 = 'y:student:sum';
    
    // score field score field ...
    const value = [90, "zhangsan", 20, "lisi", 99, "xiaoming", 60, "xiaohong"];
    const value1 = [30, "zhangsan", 59, "lisi", 80, "xiaoming", 90, "xiaohong"];


    const YmathValue = [30, "lucy", 59, "nick", 80, "davi"];


    // 1. 添加
    await redis.zadd(key, value);
    await redis.zadd(key1, value1);
    // 默认按score生序排列, 0 -1 获取全部的
    const zrange = await redis.zrange(key, 0, -1);
    // !分数由高到低,排 优秀的学生, 得逆序写 【100,80】 反之,顺序排,顺序写数字
    const zrevrange = await redis.zrevrangebyscore(key, 100, 80);
    console.log("zrange", zrange);
    console.log("zrevrange", zrevrange);
    // 2. 删除, 按score区间、起止、
    await redis.zrem(key1, 'xiaming');
    await redis.zrem(key2, 'xiaming');
    // await redis.zremrangebyscore(key, 10, 60);
    // 3. 总个数
    const count = await redis.zcard(key);
    // 4. 区间内个数
    const youxiu = await redis.zcount(key, 80, 100);
    // 5. 获取单个field
    const zhansan = await redis.zscore(key, "zhangsan");
    // 6. 单个fiele 加减
    await redis.zincrby(key, 1, "zhangsan")

    // 7. 多个集合操作
    // ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
    // destination 给定的一个新的集合 
    // numkeys 计算的几个集合
    // WEIGHTS 乘法: 默认算法因子 1
    // AGGREGATE 默认 sum 
    // !总成绩,单科相加
    await redis.zinterstore(key2, 2, key, key1);
    // 全都打印
    const total = await redis.zrange(key2, 0, -1, 'WITHSCORES');
    console.log(total);
    // 8. 某个field排名 从大到小
    const xiaoming = await redis.zrevrank(key, "xiaoming");
    console.log('xiaoming班级排名', xiaoming);

    // !9. 汇总整个年级成绩, 【Y Z 班级数学汇总表】
    // !并集
    await redis.zadd(Ykey, YmathValue);
    // 百分制
    const keyClassMath100 = "clas:math:100";
    const keyClassMath150 = "clas:math:150";
    // 总分
    const keyClassMathTotal = "clas:math:total";
    // 100 分制 返回元素集合的个数
    await redis.zunionstore(keyClassMath100, 2, key1, Ykey);
    // 150 分制度
    await redis.zunionstore(keyClassMath150, 2, key1, Ykey, 'weights', 1.5, 1.5);
    const classMath100 = await redis.zrange(keyClassMath100, 0, -1, 'WITHSCORES');
    const classMath150 = await redis.zrange(keyClassMath150, 0, -1, 'WITHSCORES');
    console.log("年级数学成绩--100分制", classMath100);
    console.log("年级数学成绩--150分制", classMath150);

    // 不知道 AGGREGATE 的用处。。。
    // await redis.zunionstore(keyClassMathTotal, 2, key1, Ykey, 'AGGREGATE', 'max');
    // const classMathTotal = await redis.zrange(keyClassMathTotal, 0, -1, 'WITHSCORES');
    // console.log("年级数学成绩", classMathTotal);

    // 10. 
}

全部评论: 0

    我有话说:

    Redis系列 基本用法&应用场景

        说明 redis最基本使用方法以及使用场景。 字符串 // stringasync function stringFun() { const [key

    Redis系列六 Lua

      本文目标 学习lua基本语法 能够采用redis+lua lua 基本语法 Lua 是轻量小巧脚本语言,标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用

    Redis系列七 Debug Lua

      调试redis+lua 学lua基本语法,了解redis+lua配套,但是却不知道怎么断点调试。学就学全面点, 官网中有dubug相关说明。地址:Redis Lua

    Redis系列四 锁

      本文目标 1. 熟悉乐观锁ABA概念 2. 理解掌握redis事务以及watch回滚; 3. 实战redis锁 乐观锁 乐观锁是不会阻塞其他线程并发机制,它不会使用数据库

    聊分布式场景redismemcached,及各自经典使用场景优缺点

    在BAT里,redis已经逐渐取代memcached,成为分布式场景广泛使用缓存方案。接下来,我们就分析redis是如何取代memcached,成为开发者宠儿

    为什么单线程Redis能够达到百万级QPS?

    作者:在江湖中coding链接:https://juejin.im/post/5e6097846fb9a07c9f3fe744 性能测试报告 查看阿里云 Redis 性能测试报告如下,能够

    Redis系列四 GEO附近

    GEO算法 GeoHash是地址编码方法。将二维空间经纬度数据编码成一个字符串; 地球上经度范围:[-180, 180],纬度范围:[-90,90]。如果以本初子午线、赤道为界,地球可以

    Redis系列二:位图实战,实现打卡签到

    前言 如果要统计篇文章阅读量,可以直接使用 Redis  incr 指令来完成。 如果要求阅读量必须按用户去重,那就可以使用 set 来记录阅读这篇文章所有用户 id,获取

    Redis系列九 推荐系统-布隆过滤器

      布隆过滤器 概念 布隆过滤器是空间利用率较高概率型数据结构,来测试一个元素是否在集合中。但是存在一定可能,导致结果误判。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不

    Redisson 3.13.6 发布,官方推荐 Redis 客户端

    Redisson 3.13.6 已发布,这是一个 Java 编写 Redis 客户端,具备驻内存数据网格(In-Memory Data Grid)功能,并获得 Redis 官方推荐

    Redis系列八 抢红包

      本文概述 掌握红包常见生成算法 掌握lua+redis 实现原子性抢红包 项目中还有mysql相关内容 了解jmeter基本用法 遗留问题 redis同步DB时机问题

    Martian框架发布 3.0.3 版本,Redis分布式锁

    项目简介 Martian 是一个声明式 API 编程(DAP)框架,可以帮助你快速开发后端服务。 以HttpServer作为 http服务,彻底脱离Tomcat这Web容器Servlet

    Redis 5.0.11、6.0.11、6.2 发布,修复 32 位系统上整数溢出

    Redis 同时发布 5.0.11、6.0.11 6.2 版本。对于使用 32 位 Redis 用户来说,此次更新解决一个重要安全问题,即 32 位系统上整数溢出((CVE-2021

    BAT大牛Redis客户端与服务端交互原理

    Redis实例运行在单独进程中,应用系统(Redis客户端)通过Redis协议Redis Server

    精品推荐:从数据存储角度聊聊Redis为何这么快

    Redis是一个由ANSI C语言编写,性能优秀、支持网络、可持久化K-K内存数据库,并提供多种语言API。它常用类型主要是 String、List、Hash、Set、ZSet 这5

    反向面试

      你可以反问面试问题 内容来源:https://github.com/yifeikong/reverse-interview-zh 大部分翻译自:https://github

    Jedis 3.5.0 发布,Redis Java 客户端开发包

    Jedis 3.5.0 发布。Jedis 是 Redis 官方推荐面向 Java 操作 Redis 客户端,与新版本 Redis 完全兼容。 本次更新内容包括: 增强功能 通过 COUNT

    「千万级秒杀架构」千万级并发流量,如何将流量串行化?

    流量串行化,是指在高并发场景通过排队方式将无序并发流量整理成有序串行流量。大家都知道在 Redis 集群部署模式出现之前,市面上大多数 Redis 都是采用主多从模式,写操作全部是由主

    Redis为什么变慢Redis性能问题排查详述

    Redis 作为优秀内存数据库,其拥有非常高性能,单个实例 OPS 能够达到 10W 左右。但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大情况,就会与我们预期不符。 你