专一于 PHP、MySQL、Linux 和前端开发,感兴趣的感激点个关注哟!!!文章已收录, 次要蕴含的技术有 PHP、Redis、MySQL、JavaScript、HTML&CSS、Linux、Java、Golang、Linux 和工具资源等相干理论知识、面试题和实战内容。
文章导语
大家好,前段时间始终在忙找工作相干的事件。最近工作稳固了,于是把面试过程中遇到的 Redis 相干常识问题总结下来,心愿可能对大家面试、学习有所帮忙。本文重点分享数据类型,其余内容后续章节分享,也能够点击上方收录链接。
本系列面试题会从意识 Redis、Redis 几大数据类型、常见的应用场景和解决方案、Redis 主从复制、Redis 哨兵、Redis 集群等相干知识点进行总结。不仅仅单纯的从文字方面终结,还会带有更多的图文方面,尽可能的让大家深刻的学习。
同时我也筹备了一份汇总纲要,绝对文字来说更加的清晰、明了。须要的小伙伴也能够回复Redis 面试纲要
。同时该系列问题也会一直的欠缺、更新。让大家学习到更多的常识。
意识 Redis
- REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。
- Redis 是一个开源的应用 ANSI C 语言编写、恪守 BSD 协定、反对网络、可基于内存、分布式、可选持久性的键值对 (Key-Value) 存储数据库,并提供多种语言的 API。
Redis 的数据类型都有哪些
- 有五种根本数据类型,别离是 string、hash、list、有序汇合(zset)、汇合(set)。在 5.0 之后减少了一种 Stream 类型。
- 额定的有 GEO、HyperLogLog、BitMap。
Redis 应用的场景有哪些
- 数据缓存(用户信息、商品数量、文章浏览数量)
- 音讯推送(站点的订阅)
- 队列(削峰、解耦、异步)
- 排行榜(积分排行)
- 社交网络(独特好友、互踩、下拉刷新)
- 计数器(商品库存,站点在线人数、文章浏览、点赞)
- 基数计算
- GEO 计算
Redis 性能特点都有哪些
- 长久化
- 丰盛的数据类型(string、list、hash、set、zset、公布订阅等)
- 高可用计划(哨兵、集群、主从)
- 事务
- 丰盛的客户端
- 提供事务
- 音讯公布订阅
- Geo
- HyperLogLog
- 事务
- 分布式事务锁
Redis 如何实现分布式锁
- Redis 能够应用
setnx key value
+expire key expire_time
来实现分布式锁。 - 失常状况下,下面的命令是没有问题的。当 Redis 出现异常的状况下,很容易呈现非原子性操作。
- 非原子性操作指的的 setnx 命令执行胜利,然而 expire 没有执行胜利,此时 key 就成为了一个无过期工夫的 key,始终保留在 Redis 中,导致其余的申请就无奈执行。
-
要解决该问题,能够应用 lua 脚本实现。通过 lua 实现命令的原子性操作。
在 Redis 中应用 set 命令,加参数也能够实现分布式锁。
set key vale nx ex|px ttl
<!– tabs:start –>
通过数组定义
--- getLock key
local key = KEYS[1]
local requestId = KEYS[2]
local ttl = tonumber(KEYS[3])
local result = redis.call('setnx', key, requestId)
if result == 1 then
--PEXPIRE: 以毫秒的模式指定过期工夫
redis.call('pexpire', key, ttl)
else
result = -1;
-- 如果 value 雷同,则认为是同一个线程的申请,则认为重入锁
local value = redis.call('get', key)
if (value == requestId) then
result = 1;
redis.call('pexpire', key, ttl)
end
end
-- 如果获取锁胜利,则返回 1
return result
通过数组定义
-- releaseLock key
local key = KEYS[1]
local requestId = KEYS[2]
local value = redis.call('get', key)
if value == requestId then
redis.call('del', key);
return 1;
end
return -1
<!– tabs:end –>
tips:如果对一个 key 第一次 set 增加了过期工夫,第二次操作时没有增加过期工夫,此时 key 是没有过期工夫的(过期工夫被笼罩为永恒不过期)。
Redis 底层数据结构有哪些
Redis 底层数据结构次要有六种,这六种形成了五种罕用的数据类型。其余的数据类型,例如 bitmap、hyperLogLog 也是基于这五大数据类型实现。具体的数据结构图如下:
说说 Redis 的全局 Hash 表
为了实现从键到值的快速访问,Redis 应用了一个哈希表来保留所有键值对。结构图如下:
Hash 表利用如此宽泛的一个重要起因,就是从实践上来说,它能以 O(1) 的复杂度疾速查问数据。Hash 表通过 Hash 函数的计算,就能定位数据在表中的地位,紧接着能够对数据进行操作,这就使得数据操作十分疾速。
那么咱们该如何解决哈希抵触呢?能够思考应用以下两种解决方案:
- 第一种计划,就是应用链式哈希。然而链式哈希容易导致 Hash 的链过长,查问效率升高。
- 第二种计划,就是当链式哈希的链长达到肯定长度时,咱们能够应用 rehash。不过,执行 rehash 自身开销比拟大。
del 删除大量 key 有什么问题
- 应用 del 命令能够删除一个 key 或者多个 key,其工夫复杂度为 O(N),这里的 N 示意删除的 key 数量。
- 删除单个 key 时,其工夫复杂度为 O(1)。
- 当删除单个列表、汇合、有序汇合或者哈希列表类型的 key 时,工夫复杂度为 O(M),这里的 M 示意 key 对应的外部元素个数。
说说 Redis 的全局 hash 实现原理
说说 Zset 在 skiplist 和 ziplist 实现原理
Redis 事务都有哪些命令
mutil: 开启事务;exec: 提交事务;discard: 回滚事务。watch: 监听 key;unwatch: 勾销监听 key。
Redis 中的事务是否是原子性
严格来说,Redis 中的事务并非满足事务的原子性操作。当事务在命令组队时没有产生谬误,则事务是原子性;当事务在命令组队时产生谬误,则事务是非原子性的。
Redis 如何解决事务之间的抵触
- 应用 watch 监听 key 变动,当 key 发生变化,事务中的所有操作都会被勾销。
- 应用乐观锁,通过版本号实现。
- 应用乐观锁,每次开启事务时,都增加一个锁,事务执行完结之后开释锁。
乐观锁:乐观锁 (Pessimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为他人会批改,所以每次在拿数据的时候都会上锁,这样他人拿到这个数据就会 block(阻塞) 直到它拿到锁。传统的关系型数据库外面 就用到了很多这种锁机制,比方行锁、表锁、读锁、写锁等,都是在做操作之前先上锁。
乐观锁:乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去那数据的时候都认为他人不会批改,所以 不会上锁,然而在批改的时候会判断一下在此期间他人有没有去更新这个数据,能够应用版本号等机 制。乐观锁实用于多读的利用类型,这样能够进步吞吐量。redis 就是应用这种 check-and-set 机制实现 事务的。
事务中的 watch 有什么用
在执行 multi 之前,先执行 watch key1 [key2 …],能够监督一个或者多个 key。若在事务的 exec 命令之前,这些 key 对应的值被其余命令所改变了,那么事务中所有命令都将被打断,即事务所有操作将被勾销执行。
Redis 事务的三大个性
- 事务中的所有命令都会序列化、按程序地执行,事务在执行过程中,不会被其余客户端发送来的命令申请所打断。
- 队列中的命令没有提交 (exec) 之前,都不会理论被执行,因为事务提交前任何指令都不会被理论执行。
- 事务中如果有一条命令执行失败,后续的命令依然会被执行,没有回滚。如果在组队阶段,有 1 个失败了,前面都不会胜利; 如果在组队阶段胜利了,在执行阶段有那个命令失败 就这条失败,其余的命令则失常执行,不保障都胜利或都失败。
如何应用 Redis 实现队列性能
- 能够应用 list 实现一般队列,lpush 增加到嘟列,lpop 从队列中读取数据。
- 能够应用 zset 定期轮询数据,实现提早队列。
- 能够应用公布订阅实现多个消费者队列。
- 能够应用 stream 实现队列。(举荐应用该形式实现)。
如何用 Redis 实现异步队列
- 个别应用 list 构造作为队列,rpush 生产音讯,lpop 生产音讯。当 lpop 没有音讯的时候,要适当 sleep 一会再重试。
- 如果对方诘问可不可以不必 sleep 呢?list 还有个指令叫 blpop,在没有音讯的时候,它会阻塞住直到音讯到来。
- 如果对方诘问能不能生产一次生产屡次呢?应用 pub/sub 主题订阅者模式,能够实现 1:N 的音讯队列。
- 如果对方诘问 pub/sub 有什么毛病?在消费者下线的状况下,生产的音讯会失落 ,能够应用 Redis6 减少的stream 数据类型,也能够应用业余的 音讯队列如 rabbitmq 等。
- 如果对方诘问 redis 如何实现延时队列?应用 sortedset,拿工夫戳作为 score,音讯内容作为 key 调用 zadd 来生产音讯,消费者用 zrangebyscore 指令获取 N 秒之前的数据轮询进行解决。
Stream 与 list、zset 和公布订阅区别
- list 能够应用 lpush 向队列中增加数据,lpop 能够向队列中读取数据。list 作为音讯队列无奈实现一个音讯多个消费者。如果呈现音讯解决失败,须要手动回滚音讯。
- zset 在增加数据时,须要增加一个分值,能够依据该分值对数据进行排序,实现提早音讯队列的性能。音讯是否生产须要额定的解决。
- 公布订阅能够实现多个消费者性能,然而公布订阅无奈实现数据长久化,容易导致数据失落。并且开启一个订阅者无奈获取到之前的数据。
- stream 借鉴了罕用的 MQ 服务,增加一个音讯就会产生一个音讯 ID,每一个音讯 ID 下能够对应多个生产组,每一个生产组下能够对应多个消费者。能够实现多个消费者性能,同时反对 ack 机制,缩小数据的失落状况。也是反对数据值长久化和主从复制性能。
如何设计一个网站每日、每月和每天的 PV 和 UV
实现这样的性能,如果只是统计一个汇总数据,举荐应用 HyperLogLog 数据类型。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的长处是,在输出元素的数量或者体积十分十分大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 外面,每个 HyperLogLog 键只须要破费 12 KB 内存,就能够计算靠近 2^64 个不同元素的基 数。这和计算基数时,元素越多消耗内存就越多的汇合造成鲜明对比。
Redis 如何实现间隔检索性能
实现间隔检索,能够应用 Redis 中的 GEO 数据类型。GEO 次要用于存储地理位置信息,并对存储的信息进行操作,该性能在 Redis 3.2 版本新增。然而 GEO 适宜精度不是很高的场景。因为 GEO 是在内存中进行计算,具备计算速度快的特点。
list 和公布订阅实现队列有什么问题
- list 能够应用 lpush 向队列中增加数据,lpop 能够向队列中读取数据。list 作为音讯队列无奈实现一个音讯多个消费者。如果呈现音讯解决失败,须要手动回滚音讯。
- 公布订阅能够实现多个消费者性能,然而公布订阅无奈实现数据长久化,容易导致数据失落。并且开启一个订阅者无奈获取到之前的数据。
Redis 如何实现秒杀性能
- 在秒杀场景下,超卖是一个十分重大的问题。惯例的逻辑是先查问库存在缩小库存。但在秒杀场景中,无奈保障缩小库存的过程中有其余的申请读取了未缩小的库存数据。
- 因为 Redis 是单线程的执行,同一时刻只有一个线程进行操作。因而能够应用 Redis 来实现秒杀缩小库存。
- 在 Redis 的数据类型中,能够应用 lpush,decr 命令实现秒杀缩小库存。该命令属于原子操作。
Redis 如何实现用户签到性能
- 应用 Redis 实现用户签到能够应用 bitmap 实现。bitmap 底层数据存储的是 1 否者 0,占用内存小。
- Redis 提供的数据类型 BitMap(位图),每个 bit 位对应 0 和 1 两个状态。尽管外部还是采纳 String 类型存储,但 Redis 提供了一些指令用于间接操作 BitMap,能够把它看作一个 bit 数组,数组的下标就是偏移量。
- 它的长处是内存开销小,效率高且操作简略,很适宜用于签到这类场景。
- 毛病在于位计算和位示意数值的局限。如果要用位来做业务数据记录,就不要在意 value 的值。
Redis 如何实现提早队列
- 应用 Redis 实现提早队列,能够应用 zset 数据类型。
- zset 在增加数据时,须要增加一个分值,将工夫作为分值,依据该分值对数据进行排序。
- 独自开启线程,依据分值大小定期履行数据。
Redis 实现一个积分排行性能
- 应用 Redis 实现积分排行,能够应用 zset 数据类型。
- zset 在增加数据时,须要增加一个分值,将积分作为分值,值作为用户 ID,依据该分值对数据进行排序。
字符串类型存储最大容量是多少
一个字符串最大可存储 512M。