阿里云面试准备

1、MySQL索引
索引跟目录的作用类似,在数据库表记录中,利用索引,可以快速过滤查找到数据记录;
B-tree索引
B-tree 索引普遍存在于存储引擎中,他使用B-tree数据结构来存储数据,每个叶子节点都会包含下一个节点的指针,非常方便查询数据;
B-tree 适用于全键值,键值范围,或者前缀查找
哈希索引
哈希索引基于哈希表实现,对于每一行数据,存储引擎会对所有的索引列计算一个哈希码,然后存储引擎会基于这个哈希码来查找数据。
全文索引
全文索引在几种索引结构类型中比较特殊,他查找的是文本中关键词

索引是建立在系统文件上的,会占用一定的内存空间,另外数据在更新的时候也会去维护索引,消耗内存,所以索引一定要正确的使用,索引并不是越多越好
不建议使用索引的情况
1.区分度不是很大的字段,例如 性别 sex
2. 频繁更新的字段
3. 字符串类型的字段 或者 文本类型的字段
4. 不在where列中出现的索引
2、B+树和B树对比的好处
B+树相比B树的优势:

1.单一节点存储更多的元素,使得查询的IO次数更少;
  2.所有查询都要查找到叶子节点,查询性能稳定;
  3.所有叶子节点形成有序链表,便于范围查询。
B树的范围查找需要不断依赖中序遍历。首先二分查找到范围下限,在不断通过中序遍历,知道查找到范围的上限即可。整个过程比较耗时。
而B+树的范围查找则简单了许多。首先通过二分查找,找到范围下限,然后同过叶子结点的链表顺序遍历,直至找到上限即可,整个过程简单许多,效率也比较高。
B+树中间节点没有卫星数据(索引元素所指向的数据记录),只有索引,而B树每个结点中的每个关键字都有卫星数据;这就意味着同样的大小的磁盘页可以容纳更多节点元素,在相同的数据量下,B+树更加“矮胖”,IO操作更少
B树的查找只需找到匹配元素即可,最好情况下查找到根节点,最坏情况下查找到叶子结点,所说性能很不稳定,而B+树每次必须查找到叶子结点,性能稳定
3.乐观锁和悲观锁
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
两种锁的使用场景
从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
4.sychronized原理,和ReentrantLock的区别
相同点:
加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的。
功能区别:
这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。而ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成
便利性:很明显Synchronized的使用比较方便简洁,并且由编译器去保证锁的加锁和释放,而ReenTrantLock需要手工声明来加锁和释放锁,为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
锁的细粒度和灵活度:很明显ReenTrantLock优于Synchronized
sychronized原理
Synchronized经过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

5.堆和栈的区别,在内存中分别是位于哪个方向?
在函数中定义的基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
堆内存用于存放由new创建的对象和数组。
堆和栈都是java用来在内存中存放数据的地方,与C++不同的是,java自动管理堆和栈,程序员不能自行设置堆和栈。
1、内存分配方面:
堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
栈:由编译器(Compiler)自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、申请方式方面:
堆:需要程序员自己申请,并指明大小。
栈:由系统自动分配。 例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间。
3、系统响应方面:
堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
4、大小限制方面:
堆:是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
5、效率方面:
堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片
栈:由系统自动分配,速度较快。但程序员是无法控制的。
6、存放内容方面:
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈,然后是函数中的局部变量。注意: 静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
7、存取效率方面:
堆:char *s1 = “Hellow Word”;是在编译时就确定的;
栈char s1[] = “Hellow Word”;是在运行时赋值的;用数组比用指针速度要快一些,因为指针在底层汇编中需要用edx寄存器中转一下,而数组在栈上直接读取。
6.TCP连接过程
在这里插入图片描述
整个流程为:
客户端主动打开,发送连接请求报文段,将SYN标识位置为1,Sequence Number置为x(TCP规定SYN=1时不能携带数据,x为随机产生的一个值),然后进入SYN_SEND状态
服务器收到SYN报文段进行确认,将SYN标识位置为1,ACK置为1,Sequence Number置为y,Acknowledgment Number置为x+1,然后进入SYN_RECV状态,这个状态被称为半连接状态
客户端再进行一次确认,将ACK置为1(此时不用SYN),Sequence Number置为x+1,Acknowledgment Number置为y+1发向服务器,最后客户端与服务器都进入ESTABLISHE
① A 的 TCP 向 B 发出连接请求报文段,其首部中的同步比特 SYN 应置为1,并选择序号 x,表明传送数据时的第一个数据字节的序号是 x(设置初始段序号SEQ = x ,例如SEQ = 26 500)。
② B 的 TCP 收到连接请求报文段后,如同意,则发回确认。
B 在确认报文段中应将 SYN 置为 1,其确认号ACK应为 x + 1(ACK 26 501),同时也给出自己的选择序号 y(设置初始段序号SEQ = y ,例如SEQ = 29 010)。
③ A 收到此报文段后,向 B 给出确认,其确认号应为 y + 1(ACK = 29011)。
A 的 TCP 通知上层应用进程,连接已经建立。

为什么要三次握手?
即客户端发出的第一个连接请求报文段并没有丢失,只是在某些网络节点长时间滞留了,以至于延误到连接释放以后的某个时间点才到达服务端。本来这个连接请求已经失效了,但是服务端收到此失效的连接请求报文段后,就误认为这是客户端又发出了一次新的连接请求。于是服务端又向客户端发出请求报文段,同意建立连接。假定不采用三次握手,那么只要服务端发出确认,连接就建立了。
由于现在客户端并没有发出连接建立的请求,因此不会理会服务端的确认,也不会向服务端发送数据,但是服务端却以为新的传输连接已经建立了,并一直等待客户端发来数据,这样服务端的许多资源就这样白白浪费了

在这里插入图片描述
当客户端没有数据再需要发送给服务端时,就需要释放客户端的连接,这整个过程为:
客户端发送一个报文给服务端(没有数据),其中FIN设置为1,Sequence Number置为u,客户端进入FIN_WAIT_1状态
服务端收到来自客户端的请求,发送一个ACK给客户端,Acknowledge置为u+1,同时发送Sequence Number为v,服务端年进入CLOSE_WAIT状态
服务端发送一个FIN给客户端,ACK置为1,Sequence置为w,Acknowledge置为u+1,用来关闭服务端到客户端的数据传送,服务端进入LAST_ACK状态
客户端收到FIN后,进入TIME_WAIT状态,接着发送一个ACK给服务端,Acknowledge置为w+1,Sequence Number置为u+1,最后客户端和服务端都进入CLOSED状态

7.拥塞控制
即对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。而拥塞控制的目的就是提高网络利用率\降低丢包率\保证网络资源的对每条数据流的公平性.
主要方法有:
慢启动、拥塞避免、快速重传和快速恢复
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
慢开始算法:
在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。为防止拥塞窗口cwnd的增长引起网络拥塞,还需要另外一个变量,慢开始门限状态变量ssthresh
在这里插入图片描述
拥塞避免算法:

1)让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的cwnd 拥塞窗口cwnd加1cwnd ,而不是加倍cwnd 。拥塞窗口cwnd按线性规律缓慢增长
2)不论是慢开始还是拥塞避免只要网络出现拥塞(没有按时到达)时,就把ssthresh的值置为出现拥塞时的拥塞窗口的一半(但不能小于2),以及cwnd置为1,进行慢开始。 目的是迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕

快速重传(Fast retransmit):要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等到自己发送数据时捎带确认
快速恢复:
当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。然后立即重传丢失的报文段,并将CWND设置为新的ssthresh(减半后的ssthresh)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值