一.前置知识:Redis的引出
关于磁盘和内存
磁盘 - 寻址一般是ms级别,带宽 - MB/GB级别
内存 - 寻址ns级别,带宽很大
结论:磁盘比内存在寻址上慢了十万倍
********************************************************
I/O buffer:成本问题
磁盘有磁道和扇区的概念,一扇区512byte,带来成本变大,在读取数据时,无论你读多小,都会至少从磁盘拿4k数据。
Redis的引出
1.基于文件的存储方式
最早我们把数据存在于文件里,比如data.txt,随着文件变大,读写速度越慢,此时磁盘i/o成为瓶颈
2.基于关系型数据库的存储方式
随之引入关系型数据库的概念,使用4k(可以定义大,不要定义小)的data page存储数据,每次读取都是最小4k,和操作系统保持一致,此时读取数据仍是全量I/O,所以读取速度不会有提升,引出了索引的概念,数据中的一列提取出来,存到新的data page中,比如id列。
关系型数据库建表必须给出schema类型,使得宽度统一(为空的数据可以用0填充)倾向于行级存储,有利于数据的增删改,不用移动数据,只需要修改即可。
数据库还会在内存中维护一个B+Tree,命中索引后把对应的data page读进内存,找到对应数据的data page读进内存。
至此关系型数据库库充分利用了内存和磁盘的特点,提高了读取数据的性能。
3.基于内存的存储方式
SAP HANA,是一款完全基于内存的关系型数据库,性能极高,但是价格极贵,在这里要补充一点是,数据在磁盘和内存中的体积不一样,因为磁盘中没有指针的概念,比如内存中可以使用指针指向同一个对象,但是磁盘不可以
4.基于缓存的存储方式
由于基于内存和硬盘各有优劣,所以取一个这种的方式,则使用缓存,比如memcache,redis
补充一个关于DB的技术对比网站:https://db-engines.com/en/
二.Redis介绍,源自官网:http://redis.cn/
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
三.Memcache和Redis的对比
Memcache - Key,value
Redis - Key,value
1.IO角度
Memcache在获取数据时需要获取全量数据,比如说完整的Json
Redis可以又Redis Server提供的方法取得对应的数据
由此从网络IO的角度说减少了Server端网卡IO的压力
2.客户端获取数据的角度
Memcache获取Json数据时需要在Client段解析,增加了计算压力
Redis可以直接获得对应的数据
总结:计算向数据移动的概念
四.NIO原理相关
Redis的调用模型:
1.client与server握手
2.请求发送先到达的是kernel(内核)
3.kernel使用系统调用epoll遍历,谁发送了数据就处理谁
Kernel发展的历程(IO):
1.BIO
在BIO时期socket是阻塞的,接到请求后,server端都会针对每一个请求抛出一个线程,然而一个服务器对文件描述符fd和线程数都是有上限的,并且这种模式下,CPU的性能也无法充分发挥,计算机硬件难以充分利用
抛出过多线程会产生的问题:
1.一个线程的成本大概1mb左右,只包括线程栈不包括堆,堆是共享的,这是内存成本
2.线程过多,CPU忙于调度,造成CPU浪费
2.NIO
下载/查看帮助页
yum install man man-pages
man 2 read
#2类文档 - 系统调用 - read方法
如图:
ps -fe|grep redis
cd /proc/5638/fd
如图:
man 2 socket
如图:
int socket()中,参数type的类型,有SOCK_NONBLOCK,非阻塞IO
随着kernel的升级,产生了NIO,当client连接时,由于io是非阻塞的,所以只需要一个线程就可以处理所有请求,此时在应用程序中轮询遍历所有请求,如果请求中需要处理数据,则做对应处理。这种NIO模型,轮询发生在用户空间,是一种同步非阻塞IO
3.NIO - SELECT多路复用
参考上一种NIO模型,如果用户进程轮询调用1000次kernel,会产生成本较高的问题,于是kernel产生了新的系统调用 - select
man 2 select
如图:
此时使用的多路复用技术,用户进程调用select,传入这1000个文件描述符,kernel会把其中有数据的fd返回给用户进程,进程再根据返回的fd调用read方法,减少了用户态的轮询。
4.NIO - EPOLL
上述的多路复用的处理速度已经很快了,但是文件描述符相关的数据在用户态和内核态拷贝(根据上图 是一个考来考去的过程),也会成为累赘,那么怎么解决呢,kernel又提供了系统调用system call:epoll
man 2 mmap #mmap创建一块用户态和内核态共享的空间,双方都可以直接读取,省去了来回拷贝的过程
man epoll #查看epoll
man 2 epoll_create #创建epoll,随之会创建mmap
man 2 epoll_ctl #告知kernel,增加、删除了哪些fd
man 2 epoll_wait #等待kernel告知需要处理哪些fd
1.调用epoll_create,创建用户空间和内核空间的共享区域mmap,mmap中维护了红黑树和链表结构
2.调用epoll_ctl把文件描述符fd放入mmap的红黑树中,kernel会取得这些fd并且当对应的fd需要处理时,把fd放入mmap的链表中
3.应用程序读取链表中的fd,做对应的操作
4.所有文件描述符相关的操作,都在mmap中进行,省去了拷贝fd的操作。