美团面试题

美团一面:
中间省略掉大概几个问题,因为我不记得了,下面记得的基本都是我没怎么答好的。
1、了解SOA,微服务吗?
什么是微服务?
1、由一些独立的服务共同组成系统
2、单独部署,跑在自己的进程里
3、每个服务为独立的业务开发
4、分布式的管理
特点
1、组件化与多服务
2、围绕业务组建团队
3、做产品而不是做项目
4、智能端点与弱管道
5、去中心化管理数据
6、基础设施自动化
优点
1、提升开发交流,每个服务足够内聚,足够小,代码容易理解
2、服务独立测试、部署、升级、发布
3、容易扩大开发团队,可以针对每个服务组建开发团队
4、提高容错性,一个服务的内存泄露并不会让整个系统瘫痪
5、新技术的应用,系统不会被长期限制在某个技术栈上
缺点
1、提高了系统的复杂度
2、开发人员要处理分布式系统的复杂性
3、服务之间的分布式通信问题
4、服务的注册与发现问题
5、服务之间的分布式事务问题
6、数据隔离带来的报表处理问题
7、服务之间的分布式一致性问题
8、服务管理的复杂性,服务的编排
9、不同服务实例的管理

2、分布式系统如何负载均衡?如何确定访问的资源在哪个服务器上?
负载均衡将请求派发到网络中的一个或多个节点上进行处理。
硬件负载均衡,即通过在服务器间安装专门的硬件来进行负载均衡工作
软件负载均衡,通过服务器上安装的软件来对请求进行分配派发。
一.轮询。
给每个请求标记序号,之后把请求依次分发到服务器节点上。

适用集群各节点提供服务能力相等,且无状态的场景。
但是实际情况中各个节点所能提供服务的能力并不相同。
加权轮询,对每个节点加上了权重属性,
但是合适的权重难以随实际情况的改变而改变。
二.随机。
每次请求随机分发给服务器节点。

缺点是在同一截面上发生碰撞的概率比较高;
在非对等集群组网,或者硬件配置差异较大时,各节点负载不均匀
通过加权随机进行提升。
三.最小响应时间。
根据每个服务器处理请求的时间和平均时间的差值,来动态调整分发权重,
可以保证服务延时大的服务器,处理更少的请求。

该策略可以保证处理请求能力强的服务器接收到更多的请求。
通过动态权重缩小服务调用时延的震荡范围,是所有请求的处理时间接近平均值。
但计算平均响应时间会耗费时间,滞缓请求的分发。
改进的只计算最近若干次的平均时间的策略。
四.粘滞连接。
主要适用于有状态的服务,同一客户端每次访问,处理请求的服务器为同一台。

客户端首次和服务端交互时,将链路与相应处理的服务器进行绑定。
当原有服务器宕机后,返回不可用消息,服务端重新为客户端绑定服务器。
五.哈希。
哈希值是32位的正整数,其值的分布范围按照一定规则分配给多个虚拟节点;
虚拟节点数量至少应是实际节点的两倍;
实际节点通过哈希值再对虚拟节点进行瓜分;

对“请求”取哈希值,分发到相应虚拟节点,虚拟节点对应的实际节点来处理“请求”。
相同参数的请求,总是发送到同一个服务提供者。
当某一台服务器宕机时,原本发往该节点的请求,基于虚拟节点,平摊到其他提供者,
从而避免引起集群剧烈变动。

3、设计一个分布式负载均衡缓冲系统,如何快速定位到是那个服务器(使用key分段、一致性hash)
一致性Hash算法是对2³²取模
1.一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0-2³²-1(即哈希值是一个32位无符号整形)
2.下一步将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置
3.接下来使用如下算法定位数据访问到相应服务器:将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器
一致性hash算法的倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题。
为了解决这种数据倾斜问题,一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。定位到虚拟节点的映射即定位到对应的节点上。

4、如何保证缓冲区和数据库之间的强一致性(使用分布式锁解决)
每次处理写请求时,将会经过如下几个步骤:
1.首先针对要写入的数据设置一个状态,失败则结束,成功则清除缓存。
2.如果设置状态成功,则直接清除缓存,失败则解除状态并结束,成功则写DB。
3.清除缓存后,再写入DB,失败则解除状态并结束,成功则回填缓存。
4.DB写入成功以后,把新值回填缓存,失败则解除状态并结束,成功则解除状态并结束。
5.回填成功,解除状态并结束。
处理读请求时,经过以下几步:
1.直接从缓存读取数据,成功则结束,失败则读DB。
2.从DB读取数据,失败则返回,成功则获取状态。
3.根据从DB读取到的数据判断该数据对应的状态,如果没有状态,则回填缓存并结束,如果有状态,则直接结束。

5、HashMap高并发情况下会出现什么问题,(扩容导致环路,死循环问题,fast-fail)
JDK1.8采用的hash函数 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
hashmap计算index方法:index = hash & (tab.length – 1)
1.HashMap进行存储时,假设size超过当前最大容量*负载因子时候会发生resize。
而这段代码中又调用了transfer()方法,而这种方法实现的机制就是将每一个链表转化到新链表,而且链表中的位置发生反转,而这在多线程情况下是非常easy造成链表回路。从而发生get()死循环。
假如有两个线程P1、P2,以及链表 a=》b=》null
1、P1先运行,运行完"Entry<K,V> next = e.next;"代码后发生堵塞,或者其它情况不再运行下去,此时e=a。next=b
2、而P2已经运行完整段代码,于是当前的新链表newTable[i]为b=》a=》null
3、P1又继续运行"Entry<K,V> next = e.next;"之后的代码,则运行完"e=next;"后,newTable[i]为a《=》b。则造成回路,while(e!=null)一直死循环
2.一个线程迭代时,另一个线程做插入删除操作,造成迭代的fast-fail。
根本原因是没有锁机制,一个线程的修改会影响另一个线程的操作。

为什么HashMap使用红黑树,不用VAL(完全平衡二叉树)
AVL树比红黑树保持更加严格的平衡。AVL树中从根到最深叶的路径最多为~1.44 lg(n + 2),而在红黑树中最多为~2 lg(n + 1)。
因此,在AVL树中查找通常更快,但这是以更多旋转操作导致更慢的插入和删除为代价的。因此,如果您希望查找次数主导树的更新次数,请使用AVL树。
AVL以及RedBlack树是高度平衡的树数据结构。它们非常相似,真正的区别在于在任何添加/删除操作时完成的旋转操作次数。
两种实现都缩放为a O(lg N),其中N是叶子的数量,但实际上AVL树在查找密集型任务上更快:利用更好的平衡,树遍历平均更短。另一方面,插入和删除方面,AVL树速度较慢:需要更高的旋转次数才能在修改时正确地重新平衡数据结构。
对于通用实现(即先验并不清楚查找是否是操作的主要部分),RedBlack树是首选:它们更容易实现,并且在常见情况下更快 - 无论数据结构如何经常被搜索修改。

ConcurrentHashMap原理
底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容

Volatile原理
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
  在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
  当对非 volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的 CPU cache 中。
  而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。
当一个变量定义为 volatile 之后,将具备两种特性:
  1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。
  2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。
volatile 性能:
  volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

6、说一说在浏览器中输入一个url后,直到浏览器显示页面的过程中发生了什么(我主要说了DNS,然后他有接着问了DNS的细节,然后就是ARP路由,然后服务器处理,返回,浏览器呈现,获取html中的依赖资源)
互联网内各网络设备间的通信都遵循TCP/IP协议,利用TCP/IP协议族进行网络通信时,会通过分层顺序与对方进行通信。分层由高到低分别为:应用层(HTTP客户端)、传输层(TCP)、网络层(IP)、数据链路层(网络)。发送端从应用层往下走,接收端从数据链路层往上走。
1.在浏览器中输入url
2.应用层DNS解析域名
客户端先检查本地是否有对应的IP地址,若找到则返回响应的IP地址。若没找到则请求上级DNS服务器,直至找到或到根节点。
DNS中递归查询和迭代查询的区别
递归查询一般客户机和服务器之间属递归查询,即当客户机向DNS服务器发出请求后,若DNS服务器本身不能解析,则会向另外的DNS服务器发出查询请求,得到结果后转交客户机。
迭代查询(反复查询)一般DNS服务器之间属迭代查询,如:若DNS2不能响应DNS1的请求,则它会将DNS3的IP给DNS2,以便其再向DNS3发出请求。
以一个DNS请求解析为例:
1)用户发起域名请求到dnsA,这时dnsA有这个记录,将结果返回给用户,这个过程是递归查询。
2)用户发起域名请求到dnsA,这时dns没有这个记录,它去向dnsB问有没有这个记录,以此类推,直到把结果返回给用户,这个过程是递归查询。
3)用户发起域名请求到dnsA,这时dnsA没有这个记录,它告诉用户,我没有这个记录,你去问dnsB吧,这个过程是迭代查询。
3.应用层客户端发送HTTP请求
HTTP请求包括请求报头和请求主体两个部分,其中请求报头包含了至关重要的信息,包括请求的方法(GET / POST)、目标url、遵循的协议(http / https / ftp…),返回的信息是否需要缓存,以及客户端是否发送cookie等。
4.传输层TCP传输报文
位于传输层的TCP协议为传输报文提供可靠的字节流服务。它为了方便传输,将大块的数据分割成以报文段为单位的数据包进行管理,并为它们编号,方便服务器接收时能准确地还原报文信息。TCP协议通过“三次握手”等方法保证传输的安全可靠。
5.网络层IP协议查询MAC地址
IP协议的作用是把TCP分割好的各种数据包传送给接收方。而要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址。IP地址和MAC地址是一一对应的关系,一个网络设备的IP地址可以更换,但是MAC地址一般是固定不变的。ARP协议可以将IP地址解析成对应的MAC地址。
6.数据到达数据链路层
在找到对方的MAC地址后,就将数据发送到数据链路层传输。这时,客户端发送请求的阶段结束
7.服务器接收数据
接收端的服务器在链路层接收到数据包,再层层向上直到应用层。这过程中包括在运输层通过TCP协议讲分段的数据包重新组成原来的HTTP请求报文。
8.服务器响应请求
服务接收到客户端发送的HTTP请求后,查找客户端请求的资源,并返回响应报文,响应报文中包括一个重要的信息——状态码。
9. 服务器返回相应文件
请求成功后,服务器会返回相应的HTML文件。接下来就到了页面的渲染阶段了。
现代浏览器渲染页面的过程是这样的:解析HTML以构建DOM树 –> 构建渲染树 –> 布局渲染树 –> 绘制渲染树。

7、字符串中句子的反转(比如ABC DEF,输出DEF ABC)(很简单,可以先反转整个字符串,然后反转单词,或者先将句子切分为单词,然后反转);

8、给任意二叉树的所有结点加next指针(这个有原题,也可以参考按层打印二叉树)。
仔细考虑题目要求,发现是在同一层上进行操作,应该想到用层次法对二叉树进行操作,思路如下,判断当前节点如果不是叶子结点,说明他有两个孩子结点,将左孩子结点指向右孩子结点,再判断该结点有没有next结点,如果有,那他也有两个孩子结点,就可以把当前结点的右孩子结点指向next结点的左孩子结点,然后再判断next结点的情况,判断完这一层的,再去判断下一层。

9、用过反向代理吗?
反向代理隐藏了真实的服务端,当我们访问 www.baidu.com 的时候,就像拨打 10086 一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,www.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到提供真实计算的服务器那里去。Nginx 就是性能非常好的反向代理服务器,它可以用来做负载均衡。

美团二面:
1、进程间共享内存的方式有哪些?(8种)
1.无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2.高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。
3.有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
4.消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5.信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6.信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
7.共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
8.套接字( socket ) : 套解字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

2、linux下如何查看网络端口状态(netstat),如何查看内存使用情况(top)

3、ConcurrentHashMap如何扩容?
JDK8的HashMap扩容
在JDK8里面,HashMap的底层数据结构已经变为数组+链表+红黑树的结构了,因为在hash冲突严重的情况下,链表的查询效率是O(n),所以JDK8做了优化对于单个链表的个数大于8的链表,会直接转为红黑树结构算是以空间换时间,这样以来查询的效率就变为O(logN)。
JDK7里面是先判断table的存储元素的数量是否超过当前的threshold=table.length*loadFactor(默认0.75),如果超过就先扩容,在JDK8里面是先插入数据,插入之后在判断下一次++size的大小是否会超过当前的阈值,如果超过就扩容。
JDK8的ConcurrentHashMap扩容
ConcurrentHashMap的JDK8与JDK7版本的并发实现相比,最大的区别在于JDK8的锁粒度更细,理想情况下talbe数组元素的大小就是其支持并发的最大个数,在JDK7里面最大并发个数就是Segment的个数,默认值是16,可以通过构造函数改变一经创建不可更改,这个值就是并发的粒度,每一个segment下面管理一个table数组,加锁的时候其实锁住的是整个segment,这样设计的好处在于数组的扩容是不会影响其他的segment的,简化了并发设计,不足之处在于并发的粒度稍粗,所以在JDK8里面,去掉了分段锁,将锁的级别控制在了更细粒度的table元素级别,也就是说只需要锁住这个链表的head节点,并不会影响其他的table元素的读写,好处在于并发的粒度更细,影响更小,从而并发效率更好,但不足之处在于并发扩容的时候,由于操作的table都是同一个,不像JDK7中分段控制,所以这里需要等扩容完之后,所有的读写操作才能进行,所以扩容的效率就成为了整个并发的一个瓶颈点,好在Doug lea大神对扩容做了优化,本来在一个线程扩容的时候,如果影响了其他线程的数据,那么其他的线程的读写操作都应该阻塞,但Doug lea说你们闲着也是闲着,不如来一起参与扩容任务,这样人多力量大,办完事你们该干啥干啥,别浪费时间,于是在JDK8的源码里面就引入了一个ForwardingNode类,在一个线程发起扩容的时候,就会改变sizeCtl这个值。

4、知道java的异常吗?
Error类代表了编译和系统的错误,不允许捕获;
Exception类代表了标准Java库方法所激发的异常。Exception类还包含运行异常类Runtime_Exception和非运行异常类Non_RuntimeException这两个直接的子类。

算术异常类:ArithmeticExecption
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组负下标异常:NegativeArrayException
数组下标越界异常:ArrayIndexOutOfBoundsException
违背安全原则异常:SecturityException
文件已结束异常:EOFException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException

5、运行时异常如果不处理会怎么样?应该怎么处理运行时异常?

6、写代码:给你5000万个int,求出前1000个最大的数,有2G内存。(我刚开始以为5000万个int很多,还把G和byte的换算忘了,后来面试官指导才想起来。我的方法是维护一个1000的小根堆,然后遍历数组,总体下来时间复杂度是O(nlg1000))
最大堆:父节点永远比子节点大。
最小堆:父节点永远比子节点小。插入元素后与左侧节点按个比较重排序。

7、给你n个不重复的整数,随机找出m个不重复的整数,要求时间和空间复杂度都是O(m)。(方法很简单,就是每次把取出来的数放到后面,只在前面的数组随机访问就可以了,时间复杂度是O(m),空间复杂度是O(1),不过我刚开始没有想到把选出来的放后面去)。

8、对于SQL慢查询的优化?(主要是从查询语句和数据库表设计两个方面来考虑,查询语句方面可以增加索引,增加查询筛选的限制条件;数据库表设计的时候可以拆分表,设计得更细粒度。但是后来才发现面试官想要的就是查询大量数据的慢查询问题的优化。)

9、用过哪些容器?(tomcat)对比过Tomcat与其他服务器的区别吗?比如nginx?
10、用过动态代理吗?以后会经常用到的。
美团三面:
1、自我介绍,说说你项目中的数据流向(画结构框图)
2、看过什么书?
3、说说深入理解JVM中印象最深刻的章节(我说了JVM内存模型,垃圾回收和类加载);
补充:还问了堆和栈中存的是什么?static修饰的遍历存在哪里?(方法区)

4、说说《Effective Java》中你印象最深的三条和你的理解
5、你觉得你哪一块只是最熟悉(我说了Java的基本数据结构)
6、那你说说HashMap的内部实现;
7、HashMap是线程安全的吗?(不是,ConcurrentHashMap是)
8、那ConcurrentHashMap内部是如何实现的?每个segment是个什么数据结构?(HashTable)
9、你的项目中用到哪些技术?(Spring)
10、说说你用了它的什么?(Spring IOC用的最多)
11、Spring的优点?Spring AOP的原理?Spring如何实现解耦合?
12、对链表了解吗?(我说是List吗)是,(了解ArrayList和LinkedList),那你说说他们的区别?
13、会做链表两个结点的交换吗?(链表反转吗?)是的,你写代码实现把。
14、再写一个,给你一个链表和一个整数k(k大于等于0,小于等于链表长度,链表长度未知),按k步长反转链表(比如1->2->3->4->5->6->7,当k=3的时候结果是3->2->1->6->5->4->7)
15、说说mybatis配置了xml过后是如何完成数据库操作的?

一面:
1.给了一道编程题,从方格中的某一点怎么去到另外的点,有多少种走法
2.数据库联合索引,比如用的是A_B_C,给出几种场景,问在搜索的时候会什么时候会用到索引,讲讲一般怎么对数据库进行优化和explain的用法
3.Integer t = null; int m=t; (抛空指针) System.out.println(t) 其实考的是拆箱和装箱,再编译阶段是把字节码加进去,真正进行拆装箱的对坐还是再运行上
4.error和Exception区别 列举几个常见的错误
二面:
1.死怼虚拟机,新生代和老年代的区别究竟是什么?垃圾回收器的算法有哪些?垃圾回收期的名字有哪些?stop-the-world究竟干嘛啦
2.数据库的索引为什么用树,对数据库有没有什么了解
1、非叶子节点的子树指针与关键字个数相同;
2、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复);
3、为所有叶子节点增加一个链指针;
4、所有关键字都在叶子节点出现(稠密索引). (且链表中的关键字恰好是有序的);
5、非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层;
6、更适合于文件系统;
1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。
2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。
他们认为数据库索引采用B+树的主要原因是:B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。

3.说说集合有哪几种?hashSet的底层是什么?它不安全再哪里?hashmap为什么不安全?(一定要把那2中情况说清楚,环出现了会有什么后果)?如果让你来做,怎么在不加锁的前提下保证它的安全??
4.JMQ是怎么保证我一定会收到别人发的消息
5.nio和aio的区别,一定要知道的很清楚,所有的细节
在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
AIO
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。 在JDK1.7中,这部分内容被称作NIO.2

6.new一个线程池的参数
corePoolSize,线程池里最小线程数
maximumPoolSize,线程池里最大线程数量
RejectedExecutionHandler:队列已满,而且任务量大于最大线程的异常处理策略。
keepAliveTime,空闲的线程保留的时间。
TimeUnit:空闲线程的保留时间单位。
BlockingQueue:阻塞队列,存储等待执行的任务。
ThreadFactory:线程工厂,用来创建线程

7.2个同步shopId让我来做同步怎么同步
8.1000000个数,找K个最大
9.快速排序
10.spring涉及到的设计模式
工厂方法
单例(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
Spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为Spring管理的是是任意的Java对象。
适配器(Adapter)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
包装器(Decorator)
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
代理(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。
观察者(Observer)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
策略(Strategy)
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
模板方法(Template Method)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

三面:
1.了解分布式锁吗,知道怎么用吗
2.知道项目中用到的远程框架它是怎么报警的,比如它怎么能在web页面进行监控,数据怎么上去的,,worker的底层吗,zooker是个什么东西,elastic是个啥
3.平常怎么进行mysql的优化
4.还出了一个概率题,比较简单,我就不说了
5.string是在新生代还是老年代还是永久代,用的虚拟机是什么版本的
6.2个同步shopId让我来做同步怎么同步
7.ibatis和mybatis的不同
iBatis就是myBatis前身,他们只有有很多地方很相似,但是在sqlMap里面已经有很多地方变动了。
然后就是增删改查了,这个两个也是一致的。都是使用的insert delete update select标签。但是iBatis可以使用statement,它是通用的。
但是里面关键字用到的就不一样了,比如:
iBatis的传入参数关键字是:parameterClass,而MyBatis是可以不写的,也可以用parameterType;
iBatis的传出参数关键字是:resultClass,而MyBatis是resultMap。
iBatis:
MyBatis:

接下来是接受参数:IBatis是使用# #和$ KaTeX parse error: Expected 'EOF', got '#' at position 23: …使用方法等同于MyBatis;#̲ #=#{ }, = = ={ }.
然后就是判断语句了,这个也是非常常用和重要的地方。
对于MyBatis的很简单,只要在where 或者if 的标签里面添加test=""就可以了,里面写判断条件了。
但是IBatis的就麻烦了许多了,它将每个都方法都进行了封装。
例如:
isNull:判断property字段是否是null

至于prepend就是代表着添加在动态语句前面。
property就是被比较的属性。

isNull:判断property字段是否不是null

isEqual相当于equals,判断状态值。


isEmpty判断参数是否为Null或者空,满足其中一个条件则其true。
isNotEmpty相反,当参数既不为Null也不为空是其为true。

这些就是非常常用的条件元素的属性了。
下面再来看看循环的:
Iterate:这属性遍历整个集合,并为 List 集合中的元素重复元素体的内容。

Iterate 的属性:
prepend - 可被覆盖的 SQL 语句组成部分,添加在语句的前面(可选)
property - 类型为 java.util.List 的用于遍历的元素(必选)
open - 整个遍历内容体开始的字符串,用于定义括号(可选)
close - 整个遍历内容体结束的字符串,用于定义括号(可选)
conjunction - 每次遍历内容之间的字符串,用于定义 AND 或 OR(可选)

遍历类型为 java.util.List的元素。

例子:

username=#userNameList[]#

MyBatis使用的是ForEach方法。他可以遍历List,,Map三种元素。

foreach属性:
item - 循环体中的具体对象(必选)
collection - 要做foreach的对象(必选)
open - 整个遍历内容体开始的字符串,用于定义括号(可选)
close - 整个遍历内容体结束的字符串,用于定义括号(可选)
separator - 元素之间的分隔符(可选)
index - 在list和数组中,index是元素的序号,在map中,index是元素的key(可选)
例子:

#{item.id}

最后的还有一个非常重要的标签没有介绍。dynamic
dynamic有一个非常重要的作用,那就是去除它的代码块中的第一个prepend里面的字符。
然后他也有自己的prepend标签,作用也是在语句前面添加修饰符,比如where。

而在MyBatis里是.

8.redis的2中持久化方式
由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。redis提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)。那么这两种持久化方式有什么区别呢,改如何选择呢?网上看了大多数都是介绍这两种方式怎么配置,怎么使用,就是没有介绍二者的区别,在什么应用场景下使用。
二者的区别
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
二者优缺点
RDB存在哪些优势呢?
1). 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
2). 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3). 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
4). 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB又存在哪些劣势呢?
1). 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

AOF的优势有哪些呢?
1). 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。
2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

AOF的劣势有哪些呢?
1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。

9.除了jsf还了解什么远程框架,说说对比

一面
被HR带领到工作区前的面试区域,有几对正在进行着面试,很热闹,看来最近美团需求量不小。
一面问题如下,只能记个大概:
自我介绍、过往工作经历
介绍一下项目流程(由于我之前没有任何面试经验,没有get到面试官的点,答得不太好,面试官应该是想让介绍一下怎么设计你的系统框架、用到哪些技术)
乐观锁和悲观锁,应用场景有哪些?什么情况下会发生死锁,怎么处理死锁?
1.悲观锁(一般都是通过锁机制来实现的)

(1)每次去拿数据都会认为别人会修改,所以每次拿数据的时候都会上锁。比如:行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。

2.乐观锁

(1)每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果发生冲突了,则返回用户的错误信息,让用户决定如何去做。(适用于多读的类型,并发大的情况。一般基于数据版本号实现)

(2)冲突检测和数据更新(版本号机制实现)

在数据表中加上一个数据版本号version字段,表示数据被修改的次数,数据被修改时version值会加1,当更新数据时,刚才读到的version值和数据库中的version值相等才可以更新。
乐观锁及悲观锁的应用场景

1.什么时候使用悲观锁?

写入频繁使用悲观锁,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

一旦通过悲观锁锁定一个资源,那么其他需要操作该资源的使用方,只能等待直到锁被释放,好处在于可以减少并发,但是当并发量非常大的时候,由于锁消耗资源,并且可能锁定时间过长,容易导致系统性能下降,资源消耗严重。

2.什么时候使用乐观锁?

读取频繁使用乐观锁,一般乐观锁只用在高并发、多读少写的场景。比如,GIT,SVN,CVS等代码版本控制管理器。

例如:A、B,同时从SVN服务器上下载了hello.java文件,当A完成提交后,此时B再提交,那么会报版本冲突,此时需要B进行版本处理合并后,再提交到服务器。这其实就是乐观锁的实现全过程。如果此时使用的是悲观锁,那么意味者所有程序员都必须一个一个等待操作提交完,才能访问文件,这是难以接受的。

hashMap的原理,由此延伸问红黑树是什么,hash冲突怎么解决
排序算法记得多少
写代码:两个stack实现一个queue
二面
一面结束后,面试官让我稍等,由于没经验,我以为今天面试就结束了,结果等来了第二位面试官,也是技术面。
自我介绍,介绍一下项目流程
进程和线程的区别
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。

线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。

一个程序至少一个进程,一个进程至少一个线程。

为什么会有线程?

每个进程都有自己的地址空间,即进程空间,在网络或多用户换机下,一个服务器通常需要接收大量不确定数量用户的并发请求,为每一个请求都创建一个进程显然行不通(系统开销大响应用户请求效率低),因此操作系统中线程概念被引进。

线程的执行过程是线性的,尽管中间会发生中断或者暂停,但是进程所拥有的资源只为改线状执行过程服务,一旦发生线程切换,这些资源需要被保护起来。
进程分为单线程进程和多线程进程,单线程进程宏观来看也是线性执行过程,微观上只有单一的执行过程。多线程进程宏观是线性的,微观上多个执行操作。

线程的改变只代表CPU的执行过程的改变,而没有发生进程所拥有的资源的变化。

进程线程的区别:

地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。

一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程

执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
线程是处理器调度的基本单位,但是进程不是。
两者均可并发执行。

优缺点:
  线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
  进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
何时使用多进程,何时使用多线程?
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。

Synchronized与Lock的区别
事务隔离级别和实现原理,mysql发生锁死怎么办?
1、读不提交(Read Uncommited,RU)
这种隔离级别下,事务间完全不隔离,会产生脏读,可以读取未提交的记录,实际情况下不会使用。
2、读提交(Read commited,RC)
本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)
3、可重复读(Repeatable Read,RR)【MySQL 默认的级别】
在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同一个事务同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象
4、 串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥

Java的垃圾回收机制
线程池了解多少,线程池的参数有哪些?
HashMap原理(感觉是必问题)
问了三道题:
有100只瓶液体,其中一瓶是毒药,一只小白鼠喝到毒药一周后会死亡。请问给一周时间,至少需要多少只小白鼠能确定哪瓶是毒药?(把瓶子序号变成二进制)
代码实现链表的反转
一个整数数组先升序后降序,给一个整数k,返回它在数组中的index,找不到的话返回-1
三面(部门leader面)
二面和三面中间等了二十多分钟,因为三面面试官在开会。面试官很友好,没怎么问技术问题,看到我用的是oracle数据库,问oracle里面的函数有什么用,我没答很好。接着就是聊聊我应聘的部门在做什么,谈谈我对项目的理解,怎么开展一个项目。

美团一面(电话)

自我介绍
项目介绍
Redis介绍
了解redis源码么
了解redis集群么
Hashmap的原理,增删的情况后端数据结构如何位移
hashmap容量为什么是2的幂次
hashset的源码
object类你知道的方法
hashcode和equals
你重写过hashcode和equals么,要注意什么
假设现在一个学生类,有学号和姓名,我现在hashcode方法重写的时候,只将学号参与计算,会出现什么情况?
往set里面put一个学生对象,然后将这个学生对象的学号改了,再put进去,可以放进set么?并讲出为什么
Redis的持久化?有哪些方式,原理是什么?
讲一下稳定的排序算法和不稳定的排序算法
讲一下快速排序的思想

美团二面(现场)

自我介绍
讲一下数据的acid
什么是一致性
什么是隔离性
Mysql的隔离级别
每个隔离级别是如何解决
Mysql要加上nextkey锁,语句该怎么写
Java的内存模型,垃圾回收
线程池的参数
每个参数解释一遍
然后面试官设置了每个参数,给了是个线程,让描述出完整的线程池执行的流程
Nio和IO有什么区别
Nio和aio的区别
Spring的aop怎么实现
Spring的aop有哪些实现方式
动态代理的实现方式和区别
JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。
总结:1.JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;2.JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

Linux了解么
怎么查看系统负载
linux 查看当前系统的负载情况

uptime
linux uptime命令主要用于获取主机运行时间和查询linux系统负载等信息。
eg:
uptime
02:03:50 up 126 days, 12:57, 2 users, load average: 0.08, 0.03, 0.05
10:19:04 up 257 days, 18:56, 12 users, load average: 2.10, 2.10,2.09
显示内容说明:
10:19:04 //系统当前时间
up 257 days, 18:56 //主机已运行时间,时间越大,说明你的机器越稳定。
12 user //用户连接数,是总连接数而不是用户数
load average // 系统平均负载,统计最近1,5,15分钟的系统平均负载

那么什么是系统平均负载呢?

系统平均负载是指在特定时间间隔内运行队列中的平均进程数。
如果每个CPU内核的当前活动进程数不大于3的话,那么系统的性能是良好的。
如果每个CPU内核的任务数大于5,那么这台机器的性能有严重问题。
如果你的linux主机是1个双核CPU的话,当Load Average 为6的时候说明机器已经被充分使用了。
1可以被认为是最优的负载值。负载是会随着系统不同改变得。
单CPU系统1-3和SMP系统6-10都是可能接受的。
cat /proc/loadavg
0.00 0.01 0.05 2/384 4482
0.00 0.01 0.05 表示最近1分钟,5分钟,15分钟 系统的平均负载; 系统负载越高,代表CPU越繁忙;
2/384 2代表此时运行队列中的进程个数;384 代表系统中进程的总数
4482 代表到此为止创建的最后一个进程的ID.
w
02:14:34 up 126 days, 13:08, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 - 29Jul16 114days 0.63s 0.30s -bash
ceshi pts/0 118.247.5.122 02:03 0.00s 0.00s 0.00s w
USER:用户名
TTY:录后系统分配的终端号
FROM: 远程主机名(即从哪儿登录来的)
LOGIN@:何时登录
IDLE:空闲了多长时间,表示用户闲置的时间。
JCPU:和该终端(tty)连接的所有进程占用的时间,这个时间里并不包括过去的后台作业时间,但却包括当前正在运行的后台作业所占用的时间
PCPU:指当前进程(即在WHAT项中显示的进程)所占用的时间
WHAT:当前正在运行进程的命令行
tload
0.23, 0.32, 0.45
平均负载:0.23, 0.32, 0.45 表示最近1分钟,5分钟,15分钟的系统平均负载.
top
top - 21:23:53 up 40 min, 2 users, load average: 0.19, 0.35, 0.47
Tasks: 255 total, 1 running, 253 sleeping, 0 stopped, 1 zombie
%Cpu(s): 1.1 us, 0.3 sy, 0.0 ni, 98.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 4022756 total, 2130488 used, 1892268 free, 192608 buffers
KiB Swap: 19999740 total, 0 used, 19999740 free. 919724 cached Mem

第一行:时间为:21:23:53; 已经运行了 40min; 当前在线用户:2个; 平均负载:0.19, 0.35, 0.47 表示最近1分钟,5分钟,15分钟的系统平均负载.
第二行:进程总数:255 正在运行进程数:1 睡眠进程数:253 停止的进程数:0 僵尸进程数:1
第三行:用户空间占用CPU百分比: 1.1% 内核空间占用CPU百分比:0.3% 用户进程空间内改变过优先级的进程占用CPU百分比:0.0% 空闲CPU百分比:0.0 等待输入输出的CPU时间百分比:0.0 CPU服务软中断所耗费的时间总额:0.0% StealTime:0.0%
第四行: 物理内存总量:4022756 使用的物理内存总量:2130488 空闲内存总量:1892268 用作内核缓存的内存量:192608
第五行: 交换区总量:19999740 使用的交换区总量:0 空闲交换区总量:19999740 缓冲的交换区总量:919724
第六行: 进程ID、进程所有者、优先级、nice值,负值表示高优先级,正值表示低优先级、进程使用的虚拟内存总量、进程使用的、未被换出的物理内存大小、共享内存大小、进程状态、上次更新到现在的CPU时间占用百分比、进程使用的物理内存百分比、进程使用CPU总时间、命令名、命令行

Cpu load的参数如果为4,描述一下现在系统处于什么情况
Linux,查找磁盘上最大的文件的命令
Linux,如何查看系统日志文件
手撕算法:leeetcode原题 22,Generate Parentheses,给定 n 对括号,请写一个函数以将其生成新的括号组合,并返回所有组合结果。

美团三面(现场)
三面没怎么问技术,问了很多技术管理方面的问题
自我介绍
项目介绍
怎么管理项目成员
当意见不一致时,如何沟通并说服开发成员,并举个例子
怎么保证项目的进度
数据库的索引原理

非聚簇索引和聚簇索引
聚集索引就像我们根据拼音的顺序查字典一样,可以大大的提高效率。在经常搜索一定范围的值时,通过索引找到第一条数据,根据物理地址连续存储的特点,然后检索相邻的数据,直到到达条件截至项。

非聚集索引

索引的逻辑顺序与磁盘上的物理存储顺序不同。非聚集索引的键值在逻辑上也是连续的,但是表中的数据在存储介质上的物理顺序是不一致的,即记录的逻辑顺序和实际存储的物理顺序没有任何联系。索引的记录节点有一个数据指针指向真正的数据存储位置。

非聚集索引就像根据偏旁部首查字典一样,字典前面的目录在逻辑上也是连续的,但是查两个偏旁在目录上挨着的字时,字典中的字却很不可能是挨着的。

总结一下:一条记录的存储物理位置是由聚集索引的值(即id)所决定,聚集索引的值(即id)可以直接定位记录的物理位置并且取到记录的值;但是非聚集索引与记录存储的物理位置不一致,只是会有一个指针指向数据存储的物理位置;所以用聚集索引(即id)定位记录的物理位置可以直接定位,但是非聚集索引需要经过指针移动才能定位数据的物理位置。

索引的使用注意事项
联合索引

从底层解释最左匹配原则
1.简单说下什么是最左匹配原则
顾名思义:最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。
例如:b = 2 如果建立(a,b)顺序的索引,是匹配不到(a,b)索引的;但是如果查询条件是a = 1 and b = 2或者a=1(又或者是b = 2 and b = 1)就可以,因为优化器会自动调整a,b的顺序。再比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,因为c字段是一个范围查询,它之后的字段会停止匹配。

2.最左匹配原则的原理
最左匹配原则都是针对联合索引来说的,所以我们有必要了解一下联合索引的原理。了解了联合索引,那么为什么会有最左匹配原则这种说法也就理解了。

我们都知道索引的底层是一颗B+树,那么联合索引当然还是一颗B+树,只不过联合索引的健值数量不是一个,而是多个。构建一颗B+树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。
B+TREE
1.单节点可以存储更多的元素,使得查询磁盘IO次数更少。
2.所有查询都要查找到叶子节点,查询性能稳定。
3.所有叶子节点形成有序链表,便于范围查询。

Mysql对联合索引有优化么?会自动调整顺序么?哪个版本开始优化?
Redis的应用
Redis的持久化的方式和原理
技术选型,一个新技术和一个稳定的旧技术,你会怎么选择,选择的考虑有哪些
说你印象最深的美团点评技术团队的三篇博客
最近在学什么新技术
你是怎么去接触一门新技术的
会看哪些书
怎么选择要看的书
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值