当前位置: 首页 > 后端技术 > Node.js

Redis系列总结——这几点你知道吗?

时间:2023-04-03 14:15:21 Node.js

本文首发于公众号:程序员周先森。本平台不定期更新,喜欢我的文章,请关注我的微信公众号。前面的文章已经介绍了Redis中的几个关键知识点。本文主要总结了Redis系列,简单介绍了Redis常见的面试题。首先我们总结一下前面几篇文章提到的Redis知识点。第1部分:Redis系列开篇,可以一篇文章了解Redis。http://mp.weixin.qq.com/s?__b...第二部分:通过比较Redis和Memcached的区别,解释为什么Redis具有高性能。http://mp.weixin.qq.com/s?__b...第三部分:Redis中最常用的5种数据类型:String、List、Hash、Set、SortSet。本文对Redis常用的五种数据类型进行了详解,并介绍了常见的使用场景。http://mp.weixin.qq.com/s?__b...Part4:如果同一台机器有多个线程抢夺同一个共享资源,同一个线程执行多次就会出现异常。这样的话就会有非线程安全。我们的解决方案通常是使用锁。但是如果有多台机器呢?这时候,我们通常会使用分布式锁来解决分布式环境下共享资源的同步问题。本文介绍单机部署分布式锁的几种常规方案。http://mp.weixin.qq.com/s?__b...第五部分:双库数据一致性维护方案分析,数据一致性维护的一般方案分析。http://mp.weixin.qq.com/s?__b...第六部分:主要针对Redis的内存淘汰机制以及Redis容易引发的三大问题:缓存击穿、缓存穿透、缓存雪崩进行讲解详细介绍并提供行业通用的解决方案。因为涉及到缓存淘汰机制,内容包含了缓存淘汰机制的知识点。http://mp.weixin.qq.com/s?biz...Part7:Redis之所以受到开发者的青睐,其中一个原因就是因为它持久化的特性。如何保证Redis宕机重启后能够恢复数据呢?所以一般情况下,我们需要坚持定时将内存中的数据写入硬盘。Redis支持两种不同的持久化机制:RDB持久化和AOF持久化。http://mp.weixin.qq.com/s?biz...第八部分:约80%的企业使用Redis单机服务。Redis在生产环境中使用单机环境容易出现风险。如果Redis持久化硬盘出现故障,那么持久化备份的数据可能会丢失,所以我们需要解决这个问题,所以我们需要将原来集中式数据库中的数据复制到不同的Redis节点中进行存储。这就是Redis中的主从复制。http://mp.weixin.qq.com/s?__b...第9章:Redis主从复制其实就是将Redis主节点的数据复制到其他Redis从节点进行存储。异常宕机后,如何将Slave节点切换为主节点继续提供服务?Redis主从切换主要分为以下两种方式:手动切换和哨兵模式。http://mp.weixin.qq.com/s?__b...Part10:如果没有分片机制,Redis受限于单机支持的内存容量。Redis的分片机制允许数据被拆分并存储在不同的Redis实例上,每个Redis实例只包含所有键的一个子集。可以减轻单个Redis的压力,提高Redis的扩展和计算能力。http://mp.weixin.qq.com/s?__b...对之前Reids系列文章的简单总结,让我们来看看Reids问的一些比较常见的面试问题。简单介绍RedisRedis是一个key-value存储系统。它支持相对较多的存储值类型,包括string(字符串)、list(链表)、set(集合)、zset(有序集合)和hash(散列类型)。这些数据类型支持push/pop、add/remove、intersection、union、difference等更丰富的操作,而且这些操作是原子的。在此基础上,redis支持多种方式的排序。为了保证效率,数据缓存在内存中。不同的是redis会周期性的将更新的数据写入磁盘或者将修改操作写入附加的记录文件,并在此基础上实现主从同步。简单的说,Redis就是一个数据库,但是与传统数据库不同的是,Redis的数据是存储在内存中的,所以存储和写入的速度都非常快,所以Redis在缓存方向应用的比较广泛。Redis也常用于分布式锁。Redis提供了多种数据类型来支持不同的业务场景。此外,Redis支持事务、持久化、LUA脚本、LRU驱动的事件和多集群解决方案。Redis和Memcached的区别Redis支持常见的数据类型:Redis不仅支持简单的key/value类型的数据,还提供了string(字符串)、list(链表)、set(集合)、zset(有序集)和Storageofhash(散列类型)等数据结构。Memcache仅支持简单数据类型String。Redis支持数据持久化。它可以将内存中的数据保存在磁盘上,重启时可以再次加载使用。Memecache将所有数据存储在内存中。集群模式:Memcached没有原生集群模式,需要依赖客户端向集群分片写入数据;但Redis目前原生支持Cluster模式。Memcached是一种多线程、非阻塞IO多路复用网络模型;Redis采用单线程多路复用IO多路复用模型。Redis中常用的五种数据类型Redis支持相对较多的值类型存储,包括string、list、set、sortedset、hash。这些数据类型支持push/pop、add/remove、intersection、union、difference等更丰富的操作,而且这些操作是原子的。在此基础上,Redis支持多种方式的排序。为了保证效率,数据缓存在内存中。这五种数据类型的具体分析请看:浅谈Redis的五大数据类型。Redis的缓存淘汰策略Redis可以为缓存中存储的数据设置一个过期时间。作为缓存数据库,这是一个非常有用的特性。之前写过一篇前后端交互的文章,说Token或者一些登录信息,尤其是短信验证码,是有时间限制的。按照传统的数据库处理方式,一般都是自己判断过期,这无疑会严重影响项目性能。而有一个很好的方案就是将验证信息存储在Redis中,设置过期时间。如果生存时间设置为30分钟,则数据会在半小时后从Redis中删除。说到删除,Redis是如何删除这些数据的:周期性删除:默认情况下,Redis每隔100ms随机选择一些设置了过期时间的Key,检查是否过期,如果过期则删除。为什么它是随机抽取而不是检查所有键?因为如果你设置了上万个key,每100毫秒检查一次所有存在的key,会对CPU造成很大的压力。Lazydeletion:周期性的删除可能会导致很多过期的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个hash槽,然后每个key通过hash函数crc16()将key名转换成一个长整型数,然后取16384的余数,最终确定存放key的hash槽。每个Redis实例负责维护一些哈希槽,所有实例共同维护所有哈希槽。使用hashslots最明显的特点就是可以非常方便的增加或移除Redis实例,而无需暂停所有Redis实例服务。Redis分区方案中的数据分片一般有三种方式:客户端分片、代理分片和服务器分片。具体可以参考文章:http://mp.weixin.qq.com/s?__b...客户端分片的定义:客户端计算key需要映射到哪个Redis实例。优点:客户端分片最明显的好处是降低了集群的复杂度,服务器之间没有关联,客户端负责实现数据分片。缺点:如果客户端实现分片,客户端需要知道当前集群中不同Redis实例的信息。添加新的Redis实例时,需要支持动态分片。大多数Redis需要重启才能实现这个功能。代理分片定义:客户端向代理发送请求,代理通过计算得到需要映射的集群实例信息,然后将客户端的请求转发给对应的集群实例,然后返回响应给客户端。优点:降低了客户端的复杂度,客户端不需要关心后端Redis实例的状态信息。缺点:多了一个中间分发环节,因此在性能上有一些损失。服务器分片定义:客户端可以与集群中的任何Redis实例进行通信。当客户端访问一个实例时,服务端会计算该key应该映射到哪个具体的Redis实例进行存储。如果映射的实例不是当前实例,该实例会主动引导客户端对该实例对应的key进行操作。这实际上是一个重定向过程。这个过程并不是从当前的Redis实例转发到对应的Redis实例,而是服务端通知客户端将具体映射的Redis实例重定向到映射的实例。目前还不能完全适用于生产环境。优点:支持高可用,任何实例都有master和slave,master挂了slave会自动接管。缺点:需要客户端语言来实现服务器集群协议,但是目前大部分语言都有自己的客户端实现。Redis持久化机制Redis持久化支持两种不同的持久化操作。RDB持久化是全量备份,备份是内存数据的二进制序列化格式。AOF持久化是增量备份,记录内存数据修改的指令记录文本。所以AOF持久化产生的日志会随着运行时间变长而越来越臃肿。每次重启Redis都需要加载AOF日志进行命令重放,所以需要定期重写AOF日志进行瘦身操作。对于Redis的两种持久化机制的选择,主要针对具体的系统进行讨论,看是否可以牺牲一定的性能,使用AOF持久化来换取缓存的一致性,或者在增删改查时关闭备份比较频繁,等Redis空闲了再手动保存做RDB持久化备份。所以其实最好的方案应该是采用混合持久化方案。开启混合持久化后,AOF重写日志时,会将RDB持久化的内容写入AOF文件的开头。因此,当Redis重启时,可以先加载RDB内容。然后重放增量AOF日志,提高Redis重启效率。Redis如何使用分布式锁?当一台机器在Redis中占用锁并设置一个key时,将value设置为一个随机数,在请求处理完成后释放锁前增加一个步骤:判断key的值是否等于之前设置的随机数,如果代表锁的拥有者还是自己,则可以进行释放锁操作,否则说明锁已经被其他人占用,无法进行释放锁操作。由于查询和释放锁的操作是非原子的,所以需要另一种方法:引入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宕机。这称为缓存雪崩。欢迎关注公众号:程序员周先森。本文由博客多发平台OpenWrite发布!