redis看这一篇就足够了

最近面试发现狂问redis,仔细想想也是,毕竟现在的并发量这么大,缓存几乎是必不可少的了,结合这两天面试从面试官那里get到的知识加上面试前在网上恶补的一堆帖子,整理一下redis的知识。

一,Redis的执行原理

都说redis是单线程+多路IO复用技术,那么,到底是啥玩意?为啥redis又使用这种方式?
首先redis的所有数据都是放在内存中的,所以单线程去操作的时候就不会有cpu的上下文切换,这样会节省大量的时间。redis是使用的单个CPU绑定一块内存区域的数据,所以针对这块数据的读写都是在一个CPU上完成的,所以他是单线程处理这件事,在内存情况下,这个方案就是效率最高的。
IO多路复用
这里的IO我觉得应该就是网络IO,多路复用就是多个TCP连接都被这一个线程监控操做。
redis使用这种方式减少了上下文的切换,也不用像阻塞IO一样消耗时间,这样就会大大提高效率。

二,redis的数据类型

redis一共有九种数据类型,常用的有五种,分别是string,list,hash,set,zset。
命令请看官方
下面简单说说他们的使用场景

string

1.最经典的用法也是用到最多的,做缓存。由于redis适用于高并发的场景,所以做缓存再适合不过了。
2.计数器
3.分布式场景下的单点登录session共享
4.验证码,今天经过点播才晓得,原来验证码的作用并不仅仅是防止恶意攻击,在并发量高的情况下,他还可以有效地做到削峰,比如秒杀的场景下,我们一般都会考虑用到这种方式来削峰。

hash

类型:key field:value field:value…类似于java中的hashmap。
可以用来缓存一些比如电商项目的购物车用户的详细信息某个商品的详细信息

list

可以用来做消息队列,或者做分页功能
生产者从一方插入,消费者从另一方消费。

set

set天然具有去重的功能,可以用来查找一些共同的数据

zset

因为它自带一个评分嘛,所以可以考虑用它来做一些排行榜之类的东西。

三,redis的过期策略以及内存淘汰机制

设置了过期时间,可是redis内存还是出现内存使用过高的情况?redis过期的数据是如何删除的?有没有想过?
首先肯定排除了实时监控删除,因为这样虽然内存可以实时减轻压力,但是实时监控删除肯定会严重消耗cpu的性能,高并发的情况下岂不是直接GG。
事实上看了网上的一些博客,redis采用的是定期删除+惰性删除策略。具体怎么干的呢?他会每100ms随机抽取一些key来看看有没有过期的,有的话直接就丢掉了,但是如果只是使用这一种方法,岂不是数据量的时候很多过期的key还存在,所以redis还有另外的过期策略配合定期删除,那就是惰性删除,所谓的惰性删除,实际上就是当你用一个key的时候,他会去看看是不是过期了,过期了就直接丢掉。
但是这样还是会有问题,假如内存的数据很多,但是在某一个时间段内,获取数据的次数很少,这样就会导致很多过期的key还是来不及丢掉,所以就引发了redis的内存淘汰策略
redis.conf中有这样一行配置 :

# maxmemory-policy allkeys-lru

推荐的是 allkeys-lru 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
其他的还有好几种不推荐的,适用于一些特殊场景的,精力有限,实在记不得了。

四,redis与数据库的双写一致性问题

如果你非得要保证强一致性,那就别用了,因为毕竟存在两个地方,无论怎么操做,都会存在某一个时间点的数据不一致,但是这种要求强一致性的实际上很少,只有银行和股票这类的才不考虑效率只要数据一致性。弱一致性的话肯定以数据库为主,先更新数据库,在更新缓存,同时为了防止出现异常,可以采用mq发消息作为补偿策略。

五,redis的持久化

先看看官网怎么说:

RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

rdb的优点

RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复.
RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

rdb的缺点

如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),但是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据.
RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度.

aof的优点

使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

aof的缺点

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)

如何选择使用哪种持久化方式?

一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。

快照

在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。你可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。你也可以通过调用 SAVE或者 BGSAVE , 手动让 Redis 进行数据集保存操作。

比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集:

save 60 1000

rdb执行流程

1.Redis 调用forks. 同时拥有父进程和子进程。
2.子进程将数据集写入到一个临时 RDB 文件中。
3.当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。

只追加操作的文件

快照功能并不是非常耐久(dura ble): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。

你可以在配置文件中打开AOF方式:

appendonly yes

从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。这样的话, 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

日志重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。

为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写。

如果AOF文件损坏了怎么办

服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:


    为现有的 AOF 文件创建一个备份。

    使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:

    $ redis-check-aof –fix
    (可选)使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。
    重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

aof工作原理

AOF 重写和 RDB 创建快照一样,都巧妙地利用了写时复制机制:
Redis 执行 fork() ,现在同时拥有父进程和子进程。
子进程开始将新 AOF 文件的内容写入到临时文件。
对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。
AOF和RDB同时开启,redis听谁的?

AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
如何验证:将数据flushall,然后停止redis,将rdb文件恢复,查看数据是不是之前备份的rdb文件中的数据。如果是,说明RDB起作用,否则,AOF起作用。

六,redis事务

1.redis事务定义

redis事务是一个单独的隔离操作:事务中的所有命令都会序列化,
按顺序的执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。

2.Multi,Exec,discard

从输入Multi命令开始,输入的命令都会依次进入命令队列中,
但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。
组队的过程中可以通过discard来放弃组队。

3.事务的错误处理

组队阶段,如果某个命令出现了报告错误,执行时整个的所有队列都会被取消。
如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。

4.事务冲突的问题

3个人同时用一个账号购物
A想花8000
B想花5000
C想花1000

5.锁机制

悲观锁
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁
每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
WATCH key [key …]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
unwatch
取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。

6.事务三特性

单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

七,redis主从复制

1.主从复制

主从复制的原理看我上一篇的博客
在主机上写数据,再从机上读取数据,在从机上写数据会报错

主机挂掉,重启就行,一切如初

从机重启需要设置他的主机

2. 哨兵模式

能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
如果是在启动时指定的主机,那么如果主机down掉再重启就变成了从机了,如果是在配置文件指定的主机,那么主机down掉再回来还是主机
复制延时问题
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
优缺点
在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
哨兵的核心功能是主节点的自动故障转移。
哨兵模式的架构
在这里插入图片描述
主要有两部分:哨兵结点部分和数据节点部分
哨兵结点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的 Redis 节点,不存储数据。
数据节点:主节点和从节点都是数据节点。

1.哨兵节点其实就是redis节点,只不过他不存储数据。
2.哨兵节点只需要配置监控主节点就可以自动发现其他哨兵节点和从节点。
3.在哨兵节点启动和故障转移阶段,每个节点的配置文件会被重写。
4.一个哨兵可以监控多个主节点

api操做哨兵模式的集群
这个模式主要就是考虑节点down机,主节点切换,所以我们肯定无法在Java的配置文件写死主节点,那么就可以推断出,我们需要写的是哨兵节点。此外根据官方文档可知,我们还需要指定主节点的名字,这样就可以通过api来连接到redis集群。
原理
每个哨兵节点实际上维护了3个定时任务,定时任务的功能如下:
此处原文参考

    通过向主从节点发送 info 命令获取最新的主从结构。
    通过发布订阅功能获取其他哨兵节点的信息。
    通过向其他节点发送 ping 命令进行心跳检测,判断是否下线。

主观下线和客观下线
ping-pong的机制中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。
哨兵节点在对主节点进行主观下线后,会通过 sentinelis-master-down-by-addr 命令询问其他哨兵节点该主节点的状态。

如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。

需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。

八,无中心化集群

1.问题

容量不够,redis如何进行扩容?

并发写操作, redis如何分摊?

另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。

之前通过代理主机来解决,但是redis3.0中提供了解决方案。就是无中心化集群配置。

2.集群

Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。

Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

3.浅谈原理

官方推荐:去中心化分片集群Redis Cluster,原理采用hash槽的概念,预先分配16384个卡槽,并且将该卡槽分配给提供具体服务的Redis片区节点用来存放每个Redis主机对应卡槽范围的数据。

1.hash卡槽只会分配给每个节点的主机
2.每个卡槽可以放很多key-value对
3.卡槽的存在是为了方便定位key所在位置
4.每个节点的卡槽树都对应着一个范围:1 -500 501-1000 ......

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45596022/article/details/108434228