1.关系型数据库
建表:必须给出schema
类型:字节宽度
存:倾向于行级存储
2.数据库表很大,性能下降怎么办
如果表有索引:
增删改查变慢;
查询速度
1.1个或者少量的查询依然很快
2.并发大的时候会受到硬盘带宽影响速度
3.memcached和redis 的不同
相同:都是key---value的形式
不同:memcached没有类型的概念,redis有类型的区分
4.为什么 Redis 中要使用 I/O 多路复用这种技术
首先,Redis 是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以 I/O 操作在一般情况下往往不能直接返回,这会导致某一文件的 I/O 阻塞导致整个进程无法对其它客户提供服务,而 I/O 多路复用就是为了解决这个问题而出现的
5. redis的io模型
1.epoll(默认主要)
2.select/poll
3.kqueue
6. epoll有诸多优点
-
epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048,
一般来说这个数目和系统内存关系很大
,具体数目可以 cat /proc/sys/fs/file-max 察看。 -
效率提升, Epoll 最大的优点就在于它
只管你“活跃”的连接
,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。 -
内存拷贝, Epoll 在这点上使用了“
共享内存
”,这个内存拷贝也省略了。
7.多路复用I/O(Multiplexing I/O)
- select:能打开的文件描述符个数有限(最多1024个),如果有1k请求,用户进程每次要把1k个文件描述符发送给内核,内核在内部轮询后将可读描述符返回,用户进程再依次读取。因为文件描述符(fd)相关数据需要在用户态和内核态之间拷来拷去,所以性能还是比较低;
- poll:可打开的文件描述符数量提高,因为用链表存储,但性能仍然不够,和select一样数据需要在用户态和内核态拷来拷去;
3. epoll(Linux下多为技术):用户态和内核态之间不用文件描述符(fd)的拷贝,而是通过mmap技术实现开辟共享空间,所有fd用红黑树存储,有返回结果的fd放在链接中,用户进程通过链表读取返回结果,伪异步I/O,性能较高。epoll分为水平触发和边缘触发这两种模式,ET是边缘触发,LT是水平触发,一个表示只有在变化的边际触发,一个表示在某个阶段都会触发;
8.epoll给我们提供了3个api
1: int epoll_create(int size);
生成一个 epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的 socket fd 上是否发生以及发生了什么事件。 size 就是你在这个 epoll fd 上能关注的最大 socket fd 数,大小自定,只要内存足够。
2: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );
控制某个 epoll 文件描述符上的事件:注册、修改、删除。参数说明:
epfd 是 epoll_create() 创建 epoll 专用的文件描述符。相对于 select 模型中的 FD_SET 和 FD_CLR 宏;
op就是你要把当前这个套接口fd如何设置到epfd上边去,一般由epoll提供的三个宏指定:EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD。
fd: 当事件发生时操作的目标套接口。
event指针就是你要给这个套接口fd绑定什么事件。
3: int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);
等待 I/O 事件的发生;参数说明:
epfd: 由 epoll_create() 生成的 Epoll 专用的文件描述符;
epoll_event: 用于回传代处理事件的数组;
maxevents: 返回的最大事件数;
timeout: 等待 I/O 事件发生的超时值(毫秒);
epoll_wait返回触发的事件数。
9.epoll的LT和ET模式的区别
LT模式:epoll就是一个快速版poll,可读可写就绪条件和传统poll一致
ET模式:为了避免Starvation,建议
1)文件描述符设置为非阻塞
2)只在read或write返回EAGAIN后,才能调用下一次epoll_wait
3)应用层维护一个就绪链表,进行轮询,可以防止大量IO时在一个描述符上长期read或write(因为只有等到read
或 write返回EAGAIN后才表示该描述符处理完毕)而令其它描述符starve
理解ET的含义后,上面那些操作其实都是显然的。以wirte为例说明,LT时只要有一定范围的空闲写缓存区,每次epoll_wait都是可写条件就 绪,但是ET时从第一次可写就绪后,epoll_wait不再得到该描述符可写就绪通知直到程序使描述符变为非可写就绪(比如write收到 EAGAIN)后,epoll_wait才可能继续收到可写就绪通知(比如有空闲可写缓存)
其实ET相对于LT来说,把文件描述符状态跟踪的部分责任由内核空间推到用户空间,内核只关心状态切换即从未就绪到就绪切换时才通知用户,至于保持就绪 状态的,内核不再通知用户,这样在实现非阻塞模型时更方便,不需要每次操作都先查看文件描述符状态。上述多数内容取自man epoll
10.redis 持久化 RDB
RDB使用的是:fork(系统调用)和copy on write(内核机制)
RDB会要是用fork()创建一个子进程,子进程记录的是创建时间节点的数据。
copy on write: 写时复制
在fork子进程的时候,只拷贝指针,并不发生内存的复制。
只有当其中的某一个进程试图对该区域进行写操作时,内核就会在物理存储器中为子进程开辟一个新的物理页面,将需要写的区域将父进程的内容复制一份给子进程,然后对新的物理页面进行写操作。
这时就是实现了对不同进程的操作而不会产生影响其他的进程,同时也节省了很多的物理存储器。
并且根据经验来看,不可能父子进程将所有数据都改一遍。下图redis也用了这个机制,而且redis的子进程不会去修改数据:
11.快照(RDB)保存过程:
- redis调用fork,现在有了子进程和父进程。
- 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
- 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出(fork一个进程入内在也被复制了,即内存会是原来的两倍)。