Treasure / redis

Created Thu, 24 Nov 2022 00:00:00 +0000
3371 Words

Redis

Remote Dictionary Server,采用 ANSI C 编写的 K-V数据库

Redis命令

Redis下载

类型

  • string 最大存储值为256mb,底层由SDS(simple dynamic string)实现,优势是访问长度仅需O(1)

  • hash

  • list 存储有序字符串,最大2^32-1个元素

  • set

同list,但不允许重复

  • sorted set 已排序的都字符串集合,但不允许重复

– 其它

  1. GEO 地理位置
  2. HyperLogLog 基数统计
  3. Bitsmap bit数组,类似boolean filter

redis设计架构

  1. 单线程业务,多线程存储,redis6.0引入多线程也仅仅是为了提高解析命令的速度

  2. 虚拟内存

虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。

击穿,穿透,雪崩

击穿

某个key在过期点的时候,突然出现大量请求查找这个key

穿透

访问一个不存在的key的时候

雪崩

指缓存中数据大批量到过期时间,访问落到db上,造成db压力过大

持久化机制

RDB

RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式。执行完操作后,在指定目录下会生成一个dump.rdb文件,Redis 重启的时候,通过加载dump.rdb文件来恢复数据

分为手动触发和自动触发

优点 适合大规模的数据恢复场景,如备份,全量复制等

缺点 没办法做到实时持久化/秒级持久化。

AOF

采用日志的形式来记录每个写操作,追加到文件中,重启时再重新执行AOF文件中的命令来恢复数据。它主要解决数据持久化的实时性问题

优点 数据一致性和完整性更高 缺点 内容越多,文件越大,恢复变慢,它需要将所有命令执行一遍

高可用

主从

类似mysql主从,master负责写,slave负责读

哨兵

监视其他节点的状态

集群

Gossip,HashSlot 16384

View

分布式锁

setnx

setnx nx [expired]

优点:实现简单

缺点:若在分布式中未同步则会造成多个client获取到锁

redisson

优点:可用性高

缺点:需要自己循环获取锁,性能消耗高,同setnx的缺点

redlock

优点:解决了单点问题

缺点:维护成本高

持久化

AOF

  • AOF文件中实际存储的是 Redis 协议下的命令记录,把每一次写操作以追加的形式记录在其中以文件的形式刷到磁盘里.

  • fsync 策略

    1. fsync : 数据容易丢失

    2. 每秒 fsync : 默认策略,性能正常,由后台线程执行,最多丢失1秒的数据,但文件大小随着时间线性增长,若用来恢复数据会非常缓慢。

    3. 每次写 fsync : 牺牲大部分性能,文件也大,但基本不会丢失数据

RDB

  • 一种快照机制,每个一段时间会对内存数据进行一次快照,保存在 rdb 文件中

  • SAVEBGSAVE 命令分别是同步保存和 fork 子进程保存

  • RDB 文件非常紧凑,它保存了 Redis 某个时间点上的数据集。RDB 恢复大数据集时速度要比 AOF 快。但是 RDB 不适合那些对时效性要求很高的业务,因为它只保存了快照,在进行恢复时会导致一些时间内的数据丢失。

  • 如果数据量很大的话 rdb 它要保存一个完整的数据集 是一个大的工作 如果时间间隔设置的太短,那么严重影响redis的性能 但是按照常规设置的话 如5分钟一次 那么如果宕机或者重启 就会基于上次做rdb的时间,而丢失分钟级的数据

管道技术

  • Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。

  • 管道技术最显著的优势是提高了 redis 服务的性能。

事务

  • 批量操作在发送 EXEC 命令前被放入队列缓存。

  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。
+命令 描述
DISCARD 取消事务,放弃执行事务块内的所有命令。
EXEC 执行所有事务块内的命令。
MULTI 标记一个事务块的开始。
UNWATCH 取消 WATCH 命令对所有 key 的监视。

WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

集群

概念

  1. Redis集群中的节点都需要打开两个TCP连接。一个连接用于正常的给Client提供服务,比如6379,还有一个额外的端口(通过在这个端口号上加10000)作为数据端口,比如16379。第二个端口用于集群总线,这是一个用二进制协议的点对点通信信道。这个集群总线(Cluster bus)用于节点的失败侦测、配置更新、故障转移授权,等等。客户端从来都不应该尝试和这些集群总线端口通信,它们只应该和正常的Redis命令端口进行通信。注意,确保在你的防火墙中开放着两个端口,否则,Redis集群节点之间将无法通信。

  2. 所有 Redis 节点彼此相连(内部 PING-PONG)机制

  3. 节点 Fail 至少通过集群半数以上的节点检测失效才生效

  4. 客户端只需要连接其中一个节点即可

  5. Redis-Cluster 负责把物理节点映射到 [0-16383]slot 上, cluster 负责维护 node <-> slot <-> value

Redis 集群分片

  • Redis 集群不同一致性哈希,它用一种不同的分片形式,在这种形式中,每个key都是一个概念性(hash slot)的一部分。 Redis集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

  • 允许添加和删除集群节点。如增加一个新的节点D,那么需要从A、B、C节点上删除一些hash slot给到D。同样地,如果从集群中删除节点A,那么会将A上面的hash slot 移动到B和C,当节点A上是空的时候就可以将其从集群中完全删除。

Redis 集群主从模式

  • 投票过程需要整个集群的 Master 节点参与,当其中存在与集群半数以上的 Master 节点通讯失败,则剔除此 Master

  • Master-Slave 模式,当集群中有 Master 节点失败的话,则其 Slave 节点将有一个提升为新的 Master 节点。

  • 如果集群任意 Master 挂掉,若其没有 Slave .集群进入 fail 状态,也可以理解成集群的slot映射 [0-16383] 不完整时进入fail状态.

  • 如果集群超过半数以上 master 挂掉,无论是否有 slave ,集群进入 fail 状态.

#主从模式配置

bind 0.0.0.0

port 6379

logfile "6379.log"

dbfilename "dump-6379.rdb"

daemonize yes

rdbcompression yes

#slaveof 192.168.81.135 6379 这个配置应用的是slave节点,指定的是 master 节点

# slave-read-only yes 默认 slave 节点只提供读取,可以通过设置

Reids 集群一致性

  • Redis 集群不能保证强一致性

集群搭建

  • 修改配置文件
    # redis.conf文件
    
    #客户端端口
    port 7000
    
    #pid文件
    pidfile /var/run/redis_6379.pid
    
    # 启用集群
    cluster-enable yes
    
    # 由redis集群自动生成
    cluster-config-file nodes.conf
    
    # 集群ping-pong超时时间 ms
    cluster-node-timeout 5000
    
    #是否启用aof
    appendonly on
    

名词解释

这几种情况都是从缓存没有获取到数据,大量的并发请求到了数据源,给数据源造成很大压力,从而可能引发问题

  • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

  • 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

  • 缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

优化

  • 使用短的key

  • 避免使用 keys *:这个命令是阻塞的,使用 SCAN 代替

  • 设置 key 的过期时间

线上解决方案

  • 缓存穿透 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。 对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。

  • 缓存击穿 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。

  • 缓存雪崩 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期