redis是一个key-value类型的内存数据库,整个数据库加载在内存当中操作,定期通过异步的方式把数据库的数据flush到硬盘保存,是一种非关系型(NOSQL)的数据库
优点:
缺点:
由于 Redis 是内存数据库,所以redis目前还只能作为小数据量存储(全部数据能够加载在内存中)
redis是单线程的,单台服务器无法充分利用多核服务器的CPU。
将一些热点数据存储到Redis中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。
1、缓存(最常用)
2、消息队列(支付)
3、活动排行榜或计数
4、发布,订阅消息(消息通知)
5、分布式锁
持久化:将redis内存中的数据写入到磁盘
redis两种持久化方法:快照RDB(适合做备份),只追加文件AOF(保证数据不丢失)
RDB:在指定的时间间隔内 将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存的
优点:适合大规模的数据恢复,对数据的完整性和一致性要求不高
缺点:在一定的时间间隔内做一次备份,当redis宕机时,就会丢失最后一次快照后的所有修改
AOP:以日志的形式来记录每个写操作,将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件,不能改写文件。redis重启时,会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
优点:
- 每修改同步:每次发生数据的修改都会同步到磁盘,性能较差但是数据的完整性较好
- 每秒同步:异步操作,每秒记录,如果最后一面宕机,有数据丢失
- 不同步:从不同步
缺点:
- 对相同数据集的数据而言aof文件要远大于rdb文件,恢复速度也慢与rdb
- aof运行效率慢与rdb,每秒同步效率较好,不同步和rbd引用
通过expire key time
(秒)、pexpire key time
(毫秒)
定期删除和惰性删除两种策略配合使用:
惰性删除策略:惰性删除不会主动删除数据,而是在访问数据的时候再去检查当前的键值是否过期,如果过期就删除并且返回null给redis,如果没有过期则返回正常的信息
定期删除策略:redis会周期性的随机测试一批设置了过期时间的key并进行处理,测试到已过期的key会被删除
删除key的三种方式:
redis的缓存异常有四种:缓存和数据库的数据不一致、缓存穿透、缓存击穿、缓存雪崩
redis的缓存过程:
因为redis数据是在内存中的,与数据库隔离,数据库做了修改内存不知道
- 先删除缓存,再更新数据库
- 先更新数据库,在删除缓存
缓存穿透是指:要访问的数据既不在缓存也不在数据库,当大量的请求这个不存在的数据时,会一直访问访问数据库,导致数据库的压力剧增。
解决方法:
一定不存在还是可能存在
。对应缓存击穿,我们将大量查询的请求都放到布隆过滤器中,由布隆过滤器先过滤请求,一定不存在的数据的请求直接拦截返回,从而减轻数据库的压力。
缓存击穿是指:key中对应数据存在,当key中对应的数据在缓存中过期,而此时又有大量的请求访问该数据,这些请求会直接访问数据库,高并发的访问数据库会导致数据库崩溃
解决方法:
预先设置热门数据:在访问的高峰期之前,把一些热门的数据提前存放到redis,并且延长过期时间
实时调整:实时监控哪些数据热门,实时调整key过期时间。
互斥锁,这是解决缓存穿透比较常用的方法。
互斥锁简单来说就是在Redis中根据key获得的value值为空时(也就是缓存失效时),先锁上,然后从数据库加载,加载完毕,释放锁。若其他线程也在请求该key时,发现获取锁失败,则睡眠一段时间(比如100ms)后重试。
缓存雪崩是指:key中对应数据存在,在某一时刻,缓存中大量的key过期,而此时大量高并发请求访问,会直接访问后端数据库,导致数据库崩溃。(redis宕机、采用了相同的过期时间)
解决方法:
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的性能问题,通常采取的一种方式就是主从架构 Master/Slave,Master(主节点)以写为主,Slave(从节点)以读为主,并且会伴随哨兵进行监控。
主从复制:是指将Redis主服务器的数据,复制到其他的Redis从服务器。数据的复制是单向的,只能由主节点到从节点。
在搭建redis读写分离架构(基于RDB持久化)时,master必须开启持久化。因为一旦Master宕机,虽然slave可以提高读服务,但是随着Master重启,由于没有持久化,内存没有任何数据,此时master再次做数据同步的时候,会将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 解决:哨兵模式
哨兵模式:能够在后台监控主机是否故障,当主服务器宕机时,哨兵会自动选举一个从服务器作为主机,其它的从机都变成该服务器下的从机,当主服务器重启时,主机变为从机。
哨兵主要具有三个作用, 监控、选主与通知 。
哨兵的工作过程:
每个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机时,客户端仍然能够获取锁和释放锁
分布式锁的三个核心命令:
setnx
来加锁,key是锁的唯一标识SETNX key value
:当一个线程执行setnt返回1时,什么key不存在,该线程成功获取到了锁;当返回0时,说明key已经存在,该线程获取锁失败。
解锁:释放锁的指令是del指令 delete key
释放锁后,其它线程可以继续执行sexnt获取锁
锁超时:如果一个得到锁的线程在执行任务的过程中挂掉,来不及释放锁,这块资源将会被永远锁住,别的线程获取不到锁,所以,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:设置过期时间,单位是毫秒