Redis
1.介绍
redis是用C语言编写的,高性能非关系型的键值对数据库。与传统数据库不同,它是存在于内存中的,读写速度很快,被广泛用于缓存方向。
问题1:为什么使用redis而不用本地缓存?
本地缓存优势是低依赖,比较轻量,且比使用分布式缓存更加简单。但是优缺点:1.本地缓存对分布式架构不友好,数据无法共享
2.容量跟随服务器限制明显
分布式缓存:
1.缓存单独部署在一台服务器上
2.缓存数据共享,其实就一种数据库,单独放在了一个服务器上,然后其他的服务都去这个缓存数据库拿数据(个人理解)。
3.单独分布式缓存服务的性能,容量和功能都比较强大
因此,分布式缓存redis的优点?
1.基于内存操作,读写速度快
2.单线程,避免切换开销和多线程竞争大问题。单线程是指在处理网络请求(一个或多个客户端连接)的时候只有一个线程处理。redis运行不止一个线程,数据持久化或向slave同步会另起线程
3.支持多种数据类型,string,hash,list,set,zset
4.支持持久化,RDB和AOF,避免数据丢失
5.支持事务,redis的操作是原子性的
6.支持主从复制,主节点会自动将数据同步到从节点,可以进行读写分离。
缺点:
1.对join或其他结构化查询的支持比较差
2.数据库容量受到物理内存的限制,不适合做海量数据的高性能读写,适合的场景主要局限在较小数据量的操作。
因此,为什么要使用redis?
从“高性能”:将一些高频且不经常改变的数据存入缓存中。先访问缓存,没有则访问数据库。可以保证用户下一次访问某些数据的时候,能够直接从缓存中取,直接操作内存,速度很快、
从“高并发”:与访问数据库例如Mysql相比,mysql的QPS在1w左右,redis能够达到10w~30w,直接操作缓存能够承受的请求数量远远大于直接访问数据库。
此外:除了用做缓存,还可以来做分布式锁,限流(redis+LUA脚本),消息队列(list结构,支持消息持久化和ack机制),复杂业务场景(排行榜,社交网络中的共同关注等)
问题2: redis为什么快?
1.内存存储,数据在内存中,读写速度快
2.单线程实现(6.0以前),避免线程切换和锁资源的争用。单线程即一个线程处理所有网络请求。
3.IO多路复用:多个socket请求连接,复用一个线程。redis使用epoll模型。将数据库的开关读写都转化为事件。
4.数据结构简单:redis数据结构专门优化过。
为什么采取IO多路复用的模型?
1.redis是单线程的,所有操作按顺序线性执行,但是读写等IO操作是阻塞的,这会导致期间整个进程无法为其他客户提供服务,因此需要采用IO多路复用
IO多路复用的模型:
select:单个进程能够监视的文件描述符存在最大限制,1024(基于数组结构)。并且采用轮询的方式扫描文件描述符,来查询哪些发生了事件。用户空间和内核空间的复制非常消耗资源(需要复制大量的句柄数据结构)
poll:调用过程和select类似,时间复杂度:O(n)
其和select不同的地方:采用链表的方式替换原有fd_set
设想一下如下场景:有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?
在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。
epoll:在Linux内核中申请一个简易的文件系统(B+树),
(1)调用epoll_create()建立一个epoll对象,在文件系统中分配资源:建立eventpoll结构体:红黑树,存储添加到epoll(第二步)中需要监控的事件。双链表存放通过epoll_wait返回给用户的满足条件的事件。
(2)epoll_ctl中向对象添加100万个连接的套接字,所有添加到epoll中的事件都会与设备驱动程序建立回调关系,当发生相应的事件时会调用这个回调方法(ep_poll_callback,将发生的事件添加到双链表中)
(3)调用epoll_wait收集发生的事件的连接,即检查双链表中是否有epitem元素,不为空则将发生的事件复制到用户态。
问题3:redis之前是单线程,为什么又引入多线程&#