深度redis

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有诸多优点

  1. epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048,
    一般来说这个数目和系统内存关系很大
    ,具体数目可以 cat /proc/sys/fs/file-max 察看。

  2. 效率提升, Epoll 最大的优点就在于它
    只管你“活跃”的连接
    ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。

  3. 内存拷贝, Epoll 在这点上使用了“
    共享内存
    ”,这个内存拷贝也省略了。

7.多路复用I/O(Multiplexing I/O)

  1. select:能打开的文件描述符个数有限(最多1024个),如果有1k请求,用户进程每次要把1k个文件描述符发送给内核,内核在内部轮询后将可读描述符返回,用户进程再依次读取。因为文件描述符(fd)相关数据需要在用户态和内核态之间拷来拷去,所以性能还是比较低;
  2. 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)保存过程:

  1. redis调用fork,现在有了子进程和父进程。
  2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
  3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出(fork一个进程入内在也被复制了,即内存会是原来的两倍)。

12.redis RDB持久化中save和bgsave区别


SAVE 直接调用 rdbSave ,阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。 比较适合关机维护

BGSAVE 则 fork 出一个子进程,子进程负责调用 rdbSave ,并在保存完成之后向主进程发送信号,通知保存已完成。 Redis 服务器在BGSAVE 执行期间仍然可以继续处理客户端的请求。
Save是阻塞方式的;bgsave是非阻塞方式的。

13.AOF持久化

4.0以前
1.如果开启了AOF只会用AOF恢复
2.重写操作删除抵消的命令合并重复的命令,最终也是一个纯指令的日志文件
4.以后
1.AOF中包含RDB全量,增加记录新的写操作
2.重写操作将老的数据RDB添加到aof文件中,将增量的指令的方式Append到AOF,AOF是一个混合体利用了RDB的快利用了日志的全量。也就是说包括了RDB也包括了AOF。
14.Redis过期键的删除策略
1. 定时删除:在设置键的过期时间的同时,创建一个定时器(timer),让定时器在键的过期时间来临时,立即执行对键的删除操作;
2. 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,那就返回该键;
3. 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于删除多少过期键,以及要检查多少个数据库,则由算法决定。
15.redis的底层数据结构
1.String的底层实现:
// 用于记录buf数组中使用的字节的数目
  // 和SDS存储的字符串的长度相等  
	int len;    
  // 用于记录buf数组中没有使用的字节的数目   
	int free;    
  // 字节数组,用于储存字符串
	char buf[];   //buf的大小等于len+free+1,其中多余的1个字节是用来存储’\0’的。  

底层字符串特性?
     1).二进制安全(体现在len这个字段上)
     2).避免频繁的内存分配,进行内存得预分配(体现在上面扩容过程)
     3).兼容c语言函数库(其实底层都会加\0占一个字节作为字符串结尾)

2.哈希底层的实现:数组 + 链表(采用头插法解决冲突,不会像java语言的map一样转化为红黑树),redis会把kay、value封装成一个dictEntry的结构体(dictEntry的key为string类型,value是一个指针,指向一个redisObject对象(不像java语言一样把hashmap的key、value的具体值封装在一起))

      redis为什么要把value封装成一个redisObject对象?

      因为redis支持value的值有多种不同的数据类型

3.list底层实现:底层是链表,有两个list,一个是ziplist,字面意是压缩列表,另一个是quicklist,字面意是快速列表,在redis中直接使用的是quicklist

4.set底层实现: Set是一个特殊的value为空的Hash。

5.zset底层实现:Zset底层是一种跳表SkipList数据结构(跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找,实质就是一种可以进行二分查找的有序链表)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值