Redis介绍
关系型数据库(如MySQL)的I/O瓶颈通常是由于数据存储和检索操作的频繁性以及磁盘读写速度限制所致。随着互联网应用和数据量的爆炸式增长,传统的关系型数据库在某些场景下可能无法满足高并发和低延迟的需求,特别是在读取密集型或者数据量非常大的情况下。在这种情况下,开发人员开始寻找更高效的解决方案。
Redis产生的背景正是基于这样的需求。Redis是一个基于内存的数据存储系统,具有高性能、低延迟和高并发的特点。相比于传统的关系型数据库,Redis通过将数据存储在内存中,极大地提高了数据的读取和写入速度。在Redis中,数据通常存储在内存中,并通过快速的键值对存储和检索来实现。
Redis的产生背景可以追溯到互联网应用需要处理大量实时数据、缓存和会话管理等需求。随着这些需求的不断增长,传统的关系型数据库在性能方面逐渐暴露出瓶颈。于是,人们开始寻求一种更适合这些需求的解决方案,Redis就是其中之一。
Redis的出现不仅解决了传统关系型数据库的I/O瓶颈问题,还为开发人员提供了更多的工具和机会来构建高性能、可扩展的应用程序。通过将数据存储在内存中,并提供丰富的数据结构和功能,Redis成为了许多互联网公司构建实时应用、缓存系统和消息队列等方面的首选解决方案之一。
根据DB-Engines的K-V数据库排名,Redis一直是最受欢迎的键值存储数据库。
DB-Engines Ranking - popularity ranking of key-value stores
Redis做为一个KV内存NoSQL数据的优点如下:
-
高性能:Redis是一个基于内存的数据库,这意味着它可以提供非常快速的数据读写操作。由于数据存储在内存中,Redis能够实现非常低延迟的访问,适用于需要快速响应的应用场景,如实时数据处理、缓存等。
-
丰富的数据结构支持:Redis支持丰富的数据结构,包括字符串、哈希、列表、集合、有序集合等。这使得开发人员可以更灵活地处理数据,不仅可以简单地存储键值对,还可以实现复杂的数据操作和数据结构。
-
持久化支持:尽管Redis是一个内存数据库,但它也提供了持久化的支持,可以将数据定期写入磁盘,以防止数据丢失。这种持久化支持可以在数据库重启时恢复数据,确保数据的安全性和持久性。
-
高可用性和可扩展性:Redis支持主从复制和分片等机制,可以实现数据的高可用性和可扩展性。通过配置主从复制和分片,可以实现数据的备份和负载均衡,提高系统的稳定性和可靠性。
-
丰富的生态系统和社区支持:Redis拥有一个活跃的开源社区和丰富的生态系统,有大量的第三方工具和库可供使用。这些工具和库可以帮助开发人员更轻松地集成Redis到他们的应用中,并提供更多的功能和特性。
Redis为什么快
我们对Redis的印象就是快,高性能,理论能达到10wqps。这得益于它的线程模型和基于内存的高速读写
Redis的单线程模型具体工作方式如下:
-
Redis它属于内存数据库,数据的读写都在内存中进行,减少了对磁盘IO的依赖
-
多路复用技术的非阻塞IO:Redis使用同步非阻塞I/O操作来处理客户端请求。可以通过单个线程借助系统内核的epoll实现IO多路复用,用极少的线程资源处理大量的并发请求,增加了CPU的利用率。同时在Redis6.0之后当主线程epoll监听到多个可读写的IO事件时,利用多线程并行处理IO读写事件,加快了IO处理的并行度
-
数据处理事件单线程处理:在任何给定的时间点,Redis只有一个线程在执行。这个线程负责处理所有的客户端达到的请求挨个进行处理,避免了多线程间的同步和锁竞争。
虽然Redis采用了单线程模型,但它仍然能够实现高性能和高并发。这是因为Redis的主要瓶颈通常是在CPU或者网络带宽上,而不是在线程调度或者同步开销上。此外,Redis通过使用多路复用技术(如epoll、kqueue等)来同时处理大量的客户端连接,从而提高了系统的吞吐量和并发性能。
所以总结两点,redis为什么快:
-
基于内存单线程,内存快速存取无锁竞争,摆脱了磁盘IO瓶颈
-
使用IO多路复用,提升了处理客户端请求并发能力,摆脱了网络IO的瓶颈
如何理解IO多路复用
简单理解就是一个服务端线程可以同时接受处理来自多个不同网络链路的网络IO请求。
其中多路复用我们分开理解
- 多路:多个客户端连接
- 复用:使用单进程就能够实现同时处理多个客户端的连接
它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件描述符。客户端在操作的时候,会产生具有不同事件类型的 socket。
I/O多路复用经过一下这几个阶段发展
阻塞式IO(Blocking IO)
这里的阻塞是指注册用户进程,从用户进程发起IO读取请求到真正读到数据过程中完全阻塞,用户线程做不了任何事情,CPU资源利用率极低。
非阻塞是IO
这个过程就是用户线程向内核发起网络IO请求,如果当前有就绪可用的IO就开始读取数据并返回,如果没有就绪链路就返回异常,用户线程继续轮询,知道返回可读数据。那么这个过程用户线程全程没有闲着,不停地在询问系统内核是否有可用IO,虽然是非阻塞的,但也效率低下,都在做一些大量重复且没有太多价值的事情。
I/O多路复用
以上两种如果在高并发场景下,如果一个线程只能处理一个网络IO请求,必然考开启多个线程进行,这样避免不了上下文切换的开销,而随着系统内核技术的发展,IO多路复用技术出现,只需要一个用户线程即可处理多个网络IO请求,极大的提高了并发处理的性能。
信号量驱动的IO模型
应用进程先调用系统的sigacation,建立SIGIO信号,接着等待系统内核主动通知是否有准备就绪的可读IO,接下来交给用户进程进行数据读取
异步IO
异步IO是指从等待IO时间准备就绪到IO数据独居都已经在系统内核执行完成,整个过程无需用户进程参与,也不需要读取IO数据,直接用即可,真正的一部IO只有window实现了,linux目前大多只实现了IO多路复用(基于select/poll/epoll)
操作系统内核select/poll/epoll的区别
这三个都是IO多路复用操作系统底层的实现,都是同步非阻塞是的IO模型,这是三个不同的发展阶段
最开始的select
如上图所示,用户进程需要记录所有需要遍历的套接字集合,传给操作系统内核帮我们挨个轮询是否有准备就绪的IO,且linux最大上限是1024连接描述符
poll
本质上原理和select一样,算是select改进版本,但底层还是有系统内核进行轮询实现,主要改进是在文件描述符上限远大于1024
epoll
这是目前主流的linux多路复用底层实现,它相较于select/poll。操作系统内核不在使用主动轮询的方式来确定是否有可用的IO socket,而是基于网络IO事件的主动通知机制,准备就绪的IO事件会被操作系统内核缓存起来(红黑树结构),等待用户进程调用epoll_wait时返回。返回之后任然需要用户进程进行主动读取IO数据