Redis教程

redis

本文主要是介绍redis,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Redis

什么是redis?

redis是一个key-value类型的内存数据库,整个数据库加载在内存当中操作,定期通过异步的方式把数据库的数据flush到硬盘保存,是一种非关系型(NOSQL)的数据库

优点:

  • 因为数据是存储在内存中,所有读写速度非常快
  • 支持数据的持久化
  • 支持主从复制,主机自动将数据同步到从机,可以进行读写分离
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  • 支持丰富数据类型,支持 string,list,set,Zset,hash 等

缺点:  

  • 由于 Redis 是内存数据库,所以redis目前还只能作为小数据量存储(全部数据能够加载在内存中)

  • redis是单线程的,单台服务器无法充分利用多核服务器的CPU。

redis为什么这么快?

  • 基于内存存储,没有磁盘上的IO开销
  • redis是单线程的,没有多线程的切换和锁的开销
  • 非阻塞式IO:redis采用多路IO复用技术

为什么使用redis做缓存?

  1. 高并发:直接操作缓存可以承受的请求是远远大于直接操作数据库的,所以我们把数据库中的部分数据转移到redis,这样用户的一部分请求就会到缓存而不用经过数据库
  2. 高性能:用户第一次访问数据库的某些数据,是操作数据库从磁盘中读取的,速度较慢,当把数据放入到缓存时,就可以直接读取。操作缓存就是操作内存,速度非常快

redis的常用场景

将一些热点数据存储到Redis中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。

​ 1、缓存(最常用)

​ 2、消息队列(支付)

​ 3、活动排行榜或计数

​ 4、发布,订阅消息(消息通知)

​ 5、分布式锁

redis的数据类型

img

redis的持久化

持久化:将redis内存中的数据写入到磁盘

redis两种持久化方法:快照RDB(适合做备份),只追加文件AOF(保证数据不丢失)

RDB:在指定的时间间隔内 将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存的

优点:适合大规模的数据恢复,对数据的完整性和一致性要求不高

缺点:在一定的时间间隔内做一次备份,当redis宕机时,就会丢失最后一次快照后的所有修改

AOP:以日志的形式来记录每个写操作,将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件,不能改写文件。redis重启时,会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

优点:

  • 每修改同步:每次发生数据的修改都会同步到磁盘,性能较差但是数据的完整性较好
  • 每秒同步:异步操作,每秒记录,如果最后一面宕机,有数据丢失
  • 不同步:从不同步

缺点:

  • 对相同数据集的数据而言aof文件要远大于rdb文件,恢复速度也慢与rdb
  • aof运行效率慢与rdb,每秒同步效率较好,不同步和rbd引用

redis的key的过期时间设置

通过expire key time(秒)、pexpire key time(毫秒)

redis过期键的删除策略

定期删除和惰性删除两种策略配合使用:

惰性删除策略:惰性删除不会主动删除数据,而是在访问数据的时候再去检查当前的键值是否过期,如果过期就删除并且返回null给redis,如果没有过期则返回正常的信息

定期删除策略:redis会周期性的随机测试一批设置了过期时间的key并进行处理,测试到已过期的key会被删除

删除key的三种方式:

  • 定时删除:在设置某个 key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
  • 惰性删除:设置该 key 过期时间后,我们不去管它,当需要该 key 时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该 key。
  • 定期删除:每隔一段时间,我们就对一些 key 进行检查,删除里面过期的 key。

缓存异常

redis的缓存异常有四种:缓存和数据库的数据不一致、缓存穿透、缓存击穿、缓存雪崩

redis的缓存过程:

在这里插入图片描述

解决缓存和数据库不一致情况:

因为redis数据是在内存中的,与数据库隔离,数据库做了修改内存不知道

  • 先删除缓存,再更新数据库
  • 先更新数据库,在删除缓存

缓存穿透

缓存穿透是指:要访问的数据既不在缓存也不在数据库,当大量的请求这个不存在的数据时,会一直访问访问数据库,导致数据库的压力剧增。

解决方法:

  • 对空值做缓存:当查询key为null时(不管数据是否存在),把这个结果进行缓存。(这样其它的请求在缓存中发现没有这个key时,会直接把缓存中的结果返回,就不会一直访问数据库,减少数据库压力)
  • 布隆过滤器:是一种数据结构,可以利用极小的内存判断大量的数据一定不存在还是可能存在

对应缓存击穿,我们将大量查询的请求都放到布隆过滤器中,由布隆过滤器先过滤请求,一定不存在的数据的请求直接拦截返回,从而减轻数据库的压力。

  • 可能被黑客利用攻击(黑客一直请求不存在的数据),当有大量的恶意请求时,可以把其ip拉入黑名单

缓存击穿

缓存击穿是指:key中对应数据存在,当key中对应的数据在缓存中过期,而此时又有大量的请求访问该数据,这些请求会直接访问数据库,高并发的访问数据库会导致数据库崩溃

解决方法:

  • 预先设置热门数据:在访问的高峰期之前,把一些热门的数据提前存放到redis,并且延长过期时间

  • 实时调整:实时监控哪些数据热门,实时调整key过期时间。

  • 互斥锁,这是解决缓存穿透比较常用的方法。

    互斥锁简单来说就是在Redis中根据key获得的value值为空时(也就是缓存失效时),先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则睡眠一段时间(比如100ms)后重试。

缓存雪崩

缓存雪崩是指:key中对应数据存在,在某一时刻,缓存中大量的key过期,而此时大量高并发请求访问,会直接访问后端数据库,导致数据库崩溃。(redis宕机、采用了相同的过期时间)

解决方法:

  • 使用锁或者队列:可以保证不会有大量的线程一次性对数据库进行读写(该法不适合高并发)
  • 设置过期标志更新缓存:记录缓存数据是否过期,如果过期会触发通知别的线程去更新key
  • 将缓存失效的时间隔开:防止了在某一时间突然大量的key失效导致高并发访问数据库

redis事务

  • 单个的redis的执行命令是原子性的
  • redis事务中如果有某一条命令执行失败,之前的命令不会回滚,其后的命令会继续执行
  • redis事务中所有的命令都会按照顺序执行。事务在执行的过程中,不会被其它客户端命令打断

Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令

redis事务三个阶段

  • 第一阶段:multi开启事务
  • 第二阶段:命令入队
  • 第三阶段:exec执行事务内命令,至此一个事务已经结束

redis事务相关命令

redis事务概念是通过multi、exec、discard和watch四个原语实现

  • multi命令:用于开启事务。在执行multi后,客户端可以继续向服务器发送多条命令,这些命令不会立即执行,而是会放入到一个队列,当exec命令被调用时才会执行。

  • exec命令:执行事务中所有命令

  • discard命令:清空事务队列,并放弃执行事务。

  • watch命令:是一个乐观锁,可以监控一个或多个键,一旦其中有一个键被修改后,之后的事务不会执行

  • unwatch命令:取消监视

从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队过程中可以通过discard来放弃组队,如果组队的时候出现了错误,整个队列都会被取消,如果执行的时候出现了错误,只是错误的命令会失败,其它的会成功

Redis就是利用这种check-and-set 机制实现事务的,乐观锁

redis事务三特性

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

redis作为缓存的时候,随着项目访问量的增加,对redis操作也越来越频繁,这样对redis性能会造成一定的影响,严重时可能会出现故障。

为了解决高并发场景下Redis的性能问题,通常采取的一种方式就是主从架构 Master/Slave,Master(主节点)以写为主,Slave(从节点)以读为主,并且会伴随哨兵进行监控。

主从复制:是指将Redis主服务器的数据,复制到其他的Redis从服务器。数据的复制是单向的,只能由主节点到从节点。

主从复制原理:

img

在搭建redis读写分离架构(基于RDB持久化)时,master必须开启持久化。因为一旦Master宕机,虽然slave可以提高读服务,但是随着Master重启,由于没有持久化,内存没有任何数据,此时master再次做数据同步的时候,会将slave的数据清空。

  1. 当Slave初始化时首先ping向Master节点,如果ping通,从服务器向主服务器发送一个要进行数据同步的消息,主服务器接到消息后,进行数据的持久化。slave就会把master上的rdb文件复制到slave节点,然后加载到内存
  2. 后续当master节点有写操作时,master会将相同的操作命令传输给slave,slave执行命令即可

全量同步:Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。

增量同步:Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。

增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

一主二从

命令:slaveof <ip><port>

  • 当从服务器挂掉之后,再重新启动时,从服务器会变成主服务器(之前的关系会断开),当再次加入到主服务器当中作为从服务器时,会从头开始复制主机的数据
  • 当主服务器挂掉时,从服务器不做任何事情,再次重启,仍然是主服务器

薪火相传

上一个slave是下一个slave的Master,slave也可以接收其它slave连接和同步请求,slave作为该链中下一个slave的主机,可以有效减轻Master的写的压力,去中心化降低风险。

风险是一旦某个slave宕机,后面的slave都没法备份,主机挂了,丛机还是丛机,无法写数据了

反客为主

当一个master主机宕机后,后面的salve可以立刻升级为Master,其后面的slave不做任何修改

缺点:需要手动开启 命令:slaveof no one 解决:哨兵模式

哨兵模式

哨兵模式:能够在后台监控主机是否故障,当主服务器宕机时,哨兵会自动选举一个从服务器作为主机,其它的从机都变成该服务器下的从机,当主服务器重启时,主机变为从机。

哨兵主要具有三个作用, 监控、选主与通知

  • 监控:哨兵会利用心跳机制,周期性不断地检测主库与从库的存活性
  • 选主:哨兵检测到主库宕机后,选择一个从库将之切换为新主库
  • 通知:哨兵会将新主库的地址通知到所有从库,使得所有从库与旧主库slaveof新主库,也会将新主库的地址通知到客户端上

哨兵的工作过程:

  • 每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。

  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间,超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。 (就是回复超时了,认为下线了)

  • 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel ,要以每秒一次的频率确认Master的确进入了主观下线状态。

  • 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内,确认Master的确进入了主观下线状态, 则Master会被标记为客观下线

  • 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。

  • 当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率,会从 10 秒一次改为每秒一次 。

  • 若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

  • 一旦某个主库被认定为客观下线时,这个时候需要进行哨兵选举,选举出一个领导者哨兵(投票),来完成主从切换的过程

  • 在选举到领导者哨兵后,将由该哨兵完成故障恢复工作。

    故障恢复分为以下两步:

    1、首先需要在各个从库中,选出一个健康的且数据最新的从库出来。

    首先过滤不健康的节点:

    • 主观下线、客观下线或断线
    • 没在5秒内完成对哨兵ping命令的回应
    • priority=0
    • 没在3秒或5秒内(由主库状态决定)内完成对info命令的回应
    • 与主库的断开时间,超过max_master_down_time

    剩下的节点,就是健康的节点,此时再执行一次快速排序,排序的规则如下:

    • 比较优先级(priority),谁的优先级越小(除了0),就选谁
    • 比较复制偏移量。谁的偏移量大,就选谁
    • 比较runid,按照字母顺序排序。谁靠前,则选谁

    2、将该从库提升为新主库,即执行slaveof no one,其他从节点slaveof新主库。

分布式问题

分布式锁

锁在程序当中的作用就是同步,保证共享资源在同一时刻只能有一个线程访问,java里面的有许多锁,但是这些锁只能在单机里有用,在分布式集群(多个服务器)就不起作用了,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁的由来。

分布式锁:就是分布式项目开发中用到的锁,可以用来控制分布式系统之间同步访问共享资源

分布式锁的特点

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

1、互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。

2、安全性:锁只能被持有该锁的客户端删除,不能由其它客户端删除。

3、死锁:获取锁的客户端因为某些原因(如down机等)而未能释放锁,其它客户端再也无法获取到该锁。

4、容错:当部分节点(redis节点等)down机时,客户端仍然能够获取锁和释放锁

Redis实现分布式锁

分布式锁的三个核心命令:

  1. 加锁:使用setnx来加锁,key是锁的唯一标识

SETNX key value:当一个线程执行setnt返回1时,什么key不存在,该线程成功获取到了锁;当返回0时,说明key已经存在,该线程获取锁失败。

  1. 解锁:释放锁的指令是del指令 delete key 释放锁后,其它线程可以继续执行sexnt获取锁

  2. 锁超时:如果一个得到锁的线程在执行任务的过程中挂掉,来不及释放锁,这块资源将会被永远锁住,别的线程获取不到锁,所以,setnt的key必须设置一个超时时间,保证锁可以被释放。

    expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

使用setnx上锁,通过del释放锁,如果锁一直没有释放,可以设置key过期时间,自动释放

eg:set key value [NX|XX] [ex seconds][px milliseconds]

NX:只有key不存在时,才进行操作

XX:只有key存在时,才进行操作

ex:设置过期时间,单位是秒

px:设置过期时间,单位是毫秒

这篇关于redis的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!