首页 > 技术文章 > Redis重点知识

codingLiu 2020-04-20 16:59 原文

Redis线程模型

Redis内部使用文件事件处理器file event handler,这个文件事件处理器是单线程的,所以Redis才被叫做单线程模型。它采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。

文件事件处理器的结构包含4各部分:

  • 多个socket
  • IO多路复用
  • 文件事件分派器
  • 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用会同时监控多个socket,会将socket产生的事件放入队列中,事件分派器从中取出一个事件,把该事件交给对应的事件处理器进行处理。

可参考:https://www.javazhiyin.com/22943.html

    5种网络IO模型(有图,很清楚) - findumars - 博客园

 

Redis和Memcached的区别

 

Redis常见数据结构和应用场景

String

常用命令:set、get、decr、incr、mget

String数据结构是简单的key-value类型,value不仅可以是String,还可以是数字,可用于常规key-value缓存应用,常规计数:微博数、粉丝数

Hash

常用命令:hget、hset、hgetall

hash是一个string类型的field和value的映射表,hash特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段,常用来存储用户信息、商品信息

List

常用命令:LPUSH、LPOP、RPUSH、RPOP、LRANGE

list是一个双向链表,支持反向查找和遍历,需要额外的内存开销。

可通过lrange命令,从某个元素开始读取多少个元素,基于list实现分页查询,基于Redis实现简单的高性能分页

Set

常用命令:sadd、spop、smembers、sunion

set功能和list类似是一个列表的功能,特殊之处是set是可以自动去重的,还可以基于set进行交集、并集差集操作

用于共同粉丝、共同关注等

Sorted Set

常用命令:zadd、zrange、zrem、zcard

和set相比,zset增加了一个score权重,使得集合中的元素可以按照score进行有序排列

应用在直播时的礼物排行榜等

 

Redis设置过期时间

对于一个缓存数据库,设置过期时间是非常实用的,比如一些token或者登录信息都需要设置过期时间

在set key时,可以给一个expire time,就是过期时间。

定期删除+惰性删除

  • 定期删除:Redis大概每隔100ms就随机抽取一些设置过期时间的key,检查是否过期,过期就删除,之所以随机,因为Redis缓存一般比较大,每次都遍历开销太大
  • 惰性删除:定期删除会有很多的到期并没删除的,所以就用惰性删除,知道系统去查看到这个key,才进行删除

加入同时过期很多的key,可能会导致内存耗尽,通过上面两种方式并不能有效的删除,就会使用Redis内存淘汰机制。

 

Redis内存淘汰机制

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
  • allkeys-lru:当内存不足以容纳新数据写入时,在键空间中,移除最近最少使用的key
  • allkeys-random:从数据集中任意选择数据淘汰
  • no-eviction:禁止驱逐数据,也就是说当内存不足时,写入新数据操作会报错
  • 4.0后添加
  • volatile-lfu:从已设置过期时间的数据集中挑选最不经常使用的数据淘汰
  • allkeys-lfu:当内存不足以写入新数据时,在键空间中,移除最不经常使用的key

 

Redis持久化机制(怎么保证Redis挂掉后再重启数据可以进行恢复)

很多时候我们需要持久化操作,将将内存中的数据写入到硬盘,大部分原因是为了重用数据,比如机器重启、机器故障之后恢复数据,或者为了防止系统故障而降数据备份到一个远程位置。

Redis有两种持久化操作

  • 快照(snapshotting,RDB)
  • 只追加文件(append-only file,AOF)

快照持久化,RDB

Redis可以通过创建快照来获取存储在内存里的数据在某一个时间点上的副本。对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地使用。会利用系统的多进程来帮助处理,创建子进程专门进行快照的创建。

AOF持久化

与快照相比,AOF的实时性更好,默认情况下没有开启AOF,需要通过appendonly参数打开  appendonly yes

开启AOF后,每执行一条更改Redis中数据的命令,Redis就会将该命令写入硬盘中的AOF文件中。默认文件名是appendonly.aof,先执行指令再将日志下入AOF。

为了兼顾数据的写入性能,常使用appendfsync everysec ,让Redis每秒钟同步一次数据,对Redis的性能几乎没有影响,即使系统崩溃,最多损失一秒的数据。

AOF重写

Redis长期运行,AOF的日志会越来越大,如果宕机,重放AOF会非常耗时,导致Redis无法对外服务,因此需要对AOF进行“瘦身”。Redis提供 bgrewtiteaof 指令用于给AOF瘦身,其原理是开辟一个子进程,对内存进行遍历转换成一系列操作指令,序列化到一个新的AOF日志文件中,在使用 bgrewriteaof 时Redis会维护一个缓冲区,记录在瘦身这段时间执行的命令,等新AOF创建完成,追加到AOF末尾,然后再用新AOF代替旧的AOF日志文件。

Redis4.0混合持久化

重启Redis,一般很少用RDB来恢复内存,因为丢失大量数据,常使用AOF恢复数据,但是AOF恢复性能要低于RDB,启动要花很长时间,所以采用混合模式,将rdb文件的内容和增量的AOF日志文件存在一起,这里的AOF日志不再是全量日志,而是自持久化开始到持久化结束的这段时间发生的增量AOF日志,于是再重启时,我们先加载rdb文件,在加载少量的AOF文件即可。

 

Redis事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务功能,事务提供了一种将多个命令打包,然后一次性、按顺序的执行,并且在事务执行期间,服务器不会中断事务而去执行其他命令。事务往往也是具有ACID,Redis同一个事务中如果有一条命令失败,其他命令不会回滚,会继续执行,原子性是针对某一条指令,而不是整个事务的每一条命令。

 

缓存雪崩和缓存穿透问题及解决方案

缓存雪崩:

缓存同一时间大面积的失效,所以,后面的请求全部落到数据库上,导致数据库短时间内承受大量的请求而崩溃。

解决方法:

  • 事前:对缓存集群实现高可用,使用主从+哨兵,Redis cluster来避免缓存大量失效,选择合适的内存淘汰策略,不一次性淘汰大量的缓存,是指随机时间淘汰
  • 事中:要第一时间保证系统的可用性,可使用本地缓存(如果Redis不可用),给服务器限流、降级,避免MySQL崩掉
  • 事后:利用Redis持久化机制尽快恢复缓存

缓存穿透:

简单来说就是请求查询一条根本不存在的数据,缓存中肯定没有,就会走数据库,然后大量的请求就会直接打到数据库上。比如黑客攻击,很容易导致数据库宕机。

解决方法:

  • 缓存空值,讲这些key对应的值全部设置为null,在请求会直接从缓存中取出,要设置过期时间
  • BloomFilter布隆过滤器,直接使用布隆过滤器判断key是否存在,不存在直接返回,存在就去查询缓存-》数据库

当key的异常比较多、请求重复率比较低的时候使用布隆过滤器,当key重复率比较高,key的数量有限就是用缓存空值

缓存击穿:

简单来说就是大量请求同时查询一个key时,刚好这个时候key失效,导致大量请求直接打到数据库上,压力剧增

解决方法:

使用互斥锁,上面的现象是多个线程同时去请求数据库,我们在第一查询时使用一个互斥锁,没有锁就等着,等第一个请求完成就缓存到Redis中,后面的直接走缓存。

热点数据集中失效:

每个缓存往往都有一个失效时间,对于一些热点数据来说,当缓存过期失效了,会有大量的请求打到数据库上,很有可能导致数据库崩溃。

解决方法:

  • 设置不同的失效时间,不在同一时间失效,加上或减去一个随机值
  • 互斥锁,同缓存击穿

 

推荐阅读