文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。

file

前面几篇已经对Redis中几个关键知识点做了介绍,本篇主要对Redis系列做一下总结以及对Redis中常见面试题简单进行介绍一下。首先我们对前面几篇谈到的Redis知识点进行总结。

第1篇:Redis系列开篇文章可以一文了解Redis。

第2篇:通过对比Redis和Memcached的区别来解释Redis为什么高性能。

第3篇:Redis中使用最频繁的有5种数据类型:String、List、Hash、Set、SortSet。本篇对Redis中常用的5种数据类型进行了细微的讲解以及常用场景进行介绍。

第4篇:如果同一台机器有多个线程抢夺同一个共享资源,同一个线程多次执行会出现异常,这种情况下就会出现非线程安全。我们解决方法通常使用锁来解决。但是如果有多台机器呢?这时候我们通常使用分布式锁来解决分布式环境下共享资源的同步问题。本篇对于单机部署分布式锁几种常规方案进行分别的说明。

第5篇:对保持双库数据一致性方案进行了分析以及分析通用的保持数据一致性的方案。

第6篇:主要针对Redis的内存淘汰机制以及Redis容易引发的三大问题:缓存击穿、缓存穿透以及缓存雪崩进行了详细的讲解以及提供了业界常用的解决方案。因为涉及到缓存淘汰机制,所以内容包含缓存淘汰机制的知识点。

第7篇:Redis受开发者欢迎的一大原因就是因为可持久化的特性。我们如何保证Redis宕机之后重启可以将数据进行恢复?所以一般情况下我们需要定时进行持久化将内存中的数据写入到硬盘中。而Redis中支持两种不同的持久化机制:RDB持久化以及AOF持久化。

第8篇:在80%左右企业使用的都是Redis单机服务,在生产环境下使用单机环境的Redis容易面临风险,如果Redis持久化的硬盘出现故障,则有可能导致持久化的备份数据出现丢失,所以我们需要一个方案解决这个问题,所以我们需要将原来集中式的数据库中的数据分别复制到不同Redis节点上进行存储,这也就是Redis中的主从复制。

第9篇:Redis主从复制实际上就是将主Redis节点的数据,复制到其他从Redis节点去进行存储,当主节点因为出现异常宕机后,如何将从节点切换成主节点继续提供服务呢?Redis主从切换主要分为以下两种方式:手动切换以及哨兵模式。

第10篇:如果没有分片机制,Redis就被局限于单机所支持的内存容量。Redis的分片机制允许数据拆分存放在不同的Redis实例上,每个Redis实例只包含所有键的子集。可以减轻单台Redis的压力,提升Redis扩展能力和计算能力。

对前面Reids系列文章简单做了总结,接下来我们一起看看Reids几道比较常见的面试题吧。

简单介绍一下Redis

redis是一个key-value存储系统。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了主从同步。简单来说 Redis 就是一个数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的,所以存写速度非常快,因此 Redis 被广泛应用于缓存方向。Redis 也经常用来做分布式锁。Redis 提供了多种数据类型来支持不同的业务场景。除此之外,Redis 支持事务 、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

Redis与Memcached的区别

  • Redis支持常见数据类型:Redis 不仅仅支持简单的 key/value 类型的数据,同时还提供string(字符串)、list(链表)、set(集合)、zset(有序集合)和hash(哈希类型)等数据结构的存储。而Memcache 只支持简单的数据类型 String。
  • Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。
  • 集群模式:Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 Cluster 模式的。
  • Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。

file

Redis常用五大数据类型
Redis支持存储的value类型相对更多,包括string、list、set、sorted set和hash。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。对于这五种数据类型的具体分析可以看:细谈Redis五大数据类型。

Redis的缓存淘汰策略

Redis可以对存储在缓存中的数据设置过期时间。作为一个缓存数据库,这是非常实用的功能。之前写过一篇前后端交互的文章讲过,Token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。而有一个好的方案其实就是将这些验证信息存入Redis设置过期时间,如果设置了存活时间30分钟,那么半小时之后这些数据就会从Redis中进行删除。那说到删除,Redis是如果做到对这些数据进行删除的呢:

  • 定期删除:Redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 Key,检查其是否过期,如果过期就删除。为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。
  • 惰性删除 :定期删除可能会导致很多过期 Key 到了时间并没有被删除掉。用户在获取key的时候,redis会检查一下,这个key如果设置过期时间那么是否过期了,如果过期就删除这个key。

但是只是使用定期删除 + 惰性删除的删除机制还是存在一个致命问题:如果定期删除漏掉了很多过期 Key,而且用户长时间也没有使用到这些过期key,就会导致这些过期key堆积在内存里,导致Redis内存块被消耗殆尽。所以有了Redis内存淘汰机制的诞生。

Redis 提供 6 种数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • allkeys-lru:当内存不足以容纳新写入数据时移除最近最少使用的key。
  • allkeys-random:从数据集中任意选择数据淘汰。
  • no-enviction:当内存不足以容纳新写入数据时,新写入操作会报错。

Redis为何在内存中存放数据?
Redis为了保证效率,数据都是缓存在内存中。Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。简单来说 Redis 就是一个数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的,所以存写速度非常快,因此 Redis 被广泛应用于缓存方向。Redis 也经常用来做分布式锁。

Redis中哈希槽的概念
Redis没有使用一致性哈希这个概念,而是引入了哈希槽。在Redis集群中共有16384个哈希槽,然后每个key通过哈希函数crc16()将key名转化成一个长整型数字再对16384取余,最终决定这个key存储的哈希槽。而每个Redis实例负责维护一部分哈希槽,所有实例共同维护所有的哈希槽。使用哈希槽最显而易见的特点就是Redis实例的增加或者移除很方便,而不需要暂停所有Redis实例服务。

Redis分区方案
数据分片方式一般有三种:客户端分片、代理分片和服务器分片。具体可以查看文章:http://mp.weixin.qq.com/s?__b...

客户端分片

定义:客户端自己计算key需要映射到哪一个Redis实例。

优点:客户端分片最明显的好处在于降低了集群的复杂度,而服务器之间没有任何关联性,数据分片由客户端来负责实现。

缺点:客户端实现分片则客户端需要知道当前集群下不同Redis实例的信息,当新增Redis实例时需要支持动态分片,多数Redis需要重启才能实现该功能。

代理分片

定义:客户端将请求发送到代理,代理通过计算得到需要映射的集群实例信息,然后将客户端的请求转发到对应的集群实例上,然后返回响应给客户端。

优点:降低了客户端的复杂度,客户端不用关心后端Redis实例的状态信息。

缺点:多了一个中间分发环节,所以对性能有些取的损失。

服务器分片

定义:客户端可以和集群中任意Redis实例通信,当客户端访问某个实例时,服务器进行计算key应该映射到哪个具体的Redis实例中存储,如果映射的实例不是当前实例,则该实例主动引导客户端去对应实例对key进行操作。这其实是一个重定向的过程。这个过程不是从当前Redis实例转发到对应的Redis实例,而是客户端收到服务器通知具体映射的Redis实例重定向到映射的实例中。当前还不能完全适用于生产环境。

优点:支持高可用,任意实例都有主从,主挂了从会自动接管。

缺点:需要客户端语言实现服务器集群协议,但是目前大多数语言都有其客户端实现版本。

Redis的持久化机制

Redis持久化支持两种不同的持久化操作。 RDB持久化是一次全量备份,备份的是内存数据的二进制序列化格式。而AOF持久化是增量备份,记录的是内存数据修改的指令记录文本。所以AOF持久化生成的日志会随着运行时间变长而变得越来越臃肿,每次重启Redis都需要加载AOF日志进行指令重放,所以需要定期重写AOF日志进行瘦身操作。对于Redis的两种持久化机制的选择,主要还是得针对特定的系统讨论,看是可以牺牲一定的性能使用AOF持久化换取缓存一致性,还是在增删操作频繁时关闭备份,等到Redis空闲手动save做RDB持久化备份。所以其实最佳方案应该是采用混合持久化方案,开启混合持久化后,AOF重写日志时会将RDB持久化的内容写到AOF文件开头,于是在Redis重启时,可以先加载RDB的内容,再对增量的AOF日志进行重放,提升Redis重启的效率。

Redis如何使用分布式锁?

当某个机器占有锁并在Redis中设置key时,将value设置为随机数,在请求处理完毕需要释放锁之前加上一步操作:判断key的value值是否等于之前设置的随机数,如果是代表这个锁占有者还是自己,就可以执行释放锁操作,否则代表锁已经被别人占有,不能执行释放锁操作。由于查询和释放锁的操作非原子性的,所以就需要使用另一种方式:引入Jedis,使用Lua脚本将查询锁和释放锁的两部分逻辑写成脚本,于是Redis执行Lua脚本时,其他机器的所有命令都必须等到Lua脚本执行结束才能执行,所以不可能存在查询锁结束还未释放就被其他机器占领的情况。

如何使用Redis集群?

使用主从复制将主节点的数据同步到从节点去存储,然后使用哨兵模式实现集群的高可用,在主节点宕机则会从从节点中选择一个进行主从切换成主节点继续处理请求。然后当Redis内存不足时,使用分片机制对Redis进行分片存储,可以减轻单台Redis的压力,提升Redis扩展能力和计算能力。

缓存雪崩、缓存击穿、缓存穿透的区别

  • 缓存击穿:高并发的情况下,某个热门key突然过期,导致大量请求在Redis未找到缓存数据,进而全部去访问DB请求数据,引起DB压力瞬间增大。这就是缓存击穿。
  • 缓存穿透:缓存穿透是指查询缓存和DB中都不存在的数据。比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。
  • 缓存雪崩:缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。这就叫做缓存雪崩。

欢迎关注公众号:程序猿周先森。

file

本文由博客一文多发平台 OpenWrite 发布!

逆月翎
78 声望4 粉丝

目前从事Node服务端开发,对前后端都有涉猎,喜欢自己捣鼓技术。