Redis的问题汇总

一.Redis6.0之前的版本真的是单线程吗?

Redis在处理客户端的请求时,包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。但如果严格来讲从Redis4.0之后并不是单线程,除了主线程外,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大 key 的删除等等。

二.为什么redis6.0之前不使用多线程

首先分析下多线程自身优点和缺点

优点:

        当一个线程处理的任务是I/O密集型的,而I/O传输数据很慢,因此线程总是处于等待数据传输,从而造成CPU资源的浪费。这时我们可以开辟多个线程,当一个线程在等待I/O传输时,调度其他线程进行任务处理。比如java中存在等待io响应,下游交互,操作磁盘,操作文件,此时io阻塞等待时间,可以切换其他线程操作,提高效率。

缺点:

  1. cpu上下文切换需要时间,cpu在多线程之间来回切换,来不及响应带来性能损耗。
  2. 引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度。

从优点看:redis纯内存操作,避免大量访问数据库,减少直接读取磁盘数据。redis将数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制,所以速度快。再加上不会阻塞等待,redis中采用io多路复用机制。这些原因导致多线程的优点不能充分发挥。再加上多线程自带的缺点,会给redis带来性能损耗和系统的不稳定性。

三.redis是如何实现单线程

Redis基于Reactor模式。

[Reactor模式]  响应式编程

开发了网络事件处理器,这个处理器叫做文件事件处理器 file event handler。这个文件事件处理器,它是单线程的,所以 Redis 才叫做单线程的模型,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件类型来选择对应的事件处理器来处理这个事件。

[IO多路复用]  单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流 。

可以实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了 Redis 内部的线程模型的简单性。文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器以及事件处理器(命令请求处理器、命令回复处理器、连接应答处理器等)

多个 Socket 可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个 Socket,会将 Socket 放入一个队列中排队,每次从队列中取出一个 Socket 给事件分派器,事件分派器把 Socket 给对应的事件处理器。然后一个 Socket 的事件处理完之后,IO多路复用程序才会将队列中的下一个 Socket 给事件分派器。文件事件分派器会根据每个 Socket 当前产生的事件,来选择对应的事件处理器来处理。

实现具体流程:

  1. Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。
  2. 若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。
  3. 当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事件,触发命令请求处理器。处理器读取客户端的命令内容,然后传给相关程序执行。
  4. 当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。
  5. 命令回复处理器全部写完到 socket后,就会删除该socket的AE_WRITABLE事件和命令回复处理器的映射。

四、Redis的持久化机制

RDB和AOF

1.RDB:Redis DataBase

在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

自动触发:

save m n : 在m秒内,如果有n个键发生改变,则自动触发持久化,通过bgsave执行,如果设置多个,只要满足其一就会触发,配置文件有默认配置(可以删除)
save 60 10   只要满足一个就会触发 60 秒 9个未触发
save 600 100   600秒 100个也可以触发
flushall : 用于清空redis中所有的数据库,flushdb清空当前redis所在库数据(默认是零号数据库),会清空RDB文件,同时也会生成dump.rdb、内容为空 (慎用,一般是测试数据库在0,生产数据库在其他库,则执行flushdb )
主从同步:全量同步会自动触发bgsave命令,生成一个rdb文件给从节点,增量同步就不会

优点:

  1. 整个Redis数据库将只包含一个文件 dump.rdb,方便持久化。只有一个文件,方便
  2. 容灾性好,方便备份。
  3. 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
  4. 相对于数据集大时,比 AOF 的启动效率更高。

缺点:

  1. 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
  2. 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。会占用cpu
  3. fork子进程也是会造成阻塞,如果为了保证数据安全,频率是1s,甚至更低的时候,不停地fork会导致主进程的阻塞

2.AOF

AOF:Append Only File 以日志的形式记录服务器处理的每一个写删除操作,查询操作不会做记录,以文本形式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘

(即将写操作记录在log日志里)

AOF与RDB最大的区别就是aof存的是操作命令的log文件,而rdb存的是数据集,rdb存储的是二进制文件

优点:

  1. 数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。
  2. 通过 append 模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redis-check-aof 工具解决数据一致性问题。
  3. AOF 机制的 rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的

缺点:

  1. AOF 文件比 RDB 文件大,且恢复速度慢。
  2. 数据集大的时候,比 rdb 启动效率低。
  3. 运行效率没有RDB高

总结:

  • AOF文件比RDB更新频率高,优先使用AOF还原数据。
  • AOF比RDB更安全也更大
  • RDB性能比AOF好
  • 如果两个都配了优先加载AOF

五、redis主从同步机制

目的:提高redis吞吐量和高可用

方法:设置主节点和从节点,从节点一般能读取,主节点可以写操作,当主节点宕机时,从节点可以代替主节点作为备份

同步的机制(五步):

总结:

(从节点保存主节点信息-发起和主节点连接-主节点返回信息两边通信-全量同步-增量同步)

1.从节点执行slave of master ip port ,保存主节点信息

2.从节点的定时任务发现主节点信息,建立与主节点的socke连接

3.从节点发送信号,主节点返回,两边能相互通信

4.连接建立后,主节点将所有的信息发送给从节点(数据同步)

5.主节点把当前的数据同步给从节点后,便完成了复制过程。接下来主节点会持续的将写命令发送给从节点,保证主从数据的一致性

三个参数

1.runid :每个redis节点启动都会生成唯一一个uuid,每次redis重启之后,runid都会 进行一次更新

2.offset:复制偏移量,主从节点各自维护自己的复制偏移量。

当主节点有写入命令时,offset=offset+命令的长度,从节点收到主节点发送的命令时,也会增加自己的offset,并把自己的offset发送给主节点。主节点同时保存自己的offset和从节点的offset,通过对比offset,来判断主从节点数据是否一致

3.repl_backlog_size:保存在主节点上的一个先进先出队列,默认大小时1M(用于增量同步)

全量复制:

1.从节点发送psync命令,psync runid offset (由于是第一次 ,runid=?(未知),offset为-1)

2.主节点读取到runid和offset值之后,判断出是第一次,返回FULLRESYNC runid offset ,runid是主节点的runid,offset是主节点目前的offset。从节点保存runid和offset信息,作为自己的配置

3.主节点启动bgsave命令fork子进程进行RDB持久化

4.主节点将RDB文件发送给从节点,到从节点加载数据完成前,写命令写入缓冲区(即队列)

5.从节点清理本地数据并加载RDB,开启AOF则重写AOF

部分复制

1.复制偏移量:

psync runid offset

2.复制积压缓冲区:

  • 从节点保存的runid和主节点的runid相同,说明之前同步过数据,主节点会继续尝试部分复制(当主从节点的offset的差距过大,超过缓冲区长度时,将无法执行部分复制,只能执行全量复制)

  • 从节点保存的runid和主节点的runid不同,说明之前没同步过数据,全量复制

过程原理:

六.Redis的过期键的删除策略

Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当

Redis中缓存的key过期了,Redis如何处理。

惰性过期

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化

地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而

不会被清除,占用大量内存。

定期过期

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其

中已过期的key。该策略是一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,

可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

(expires字典,过期字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的

指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。)

如何实现

定期删除策略由 activeExpireCucle 函数实现,被调用时,它在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的 expires 字典中随机检查一部分键的过期时间,并删除其中的过期键。

  • 函数每次运行时,都是从一定数量的数据库键中随机取一定数量的键进行检查,并删除其中的过期键。

  • 有一个全局变量 current_db 会记录当前 activeExpireCycle 函数检查的进度,并且下一次 函数执行时,接着上一次的进度进行处理。如,当前 activeExpireCycle 函数执行到了 10, 将 current_db = 10;然后下一次函数执行时,从 current_db 取到 10 继续执行。

  • 当所有的数据库键都被检查完时, current_db = 0。

Redis中同时使用了惰性过期和定期过期两种过期策略:这也既可以合理的控制 CPU 使用 还可以 减少内存的浪费。

 redis设置key的过期时间:1、EXPIRE。2 SETEX 实现原理:(即redis过期删除机制) 1、定期删除:每隔一段时间,执行一次删除过期key的操作。 2、懒汉式删除:当使用get、getset等指令去获取数据时,判断key是否过期。过期后,就先把key删除,再执行后面的操作。 Redis是将两种方式结合来使用。 懒汉式删除 定期删除:平衡执行频率和执行时长。

定期删除时会遍历每个database(默认16个),检查当前库中指定个数的key(默认是20个)。随机抽查这些key,如果有过期的,就删除。 程序中有一个全局变量记录到秒到了哪个数据库。

其中AOF、RDB 和复制功能对过期键的处理

生成 RDB 文件

在执行 SAVE 命令或 BGSAVE 命令创建一个新的 RDB 文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新的 RDB 文件中。 如:redis中包含 r1、r2、r3 三个键,并且 r1 已经过期,那么程序只会讲 r2 和 r3 保存到 RDB 文件中。 因此,过期键不会对新的 RDB 文件造成影响。

载入 RDB 文件

在启动 redis 服务器时,如果服务器开启了 RDB 功能,那么服务器将对 RDB 文件进行载入;

  • 如果服务器以主服务器模式运行,那么在载入 RDB 文件时,过期的键会被过滤掉,不会被载入到redis数据库中。

  • 如果以从服务器模式运行,那么无论键是否过期都会被载入到数据库中。但因为主从服务器在进行数据同步时,从服务器就会被清空,所以,一般来说,过期键对从服务器也不会造成影响。

AOF 文件写入

当服务器开启 AOF 的运行模式时,如果某个键过期了,但没有被惰性或定期删除,那么 AOF 不会理会。如果被惰性或定期删除了, AOF 会在文件末尾追加一条 DEL 命令,来显示地记录该键已被删除。

AOF 重写

当 AOF 重写时,过期的键不会被载入到 redis 数据库中。

复制

当服务器在 复制 模式下时,从服务器的过期键删除动作都是由主服务器来进行的。

  • 主服务器在删除一个过期键之后,会显示地向所有从服务器发送一个 DEL 命令,告知从服务器删除这个过期键。

  • 从服务器在执行客户端发送的读命令时,即使碰到过期的键也不会删除,而是继续的正常操作。

  • 从服务器只有在接到主服务器发来的 DEL 命令之后,才会删除过期键。

七.布隆过滤器原理,优缺点

位图:

int[10], 每个int类型的整数是4*8=32个bit,则in[10]- 共有320 bit,每个bit非0即01, 初始化时都是0

添加数据时,将数据进行hash得到hash值,对应到bit位,将该bit改为1, hash函数可以定义多个,则1个数据添加会将多个(hash函数个数) bit改为1,多个hash函数的目的是减少hash碰撞的概率

查询数据: hash函数计算得到hash值,对应到bit中, 如果有一个为0, 则说明数据不在bit中,如果都为1.则该数据可能在Ebit中

哈希函数

哈希函数的概念是:将任意大小的数据转换成特定大小的数据的函数,转换后的数据称为哈希值或哈希编码。下面是一幅示意图:

img

可以明显的看到,原始数据经过哈希函数的映射后称为了一个个的哈希编码,数据得到压缩。哈希函数是实现哈希表和布隆过滤器的基础。

布隆过滤器有二个基本指令,bf.add添加元素,bf.exists查询元素是否存在,它的用法和set集合的sadd和sismember差不多。注意bf.add只能一次添加一个元素,如果想要一次添加多个,就需要用到bf.madd指令。同样如果需要一次查询多个元素是否存在,就需要用到bf.mexists指令。

优点:

●占用内存小

●增加和查询元素的时间复杂度为: O(K), (K为哈希函数的个数,一 般比较小,与数据量大小无关

●哈希函数相互之间没有关系,方便硬件并行运算

●布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

●数据量很大时,布隆过滤器可以表示全集

●使用同一组散列函数的布隆过滤器可以进行交.井.差运算

缺点:

●随着数据的增加,误判率会增加;

●还有无法判断数据一定存在;

●另外还有一个重要缺点,无法删除数据。

八.常见的缓存淘汰算法

不同于过期处理,这是由于缓存空间不够,对一些数据进行淘汰

FIFo (First in First out,先进先出),根据缓存被存储的时间,离当前最远的数据优先被淘汰; (公平机制)

LRU(Least Recently Used,最近最少使用),根据最近被使用的时间,离当前最远的数据优先被淘汰; (按时间)

LFU(Least Frequently Used,最不经常使用),在一段时间内,缓存数据被使用次数最少的会被淘汰。(按次数)

九.简述redis数据结构

基础的五个数据类型:

string:字符串

List:列表

Hash:哈希表

set:无序集合

Sorted Set:有序集合 (Sorted Set:实现,通过zset的score进行排序就可以得到坐标附近的其它元素,通过将score还原成坐标值就可以得到元素的原始坐标)

其他:

bitmap:布隆过滤器

GeoHash:坐标,借助

HyperLogLog:统计不重复数据,用于大数据基数统计

streams:内存版的kafka

十.简述Redis事务实现

事务的四大原则ACID

原子性 一致性

redis不能回滚,命令是一起执行,一起不执行(原子操作)

持久性

rdb和aof

隔离性

单线程机制

1、事务开始

MULTI命令的执行,标识着一个事务的开始。MULTI命令会将客户端状态的 flags 属性中打开

REDIS_MULTI 标识来完成的。

2、命令入队

当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客户端发送的命令为MULTIEXECWATCHDISCARD中的一个,立即执行这个命令,否则将命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复

  • 如果客户端发送的命令为 EXEC、DISCARD、WATCH、MULTI 四个命令的其中一个,那么服务器立即执行这个命令。

  • 如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。

    首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的 flags 属性关闭 REDIS_MULTI 标识,并且返回错误信息给客户端。如果正确,将这个命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复事务队列是按照FIFO的方式保存入队的命令

3、事务执行

客户端发送 EXEC 命令,服务器执行 EXEC 命令逻辑。

如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执行。

否则客户端处于事务状态(flags 有 REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然

后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;

redis 不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。Redis 事务不支持检查那些程序员自己逻辑错误。例如对 String 类型的数据库键执行对 HashMap 类型的操作!

WATCH 命令是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

MULTI命令用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。

EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。

通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。

UNWATCH命令可以取消watch对所有key的监控。

十一.redis集群方案

哨兵模式:

sentinel,哨兵是 redis 集群中非常重要的一个组件,主要有以下功能:

集群监控:负责监控 redis master 和 slave 进程是否正常工作。

消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。

故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。

配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布

式选举

即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的

哨兵通常需要 3 个实例,来保证自己的健壮性。

哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。

对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演

练。

Redis Cluster是一种服务端Sharding技术,3.0版本开始正式提供。采用slot(槽)的概念,一共分成

16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行

方案说明

通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了

16384 个槽位每份数据分片会存储在多个互为主从的多节点上

数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)

同一分片多个节点间的数据不保持强一致性

读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点

扩容时需要需要把旧节点的数据迁移一部分到新节点

在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端

口号,比如 16379。

16379 端口号是用来进行节点间通信的,也就是 cluster bus 的通信,用来进行故障检测、配置更新、

故障转移授权。cluster bus 用了另外一种二进制的协议,gossip 协议,用于节点间进行高效的数据交

换,占用更少的网络带宽和处理时间。

优点

无中心架构,支持动态扩容,对业务透明

具备Sentinel的监控和自动Failover(故障转移)能力

客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

高性能,客户端直连redis服务,免去了proxy代理的损耗

缺点

运维也很复杂,数据迁移需要人工干预

只能使用0号数据库

不支持批量操作(pipeline管道操作)

分布式逻辑和存储模块耦合等

Redis Sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用

哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上。Java

redis客户端驱动jedis,支持Redis Sharding功能,即ShardedJedis以及结合缓存池的

ShardedJedisPool

优点

优势在于非常简单,服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,

非常容易线性扩展,系统的灵活性很强

缺点

由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。

客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新

调整。连接不能共享,当应用规模增大时,资源浪费制约优化

十二.如何保证数据库缓存的一致性

由于缓存和数据库是分开的,无法做到原子性的同时进行数据修改,可能出现缓存更新失败,或者数据库更新失败的情况,这时候会出现数据不一致,影响前端业务 先更新数据库,再更新缓存。缓存可能更新失败,读到老数据 先删缓存,再更新数据库。并发时,读操作可能还是会将旧数据读回缓存先更新数据库,再删缓存。也存在缓存删除失败的可能。

方案一:最经典的缓存+数据库读写的模式,cache Aside Pattern. 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。更新的时候,先更新数据库,然后再删除缓存。为什么是删除而不是更新? 删除更加轻量,延迟加载的一种实现,更新可能涉及多个表、比较耗时

方案二:延时双删:先删除缓存,再更新数据库,休眠1s、再次删除缓存。写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据,并发还是可能读到旧值覆盖缓存

方案三:终极方案: 将访问操作串行化

1.先删缓存,将更新数据库的操作放进有序队列中

2.从缓存查不到的查询操作,都进入有序队列会面临的问题: 1.读请求积压,大量超时,导致数据库的压力:限流、熔断

2.如何避免大量请求积压:将队列水平拆分,提高并行度。

3.保证相同请求路由正确。

十三.缓存雪崩、缓存穿透、缓存击穿

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内

承受大量请求而崩掉。

解决方案:

1.缓存数据的过期时间设置随机(比如随机添加1-5ms),防止同一时间大量数据过期现象发生。

给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓

存。

2.缓存预热

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案:

1.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有

效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户

反复用同一个id暴力攻击。

2.采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案

1.设置热点数据永远不过期。

2.加锁,setnx,解决并发问题

十四、redis使用场景

1、热点数据的缓存

由于redis访问速度块、支持的数据类型比较丰富,所以redis很适合用来存储热点数据,另外结合expire,我们可以设置过期时间然后再进行缓存更新操作,这个功能最为常见,我们几乎所有的项目都有所运用。

2、限时业务的运用

redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。

3、计数器相关问题

redis由于incrby命令可以实现原子性的递增,所以可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。

4、排行榜相关问题

关系型数据库在排行榜方面查询速度普遍偏慢,所以可以借助redis的SortedSet进行热点数据的排序。

在奶茶活动中,我们需要展示各个部门的点赞排行榜, 所以我针对每个部门做了一个SortedSet,然后以用户的openid作为上面的username,以用户的点赞数作为上面的score, 然后针对每个用户做一个hash,通过zrangebyscore就可以按照点赞数获取排行榜,然后再根据username获取用户的hash信息,这个当时在实际运用中性能体验也蛮不错的。

5、分布式锁

这个主要利用redis的setnx命令进行,setnx:"set if not exists"就是如果不存在则成功设置缓存同时返回1,否则返回0 ,这个特性在俞你奔远方的后台中有所运用,因为我们服务器是集群的,定时任务可能在两台机器上都会运行,所以在定时任务中首先 通过setnx设置一个lock,如果成功设置则执行,如果没有成功设置,则表明该定时任务已执行。 当然结合具体业务,我们可以给这个lock加一个过期时间,比如说30分钟执行一次的定时任务,那么这个过期时间设置为小于30分钟的一个时间 就可以,这个与定时任务的周期以及定时任务执行消耗时间相关。

当然我们可以将这个特性运用于其他需要分布式锁的场景中,结合过期时间主要是防止死锁的出现。

6、延时操作

这个目前我做过相关测试,但是还没有运用到我们的实际项目中,下面我举个该特性的应用场景。 比如在订单生产后我们占用了库存,10分钟后去检验用户是够真正购买,如果没有购买将该单据设置无效,同时还原库存。 由于redis自2.8.0之后版本提供Keyspace Notifications功能,允许客户订阅Pub/Sub频道,以便以某种方式接收影响Redis数据集的事件。 所以我们对于上面的需求就可以用以下解决方案,我们在订单生产时,设置一个key,同时设置10分钟后过期, 我们在后台实现一个监听器,监听key的实效,监听到key失效时将后续逻辑加上。 当然我们也可以利用rabbitmq、activemq等消息中间件的延迟队列服务实现该需求。

7、分页、模糊搜索

redis的set集合中提供了一个zrangebylex方法,语法如下:

ZRANGEBYLEX key min max [LIMIT offset count]

通过ZRANGEBYLEX zset - + LIMIT 0 10 可以进行分页数据查询,其中- +表示获取全部数据

zrangebylex key min max 这个就可以返回字典区间的数据,利用这个特性可以进行模糊查询功能,这个也是目前我在redis中发现的唯一一个支持对存储内容进行模糊查询的特性。

前几天我通过这个特性,对学校数据进行了模拟测试,学校数据60万左右,响应时间在700ms左右,比mysql的like查询稍微快一点,但是由于它可以避免大量的数据库io操作,所以总体还是比直接mysql查询更利于系统的性能保障。

8、点赞、好友等相互关系的存储

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。 又或者在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。

这个在奶茶活动中有运用,就是利用set存储用户之间的点赞关联的,另外在点赞前判断是否点赞过就利用了sismember方法,当时这个接口的响应时间控制在10毫秒内,十分高效。

9、队列

由于redis有list push和list pop这样的命令,所以能够很方便的执行队列操作。

十五.Redis的大key如何处理

1.什么是redis大key

string的大小大于10kb

hash,list,set,zset元素个数大于5000个

在实际业务中,大Key的判定仍然需要根据Redis的实际使用场景、业务场景来进行综合判断。通常都会以数据大小与成员数量来判定。

2.如何删除

直接删除大Key 

直接删除会因为redis是单线程执行,阻塞期间其他请求会超时。超时越来越多的时候,会造成redis连接耗尽,造成各种异常。

分批删除 

​​​​​​​分批删除会尽量降低阻塞时间

异步删除

使用unlink代替del删除,redis会将这个key放到异步线程中进行删除,不会阻塞主线程

定期清理失效数据

如果某个Key有业务不断以增量方式写入大量的数据,并且忽略了其时效性,这样会导致大量的失效数据堆积。可以通过定时任务的方式,对失效数据进行清理。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值