乐趣区

关于后端:刨根问底-Redis-面试过程真好使

大家好,这里是 菜农曰,欢送来到我的频道。

充斥寒气的互联网如何在面试中怀才不遇,平时积攒很重要,八股文更不能少!上面带来的这篇 Redis 问答心愿可能在你的 offer 上削减一把🔥。

在 Web 利用倒退的初期阶段,一个网站的访问量自身就不是很高,间接应用关系型数据库就能够应酬绝大部分场景。然而随着互联网时代的崛起,人们对于网站访问速度有着越来越高的要求,间接应用关系型数据库的计划在性能上就呈现了瓶颈。因而在客户端与数据层之间就须要一个缓存层来分担申请压力,而 Redis 作为一款优良的缓存中间件,在企业级架构中占有重要的位置,因而 Redis 也作为面试的必问项。

本文通过 30 个问题,由浅入深,最大水平上笼罩整个 Redis 的问答内容


Redis(Remote Dictionary Server)是一个开源的、键值对型的数据存储系统。应用 C 语言编写,恪守 BSD 协定,可基于内存也可长久化的日志型数据库,提供了多种语言的 API,被宽泛用于数据库、缓存和消息中间件。并且反对多种类型的数据结构,用于应答各种不同场景。能够存储多种不同类型值之间的映射,反对事务,长久化,LUA 脚本以及多种集群计划等。

长处:

  1. 齐全基于内存操作,性能极高,读写速度快,Redis 可能反对超过 100KB/s 的读写速率
  2. 反对高并发,反对 10 万级别的并发读写
  3. 反对主从模式,反对读写拆散与分布式
  4. 具备丰盛的数据类型与丰盛的个性(公布订阅模式)
  5. 反对长久化操作,不会失落数据

毛病:

  1. 数据库容量受到物理内存的限度,不能实现海量数据的高性能读写
  2. 相比关系型数据库,不反对简单逻辑查问,且存储构造绝对简略
  3. 尽管提供长久化能力,但理论更多是一个 disk-backed 性能,与传统意义上的长久化有所区别

Memcache 也是一个开源、高性能、分布式内存对象缓存零碎。所有数据均存储在内存中,在服务器重启之后就会隐没,须要从新加载数据,采纳 hash 表的形式将所有数据缓存在内存中,采纳 LRU 算法来逐步把过期的数据革除掉。

  1. 数据类型:Memcache 仅反对字符串类型,Redis 反对 5 种不同的数据类型
  2. 数据长久化:Memcache 不反对长久化,Redis 反对两种长久化策略,RDB 快照 和 AOF 日志
  3. 分布式:Memcache 不反对分布式,只能在客户端应用一致性哈希的形式来实现分布式存储,Redis3.0 之后可在服务端构建分布式存储,Redis 集群没有核心节点,各个节点位置平等,具备线性可伸缩的性能。
  4. 内存管理机制:Memcache 数据量不能超出零碎内存,但能够调整内存大小,淘汰策略采纳 LRU 算法。Redis 减少了 VM 个性,实现了物理内存的限度,它们之间底层实现形式以及客户端之间通信的利用协定不一样。
  5. 数据大小限度:Memcache 单个 key-value 大小有限度,一个 Value 最大容量为 1MB,Redis 最大容量为 512 MB

根本数据类型:

  • String(字符串)
  • Hash(哈希)
  • List(列表)
  • Set(汇合)
  • ZSet(Sorted Set 有序汇合)

高级数据类型:

  • HyperLogLog:用来做基数统计的算法,在输出元素的数量或体积十分大时,计算基数所需的空间总是固定的,并且是很小的。HyperLogLog 只会依据输出元素来计算基数,而不会存储输出元素自身
  • Geo:用来地理位置的存储和计算
  • BitMap:实际上不是非凡的存储构造,实质上是二进制字符串,能够进行位操作,罕用于统计日沉闷用户等

扩大: geohash 通过算法将 1 个定位的经度和纬度 2 个数值,转换成 1 个 hash 字符串。如果 2 个中央间隔越近,那么他们的 hash 值的前缀越雷同。

Redis 底层实现了简略动静字符串的类型(Simple Dynamic String,SDS)来示意 String 类型。没有间接应用 C 语言定义的字符串类型。

SDS 实现绝对于 C 语言 String 形式的晋升

  1. 防止缓冲区移除。对字符批改时,能够依据 len 属性查看空间是否满足要求
  2. 获取字符串长度的复杂度较低
  3. 缩小内存调配次数
  4. 兼容 C 字符串函数,能够重用 C 语言库的一部分函数

Redis 间接以内存的形式存储能够达到最快的读写速度,如果开启了长久化则通过异步的形式将数据写入磁盘,因而 Redis 具备疾速和数据长久化的特色。

在内存中操作自身就比从磁盘操作更快,且不受磁盘 I / O 速度的影响。如果不将数据放在内存中而是保留到磁盘,磁盘 I / O 速度会重大影响到 Redis 的性能,而数据集大小如果达到了内存的最大限定值则不能持续插入新值。

如果关上了虚拟内存性能,当内存用尽时,Redis 就会把那些不常常应用的数据存储到磁盘,如果 Redis 中的虚拟内存被禁了,它就会操作系统的虚拟内存(替换内存),但这时 Redis 的性能会急剧下降。如果配置了淘汰机制,会依据已配置的数据淘汰机制来淘汰旧数据。

1、尽可能应用哈希表(hash 数据结构):Redis 在贮存小于 100 个字段的 Hash 构造上,其存储效率是十分高的。所以在不须要汇合(set)操作或 list 的 push/pop 操作的时候,尽可能应用 hash 构造。

2、依据业务场景,思考应用 BitMap

3、充分利用共享对象池:Redis 启动时会主动创立【0-9999】的整数对象池,对于 0-9999 的外部整数类型的元素,整数值对象都会间接援用整数对象池中的对象,因而尽量应用 0-9999 整数对象可节俭内存。

4、正当应用内存回收策略:过期数据革除、expire 设置数据过期工夫等

Redis 可能用来实现分布式锁的命令有 INCR、SETNX、SET,并利用过期工夫命令 expire 作为辅助

形式 1:利用 INCR

如果 key 不存在,则初始化值为 0,而后再利用 INCR 进行加 1 操作。后续用户如果获取到的值大于等于 1,阐明曾经被其余线程加锁。当持有锁的用户在执行完工作后,利用 DECR 命令将 key 的值减 1,则示意开释锁。

形式 2:利用 SETNX

先应用 setnx 来争抢锁,抢到之后利用 expire 设置一个过期工夫避免未能开释锁。setnx 的意义是如果 key 不存在,则将 key 设置为 value,返回 1。如果已存在,则不做任何操作,返回 0。

形式 3:利用 SET

set 指令有非常复杂的参数,相当于合成了 setnxexpire 两条命令的性能。其命令格局如:set($Key,$value, array('nx', 'ex'=>$ttl))

  1. 齐全基于内存
  2. 数据结构简略,操作不便,并且不同数据结构可能应对于不同场景
  3. 采纳单线程(网络申请模块应用单线程,其余模块仍用了多线程),防止了不必要的上下文切换和竞争条件,也不存在多过程或多线程切换导致 CPU 耗费,不须要思考各种锁的问题。
  4. 应用多路 I / O 复用模型,为非阻塞 I /O
  5. Redis 自身设定了 VM 机制,没有应用 OS 的 Swap,能够实现冷热数据拆散,防止因为内存不足而造成访问速度降落的问题

1、RDB(Redis DataBase)长久化

RDB 是 Redis 中默认的长久化机制,依照肯定的工夫将内存中的数据以快照的形式保留到磁盘中,它会产生一个非凡类型的文件 .rdb 文件,同时能够通过配置文件中的 save 参数来定义快照的周期

在 RDB 中有两个外围概念 forkcow,在执行备份的流程如下:

在执行 bgsave 的时候,Redis 会 fork 主过程失去一个新的子过程,子过程是共享主过程内存数据的,会将数据写到磁盘上的一个长期的 .rdb 文件中,当子过程写完临时文件后,会将原来的 .rdb 文件替换掉,这个就是 fork 的概念。那 cow 全称是 copy-on-write,当主过程执行读操作的时候是拜访共享内存的,而主过程执行写操作的时候,则会拷贝一份数据,执行写操作。

长处

  1. 只有一个文件 dump.rdb,不便长久化
  2. 容错性好,一个文件能够保留到平安的磁盘
  3. 实现了性能最大化,fork 独自子过程来实现长久化,让主过程持续解决命令,主过程不进行任何 I/O 操作,从而保障了 Redis 的高性能
  4. RDB 是一个紧凑压缩的二进制文化,RDB 重启时的加载效率比 AOF 长久化更高,在数据量大时更显著

毛病

  1. 可能呈现数据失落,在两次 RDB 长久化的工夫距离中,如果呈现宕机,则会失落这段时间中的数据
  2. 因为 RDB 是通过 fork 子过程来帮助实现数据长久化,如果当数据集较大时,可能会导致整个服务器间歇性暂停服务

2、AOF(Append Only File)长久化

AOF 全称是 Append Only File(追加文件)。当 Redis 解决每一个写命令都会记录在 AOF 文件中,能够看做是命令日志文件。该形式须要设置 AOF 的同步选项,因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区中,同步选项有三种配置项抉择:

  • always:同步刷盘,可靠性高,但性能影响较大
  • everysec:每秒刷盘,性能适中,最多失落 1 秒的数据
  • no:操作系统管制,性能最好,可靠性最差

为了解决 AOF 文件体检一直增大的问题,用户能够向 Redis 发送 bgrewriteaof 命令,能够将 AOF 文件进行压缩,也能够抉择主动触发,在配置文件中配置

auto-aof-rewrite-precentage 100
auto-aof-rewrite-min-zise 64mb

长处

  1. 实现长久化,数据安全,AOF 长久化能够配置 appendfsync 属性为 always,每进行一次命令操作就记录到 AOF 文件中一次,数据最多失落一次
  2. 通过 append 模式写文件,即便中途服务器宕机,能够通过 Redis-check-aof 工具解决数据一致性问题
  3. AOF 机制的 rewrite 模式。AOF 文件的文件大小触碰到临界点时,rewrite 模式会被运行,重写内存中的所有数据,从而放大文件体积

毛病

  1. AOF 文件大,通常比 RDB 文件大很多
  2. 比 RDB 长久化启动效率低,数据集大的时候较为显著
  3. AOF 文件体积可能迅速变大,须要定期执行重写操作来升高文件体积

形式 1:定时删除

在设置 Key 的过期工夫的同时,会创立一个定时器 timer,定时器在 Key 过期工夫来长期,会立刻执行对 Key 的删除操作

特点: 对内存敌对,对 CPU 不敌对。存在较多过期键时,利用定时器删除过期键会占用相当一部分 CPU

形式 2:惰性删除

key 不应用时不论 key 过不过期,只会在每次应用的时候再查看 Key 是否过期,如果过期的话就会删除该 Key。

特点: 对 CPU 敌对,对内存不敌对。不会破费额定的 CPU 资源来检测 Key 是否过期,但如果存在较多未应用且过期的 Key 时,所占用的内存就不会失去开释

形式 3:定期删除

每隔一段时间就会对数据库进行一次查看,删除外面的过期 Key,而查看多少个数据库,则由算法决定

特点: 定期删除是对下面两种过期策略的折中,也就是对内存敌对和 CPU 敌对的折中办法。每隔一段时间执行一次删除过期键工作,并通过限度操作的时长和频率来缩小对 CPU 工夫的占用。

Redis 主从同步分为增量同步和全量同步 Redis 会先尝试进行增量同步,如果不胜利则会进行全量同步。

增量同步:

Slave 初始化后开始失常工作时主服务器产生的写操作同步到从服务器的过程。增量同步的过程次要是主服务器每执行一个写命令就会向从服务器发送雷同的写命令。

全量同步:

Slave 初始化时它会发送一个 psync 命令到主服务器,如果是第一次同步,主服务器会做一次bgsave,并同时将后续的批改操作记录到内存 buffer 中,待 bgsave 实现后再将 RDB 文件全量同步到从服务器,从服务器接管实现后会将 RDB 快照加载到内存而后写入到本地磁盘,解决实现后,再告诉主服务器将期间批改的操作记录同步到复制节点进行重放就实现了整个全量同步过程。

在 Redis 中,最大应用内存大小由 Redis.conf 中的参数 maxmemory 决定,默认值为 0,示意不限度,这时理论相当于以后零碎的内存。但如果随着数据的减少,如果对内存中的数据没有管理机制,那么数据集大小达到或超过最大内存的大小时,则会造成 Redis 解体。因而须要内存数据淘汰机制。

设有过期工夫

  1. volatile-lru:尝试回收起码应用的键
  2. volatile-random:回收随机的键
  3. volatile-ttl:优先回收存活工夫较短的键

没有过期工夫

  1. allkey-lru:尝试回收起码应用的键
  2. allkeys-random:回收随机的键
  3. noeviction:当内存达到限度并且客户端尝试执行新增,会返回谬误

淘汰策略的规定

  • 如果数据出现幂律散布,也就是一部分数据拜访频率高,一部分数据拜访频率低,则应用 allKeys-lru
  • 如果数据出现平等散布,也就是所有的数据拜访频率大体雷同,则应用 allKeys-random
  • 对于 lru 策略,Redis 中并不会精确的删除所有键中最近起码应用的键,而是随机抽取 5 个键(个数由参数 maxmemory-samples 决定,默认值是 5),删除这 5 个键中最近起码应用的键。

问题 1:缓存穿透

缓存穿透是指缓存和数据库上都没有的数据,导致所有申请都落到数据库上,造成数据库短时间内接受大量的申请而导致宕机

解决:

  1. 应用布隆过滤器:将查问的参数都存储到一个 bitmap 中,在查问缓存前,如果 bitmap 存在则进行底层缓存的数据查问,如果不存在则进行拦挡,不再进行缓存的数据查问
  2. 缓存空对象:如果数据库查问的为空,则仍然把这个数据缓存并设置过期工夫,当屡次拜访的时候能够间接返回后果,防止造成屡次拜访数据库,但要保障当数据库有数据时及时更新缓存。

问题 2:缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(个别是缓存工夫到期),就会导致所有申请都落到数据库上,造成数据库段时间内接受大量的申请而宕机

解决:

  1. 设置热点数据永不过期
  2. 能够应用互斥锁更新,保障同一过程中针对同一个数据不会并发申请到 DB,减小 DB 的压力
  3. 应用随机退却形式,生效时随机 sleep 一个很短的工夫,再次查问,如果失败再执行更新

问题 3:缓存雪崩

缓存雪崩是指大量缓存同一时间内大面积生效,前面的申请都会落到数据库上,造成数据库段时间无奈接受大量的申请而宕掉

解决:

  1. 在缓存生效后,通过加锁或者队列来管制读数据库写缓存的线程数量。比方对某个 Key 只容许一个线程查问和写缓存,其余线程期待
  2. 通过缓存 reload 机制,事后去更新缓存,在行将产生高并发拜访前手动触发加载缓存
  3. 对于不同的 key 设置不同的过期工夫,让缓存生效的工夫点尽量平均,比方咱们能够在原有的生效工夫根底上减少一个随机值,比方 1~5 分钟随机,这样每一个缓存的过期工夫的反复率就会升高。
  4. 设置二级缓存,或者双缓存策略。

缓存降级,其实都应该是指服务降级。在访问量剧增、服务响应呈现问题(如响应提早或不响应)或非核心服务影响到外围流程的性能的状况下,依然须要保障外围服务可用,只管可能一些非次要服务不可用,这时就能够采取服务降级策略。

服务降级的最终目标是保障外围服务可用,即便是有损的。服务降级该当当时确定好降级计划,确定哪些服务是能够降级的,哪些服务是不可降级的。依据以后业务状况及流量对一些服务和页面有策略的降级,以此开释服务器资源以保障外围服务的失常运行。

降级往往会指定不同的级别,面临不同的异样等级执行不同的解决。依据服务形式:能够拒接服务,能够提早服务,也能够随机提供服务。依据服务范畴:能够临时禁用某些性能或禁用某些功能模块。总之服务降级须要依据不同的业务需要采纳不同的降级策略。次要的目标就是服务尽管有损然而总比没有好。

  1. 数据实时同步生效或更新。这是一种增量主动型的计划,能保证数据强一致性,在数据库数据更新之后,被动申请缓存更新
  2. 数据异步更新。这是一种增量被动型计划,数据一致性稍弱,数据更新会有提早,更新数据库数据后,通过异步形式,用多线程形式或音讯队列来实现更新
  3. 定时工作更新。这是一种增 / 全量被动型计划,通过定时工作按肯定频率调度更新,数据一致性最差
  1. 间接写个缓存刷新页面,上线时手工操作
  2. 数据量不大,能够在我的项目启动时主动进行加载
  3. 定时刷新缓存

Sentinel(哨兵)实用于监控 Redis 集群中 Master 和 Slave 状态的工具,是 Redis 的高可用性解决方案

次要作用

  1. 监控。哨兵会一直检查用户的 Master 和 Slave 是否运作失常
  2. 揭示。当被监控的某个 Redis 节点呈现问题时,哨兵能够通过 API 向管理员或其余应用程序发送告诉
  3. 主动故障迁徙。当一个 Master 不能失常工作时,哨兵会开始一次主动故障迁徙操作,它会将集群中一个 Slave 晋升为新的 Master,并让其余 Slave 改为与新的 Master 进行同步。当客户端试图连贯失败的 Master 时,集群也会想客户端返回新的 Master 地址。当主从服务器切换后,新 Master 的 Redis.conf,Slave 的 Redis.conf 和 Sentinel 的 Redis.conf 三者配置文件都会产生相应的扭转。

问题背景

Redis 是基于 TCP 协定的申请 / 响应服务器,每次通信都要通过 TCP 协定的三次握手,所以当须要执行的命令足够简单时,会产生很大的网络提早,并且网络的传输工夫老本和服务器开销没有计入其中,总的提早可能更大。

Pipeline 解决

  • Pipeline 次要就是为了解决存在这种状况的场景,应用 Pipeline 模式,客户端能够一次性发送多个命令,无需期待服务端返回,这样能够将屡次 I / O 往返的工夫缩短为一次,大大减少了网络往返工夫,进步了零碎性能。
  • Pipeline 是基于队列实现,基于先进先出的原理,满足了数据程序性。同时一次提交的命令很多的话,队列须要十分大量的内存来组织返回数据内容,如果大量应用 Pipeline 的话,该当正当分批次提交命令。
  • Pipeline 的默认同步个数为 53 个,累加到 53 条数据时会把数据提交

留神: Redis 集群中应用不了 Pipeline,对可靠性要求很高,每次操作都须要立刻获取本次操作后果的场景都不适宜用 Pipeline

  1. Master 最好不要做 RDB 长久化,因为这时 save 命令调度 rdbSave 函数,会阻塞主线程的工作,当数据集比拟大时可能造成主线程间断性暂停服务
  2. 如果数据比拟重要,将某个 Slave 节点开启 AOF 数据备份,策略设置为每秒一次
  3. 为了主从复制速度和连贯的稳定性,Master 和 Slave 最好在同一个局域网中
  4. 尽量避免在运行压力很大的主库上减少从库
  5. 主从复制不要用图状构造,用单向链表构造更为稳固,Mater->Slave1->Slave2->Slave3... 这样的构造不便解决单点故障问题,实现 Slave 对 Master 的替换,如果 Master 解体,能够立刻启用 Slave1 替换 Mater,而其余依赖关系则放弃不变。

形式 1:先更新数据库,再更新缓存

这种是惯例的做法,然而如果更新缓存失败,将会导致缓存是旧数据,数据库是新数据

形式 2:先删除缓存,再写入数据库

这种形式可能解决形式 1 的问题,然而仅限于低并发的场景,不然如果有新的申请在删完缓存之后,写数据库之前进来,那么缓存就会马上更新数据库更新之前数据,造成数据不统一的问题

形式 3:延时双删策略

这种形式是先删除缓存,而后更新数据库,最初提早个几百毫秒再删除缓存

形式 4:间接操作缓存,定期写入数据库

尽管 Redis 的 Transactions 提供的并不是严格的 ACID 的事务(如一串用 EXEC 提交执行的命令,如果在执行中服务器宕机,那么会有一部分命令执行一部分命令未执行),但这些 Transactions 还是提供了根本的命令打包执行的性能(在服务器不出问题的状况下,能够保障一连串的命令是程序在一起执行的。

Redis 事务的实质就是四个原语:

  1. multi:用于开启一个事务,它总是返回 OK,当 multi 执行之后,客户端能够持续向服务器发送任意多条命令,这些命令不会被立刻执行,而是放到一个队列中,当 exec 命令被调用的时候,所有队列 d 命令才会执行
  2. exec:执行所有事务队列内的命令,返回事务内所有命令的返回值,当操作被打断的时候,返回空值 nil
  3. watch:是一个乐观锁。能够为 redis 事务提供 CAS 操作,能够监控一个或多个键。一旦其中有一个键被批改(删除),之后的事务就不会执行,监控始终继续到 exec 命令执行之后
  4. discard:调用 discard,客户端能够清空事务队列中的命令,并放弃执行事务

事务反对一次执行多个命令,一个事务中的所有命令都会被序列化。在事务执行的过程中,会依照程序串行化执行队列中的命令,其余客户端提交的命令申请不会插入到事务执行命令队列中。Redis 不反对回滚事务,在事务失败的时候不会回滚,而是继续执行余下的命令。

形式 1:Cluster 3.0

这是 Redis 自带的集群性能,它采纳的分布式算法是哈希槽,而不是一致性 Hash。反对主从构造,能够扩大多个从服务器,当主节点挂了能够很快切换到一个从节点作主节点,而后其余从节点都会切换读取最新的主节点。

形式 2:Twemproxy

Twitter 开源的一个轻量级后端代理。能够治理 Redis 或 Memcache 集群。绝对于 Redis 集群来说,易于治理。它的应用办法和 Redis 集群没有任何区别,只须要设置多个 Redis 实例后,在本须要连贯 Redis 的中央改为连贯 Twemproxy,它就会以一个代理的身份接管申请并应用一致性 Hash 算法,将申请连贯到具体的 Redis 节点上,将后果再返回 Twemproxy。对于客户端来说,Twemproxy 相当于是缓存数据库的总入口,它不须要晓得后端如何部署的。Twemproxy 会检测与每个节点的连贯是否失常,如果存在异样节点就会将其剔除,等一段时间后,Twemproxy 还会再次尝试连贯被剔除的节点。

形式 3:Codis

它是一个 Redis 分布式的解决办法,对于利用应用 Codis Proxy 的连贯和应用 Redis 的服务没有显著区别,利用可能像应用单机 Redis 一样,让 Codis 底层解决申请转发,实现不停机实现数据迁徙等工作。

什么是脑裂问题

脑裂问题通常是因为网络问题导致的。让 master、slave 和 sentinel 三类节点处于不同的网络分区。此时哨兵无奈感知到 master 的存在,会将 slave 晋升为 master 节点。此时就会存在两个 master,就像大脑决裂,那么原来的客户端往持续往旧的 master 写入数据,而新的 master 就会失落这些数据

如何解决

通过配置文件批改两个参数

min-slaves-to-write 3  # 示意连贯到 master 起码 slave 的数量
min-slaves-max-lag 10  # 示意 slave 连贯到 master 最大的延迟时间
-------------------- 新版本写法 -----------------
min-replicas-to-write 3
min-replicas-max-lag  10

配置这两个参数之后,如果产生集群脑裂,原先的 master 节点接管到写入申请就会回绝,就会缩小数据同步之后的数据失落

个别应用 List 构造作为队列。Rpush 生产音讯,Lpop 生产音讯。当 Lpop 没有生产的时候,须要适当 sleep 一会再重试。然而反复 sleep 会消耗性能,所以咱们能够利用 list 的 blpop 指令,在还没有音讯到来时,它会阻塞直到音讯到来。

咱们也能够应用 pub/sub 主题订阅者模式,实现 1:N 的生产队列,然而在消费者下线的时候,生产的音讯会失落

能够应用 zset 构造,能够拿工夫戳作为 score,音讯的内容作为 key,通过调用 zadd 来生产音讯,消费者应用 zrangebyscore 指令轮询获取 N 秒之前的数据进行解决

Redis Cluster 提供了主动将数据扩散到各个不同节点的能力,但采纳的策略并不是一致性 Hash,而是哈希槽。Redis 集群将整个 Key 的数值域分成 16384 个哈希槽,每个 Key 通过 CRC16 测验后对 16384 驱魔来决定搁置到那个槽中,集群的每个节点都负责其中一部分的哈希槽。

1、数据缓存

经典的场景,当初简直是所有中大型网站都在用的晋升伎俩,正当地利用缓存可能晋升网站访问速度

2、排行榜

能够借助 Redis 提供的有序汇合(sorted set)能力实现排行榜的性能

3、计数器

能够借助 Redis 提供的 incr 命令来实现计数器性能,因为是单线程的原子操作,保障了统计不会出错,而且速度快

4、分布式 session 共享

集群模式下,能够基于 Redis 实现 session 共享

5、分布式锁

在分布式架构中,为了保障并发拜访时操作的原子性,能够利用 Redis 来实现分布式锁的性能

6、最新列表

能够借助 Redis 列表构造,LPUSHLPOPLTRIM等命令来实现内容的查问

7、位操作

能够借助 Redis 中 setbitgetbitbitcount 等命令来实现数量上千万甚至上亿的场景下,实现用户活跃度统计

8、音讯队列

Redis 提供了公布(Publish)与订阅(Subscribe)以及阻塞队列能力,可能实现一个简略的音讯队列零碎

形式 1:Set 构造

以日期为 key,以用户 ID(对应数据库的 Primary Id)组成的汇合为 value

  • 查问某个用户的签到状态 sismember key member
  • 插入签到状态 sadd key member
  • 统计某天用户的签到人数 scard key
2、bitMap 构造

Key 的格局为u:sign:uid:yyyyMM,Value 则采纳长度为 4 个字节(32 位)的位图(最大月份只有 31 天)。位图的每一位代表一天的签到,1 示意已签,0 示意未签。

# 用户 2 月 17 号签到
SETBIT u:sign:1000:201902 16 1 # 偏移量是从 0 开始,所以要把 17 减 1

# 查看 2 月 17 号是否签到
GETBIT u:sign:1000:201902 16 # 偏移量是从 0 开始,所以要把 17 减 1

# 统计 2 月份的签到次数
BITCOUNT u:sign:1000:201902

# 获取 2 月份前 28 天的签到数据
BITFIELD u:sign:1000:201902 get u28 0

# 获取 2 月份首次签到的日期
BITPOS u:sign:1000:201902 1 # 返回的首次签到的偏移量,加上 1 即为当月的某一天

两者比照

  1. 应用 set 的形式所占用的内存只与数量相干,和存储哪些 ID 无关
  2. 应用 bitmap 的形式所占用的内存与数量没有相对的关系,而是与最高位无关,比方假如 ID 为 500 W 的用户签到了,那么从 1~4999999 用户不论是否签到,所占用的内存都是 500 w 个 bit,这边是最坏的状况
  3. 应用 bitmap 最大能够存储 2^32- 1 也就是 512M 数据
  4. 应用 bitmap 只实用存储只有两个状态的数据,比方用户签到,资源(视频、文章、商品)的已读或未读状态

Redis 中 ZSet 是抉择应用 跳表 而不是红黑树

什么是跳表

  • 跳表是一个随机化的数据结构,本质上就是一种能够进行二分查找的有序链表。
  • 跳表在原有的有序链表上减少了多级索引,通过索引来实现疾速查找
  • 跳表不仅能进步搜寻性能,同时也能够进步插入和删除操作的性能

总结:

  1. 跳表是能够实现二分查找的有序链表
  2. 每个元素插入时随机生成它的 level
  3. 最底层蕴含所有的元素
  4. 如果一个元素呈现在 level(x),那么它必定呈现在 x 以下的 level 中
  5. 每个索引节点蕴含两个指针,一个向下,一个向右
  6. 跳表查问、插入、删除的工夫复杂度为 O(log n),与均衡二叉树靠近

为什么不抉择红黑树来实现

首先来剖析下 Redis 的有序汇合反对的操作:

  1. 插入元素
  2. 删除元素
  3. 查找元素
  4. 有序输入所有元素
  5. 查找区间内的所有元素

其中前 4 项红黑树都能够实现,且工夫复杂度与跳表统一,然而最初一个红黑树的效率就没有跳表高了。在跳表中,要查找区间的元素,只有定位到两个区间端点在最低层级的地位,而后按程序遍历元素就能够了,十分高效。


好了,以上便是本篇的所有内容,如果感觉对你有帮忙的小伙伴无妨点个关注做个伴,便是对小菜最大的反对。不要空谈,不要贪懒,和小菜一起做个 吹着牛 X 做架构 的程序猿吧~ 咱们下文再见!

明天的你多致力一点,今天的你就能少说一句求人的话!

我是小菜,一个和你一起变强的男人。 💋

微信公众号已开启,菜农曰,没关注的同学们记得关注哦!

退出移动版