Redis

1. 安装Redis6.0.8

2. redis传统五大数据类型的落地应用

3. 你知道分布式锁吗?有哪些实现方案?谈谈redis分布式锁的理解,删key的时候有什么问题

4. redis缓存过期淘汰策略

5. redis的LRU算法简介

0 redis 简介1 Redis安装,推荐使用6.0.8以上

官网说6.0.8之前有BUG

安装成功后, 查看redis版本的方法 

方法一:redis- server -v

方法二:redis info

2 Redis的五大数据类型

数据类型使用场景
String比如:想知道什么时候封锁一个IP地址, Incrby命令
Hash

存储用户信息【id,name,age】

Hset(key,filed,value)

Hset(userKey,id,101)

Hset(userKey,name,admin)

Hset(userKey,age,25)

------修改案例如下------

Hget(userKey,id)

Hset(userKey,id,102)

为什么不使用String类型来存储

Set(userKey,用信息的字符串)

Get(userKey)

不建议使用String类型

List

实现最新消息的排行,还可以利用List的push命令,将任务存在list集合中,同时使用另一个命令,将任务从集合中取出【pop】

Redis-list数据类型来模拟消息队列【电商秒杀可采用此方式完成秒杀活动】

Set特殊之处:可以自动排重,微博中将每个人的好友存在集合set中,可求两个人的共同好友,取交集即可
Zset以某个条件为权重进行排序。比如京东商品详情,有个综合排名,或者按照价格排名等

为什么不使用string记录数据信息

因为在修改数据的时候,仅调整某个字段,存取string方式会增加序列号及反序列化的IO次数,降低了性能

以前的SSM架构现在趋向于用SSR,Mysql被Redis代替。

2.1 除了5大数据类型的其他类型有哪些?

bitmap

HyperLogLog

GEO

Stream

3 五大数据结构都是如何使用的

官网:redis中文官方网站

官方命令查询:Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)

Redis大数据类型的落地应用

备注:

命令不区分大小写,而key是区分大小写的

help@类型名词 可查询使用命令。比如help@string, help@list

3.1 8大类型:

  • String: 字符串类型
  • Hash: 散列类型
  • List: 列表类型
  • Set: 集合类型
  • Sorted Set: 有序集合类型,简称zset
  • Bitmap: 位图
  • HyperLogLog: 统计
  • GEO: 地理

3.2 String

   

  

 

 

3.4 hash

redis中的hash如果对应java中的数据结构是哪个?

 

应用场景:

比如购物车

 

3.5 list

 

3.6 set

  

 

应用场景:

微信小程序抽奖

 

 

应用场景:

 例如 大数据推送

 

3.7 zset

 应用场景:

 

 场景2 抖音热搜

4 Redis分布式锁

有哪些实现方案?删key会有什么影响

首先要明白锁JVM层面的加锁和分布式锁不是一回事

分布式微服务架构,拆分后 各个服务之间为了避免冲突和数据故障而加入的一种锁,分布式锁。

4.1 面试题:

 一般不会部署单机版Redis,一般是一主二从,无人值守再加个哨兵,一般会用Redis cluster集群,最经典的就是三主三从。在这种集群下会有什么问题?

Redlock:Redis分布式锁的简写

4.2 DEMO

4.2.1 建立Module

boot_redis01

boot_redis02

4.2.2 pom

4.2.3 yml

4.2.4 主启动

4.2.5 业务类

4.2.5.1 config

4.2.5.2 Controller

4.2.5.3 运行测试

2号服务器代码同上述的一样,端口号2222

以上DEMO代码在单机版没问题,在高并发的情况下,会出现什么问题?

5. 问题-1 单机版没加锁:

5.1 问题

没有加锁,并发下数字不对,出现超卖现象。

5.2 思考

加synchronized,ReentrantLock,是都可以吗?具体分析?

5.3 解决

5.3.1 synchronized 会造成线程挤压拥堵,只有锁释放了,下一个线程才可以执行

5.3.2 时间太久,放弃等待;或规定时间拿不到锁就放弃

6 问题-2 nginx分布式微服务架构

6.1 问题

  

6.1.1 分布式部署后,单机锁还是出现超卖现象,需要分布式锁

6.1.2 nginx配置负载均衡

nginx的配置查看 vim nginx.conf

6.1.3 启动两个微服务

6.1.4 上面手点,下面高并发模拟

 

使用ApacheJMeter.jar测试高并发

6.2 解决 

6.2.1 上Redis分布式锁setnx

6.2.2 官网

https://redis.io/commands/set

官网加锁命令:

官网显示,解锁需要lua脚本:

思考问题:不适用LUA脚本怎么解决问题

6.2.3 修改DEMO

7 问题-3 出现异常的话,可能无法释放锁,必须要在代码层面finally释放

7.2 解决方案

加锁解锁,lock/unlock必须同时出现并保证调用

8 问题-4 机器宕机

8.1 问题

 8.2 解决

增加过期时间限定

9 问题-5 未保证原子性

9.1 问题

9.2 解决

10 问题-6 删除了其他线程的锁

10.1 问题现象

即A线程在执行过程中,由于一些原因超时完成(比如中间调用了其他服务,其他服务超时),超时锁被删除,线程B执行,在B执行的过程中线程A执行完成,A删除锁,此时删除的是线程B的锁,也就是A自己的锁因为超时被删除,现在又删了B的锁

10.2 解决

只能删除自己的锁,不能删除别人的。

11 问题-7 删除锁非原子 

11.1 解决方案1:用Redis事务

11.1.1 案例一(顺利情况):

11.1.2 案例(事务):

A执行以下操作

执行完数据录入后,B执行了下面的代码

B执行后,A执行EXEC

此时k1 k2是多少?

 

结论:没有被B覆盖掉,保证了批处理成功

11.1.3 案例二(乐观锁):

A线程执行:

此时B线程执行,插队将值改掉:

B执行完A继续执行:发现数据修改失败,数据以及变更

故,A只能在BBB的基础上调整值。

乐观锁:乐观的感觉没有人在我之前操作,如果有人改了再重新操作,直到没人改位置。

DEMO-代码调整:

11.2 解决方案2:适用lua脚本可以解决(日常工作中一般都这样做的)

Redis调用lua脚本通过eval命令保证代码执行的原子性 

12 问题-8 确保RedisLock过期时间大于业务时间的问题,Redis分布式锁如何续期

缓存续命时间机制,有点类似futrueTask,如果超时了就续一些时间

redis集群可能会出现CAP的一些小故障,导致加锁失败

CAP:

Redis->AP

分区容器,高可用(因为保证高可用,牺牲了数据一致性)

redis异步复制造成的锁丢失,如:主节点没来的及把刚刚set进来这条数据给从节点,就挂了

zookeeper->CP 选举算法:

主节点获取key后会先返回给slave节点,数据传输成功后再通知加锁完成。redis是主机有了就通知加锁完成。所以zookeeper主节点挂了也不怕,数据一致性,高可用差 

一般还是用redis的多,数据异常的再修复。

13 redlock+redisson方案解决 

13.1 RedisConfig

redissonLock.unlock();可能会出现问题

即:当前线程和解锁线程不是一个,不合法的状态异常。

优化后代码:

14 小总结

  • synchronized单机版OK,多线程,高并发用锁,但是分布式锁没用
  • 取消单机锁,上redis分布式锁setnx
  • 加锁就要释放锁,finnaly释放锁保证异常也可以释放
  • 宕机无法执行finally,可能无法解锁,故增加lockKey的过期时间设定,保证解锁
  • 为redis分布式锁key加过期时间,setnx+过期时间要在一行,保证原子性
  • 只能删除自己的锁,避免删除其他线程锁
  • redis集群环境下,适用RedLock+Redisson实现即可

15 redis缓存过期淘汰策略

15.1 内存满了怎么办?

15.1.1 查看redis最大占用内存:

查看配置文件 redis.conf

15.1.2 没配置的话,默认内存多少可以用?

15.1.3 生产上如何配置?

底层借鉴了HashMap算法,负载因子0.75

一般存储的数据是热点高频查询数据,不能什么都存。

15.1.4 如何修改redis

1).修改配置文件

2)运行过程中,动态手工修改

重启REDIS并链接 

查看配置数据

配置数据

15.1.5 如何查看Redis内存适用情况?

15.1.5 Redis内存用满了这么办?

会报错OOM

提高maxmemory

使用内存淘汰策略 

15.2 缓存淘汰策略?

15.2.1 往redis里写的数据是怎么没了的

问题:如果一个键过期了,到期后是否马上从内存种被删除? 答:不是

问题:过期后什么时候删除?怎么操作?

redus过期键的删除策略(3种): 

  1. 定时删除
  2. 惰性删除
  3. 定期抽样删除 

定时删除命令:set key value ex time

例:set k1 v1 ex 10

惰性删除:

定期抽样key,判断是否过期:

 

15.2.2 有哪些缓存淘汰策略

总结:

 

15.2.3 工作中用哪一种?

一般用 allkeys- lru

所有key 使用lru算法,使用率最少的删除

15.2.4 如何配置修改 

方法一:

 

方法二:

执行命令info也可查看配置结果

16 LRU算法

16.1 简介

16.2 算法来源

https://leetcode-cn.com/problems/lru-cache/

16.3 设计思想

 

 当使用key2时,移动到队首,之前的指针指向调整。

16.4 DEMO实现

16.3 DEMO

16.3.1 利用LinkedHashMap实现

public class LRUCacheDemo<K, V> extends LinkedHashMap<K, V>{

    private int capacity;//缓存坑位

    public LRUCacheDemo(int capacity) {
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return super.size() > capacity;
    }

    public static void main(String[] args) {
        LRUCacheDemo lruCacheDemo = new LRUCacheDemo(3);
        lruCacheDemo.put(1,"a");
        lruCacheDemo.put(2,"b");
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.keySet());

        lruCacheDemo.put(4,"d");
        System.out.println(lruCacheDemo.keySet());
        
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.keySet());
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.keySet());
        
        lruCacheDemo.put(5,"c");
        System.out.println(lruCacheDemo.keySet());
    }
}

输出

[1, 2, 3]
[2, 3, 4]
[2, 4, 3]
[2, 4, 3]
[2, 4, 3]
[4, 3, 5]

如果将accessOrder的参数调整为false

accessOrder:the ordering mode - <tt>true</tt> for access-order, <tt>false</tt> for insertion-order

即:super(capacity, 0.75F, false);

输出

[1, 2, 3]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[3, 4, 5]

可以看出,false的情况忽略了中间的排序步骤

16.3.2 DEMO2 自己实现LRU

public class LRUCacheDemo2{


    //map 负责查找,构建一个虚拟的双向链表,它里面安装的就是一个个Node节点,作为数据载体

    //1. 构造一个Node节点,作为数据载体
    class Node<K, V> {
        K key;
        V value;
        Node<K, V> prev;
        Node<K, V> next;

        public Node() {
            this.prev = this.next = null;
        }

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
            this.prev = this.next = null;
        }
    }


    //2. 构建一个虚拟的双向列表,里面安放的时我们的Node
    class DoubleLinkedList<K, V> {
        Node<K,V> head;
        Node<K,V> tail;

        //2.1 构造方法
        public DoubleLinkedList() {
            head = new Node<>();
            tail = new Node<>();
            head.next = tail;
            tail.prev = head;
        }

        //2.2 添加到头
        public void addHead(Node<K,V> node) {
            node.next = head.next;
            node.prev = head;
            head.next.prev = node;
            head.next = node;
        }

        //2.3 删除节点
        public void removeNode(Node<K,V> node) {
            node.next.prev = node.prev;
            node.prev.next = node.next;
            node.prev = null;
            node.next = null;
        }

        //2.4 获得最后一个节点
        public Node getLast() {
            return tail.prev;
        }
    }

    private int cacheSize;
    Map<Integer, Node<Integer, Object>> map;
    DoubleLinkedList<Integer, Object> doubleLinkedList;

    public LRUCacheDemo2(int cacheSize) {
        this.cacheSize = cacheSize;
        map = new HashMap<>();
        doubleLinkedList = new DoubleLinkedList<>();
    }

    public Object get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }

        Node<Integer, Object> node = map.get(key);
        doubleLinkedList.removeNode(node);
        doubleLinkedList.addHead(node);

        return node.value;
    }

    public void put(int key, Object value) {
        if (map.containsKey(key)) {
            Node<Integer, Object> node = map.get(key);
            node.value = value;
            map.put(key, node);
            doubleLinkedList.removeNode(node);
            doubleLinkedList.addHead(node);
        } else {
            //满了
            if (map.size() == cacheSize) {
                Node<Integer, Object> lastNode = doubleLinkedList.getLast();
                map.remove(lastNode.key);
                doubleLinkedList.removeNode(lastNode);
            }
            //新增
            Node<Integer, Object> newNode = new Node<Integer, Object>(key, value);
            map.put(key, newNode);
            doubleLinkedList.addHead(newNode);
        }
    }

    public static void main(String[] args) {
        LRUCacheDemo2 lruCacheDemo = new LRUCacheDemo2(3);
        lruCacheDemo.put(1,"a");
        lruCacheDemo.put(2,"b");
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.map.keySet());

        lruCacheDemo.put(4,"d");
        System.out.println(lruCacheDemo.map.keySet());

        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.map.keySet());
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.map.keySet());
        lruCacheDemo.put(3,"c");
        System.out.println(lruCacheDemo.map.keySet());

        lruCacheDemo.put(5,"e");
        System.out.println(lruCacheDemo.map.keySet());
    }
}

输出

[1, 2, 3]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
[3, 4, 5]

17 其他知识

2种不同形式的持久化方式:

RDB(Redis DataBase)

AOF(Append Of File)

RDB(Redis DataBase)

指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照, 恢复时是将快照文件直接读到内存里

备份如何执行的?

RDB优缺点

RDB保存的文件

RDB保存策略

 

RDB备份、恢复

AOF(Append Of File) (增量操作)

优缺点

AOF同步频率设置


Rewrite

 

Redis如何实现重写

何时重写

参考文献

以上内容均来自下方视频,博客内容仅作为个人学习笔记使用

Java面试_高频重点面试题 (第一、二、三季)_ 面试 第1、2、3季_柴林燕_周阳_哔哩哔哩_bilibili

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
概要介绍: 本课程主要是介绍并实战一款java中间件~redisson,介绍redisson相关的核心技术栈及其典型的应用场景,其中的应用场景就包括布隆过滤器、限流器、短信发送、实时/定时邮件发送、数据字典、分布式服务调度等等,在业界号称是在java项目里正确使用redis的姿势。本课程的目标就在于带领各位小伙伴一起学习、攻克redisson,更好地巩固自己的核心竞争力,而至于跳槽涨薪,自然不在话下!  课程内容: 说起redisson,可能大伙儿不是很熟悉,但如果说起redis,想必肯定很多人都晓得。没错,这家伙字如其名,它就是架设在redis基础上的一款综合性的、新型的中间件,号称是java企业级应用开发中正确使用redis的姿势/客户端实例。 它是架设在redis基础之上,但拥有的功能却远远多于原生Redis 所提供的,比如分布式对象、分布式集合体系、分布式锁以及分布式服务调度等一系列具有分布式特性的对象实例… 而这些东西debug将在本门课程进行淋漓尽致的介绍并实战,除此之外,我们将基于spring boot2.0搭建的多模块项目实战典型的应用场景:对象存储、数据字典、短信发送、实时/定时邮件发送、布隆过滤器、限流组件、分布式服务调度....课程大纲如下所示: 下面罗列一下比较典型的核心技术栈及其实际业务场景的实战,如下图所示为redisson基于订阅-发布模式的核心技术~主题Topic的实际业务场景,即实时发送邮件: 而下图则是基于“多值映射MultiMap”数据结构实战实现的关于“数据字典”的缓存管理: 除此之外,我们还讲解了可以与分布式服务调度中间件dubbo相媲美的功能:分布式远程服务调度,在课程中我们动手搭建了两个项目,用于分别充当“生产者”与“消费者”角色,最终通过redisson的“服务调度组件”实现服务与服务之间、接口与接口之间的调用!  课程收益: (1)认识并掌握redisson为何物、常见的几种典型数据结构-分布式对象、集合、服务的应用及其典型应用场景的实战; (2)掌握如何基于spring boot2.0整合redisson搭建企业级多模块项目,并以此为奠基,实战企业级应用系统中常见的业务场景,巩固相应的技术栈! (3)站在项目管理与技术精进的角度,掌握对于给定的功能模块进行业务流程图的绘制、分析、模块划分、代码实战与性能测试和改进,提高编码能力与其他软实力; (4)对于Java微服务、分布式、springboot精进者而言,学完本课程,不仅可以巩固提高中间件的实战能力,其典型的应用场景更有助于面试、助力相关知识点的扫盲! 如下图所示: 关键字:Spring Boot,Redis,缓存穿透,缓存击穿,缓存雪崩,红包系统,Mybatis,高并发,多线程并发编程,发送邮件,列表List,集合Set,排行榜,有序集合SortedSet,哈希Hash ,进阶实战,面试,微服务、分布式 适用人群:redisson学习者,分布式中间件实战者,微服务学习者,java学习者,spring boot进阶实战者,redis进阶实战者

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值