Redis

387 阅读16分钟

图片描述

基本使用

Redis是什么,有什么优点?

Redis是现在最受欢迎的NoSQL数据库之一,其有以下优点:

  • 速度快,数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
  • 支持丰富数据类型,除了支持string、list、set、Zset、hash,还支持一些实用的类型;
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行;
  • 应用场景多,可做分布式锁,支持事务持久化、LUA脚本、LRU驱动事件、多种集群方案;
  • 单线程,能够保证多线程场景下的数据安全;

假如Redis里有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表但是Redis是单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,需要在客户端做一次去重,而且整体所花费的时间会比直接用keys指令长。

数据类型

Redis支持的存储类型及应用场景有哪些?

  • String:Redis 中的 String 类型和其他很多编程语言中的语义类似,value 可以是 String 也可以是数字。 一般做一些复杂的计数功能的缓存,比如说微博数,粉丝数等;
  • Hash:也可以称为 hashes,是一个string类型的key和value的映射表,特别适合用于存储对象。比如存储用户信息,商品信息等等。内部可以用hashtable和ziplist两种承载方式来实现;
  • List:List是redis 最重要的数据结构之一,可以做简单的消息队列的功能,比如论坛点赞人列表、微博粉丝列表等;另外还可以利用 lrange 命令,可以从某个元素开始读取多少个元素,实现简单的高性能分页,类似微博那种下拉不断分页的东西,性能极佳,用户体验好;
  • Set:set类似List,但是它是一个无序集合,且其中的元素不重复。可以做全局去重的功能,比如说是否给帖子点赞数;也可以判断某个元素是否在set,比如说判断是否给某个回复点赞。另外还可以利用交集、并集、差集等操作来支撑更多的业务场景,比如说找出两个微博ID的共同好友等;
  • Sorted Set(ZSet):sorted set 相比set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如说可以用于取排行榜top N的用户。

除此之外随着Redis的发展出现了支持更多业务场景的数据类型:

  • BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
  • HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
  • GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
  • Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

每种数据类型使用的数据结构是怎样的?

image.png

ZSet数据结构是怎样实现的?

  • 基于红黑树,采用跳表+压缩表的方式实现;
  • 一般情况下,ZSet将每个节点以双向链表的方式连接,但是当长度超过了128个节点或key特别大,则会跳跃连接,例如开始时连接方式为1-2-3-4-5,跳表就会连接1-3-5。

Redis线程

Redis是怎样体现单线程的?

Redis单线程指的是「接收客户端请求 -> 解析请求 -> 进行数据读写等操作 -> 发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因,或者说Redis命令的执行过程是单线程的。

内存机制

Redis过期策略有哪些?

  • 定期删除策略:用一个定时器来负责检查 key,过期则删除 key,注意这里并不是检查所有的 key 而是随机抽取进行检查。定期策略虽然让内存及时释放,但也会额外消耗 CPU 资源,通常 CPU 应该将时间尽量用于处理业务请求,而不是删除 key。
  • 惰性删除策略:在你获取某个 key 的时候,redis 会检查一下,这个 key 如果设置了过期时间那么是否过期了,如果过期则删除该 key。

内存淘汰机制是怎样的?

如果定期删除没删除 key,然后也没及时去请求 key,即惰性删除也没生效,持续下去 redis 的内存会越来越高,当超过redis设置的内存最大使用量时,就会进行内存数据淘汰。redis有6种淘汰策略:

策略描述
volatile-lru已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru所有数据集中挑选最近最少使用的数据淘汰
allkeys-random所有数据集任意选择数据进行淘汰
noeviction内存不足以容纳新写入数据时,新写入操作会报错。很少使用

持久化

Redis的持久化方式有哪些,区别和优缺点是怎样的?

  • RDB(Redis DataBase),用数据集快照的方式,定时将Redis存储的数据生成快照并存储到磁盘等介质上;

    优点:

    特别适合备份;

    性能最大化,fork子进程来完成写操作,让主进程继续处理命令且不会进行任何IO操作,确保Redis性能;

    相对于数据集大时,比AOF的启动效率更高。

    缺点: 数据安全性低,RDB 是间隔一段时间进行持久化,如果持久化之间发生故障,会发生数据丢失,所以这种方式更适合数据要求不严谨的时候;

  • AOF(Append -only file),命令行记录以Redis命令请求协议的格式完全持久化存储,保存为.aof 文件。

    优点:

    数据安全,可以配置append fsync属性,比如无fsync,每秒钟一次 fsync,或者每次执行写入命令时 fsync,一般只会丢失一秒钟的数据,或者最后一次执行的数据,对缓存来说,这已经足够。

    某些场景下还可以恢复数据。比如说某同学在操作Redis时,不小心执行了FLUSHALL,导致Redis内存中的数据全部被清空了。如果 AOF 文件还没有被重写(rewrite),可以暂停Redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启Redis,就可以恢复所有数据到FLUSHALL之前的状态。

    缺点:

    AOF 文件比 RDB 文件大,且根据不同的fsync策略,其恢复速度可能较慢; 数据集大的时候,比RDB启动效率低。

AOF 文件太大会怎么样?

后台会自动地对AOF进行重写(rewrite),重写时会压缩 AOF 文件内容,只保留可以恢复数据的最小指令集,比如调用了100次INCR指令,完全可以合并成一条 SET 指令。

进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。

RDB和AOF对比

命令RDBAOF
启动优先级
体积
恢复速度
数据安全性丢数据根据策略决定
轻重

你们公司项目使用的是哪种持久化方式?

现在一般是基于Redis4.0的混合持久化方式。默认是关闭,可以通过配置项aof-use-rdb-preamble开启。

开启混合持久化,AOF文件的前半部分RDB格式的全量数据,后半部分是AOF格式的增量数据。

如果把混合持久化打开,AOF重写的时候就直接把RDB的内容写到AOF文件开头。这样做的好处是可以结合RDB和AOF的优点,快速加载同时避免丢失过多的数据。缺点是AOF里面的RDB部分是压缩格式不再是AOF格式,可读性较差。

高可用

Redis保证高可用的方案有哪些?

主从模式

Redis多副本,采用主从(replication)部署结构,相较于单副本而言最大的特点就是主从实例间数据实时同步,并且提供数据持久化和备份策略。主从实例部署在不同的物理服务器上,根据公司的基础环境配置,可以实现同时对外提供服务和读写分离策略。主从模式是实现Redis高可用的基础。

哨兵模式

sentinel,中文名是哨兵。哨兵是 Redis 集群架构中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控 Redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

集群模式

目前官方推荐的Redis高可用解决方案全名Redis-Cluster。 Redis 的哨兵模式虽然已经可以实现高可用,读写分离 ,但是存在几个方面的不足:

  • 哨兵模式下每台 Redis 服务器都存储相同的数据,很浪费内存空间;数据量太大,主从同步时严重影响了master性能。
  • 哨兵模式是中心化的集群实现方案,每个从机和主机的耦合度很高,master宕机到salve选举master恢复期间服务不可用。
  • 哨兵模式始终只有一个Redis主机来接收和处理写请求,写操作还是受单机瓶颈影响,没有实现真正的分布式架构。

Redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster模式为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。Redis Cluster是一种服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。Redis Cluster集群采用了P2P的模式,完全去中心化。

图片

如上图,官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式。Redis Cluster集群具有如下几个特点:

  • 集群完全去中心化,采用多主多从;所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 每一个分区都是由一个Redis主机和多个从机组成,分片和分片之间是相互平行的。
  • 每一个master节点负责维护一部分槽,以及槽所映射的键值数据;集群中每个节点都有全量的槽信息,通过槽每个node都知道具体数据存储到哪个node上。

redis cluster主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster,数据量不是很大时,使用sentinel就够了。redis cluster的性能和高可用性均优于哨兵模式。Redis Cluster采用虚拟哈希槽分区而非一致性hash算法,预先分配一些卡槽,所有的键根据哈希函数映射到这些槽内,每一个分区内的master节点负责维护一部分槽以及槽所映射的键值数据。

这几种高可用方案的区别?

  • 主从模式:可以实现读写分离,数据备份。但是并不是「高可用」的;
  • 哨兵模式:可以看做是主从模式的「高可用」版本,其引入了 Sentinel 对整个 Redis 服务集群进行监控。但是由于只有一个主节点,因此仍然有写入瓶颈;
  • Cluster模式:不仅提供了高可用的手段,同时数据是分片保存在各个节点中的,可以支持高并发的写入与读取,实现了去中心化。当然实现也是其中最复杂的。

Redis哈希槽和节点的关系是怎样的?

  • Redis引入了哈希槽的概念,Redis集群有16384(2^14)个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽,具体分多少可以指定。
  • Redis 集群最大节点数也是16384,至少保证一个节点有一个哈希槽。

缓存一致性

什么是缓存的一致性?

缓存的一致性是指缓存数据库数据的一致性。

如何保证缓存的一致性?

  • 更新缓存/缓存失效

如果获取数据的带价很小,更新缓存代价也很小,那么可以先更新缓存;

如果是更新缓存的代价很大,例如需要多个接口调用和数据查询才能获得最新的结果,那么可以先淘汰缓存,后续请求通过数据库中检索;

  • 先更新数据库再让缓存失效/先让缓存失效再更新数据库

更新数据库和更新缓存无法保证原子性的,需要根据业务场景选择。

  • 通过MQ解决缓存一致性问题

第一步更新数据库,第二步尝试让缓存失效,如果第二步对个某个key的缓存失效,可以发送失败消息到MQ中,然后自己消费MQ的消息不断的重试让缓存失效。

这种方式的思路是实现更新数据库与缓存的异步,更新数据库之后,通过消息使缓存更新,保持一致性。

解释一下缓存穿透、缓存击穿、缓存雪崩,有什么解决方式?

  • 缓存穿透

    缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

    接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

    从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止用户反复用同一个id暴力攻击。

  • 缓存击穿

    缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

设置热点数据永远不过期;

对访问数据加互斥锁,并发数据会等待数据查询完并放到缓存中,使用syncronized等;

使用消息组件将访问排队,由最早的访问操作Redis;

  • 缓存雪崩

    缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

    缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

    如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。

    设置热点数据永远不过期。

缓存对比

Memcached与Redis有什么区别?

  • 支持的存储类型不同

    Redis和Memcached都是内存型数据库;

    Memcached存储String、图片、文件、视频等格式的文件;

    Redis存储String、Hash、List、Set及ZSet(sorted set:有序集合),适合分布式缓存的实现。

  • 数据持久化及数据恢复

    Memcached数据不可恢复;

    Redis支持在配置里打开数据落盘(RDB)及操作持久化(AOF)来找回数据。

  • 内存管理

    Memcached可以修改最大内存,使用LRU(Least recently used,最近最少使用)算法;

    Redis使用自身VM,突破物理内存限制;

    Value值Redis最大可以达到1GB,Memcache只有1MB。

  • 分布式支持

    Memcached一般是在客户端通过一致性哈希等算法来实现分布式存储;

    Redis更偏向于在服务器端构建分布式存储,支持主从复制等集群模式;

  • 内存管理

    Memcached使用Slab Allocation(将分配的内存分割成各种尺寸的块(Chunk),并把尺寸相同的块分成组(Chunk的集合),每个Chunk集合被称为Slab)管理内存,其主要思想是按照预先规定的大小呈阶梯状分配好,会根据接收到数据的大小选择一个最合适的Slab Class进行存储,该方法可以避免内存碎片问题,可也不可避免地出现一定的空间浪费;

    Redis 则采用包装过的mallc/free来分配内存,什么时候需要什么时候分配,更简单一些。

  • 使用场景

    Memcached是一个分布式内存对象缓存系统,旨在通过减轻数据库负载来加速动态 Web 应用程序;

    Redis 是内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。

MemcachedRedis
类型Key-Value数据库Key-Value数据库
过期策略支持支持
数据类型单一数据类型五大数据类型
持久化不支持支持
主从复制不支持支持
虚拟内存不支持支持