Redis 技术详解

Redis 详解

一、 NoSql介绍

NoSql (Not Only Sql),意为不仅仅是SQL,泛指非关系型数据库,NoSql 这门数据库早期就有人提出了,发展到2009年开始越发高涨。

NoSql 应该场景:

  • 商城网站对商品数据的频繁查询
  • 对热搜商品的排行统计
  • 订单超时问题
  • 音视频存储等

NOSQL 的四大分类

  • 1、键值对(key-value)存储数据库

    • 代表产品有:Redis, Tokyo Cabinet/Tyrant, SSDB, Oracle BDB
    • 这类数据库主要用到一个哈希表,这个表中有一个特定的 和 一个 指针 指向特定数据
    • 特点:key-value模型,对于IT系统来说简单,易部署。
    • 但是如果DBA 只对部分值进行查询或更新的时候,key-value 就显得效率低下了,因为他要查找遍历麻烦。
  • 2、列存储数据库

    • 相关产品:CassandraHBase
    • 这部分数据库通常是用来对分布式存储的 海量数据库
    • 特点:键仍然存在,他的特点是指向了多个列, 这些列是由 家族 来安排的.
  • 3、文档型数据库

    • MongoDBCouchDB
    • 文档型数据库,可以看成是键值数据库的升级版,允许之间嵌套键值。
    • 文档型数据库,比键值型数据库查询效率更高。
  • 4、图形数据库

    • 图形数据库同其他行列一级结构性SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上
    • 相关产品: Neo4JInfoGridInfinite Graph

NOSQL 应用场景

  • 数据模型比较简单
  • 需要灵活性更强的IT系统(系统要求设计灵活,性能要求高)
  • 对数据库性能要求高
  • 不需要高度一致性(因为NoSql 产品对于事务的支持都不是特别良好)

Redis 优势

  • 性能极高,Redis 速度是 110000次/s 速度是 81000次/s
  • 丰富的数据类型, Strings, List, Hashes, Sets, ZSets
  • 原子性 Redis所有操作都是原子性的,意思要么成功执行,要么失败完全不执行,单个操作是原子性的,多个操作也支持事务。通过MULTI 和 EXEC 指令包起来。
  • 丰富的特性, Redis 还支持 publish/subscibe,通知,key 过期等特性。

总结

  • Redis 是一个开源的,遵循BSD 基于内存数据存储,被用于作为数据库缓存、消息中间件等

  • Redis 数据内存中: 内存特点:读写块,但断电立即消失。

  • 问题: 为什么数据在内存中,但是Redis还是一个数据库呢?

    • 答:因为他可以将数据持久化到硬盘中。

总的来说:Redis 是一个内存性的数据库。

二、 Redis的数据类型

Redis总共有 5种 数据类型,分别是:

  • String
  • List , 无序列表
  • Set, 不重复的无序列表
  • ZSet, 不重复的有序列表
  • Hash, Key-value 键值对存储

1、String 类型内存模型

image-20200623132104399

2、List 列表,相当于 Java中的List 集合,特点:元素有序且可以重复

image-20200623161114380

  • List 在添加(put)或删除数据(pop)时,分左右

  • lpush,从左往右推数据,rpush,从右往左推数据。

  • 3、Set 数据类型

    • 特点: Set 集合,元素无序,不可重复

image-20200623161114380

  • 4、 ZSet 数据类型
    • 特点: 元素可排序,不可重复

image-20200623194903967

  • 5、 Hash 数据类型

    • 特点: 外面一层 大 key,里面 value 存储的是 一对key-value 数据

image-20200623200348408

三、Redis 启动细节

1. Redis启动服务细节

注意:直接使用 ./redis-server 方式启动使用的是 redis-server 这个 shell 脚本中的默认配置

3. Redis修改 默认端口号

	vim redis.conf 修改里面 port 7000 保存退出

4. Redis中库的概念

  • 库:Redis中 database用来存放一个基本单元,一个库可以存放 key-value 键值对,Redis中每一个库都有一个唯一名称。
    库的编号从 0 开始,默认库个数是 16 个,库的编号 0~15

  • 如何切换库?

      select dbid(库编号)
    

注意:库与库之间是相互隔离的,比如我在1库中写了一个和2库一样的 key - value值,清除1 库,不会将2库中数据清除掉。

5. Redis中清除库指令

  • flushDB 清除当前库中所有数据
  • flushAll 清除所有库中的数据

6. Redis中指令

九、 Redis中的持久化

Redis的持久化,过程为将内存中的数据持久化保存到磁盘中,它的持久化分为:

  • 快照持久化

  • AOF 持久化(Append Only File) 只追加日志文件

特点: 默认Redis是快照持久化的,需要我们手动配置是否AOF 持久化。

9.1 快照持久化

特点:Redis 操作写命令,写一条数据,然后定期有快照去保存,快照默认是保存的是以 .rdb 结尾的文件,如官方 dump.rdb,当出现宕机等,重启机器会根据快照重新将数据加载到内存中。

image-20200623204303074

快照生成方式

  • 客户端方式: BGSAVE 和 SAVE 指令来保存快照

  • 服务器配置自动触发:通过配置定期时间,来定期通过快照保存内存数据

BGSAVE 原理

  • 客户端可以使用BGSAVE 指令来创建一个快照,当Redis接收到该指令后,Redis会调用一个 fork 来创建一个子进程,然后子进程负责将数据写到磁盘中,而父进程负责继续处理请求命令。

名词解释: 当创建子进程时,底层操作系统会创建该进程的一个副本,在Unix系统中创建的子进程会进行优化,在刚开始的时候,父子进程会共享内存,直到父进程或子进程对内存进行了写之后,对呗写入的内存共享才会结束。

image-20200623205132460

好处:

  • 子进程在写快照的时候,不会阻塞主线程处理Redis读写数据。
  • 共享内存,主进程和子进程 共享内存,当没有数据写入Redis时,这时执行BGSAVE,子进程可以取最大共享内存去处理写快照操作。

SAVA 原理

说明:通过SAVE 命令保存快照,那么Redis在保存快照完成之前,不再响应任何客户端请求来的数据。

image-20200623205444101

坏处: 会阻塞Redis数据读写。 注意SAVE 指令并不常用,一般在关闭Redis保存才会用到这个指令。

SAVE 执行场景:

在Redis通过shutdown 命令接收关闭服务器请求时,会自动执行一个SAVE 命令,这时阻塞所有客户端读写请求,不再执行客户端发送的任何命令,并且在save命令执行完毕后关闭服务器。

9.2 AOF 持久化

Redis 操作写命令,每操作一条,会记录操作的日志文件,Redis会根据写的日志文件,如某一时刻Redis 宕机了,但是重启之后他会根据我写的日志文件,再去从硬盘恢复数据到内存。

注意: 每一次的写入记录,都会被记录,恢复数据到内存时,会挨个执行你的写命令,当然同一个 key 恢复到的数据肯定是最后一次写入记录。

image-20200623211330798

日志追加频率:

日志分为3种方式追加,always | everysec | no

1. always 【谨慎使用】

  • 说明: 每个Redis 每个写命令都要写数据到硬盘,严重降低Redis速度
  • 解释: 如果用户使用了always选项,那么每个redis写命令都会被写入硬盘,从而将发生系统崩溃时出现的数据丢失减到最少;遗憾的是,因为这种同步策略需要对硬盘进行 大量的写入操作,所以redis处理命令的速度会受到硬盘性能的限制;
  • 注意: 转盘式硬盘在这种频率下 200左右个命令/s ; 固态硬盘(SSD) 几 百万个命令/s ;
  • 警告: 使用SSD用户请谨慎使用always选项,这种模式不断写入少量数据的做法有可能会引发严重的 写入放大 问题,导致将固态硬盘的寿命从原来的几年降低为几个月。

总结: always 方式可以最大限度降低数据的丢失率,但是在大量写入操作执行时会影响Redis性能,且对硬盘伤害更大一些。

2. everysec 【推荐】

  • 说明:每秒执行一次同步显式的将多个写命令同步到磁盘, 推荐使用。

  • 解释: 为了兼顾数据安全和写入性能,用户可以考虑使用everysec选项,让redis每秒一次的频率对AOF文件进行同步;redis每秒同步一次AOF文件时性能和不使用任何持久化特性时的性能相差无几,而通过每秒同步一次AOF文件,redis可以保证,即使系统崩溃,用户最多丢失一秒 之内产生的数据。

总结: 对性能上友好,但是有可能会丢失1秒内的写入数据,适用于对数据严谨性不高的场景,如热搜排行榜等。

3. no 【不推荐】

  • 说明: 由操作系统决定何时同步
  • 解释: 由系统决定什么时候去同步AOF文件,
  • 引入的问题:
    • 这种方式不会对Redis性能带来影响,但是系统崩溃时,可能会丢失不定量数据。
    • 另外如果用户硬盘处理写入操作不够快的话, 当缓冲区被等待写入硬盘数据填满时, redis会处于 阻塞状态,并导致redis的处理命令请求的速度变慢。

9.3 AOF 代理的问题

AOF 问题: AOF 会导致持久化日志文件变得越来越大,而且重复执行同样命令,会重复记录日志操作到日志文件,如我执行100条相同的 set ,那么将会写100条相同的 set 日志到日志文件,其实99条是多余的,因为宕机重启后仅靠最后一条就可恢复这条key 的数据。

如何解决上述问题?

通过AOF 重写,用来在一定程度上减小 AOF 文件的体积,即文件压缩。

AOF 文件重写原理

注意: 重写 AOF 文件的操作,并没有读取旧的 aof 文件,而是将这个内存中的数据库内容用命令的方式重写了一个新的AOF 文件,替换原有的文件和快照有点类似。

也就是说,它并不是在原来的 aof 中去挑记录,而是重新保存了新的 aof 文件。

重写流程

    1. redis调用fork ,现在有父子两个进程 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
    1. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
    1. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
    1. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。

image-20200623214843123

四、 Redis 分布式缓存

1、缓存优化策略:

  • 对放入 Redis中的key 进行优化,key 要简洁一点,可以使用MD5 进行加密处理,

  • MD5特点:

      1. 一切文字经过md5 处理后,都会生成32位16进制字符串。
      1. 不同内容文件经过 md5 进行加密,加密结果一定不一致, 如aa.txt 和 bb.txt 中内容不一致,则加密后结果不一致。
      1. 相同内容,文件多次经过MD5 生成结果始终一致,如 aa和 bb 内容一样,加密一次,再加密一次,多次加密后,只要原内容一样,则最后加密也一样
  • 推荐: 在 Redis整合 mybatis 过程中,建议将 key 进行 md5 加密处理。

2、面试相关概念:

  • 1)什么是缓存穿透,缓存击穿

    • 定义: 客户端查询了一个数据库中没有的数据记录,导致缓存在这种情况下无法利用,称为缓存穿透,或者缓存击穿。

    • 如何解决?

      设置缓存为null: 如mybatis中是已经解决了这个问题,它通过如果差不到,则会向缓存写null 的方式来解决

      布隆过滤:可以通过尽可能校验Redis key 的生成方式,提前挡住请求中 key 的请求。

    • 问: 当我存储了一个 null 后,恰好这个key 又有数据添加,这时该如何?

      答: 这种情况一般我们在添加数据时,会对key 做 清空操作,保证添加后查出的数据都是最新的,如 mybatis 就已经实现了这个功能在添加一条key 数据之后,会对缓存做清空操作。

  • 2)什么是缓存雪崩

    • 在系统运行的某一时刻,突然系统中的缓存全部失效,恰好在这一刻涌来大量客户端请求,导致所有缓存模块无法利用,大量请求涌向数据库导致极端情况,数据库阻塞或者挂起。

    • 如何解决?

      方案一: 缓存永久存储(不推荐)

      方案二: 设置超时时间不同:大多数企业,业务系统非常大,模块多,所以针对不同的模块,放入缓存时,都会设置缓存的一个 TTL 超时时间,

五、Redis 架构

一、 主从复制架构

1、 主从复制

说明: 主从复制,仅仅用来解决数据的 冗余备份,从节点不负责外部请求处理, 从节点仅仅用来用同步数据

这种方式无法解决: master节点出现故障的 自动故障转移

注意: salve 从节点,仅用于同步主节点数据,默认数据是只读的,即无法向从节点写数据。

2、架构图:

image-20200627201722700

二 、 哨兵模式

1、说明:

哨兵(sentinel) 是Redis的一个 高可用 解决方案,由一个或多个 sentinel实例组成的 sentinel系统,可以 监视多个主服务器,以及主服务器下的所有 从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器下的某个从服务器提升为新的主服务器。

简单说来: 哨兵就是带有自动故障转移的主从架构

注意: 首先, 哨兵机制必须是一个 主从架构,即要哨兵监听,则必须有master-salve节点。

2、哨兵是如何监听节点的呢?

其实就是用了 心跳检测 机制,定期给节点发数据包,谁挂掉自然就知道了。

Redis 如何在master节点挂掉后,推举新master?

  • 通过 选举机制,一般节点我们推荐 奇数个,方便选举出新master

  • 脑裂: 注意避免脑裂的问题,即如果仅是网络抖动,导致Redis误认为master挂掉,又推举新的master,其实旧的master并没有挂掉,这样存在多个master就会有脑裂的问题。

  • 如何避免脑裂?

    • 一般都会整一组哨兵监控,避免单哨兵监听单主从节点,只要在超过半数以上认为master挂掉,才能去推举新master。

3、master 又活了怎么办?

如果某一时刻,master节点又恢复,哨兵会将该master重新拉回来,这个旧master会作为新master的从节点。

哨兵模式架构原理:

image-20200627204422750

4、哨兵模式存在问题:

  • 单节点压力问题: 无论是主从架构,还是哨兵架构,都无法解决单节点压力问题。

  • 物理上限问题: 因为Redis会开启持久化,当数据持久化到单台机器上,肯定会有物理存储上限问题

三、集群模式

1、说明: Redis 在 3.0 后开始支持 cluster(集群模式),目前Redis集群支持如下功能:

  • 集群节点的自动发现
  • slave-master的选举容错
  • 在线分片(Sharding shard)等特性

2、集群细节:

  • 所有的Redis节点,彼此互联(通过 PING-PANG 机制),内部使用 二进制协议 优化传输速度和带宽
  • 节点的fail是通过 集群中超过半数 的节点检测失效,才认为某个节点挂掉了
  • 客户端与Redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任意一个节点就可以了
  • redis-cluster 把所有的物理节点映射到 [0-16383]slot 上, cluster 负责维护 node<->slot<->value

3、集群原理

image-20200629165226329

解析

  • 每一个集群节点,都必须有它的slave节点,方便做 自动故障转移和数据备份

  • 集群中的节点,会平均分配 slot(槽),槽用于存储数据,即 数据是存储在槽上

  • 总共有多少个槽,槽是怎么分配到节点上的?

    • Redis 集群中最大有 0~16383 即 16384个槽,这些槽用于存储数据,他会平均分配到Redis中的集群节点中。
    • 比如,集群中有3个master-slave节点,节点1 可分配槽为(05000),节点2为(500110001),第三个就是剩余的(10001~16383)
  • client调用,集群节点,会如何请求?
    • 当client调用集群时,会对一个key 先做 CRC 16算法,会计算该key 落到哪个槽上,然后会看该槽在哪个范围,比如我做 CRC 16算法,落到了3000 这个槽上,那么他属于 0~5000 这个范围,那么他就会请求到集群1上。
  • 如果新加集群节点了,槽会如何分配?
    • 这就要看你是如何加的,例如可以将 集群1上的槽在分出一半,到新加的那个集群上,那么新的集群就拥有2501~5000 这些槽。
  • 新加集群节点后,之前key 经过 CRC 16 算出来的,跑到另一个集群上了怎么办?例如我之前请求到集群1,加集群后,我请求到集群4(新增集群),这时会请求不到数据吗?
    • 肯定可以请求到数据,因为同一条数据,你CRC 16 算出来的,肯定是在同一个槽上,而槽上维护的数据,当你重新分配集群节点,槽也跟着分配了,还是请求到那个原来的槽上,当然可以请求到数据,只是请求到新的集群节点上罢了。
    • 也就是说,请求到的数据是跟槽走的,CRC 16 加出来在哪个槽,就会一直在哪个槽,只是槽重新分配请求到不同节点上罢了。
  • 当集群中某个master 宕机后,slave顶上来后,槽会如何分配?

    • 之前该集群节点上master上的槽,会转移到新的master上,故依然可以请求到数据。
  • 最大支持多少个节点?
    • 因为最大是 0~16383 个槽,顶多就是1个集群分一个,所以最大是 16384 个集群,再大就做不了了, 这说的是master节点,slave你可以任意创建个数。

4、集群进入fail状态的必要条件

  • 某个主节点和从节点全部挂掉,我们集群就进入fail状态
  • 如果集群超过半数以上master挂掉,无论是否有slave,集群进入fail状态
  • 如果集群任意master挂掉,且当前master没有slave

5、 投票机制

  • Redis是如何去判断某个节点挂掉了呢?

    • 答: 是通过集群中超过半数节点投票决定的,超过半数节点投票说某个节点挂了,它才真正是挂了,切不可因为网络抖动等误认为某个节点轻易挂掉。

Redis投票机制

总结: 集群即解决了 单节点并发压力 问题,又解决了 物理上限问题,同时解决了 主节点发生故障时从节点的自动故障转移

原文地址:https://www.cnblogs.com/vpersie2008/p/14449858.html