存取速度快的原因
-
非阻塞IO: Redis使用了异步非阻塞IO模型,这意味着在处理IO操作时,允许程序在等待 I/O 操作完成的同时继续执行其他任务,不会阻塞整个进程。这使得Redis能够高效地处理大量并发请求,从而提高程序的并发性和响应性。
-
内存存储: Redis是基于内存的数据库,它将数据存储在内存中,这使得数据的读取和写入速度非常快。内存访问速度远远超过了传统的磁盘存储。
-
数据结构简单: Redis支持多种简单而高效的数据结构,如字符串、哈希表、列表、集合、有序集合等。这些数据结构的设计和实现使得存储和读取操作都可以在常数时间内完成,从而保证了高速的存取速度。
-
单线程模型: Redis采用单线程模型处理客户端请求,虽然听起来似乎会影响性能,但实际上通过非阻塞IO和事件循环机制,Redis能够高效地处理大量并发请求,而无需额外的线程上下文切换开销。
-
持久化方式: Redis支持多种持久化方式,包括RDB快照和AOF日志文件。虽然RDB快照方式会触发阻塞,但由于可以异步执行,因此不会对性能产生过大的影响。
-
高效的网络通信: Redis的协议设计非常紧凑,使用了文本协议或RESP(REdis Serialization Protocol),这使得网络通信的开销很小。
-
数据分片: Redis支持数据分片(sharding),可以将数据分散到多个节点上,从而在某些情况下进一步提高读写性能。
-
多路复用技术:Redis 使用了多路复用技术来实现高效的网络通信。多路复用允许一个进程同时监视多个网络套接字,从而在一个线程内处理多个连接,提高网络通信的效率。在 Redis 中,多路复用技术通常与非阻塞IO一起使用,这样可以在一个线程内同时处理多个客户端请求,而不需要为每个连接创建一个独立的线程。
Redis持久化
redis是一个内存数据库,当redis服务器重启,获取电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
redis提供两种持久化方式:
- RDB:快照,通过从服务器保存和持久化
- AOF:日志,操作生成相关日志,并通过日志来恢复数据。couchDB对于数据内容,不修改,只追加,则文件本身就是日志,不会丢失数据。
1、RDB
全称redis database backup file(redis 数据备份文件)持久化
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
缺点:它可能会导致在发生故障时丢失最后一次快照后的所有数据。
注意:
fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,在每次redis服务器启动的时候,会自动把dump.rdb这个文件的键值对 全部读取到内存。
2.AOF 持久化
内存每写一条,就备份一条,时间间隔是1秒钟,缺点:文件大,写操作频繁。
-
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),
-
只许追加文件但不可以改写文件,redis启动之初会读取该文件(aof文件)重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
如果对同一个key进行多次操作,在aof日志中表现的操作记录是n条。日志会将每一步操作都记录,如果要对一个key操作多次,在数据上的表现只有一个,但在日志中会有n条记录。
注意:
- 在dump rdb过程中,aof如果停止同步,dump rdb不会丢失,所有的操作缓存在内存的队列里, dump完成后,统一操作。
- 如果rdb文件和aof文件都存在,优先用aof来恢复数据。
- 持久化时,2种可同时使用,而且推荐这么做。
- 恢复时rdb快,因为其是数据的内存映射,直接载入到内存,而aof是命令,需要逐条执行。
相关名词:
aof重写
aof重写是将内存中的key和value逆化为redis命令重新保存到日志中,就好像是将所执行的操作做的总结,以解决 aof日志过大的问题。
多路复用技术
Redis 使用的多路复用技术主要有两种:
-
select 模型: 在早期版本的 Redis 中,使用的是 select 模型。select 函数允许一个进程同时监视多个文件描述符,一旦其中一个文件描述符可读或可写,select 就会返回并通知进程进行相应的操作。但是 select 模型在文件描述符较多时会有性能问题,因为它在每次调用时都需要遍历所有文件描述符,造成效率低下。
-
epoll 模型: 在支持 epoll 的操作系统中(如 Linux),Redis 使用 epoll 模型来实现多路复用。epoll 提供了更高效的事件通知机制,可以有效处理大量的文件描述符。与 select 模型不同,epoll 模型通过注册事件来监视文件描述符的状态变化,并在有事件发生时通知应用程序。
多路复用的好处在于可以大大减少线程或进程的数量,从而降低上下文切换的开销,并减少系统资源的消耗。这对于高并发的服务器应用非常有益,因为它可以在一个线程内同时处理成百上千个连接,而无需创建大量的线程。
序列化(Serialization)和反序列化
序列化(Serialization)和反序列化(Deserialization)是将对象在内存中的状态转换为可存储或传输的格式,以及将存储或传输格式的数据重新还原为内存中的对象状态的过程。
序列化: 在编程中,当你需要将对象保存到文件、数据库、网络传输等地方时,需要将对象的数据转换成字节序列。这个过程称为序列化。序列化将对象的状态包括属性和数据转换成二进制数据流,以便在需要的时候可以保存或传输。
反序列化: 反序列化是将序列化的数据重新还原成原始的对象状态的过程。这在从文件、数据库、网络传输等地方读取数据并重新创建对象时非常有用。反序列化会将二进制数据流还原成对象,使得你可以继续在程序中操作这些对象。
序列化和反序列化在许多场景中都很有用,比如:
-
持久化存储: 将对象保存到磁盘或数据库中,以便在程序重启后可以恢复状态。
-
分布式通信: 在网络上传输对象数据,例如在客户端和服务器之间进行交互。
-
缓存: 将对象序列化后存储在缓存中,以提高读取速度。
三个问题
缓存穿透、缓存击穿、缓存雪崩
redis的工作概图:
1、缓存穿透
指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。当用户使用这条不存在的数据疯狂发起查询请求的时候,对数据库造成的压力就非常大,甚至可能直接挂掉。这种情况的流程就变成下图这样了:
解决方案:
- 缓存空对象
- 使用布隆过滤器
当数据库中查不到数据的时候,缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候,就可以直接从缓存中拿到,从而达到了减小数据库压力的目的。
缺点:
(1)需要缓存层提供更多的内存空间来缓存这些空对象,当这种空对象很多的时候,就会浪费更多的内存;
(2)会导致缓存层和存储层的数据不一致,即使在缓存空对象时给它设置了一个很短的过期时间,那也会导致这一段时间内的数据不一致问题。
2、缓存击穿
指当缓存中某个热点数据过期了,在该热点数据重新载入缓存之前,有大量的查询请求穿过缓存,直接查询数据库。这种情况会导致数据库压力瞬间骤增,造成大量请求阻塞,甚至直接挂掉。
解决方案:
- 设置key永不过期;
- 使用分布式锁,保证同一时刻只能有一个查询请求重新加载热点数据到缓存中,这样,其他的线程只需等待该线程运行完毕,即可重新从Redis中获取数据。
3、缓存雪崩
指当缓存中有大量的key在同一时刻过期,或者Redis直接宕机了,导致大量的查询请求全部到达数据库,造成数据库查询压力骤增,甚至直接挂掉。
解决方案:
将每个key的过期时间打散即可,使它们的失效点尽可能均匀分布。
数据一致性问题
一般对缓存对应的数据库数据进行新增,修改,删除 操作时会发生。
1、先删除缓存,再操作数据库
一个线程删除缓存,在操作数据库时比如(update set age = 20 原值等于10)另外一个线程进来
查缓存没有(被第一个线程删了)查数据库放入缓存放的还是10,这时候线程一修改数据库完成。结果导致缓存为10,数据库为20,数据不一致性问题。
2、先操作数据库,再删除缓存
缓存数据突然失效,线程一查询没有缓存,去查数据库(age = 10)然后写入缓存中时,线程2执行修改数据库操作(set age = 20)
删除缓存,这时线程一开始写缓存(age = 10),造成了数据不一致问题。
3、延迟双删(选中方案)
1、删除缓存;
2、动数据库;
3、延迟200ms;(作用:等查询方法走完)
4、再删除缓存。