常见面试题-Redis专栏(二)


theme: cyanosis

typora-copy-images-to: imgs

Redisson 分布式锁?在项目中哪里使用?多久会进行释放?如何加强一个分布式锁?

答:

首先入门级别的分布式锁是通过 setnx 进行实现,使用 setnx 实现有四个注意点

  1. 需要设置锁的超时时间(如果不设置,在释放锁时,如果机器宕机,会导致锁无法释放)

  2. 需要设置一个唯一 ID,表示这个锁是哪个用户添加的,必须由添加锁的用户释放

    (如果不设置,线程1在执行任务时,可能锁的超时时间已经达到,被自动释放,此时线程2加锁,开始执行业务,但正好线程1执行完毕,释放锁,由于没有唯一ID表示,线程1将线程2加的锁给释放掉了)

  3. 需要锁续命

    有可能锁的过期时间设置的太短,导致业务没有执行完毕,锁就被自动释放,因此要使用锁续命来解决(大概逻辑是使用子线程执行定时任务,定时任务间隔时间要小于 key 的过期时间,子线程隔一段时间判断主线程是否在执行,如果在执行,就重新设置一下过期时间)

  4. 可重入问题:setnx 实现的分布式锁不可重入,这样获取锁的线程在重复进入相同锁的代码块中会造成死锁

而在 Redission 中已经帮我们实现好了分布式锁,下来看一下 Redission 中的分布式锁:

Redission 中获取锁逻辑:

在 Redission 中加锁,通过一系列调用会到达下边这个方法

他的可重入锁的原理也就是使用 hash 结构来存储锁,key 表示锁是否存在,如果已经存在,表示需要重复访问同一把锁,会将 value + 1,即每次重入一次 value 就加 1,退出一次 value 就减 1

下列方法有三个参数分别为:

  • KEYS[1] : 锁名称
  • ARGV[1]: 锁失效时间
  • ARGV[2]: id + “:” + threadId; 锁的小key
    <T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);
​return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hset', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}

Redission 中锁续命原理:

Redission 底层有个看门狗机制,加锁成功后会有一个定时任务,默认锁的失效时间是 30s,该定时任务每隔锁失效时间的 1/3 就会去续约锁时间,也就是每隔 10s 进行锁续命

如何加强一个分布式锁?

也就是如何提升一个分布式锁的性能,分布式锁本质上是将并行操作改为串行,那么我们可以通过使用分段锁来提升性能,比如说有 1000 个库存的话,读入到缓存中将分为 10 份进行存储,即 product_stock_1 = 100, product_stock_2 = 100, ...,给每一份都加上所,那么多个线程来竞争这 10 把锁,比原来竞争 1 把锁的性能提高 10 倍

zset 的底层实现?为什么不用红黑树?

答:

zset 的底层实现是:压缩列表 + 跳表

什么时候使用压缩列表?

  • 有序集合保存的元素个数要小于 128 个;
  • 有序集合保存的所有元素成员的长度都必须小于 64 字节。

否则使用跳表

跳表中每个节点都有多个跳跃指针,因此每个节点的平均跳跃长度较长,可以一次跳过多个节点,当找到大于或等于目标元素的节点后,再使用普通指针开始移动(可以向后移动,也可以向前移动,跳表含有前边节点的指针)寻找目标元素,跳表可以在 O(logn) 的时间内遍历跳表

跳表结构图:

1697874023019.png

为什么不用红黑树?

  • 跳表和红黑树的查找时间复杂度都是O(logn),但是红黑树比跳表的插入/删除效率更低

    • 跳表在插入或删除时,只需考虑相邻节点,而红黑树需考虑节点的旋转问题,焦虑较低
  • 跳表实现比红黑树更简单

zset 几个命令的时间复杂度?

答:

  • zadd:O(logn),添加一个元素的时间复杂度是 O(logn)(因为插入元素的话,时间开销都在查找插入位置上,在 zset 中,查找时间复杂度是 O(logn),因此插入复杂度同是)
  • zrange:O(logn + m) ,n 是集合中元素数量,m 是指定范围内的用户数量

redis 里面的命令,比如 setnx 和 setex 还有 zset 中的命令?

答:

zset 中的常用命令为:

  • zadd <key> <score1> <value1> <score2> <value2> ...

    向集合 key 中添加元素

  • zrange <key> <start> <stop> [withscores]

    查找下标在 start 和 stop 之间的元素,如果后边带上 withscores 参数,会将分数也查询出来

  • zrevrange <key> <start> <stop> [withscores]

    将分数从大到小进行查询,和 zrange 查询顺序相反

  • zrangebyscore <key> <min> <max> [withscores]

    返回集合 key 中所有 score 介于 min 和 max 之间的成员,如果后边带上 withscores 参数,会将分数也查询出来

  • setex <key> <seconds> <value>

    设置 key、value 并且设置过期时间

  • setnx <key> <value>

    仅当 key 不存在时,才将 key 的值设置为 value,成功返回1,失败返回0

缓存怎么保证数据的一致性?

答:

  • 先删除缓存,再更新数据库 (操作简单)

    这种情况造成的缓存不一致为:线程 A 先删除缓存,再去更新数据库,在线程 A 更新数据库之前,如果线程 B 去读取缓存,发现并不存在,去读取数据库,此时读取的是旧数据,再将旧数据写入缓存,此时缓存存储的就是脏数据了。

    使用更新数据库 + 延时双删可以解决此情况的数据不一致,在延时双删中,会删除两次缓存,分为以下几步:

    
    1. 删除缓存
    2. 更新数据库
    3. 睡眠  Thread.sleep()
    4. 再删除缓存
    

    即延时双删在线程 A 更新完数据库之后,休眠一段时间,再去删除缓存中可能存在的脏数据。

    这样第二次删除缓存可能导致线程 A 执行时间过长,第二次可以使用异步去删除缓存

  • 先更新数据库,再删除缓存

    这种情况可能因为线程 A 没有及时删除缓存或者删除缓存失败而导致线程 B 读取到旧数据

    可以使用消息队列来完成:

    1. 先将要删除的缓存值或者是要更新的数据库值暂存到消息队列中
    2. 当程序没有成功删除缓存值或者更新数据库值时,从消息队列中读取这些值,再次进行删除或更新
    3. 如果成功删除缓存或者更新数据库,要将这些值从消息队列中取出,以免重复操作

高级扩展点Canal 监听日志更新 + 定时任务缓存处理,简单概括来说就是 Canal 可以监控 MySQL 的 binlog,当发现数据库的数据发生变化后,就去同步缓存,就可以达到最终的数据一致性了

redis基本数据结构?

答:

有 5 中基本数据结构:字符串、list列表、hash字典、set集合、zset有序集合

基本数据结构添加数据命令:

  • 字符串: set key value [ex seconds | px milliseconds] [nx | xx]

    nx:指定 key 不存在才会设置成功

    xx:指定的 key 必须存在才会设置成功,用于更新 key

  • list:lpush key value [value...] rpush key value [value...]

  • hash: hset key field value 将 key 中的 field 的值设置为 value

  • set: sadd key value [value...]

  • zset:zadd key score value 向 key 中添加一个 value 和 score,根据 score 排序

数据结构基本介绍:

  • list 列表是链表,不是数据
  • set 集合内部的键值对是无序且唯一的

基本数据结构应用场景:

  • 字符串

    • 限速器:防止 DoS 攻击,对 ip 进行访问次数限制,但是无法防止 DDoS 攻击,因为 DDoS 是分布式拒绝服务,使用了不同 ip 不断访问服务器

      
      // 等价于 set 192.168.55.1 ex 60 nx
      // 如果该ip不存在,指定key为ip,value为1,过期时间为60秒
      Boolean exists = redis.set(ip, 1, "ex 60", "nx");
      if(exists != null || redis.incr(ip) <= 5) {// 通过访问
      } else {// 限流
      }
      
  • list

    • lpush + lpop 实现
    • 列表 rpush + lpoplpush + rpop 实现
    • 阻塞式消息队列 lpush + brpop 实现
  • hash

    • 存储对象数据:key 为对象名称,value 为描述对象属性的 Map,对象属性的修改在 Redis 中就可直接完成
  • set

    • 去重操作
  • zset

    • 用户排行榜
    • 用户点赞统计

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/166247.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学生学徒作品分享——金融大模型-房屋租金价格影响因素分析与预测

金融大模型-房屋租金价格影响因素分析与预测项目背景 广州作为中国最发达的城市之一&#xff0c;每年都吸引大量务工人员前来就业&#xff0c;而租房是他们需要解决的最大问题之一&#xff0c;各地区租房需求日益增长。在租房过程&#xff0c;价格、交通是重要的考虑因素&a…

LabVIEW中管理大型数据

LabVIEW中管理大数据 LabVIEW的最大优势之一是自动内存管理。这种内存管理允许用户轻松创建字符串、数组和集群&#xff0c;而无需C/C用户经常担心。但是&#xff0c;这种内存管理设计为绝对安全&#xff0c;因此数据被非常频繁地复制。这通常不会造成任何问题&#xff0c;但是…

Python--循环中的两大关键词 break 与 continue

在Python循环中&#xff0c;经常会遇到两个常见的关键词&#xff1a;break 与 continue break&#xff1a;代表终止整个循环结构 continue&#xff1a;代表中止当前本次循环&#xff0c;继续下一次循环 break&#xff1a; 英 /breɪk/ v. 打破&#xff0c;打碎&#xff0c…

asp.net企业招聘管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio计算机毕业设计

一、源码特点 asp.net 企业招聘管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net企业招聘管理系统 二、功…

【LeetCode】94. 二叉树的中序遍历 [ 左子树 根结点 右子树 ]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯ C…

库的操作【MySQL】

文章目录 创建数据库字符集和校验规则概念分类例子 查看数据库显示创建语句修改数据库删除数据库备份和恢复备份恢复 创建数据库 SQL: CREATE DATABASE [IF NOT EXISTS] db_name [[DEFAULT] CHARSETcharset_name] [[DEFAULT] COLLATEcollation_name];其中&#xff0c;大写的单…

ubuntu 18.04 开机自启 打开终端执行脚本

一 打开设置开机自启配置程序 alt F2 输入 gnome-session-properties 或 终端输入 gnome-session-properties 二 添加开机自启配置 点右方的add加入 填写名称&#xff0c;可自定义 填写指令&#xff0c;即开机想要执行的指令 gnome-terminal -x bash -c “ls; exec bash” …

SpringBoot(二)集成 Quartz:2.5.4

Quartz是一个广泛使用的开源任务调度框架&#xff0c;用于在Java应用程序中执行定时任务和周期性任务。它提供了强大的调度功能&#xff0c;允许您计划、管理和执行各种任务&#xff0c;从简单的任务到复杂的任务。 以下是Quartz的一些关键特点和功能&#xff1a; 灵活的调度器…

Oracle的立场:官网更换首页与以色列站在一起

Oracle公司的官网&#xff0c;更换了首页内容&#xff0c;明确表明立场&#xff1a;Oracle与以色列站在一起。 声明指出&#xff1a; Oracle谴责针对以色列及其公民的恐怖袭击。Oracle将为其员工、以色列政府和国防机构提供一切必要的支持。 Magen David Adom是一家为以色列公民…

Android屏幕刷新机制

基础知识 CPU运行在Android设备上的中央处理器&#xff08;Central Processing Unit&#xff09;是Android设备的核心组件之一&#xff0c;负责执行计算和控制设备的各种操作。 Android设备上的CPU通常采用ARM架构&#xff0c;如ARM Cortex-A系列处理器。这些处理器具有高性能…

Hadoop3教程(三十三):(生产调优篇)慢磁盘监控与小文件归档

文章目录 &#xff08;161&#xff09;慢磁盘监控&#xff08;162&#xff09;小文件归档小文件过多的问题如何对小文件进行归档 参考文献 &#xff08;161&#xff09;慢磁盘监控 慢磁盘&#xff0c;是指写入数据时特别慢的一类磁盘。这种磁盘并不少见&#xff0c;当机器运行…

Java学习笔记(五)——数组、排序和查找

一、数组 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。即数组就是一组数据。 &#xff08;一&#xff09;数组的使用 1、使用方式1——动态初始化 &#xff08;1&#xff09;数组的定义&#xff1a; 数据类型 数组名[] new 数据类型…

【C语言】每日一题(添加逗号)

添加逗号&#xff0c;链接奉上 目录 方法1&#xff1a;整体存入思路&#xff1a;代码实现&#xff1a; 方法2&#xff1a;分段输出思路&#xff1a;代码实现&#xff1a; 方法1&#xff1a;整体存入 思路&#xff1a; 整体思路&#xff1a; 我们发现这个整数N对于最后1位是比…

会声会影2023官方破解版激活码

随着短视频、vlog等媒体形式的兴起&#xff0c;视频剪辑已经成为了热门技能。甚至有人说&#xff0c;不会修图可以&#xff0c;但不能不会剪视频。实际上&#xff0c;随着各种智能软件的发展&#xff0c;视频剪辑已经变得越来越简单。功能最全的2023新版&#xff0c;全新视差转…

Hadoop3教程(三十四):(生产调优篇)MapReduce生产经验汇总

文章目录 &#xff08;164&#xff09;MR跑得慢的原因&#xff08;165&#xff09;MR常用调优参数Map阶段Reduce阶段 &#xff08;166&#xff09;MR数据倾斜问题参考文献 &#xff08;164&#xff09;MR跑得慢的原因 MR程序执行效率的瓶颈&#xff0c;或者说当你觉得你的MR程…

《动手学深度学习 Pytorch版》 9.4 双向循环神经网络

之前的序列学习中假设的目标是在给定观测的情况下对下一个输出进行建模&#xff0c;然而也存在需要后文预测前文的情况。 9.4.1 隐马尔可夫模型中的动态规划 数学推导太复杂了&#xff0c;略。 9.4.2 双向模型 双向循环神经网络&#xff08;bidirectional RNNs&#xff09;…

解决windows10、windows11故障:Microsoft-Windows-Kernel-Processor-Power 事件ID:37

一、现象 windows系统日志中出现大量的“Microsoft-Windows-Kernel-Processor-Power”错误。 经过分析&#xff1a;原因是windows配置的【使用电池】默认值是5%&#xff0c;按5%计算出来的功率与CPU的最小功率不兼容&#xff0c;如&#xff1a;本机CPU最高功率是25W&#xff0…

flink中使用GenericWriteAheadSink的优缺点

背景 GenericWriteAheadSink是flink中提供的实现几乎精确一次输出的数据汇抽象类&#xff0c;本文就来看一下使用GenericWriteAheadSink的优缺点 GenericWriteAheadSink的优缺点 先看一下GenericWriteAheadSink的原理图 优点&#xff1a; 几乎可以精确一次的输出&#xf…

数据库MongoDB

MongoDB记录是一个文档&#xff0c;由一个字段和值对组成的数据结构&#xff0c;文档类似于JSON对象。 一个文档认为就是一个对象&#xff0c;字段的数据类型是字符型&#xff0c;值除了使用基本类型外&#xff0c;还可以包括其他文档&#xff0c;普通数组和文档数组。 一、…

FreeRTOS介绍 和 将FreeRTOS移植到STM32F103C8T6

一、FreeRTOS 介绍 什么是 FreeRTOS &#xff1f; Free即免费的&#xff0c;RTOS的全称是Real time operating system&#xff0c;中文就是实时操作系统。 注意&#xff1a;RTOS不是指某一个确定的系统&#xff0c;而是指一类操作系统。比如&#xff1a;uc/OS&#xff0c;Fr…