丰富的线上&线下活动,深入探索云世界
做任务,得社区积分和周边
最真实的开发者用云体验
让每位学生受益于普惠算力
让创作激发创新
资深技术专家手把手带教
遇见技术追梦人
技术交流,直击现场
海量开发者使用工具、手册,免费下载
极速、全面、稳定、安全的开源镜像
开发手册、白皮书、案例集等实战精华
为开发者定制的Chrome浏览器插件
面经总结:
直播是眼下最为火爆的行业,而弹幕无疑是直播平台中最流行、最重要的功能之一。本文将讲述如何实现兼容PC浏览器和移动浏览器的弹幕。
一般来说,弹幕数据会通过异步请求或socket消息传到前端,这里会存在一个隐患——数据量可能非常大。如果一收到弹幕数据就马上渲染出来,在量大的时候:
所以在接收和渲染数据之间,要引入队列做缓冲。把收到的弹幕数据都存入数组(即下文代码中的this._queue),再通过轮询该数组,把弹幕逐条渲染出来:
弹幕的滚动本质上是位移动画,从显示区域的右侧移动到左侧。前端实现位移动画有两种方案——DOM和canvas。
DOM方案实现的动画较为流畅,且一些特殊效果(如文字阴影)较容易实现(只要在CSS中设置对应的属性即可)。Canvas方案的动画流畅度要差一些,要做特殊效果也不那么容易,但是它在CPU占用上有优势。
在DOM方案下,每条弹幕对应一个HTML元素,把元素的样式都设定好之后,就可以添加到HTML文档里面:
首先,弹幕的文字大小不一定一致,从而占用的高度也不尽相同。为了能充分利用显示区域的空间,我们可以把显示区域划分为多行,一行即为一条轨道。一条弹幕至少占用一条轨道。而存储结构方面,可以用二维数组记录每条轨道中存在的弹幕。下图是弹幕占用轨道及其对应存储结构的一个例子:
其次,要防止弹幕重叠。原理其实非常简单,请看下面这题数学题。假设有起点站、终点站和一条轨道,列车都以匀速运动方式从起点开到终点。列车A先发车,请问:如果在某个时刻,列车B发车的话,会不会在列车A完全进站之前撞上列车A?
聪明的你可能已经发现,这里的轨道所对应的就是弹幕显示区域里面的一行,列车对应的就是弹幕。解题之前,先过一下已知量:
那在什么情况下,两车不会相撞呢?
有了理论支撑,就可以编写对应的代码了。
上一节已经实现了弹幕的基本功能,但仍有一些细节需要完善。
回顾渲染的代码可以发现,该流程总是先检查第一条弹幕能不能入轨,倘若不能,那后续的弹幕都会被堵塞,从而导致弹幕密集度不足。然而,每条弹幕的长度、速度等参数不尽相同,第一条弹幕不具备入轨条件不代表后续的弹幕都不具备。所以,在单次渲染过程中,如果第一条弹幕还不能入轨,可以往后多尝试几条。
防重叠检测是弹幕渲染过程中执行得最为频繁的部分,因此其优化显得特别重要。JavaScript性能优化的关键是:尽可能避免DOM操作。而整个防重叠检测算法中涉及的唯一一处DOM操作,就是弹幕已滚动路程的获取:
然后,获取弹幕已滚动路程的代码就可以优化成:
首先要解释一下为什么要做暂停和恢复,主要是两个方面的考虑。
第一个考虑是浏览器的兼容问题。弹幕渲染流程会频繁调用到JS的setTimeout以及CSS的transition,如果把当前标签页切到后台(浏览器最小化或切换到其他标签页),两者会有什么变化呢?请看测试结果:
可见,不同浏览器的处理方式不尽相同。而从实际场景上考虑,标签页切到后台之后,即使渲染弹幕用户也看不见,白白消耗硬件资源。索性引入一个机制:标签页切到后台,则弹幕暂停,切到前台再恢复:
lethiddenProp,visibilityChangeEvent;if(typeofdocument.hidden!=='undefined'){hiddenProp='hidden';visibilityChangeEvent='visibilitychange';}elseif(typeofdocument.msHidden!=='undefined'){hiddenProp='msHidden';visibilityChangeEvent='msvisibilitychange';}elseif(typeofdocument.webkitHidden!=='undefined'){hiddenProp='webkitHidden';visibilityChangeEvent='webkitvisibilitychange';}document.addEventListener(visibilityChangeEvent,()=>{if(document[hiddenProp]){this.pause();}else{//必须异步执行,否则恢复后动画速度可能会加快,从而导致弹幕消失或重叠,原因不明this._resumeTimer=setTimeout(()=>{this.resume();},200);}},false);先看下暂停滚动的主要代码(注意已滚动路程rolledDistance,将用于恢复播放和防重叠):
this._eachDanmakuNode((node,y,id)=>{constdata=this._findData(y,id);if(data){//获取已滚动距离data.rolledDistance=-getTranslateX(node);//移除动画,计算出弹幕所在的位置,固定样式node.style.transition='';node.style.transform=`translateX(-${data.rolledDistance}px)`;}});接下来是恢复滚动的主要代码:
弹幕并发量大时,队列中的弹幕数据会非常多,而在防重叠机制下,一屏能显示的弹幕是有限的。这就会出现“供过于求”,导致弹幕“滞销”,用户看到的弹幕将不再“新鲜”(比如视频已经播到第10分钟,但还在显示第3分钟时发的弹幕)。
while(count&&i
1.通过首页来访问的时候开始计时,为用户生成id(全局唯一)
2.每五分钟动态模拟登陆人数,并在0点重置.在同一时刻同一个用户访问时需要和计数器实现一致性
3.不适用消息中间件,仅用数据库的方式实现,
介绍一些web服务器的项目,都做了什么
介绍select,poll,epoll的区别,特点
说一下升序链表的实现思路,有什么优化的地方吗
进程和线程的区别,分别占有什么
协程了解吗,协程的主要作用?协程有什么优点
户自己控制切换的时机,不再需要陷入系统的内核态。协程的执行效率非常高。因为子程序切换不是线程切换,而是由程序自身控制。因此,没有线程切换的开销,和多线程相比,线程数量越多,相同数量的协程体现出的优势越明显。不需要多线程的锁机制。
说一下进程间通信的方式?详细说一下管道和消息队列
\1.管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。\2.命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
\4.消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
\5.共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
\6.信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
\7.套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
\8.信号(sinal):信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
说一下TCP三次握手,能否两次握手
两次握手会发生什么?
客户端单方面认为通
服务端不同
三次握手有什么不好的地方吗?有点懵,面试官提示我DDOS攻击角度
(SYN洪泛)
timewait状态,是干嘛的
1.收到最后应答的ack
2.之前存活的报文死亡。
等到确认
说一下输入url之后的全过程
1.dns解析为ip(dns协议)
4.解析渲染
说一下OSI七层参考模型,HTTP在那一层,dns在哪一层,tcp、udp在哪一层。
路由器工作在哪一层
说一下mysql和innodb和myisam的区别
mysql索引的存储方式
b,b+
mysql的最左前缀法则?举了个例子问我能够匹配上吗
selectid,name,stuNo(select)
讲一下事务的四种隔离级别,他们分别解决了什么问题
说一下脏读,不可重复读,幻读(插入)
说一下mysql的默认隔离级别?可重复读通过什么实现的?mvcc
讲一下mvcc的理解,如何实现的mvcc(undo,版本,锁)
讲一下mysql有哪几种锁,讲一下间隙所间隙锁都加在哪了
讲一下mvcc的事务id,他是怎么判断哪些事务能够访问到哪些版本的
熟悉redis吗?讲一下redis的数据类型
了解常用的消息队列吗,kafka之类的?
能来实习吗?
翻转链表区间元素
二:
一面
栈内存储的是局部变量,堆内存储的是实体
栈内存存储的是局部变量而堆内存存储的是实体
栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短
栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收
2.JAVA的GC垃圾回收机制
垃圾回收机制就是JVM利用一些列算法对内存进行管理回收内存空间,删除无用实例(没有被引用的对象)自动进行,减少了内存溢出.但同时也牺牲了一定的性能优化:将不用的变量和指针置位null
3.垃圾回收机制和调用System.gc()的区别?
gc函数的作用是程序员提醒虚拟机,希望进行一次垃圾回收,但是虚拟机并不保证垃圾回收一定会进行。什么时候进行依然取决于虚拟机
4.什么是多态
一个程序中存在多的同名的不同方法,包括通过子类对父类的覆盖来实现(重写)通过在一个类中方法的重载来实现(重载)向上继承:通过子类对象转化为父类对象来实现
5.重写和重载的区别
重写:子类和父类的一种关系,子类继承父类的方法重载:同一个类中(包括父类)有多个同名的不同方法
6.进程和线程的区别,线程独有什么?
进程是程序的一次执行,进程包含线程,线程是进程中的代码段。
进程通过线程来实现同时运行不同段的代码。
线程的内存范围不允许越过进程。
线程共有的部分:方法区和堆
线程独有的部分:虚拟机栈,本地方法栈,程序计数器
7.你在项目中做过哪些性能优化
8.如何优化页面卡顿
拆分代码段尽量减少使用layout简化DOM结构
9.Handler消息机制
将耗时的操作放在子线程中处理,handler用来在主线程和子线程中传递信息
10.Android自定义View
View是用于创建交互式用户界面组件(按钮、文本等)的基础类View是所有控件(包括ViewGroup)的父类,它里面有一些常见的方法,如果我们要自定义View,最简单的只需要重写onDraw(android.graphics.Canvas)即可。
11.常用的开源框架及其原理Spring框架
12.MVC模式与MVP模式
13.MVC和MVVC怎么理解,开发中怎么用的
14.HTTP常见的请求方式及主要操作
15.HTTP和HTTPS
14.TCP的三次握手是什么?四次挥手
15.用到过的加密算法有哪些?
CA(RSA非对称的)MD5
16.设计模式有哪些?
17.如何实现AOP,用到什么技术?
18.JAVA自带的代理类和cglib有啥不同?
19.消息队列的作用和场景客户端:
2.计算机基础知识:堆,栈,编译,运行,进程,线程,操作系统,IO等等
本人双非非科班大三
----------------------------------------------------------------
一面:
介绍你参与比较多的[项目]()
说说解决的问题
聊聊加密[算法](),你在[项目]()中怎么用的?
加密[算法]()你了解多深(介绍了MD5,非对称加密DES,AES)(我从加密[算法]()提了RSA,CA机构加密的就是用的RSA)
怎么保证非对称加密的安全性(原话)
如果让你设计你会怎样做?
TCP和UDP的区别(后面所有话题都由这个衍生)
Java几个修饰符(protectpublic那些)
final的作用
[算法]():无序数组三数之和,输出下标
反问:评价一下我的这次面试给意见
建议:多去实习,最好来字节实习,一两天会有反馈
--------------------------------------------------------------------------
二面:
进程和线程有啥区别?(围绕这块问了一会操作系统)写DCL请你设计一个系统,将长域名转化为短链接,中途的处理[算法]()和解析流程,大概怎么解析[算法]():[把一个奇数位升序偶数位降序的单链表变成升序的,空间复杂度O(1)]()
---------------------------------------------------------------------------
三面,最让我想不明白的一面......
自我介绍
(这里面试官说一二面问基础为主,面试官反馈不错,三面就不问基础了)
[算法题]():N叉树,求走M步走到节点x的概率,如果到了目的节点但是步数没用完算走不到,只有走到叶子节点而且还没用完步数的情况才能原地走
JavaGC
能实习多久?
在学校成绩如何
(1)页面置换[算法]()?(FIFO、LRU、LFU)
(2)LRU怎么实现的?(双向[链表]()+[哈希表]())
(1)[客户端]()请求资源,如何实现断点续传?(使用HTTP请求的if-range字段+range字段)
(3)HTTP劫持?(不会)
事中规避
事后屏蔽
(4)DNS劫持?(用HTTPDNS解决)
输入百度进入渣渣灰
(5)通过HTTPDNS请求域名时,是使用IP还是域名?
将域名替换为ip
(6)如果用IP去请求,如何去实现容灾?
(7)HTTP1.0、2.0、3.0的区别?
(8)HTTP3.0用的UDP怎么做到可靠的?(自己实现了可靠传输的机制,例如流量控制、重传等)
(9)流量控制时怎么实现的?
(1)索引优缺点和原理?
(2)什么时候索引会失效?
(1)(Leetcode3)给一个字符串,求无重复字符的最长子串长度
(2)(Leetcode1)[两数之和]()
(1)TCP三次握手中,SYN、ACK、seq、ack四个字段的含义?
(2)HTTP和HTTPS的区别?
(3)HTTPS密钥协商的过程?
(4)如何实现HTTP长连接?(1.0里Connection:Keep-Alive,1.1里默认开启长连接)
(5)浏览器断点续传,分段下载时,HTTP用的哪个字段?(不会,答了个content-length字段,懂得大佬麻烦说一下)
(1)进程和线程的区别?
(2)进程间通信机制?
(3)Linux如何创建进程?(fork,写时复制)
(4)什么是僵尸进程?如何处理?(wait/waitpid)
(5)线程同步的方式?(互斥锁、自旋锁、读写锁、条件变量、信号量)
(6)互斥锁和自旋锁的区别?
(7)死锁的四个必要条件?
(8)如何避免死锁?(银行家[算法]())
(9)线程池如何设计的?(线程池、任务队列、互斥锁、条件变量)
(10)线程池里面的线程是如何实现执行完函数后不结束的(while)
(11)那会不会造成CPU空转的情况呢?(不会,条件变量,条件不满足时会自动释放锁,然后进入阻塞)
(1)从Person表中查询Age在18-25之间的数据,根据身高Tall由高到低[排序]()
(2)接着(1)增加Name包含A(查询包含A的Name)
(1)如何判断两个[链表]()是否相交?
(2)(Leetcode162)一组数据,不存在num[i]=num[i+1],返回任意一个峰值。峰值:左右都比该值小则该值称为峰值。
数组测试样例:(1)1232143(2)321(3)123
HTTP是基于哪个协议的
[算法题]()手撕:两个栈实现队列,使用线程安全实现(Synchornized和ReentrantLock都实现出来)
再撕一个Synchronized实现的
其他忘记了...最后还有一道原创[算法]()(不得不吐槽一下,之前面字节实习也是原创[算法](),看别人都是[牛客]()网字节题库原题羡慕~)
两道[算法](),验证IP、O(1)
这一面是我经历过的最难的一次面试(难度感觉比[腾讯]()、字节后端的面试都难,本以为[客户端]()会比较友好...)全程重点在操作系统和计算机网络,给一个具体的场景,分析原理
总体的面试体验还是很不错,就是别人都是三面,然后我可能表现不好被加面,然后四面被暴捶。总的来说,八股文并不是很多,挺多背了很久的八股文完全没用上。面试前,基本翻了所有字节[客户端]()的[面经](),感觉实际面试跟[牛客]()网上的[面经]()内容差距较大,感觉看到的[面经]()多数以八股文为主~还是不要过度相信[面经]()。
最后想问问,[客户端]()真的有那么劝退吗~挂后端被[客户端]()捞,还要不要接着卷卷其他厂的后端??
聊[项目]()
tcp在哪层,ip在哪层
tcp四次挥手为什么四次
页置换[算法]()有哪些
belady现象了解吗
进程线程的区别,进程和线程的切换
线程共享资源的保护
cpu怎么实现原子操作的**
1、使用总线锁
2、使用缓存锁
3、CAS
页面置换算法
内存管理
垃圾回收
数据库事物
事物隔离级别
脏读幻读的解决
提高隔离界别
智力题:有9个球,其中8个球质量相同,有1个球比较重。要求用2次天平,找出比较重的那个球
[算法题]():两个栈实现队列
抠[项目]()
[算法题]():口述前中后层序遍历,mod5转mod7【口述,用1-5随机数生成1-7】,bash博弈【也是口述】,力扣5【[最长回文子串](),这个让写代码了】
tcp如何进行流量控制的
进程之间的通信方式
1.管道:速度慢,容量有限,只有父子进程能通讯
2.FIFO:任何进程间都能通讯,但速度慢
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
4.信号量:不能传递复杂消息,只能用来同步
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
自旋锁和互斥锁的区别【这块好像没答好
消息队列的作用,使用场景
子网掩码的作用,给两对ip地址和子网掩码,判断他们是不是在同一个网段
DNS的作用
ARP协议的作用,协议原理
问[项目]()
Java基本数据类型有哪些?分别占多少字节?什么时候用int,什么时候用long?
反射了解吗?说一下应用场景
反射:运行状态
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性
4.JDBC的数据库的连接
CLass.forName()字节码已经加载到java虚拟机中,去得到字节码;java虚拟机中还没有生成字节码用类加载器进行加载,加载的字节码缓冲到虚拟机中。
5.泛型擦除中
1.getclass.getmethod.invoke();
工厂模式的改进
要新增实现类的话,只要将传入的参数改变就好,无需更改工厂内的代码。
2、反射机制的典型应用---Tomcat服务器(SSM框架中使用)
(1).Tomcat(App)首先读取配置文件web.xml中配置好的Servlet的子类名称
(2).Tomcat根据读取到的客户端实现的Servlet子类的类名字符串去寻找对应的字节码文件。如果找到就将其加载到内存。
(3).Tomcat通过预先设置好的Java反射处理机制解析字节码文件并创建相应的实例对象。之后调用所需要的方法。
【最后】Tomcat一启动,用户自定义的Servlet的子类通过Tomcat内部的反射框架也随之运行。
3、在项目中处处可见,AOP和IOC都是基于反射实现的
Java异常有哪些?介绍下你遇到过的异常
集合类框架:
1.ArrayList:和Vector对比
调用add()方法先判断大小
扩容的源码:
Vector2倍
HashMap
HashMap通过key的hashCode经过扰动函数处理过后得到hash值,然后通过(数组长度-1)&hash判断当前元素存放的位置(这的n指的是数组的度)
为什么是(n-1):
如果当前位置存在元素的话,就判断该元素与要存的元素的hash值以及key是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
所谓扰动函数指的就是HashMap的hash法。使hash法也就是扰动函数是为了防些实现较差的hashCode()法换句话说使扰动函数之后可以减少碰撞。
源码中:
右移16位扰动4次
介绍下static关键字的使用场景。什么时候要用到static?
static方法修饰的成员不再属于某个对象,而是属于它所在的类。只需要通过其类名就可以访问,不需要再消耗资源反复创建对象。
在类第一次加载的时候,static就已经在内存中了,直到程序结束后,该内存才会释放。
如果不是static修饰的成员函数,在使用完之后就会立即被JVM回收。
使用:
2.单例模式:双重校验.SingletonInstance
介绍下JVM内存区域以及每个区域的作用。类信息存储在哪个区域?常量存储在哪个区域?字节码存储在哪个区域?
1、程序计数器
随着线程的创建创建,随着线程的结束死亡。
4.可以切换线程且之间独立.
2、虚拟机栈
编译期完成,不会改变大小.
存放编译期可知的各种数据类型对象引
抛出异常:
1.StackOverflowError
2.OutOfMemory
方法函数调用:
1.return
2.抛出异常
主要是桢栈被弹出
3、本地方法栈
4、堆(GC收集的主要区域)
所有对象实例及数组都要在堆上分配内存
1、-Xms:表示java虚拟机堆区内存初始内存分配的大小,通常为操作系统可用内存的1/64大小即可,但仍需按照实际情况进行分配。2、-Xmx:表示java虚拟机堆区内存可被分配的最大上限,通常为操作系统可用内存的1/4大小。
5.方法区
Spring里面用到了哪些设计模式?
你用过哪些SpringBoot的注解?
自动装配原理:
SpringBoot中如何解决跨域问题
JSONP仅仅有GET不符合
CORS有跨域的:
通过WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
类加载过程
项目中pagehelper的原理是什么?
项目中使用aop是如何实现降低耦合和提高拓展性的?
项目细节等4.java的基本数据类型详细说说
5.一个空字符串占几个字节
源码时8*int
6.一个java文件里有多个类,编译后生成几个class文件
7.volatile的作用是什么,举例使用场景
8.hash碰撞的解决方法有哪些
9.讲一讲jdk里的hashmap有哪些值得借鉴的细节
10.垃圾回收算法有哪些
11.年轻代老年代分别用的什么算法
12.gc会停止用户进程吗?cms和g1的哪些阶段会stw?
13.抽象类和接口的区别
14.jmm讲一讲
15.线程和进程的区别
16.并行和并发的区别
17.怎么去结束一个正在运行的线程
18.乐观锁与悲观锁是什么?举个例子
19.linux了解吗,怎么查看占用cpu最高的进程
21.快速排序最坏情况
22.外部排序,找一亿个数中的top50
23.一组数中找到出现次数为奇数的那个数
24.文件系统和数据库系统的索引用什么结构?
\25.红黑树和平衡二叉树的区别、优点,b+树和红黑树比较
26.熟悉哪些数据库,mysql,redis
27.sql左连接右连接特点分析
28.对工作地点有什么看法
29.未来的发展方向
30.反问
然后就开始问我[项目](),一开始太紧张答得不太好,面试官似乎对我做的[项目]()好像也不是很感兴趣,然后就开始问八股文了,方方面面基本都有问到。
1、Java自身带的线程池有哪些问题(答了OOM面试官就问我Java堆溢出怎么排查,这块不是很熟没答出来)
2、几种垃圾回收[算法]()
3、怎么查看sql查询效率
4、MySQL左外连接和内连接的区别
5、问了spring的AOP,然后就问我动态代理的几种实现
6、问了我spring中有用到那些设计模式(就问了下也没问我怎么实现的)
JVM
讲一下有哪些垃圾回收器,说一下吞吐量优先垃圾回收器怎么使用???
什么时候会进行fullGC(答着答着发现CMS那个failure忘了叫啥了,离谱)
1.jdk动态代理
2.避免死锁怎么做
3.索引
4.分布式session
5.mybatis怎么分页的,PageHelper实现分页
6.怎么保证字节码指令的顺序,禁止指令重排,voliate关键字
7.设计模式,讲了单例的实现
8.reids宕机了怎么办,使用过[redis]()集群吗
9.怎么设计接口限流的
10.mybatis的三级缓存
11.怎么保证mysql和[redis]()的数据一致性
嘿嘿,还有一些忘记了哈
\1.在校活动。
\2.介绍[项目](),团队分工
\3.了解哪些[前端]()技术
\4.研究生阶段最有成就感的一件事
\5.你认为完成一个[项目]()的完整流程有哪些,分别会用到哪些工具?
\6.你认为5中最重要的一个环节是?
\8.[项目]()有没有考虑安全问题,如何解决?
感觉有点儿上来就是技术终面的味道。
二面(32min)
\1.介绍[项目](),[项目]()难点
\2.[项目]()给你最大的成长
\3.团队怎么分工
\4.[项目]()维护的细节(举例)
\5.提高用户体验的细节(举例)
\6.了解哪些技术栈,哪些比较熟练
JVM问了原先公司用什么垃圾回收器、为什么新生代分为eden、s0、s1区
数组扩容死循环问题
讲讲集合(讲了得10分钟,从ArrayList到LinkedList,Set,从HashMap到HashTable到ConcurrentHashMap,数据结构,线程安全问题,哈希碰撞,全讲了一遍)
恒生1.一分钟自我介绍2.集合框架每个怎么使用区别3.JVM内存结构,垃圾回收
4.Java多线程,线程池5.Servlet作用是什么6.Spring的俩个特性IOCAOP7.SpringMVC的工作流程8.学习框架遇到的困难,重来一次你会怎么学习9.大学期间自我感觉做的好的事情10.反问框架问的比较多
1自我介绍
2八股文环节2.1创建一个对象有哪些方式?2.2重载和重写区别?2.3如果想阻止一个对象序列化,可以采用什么方法?2.4反射机制是什么,有哪些实现方法,优点缺点?2.5HashMap底层数据结构,扩容机制2.6[红黑树]()是什么?如何进行自调整?与HashTable区别?2,7String、StringStringBuffer和StringBuilder的区别是什么2.8什么叫线程安全?ConcurrentHashMap做了哪些优化?2.9Synchronized和ReentrantLock有什么不同?2.10线程池是什么?构造方法有哪些参数?有哪些包和策略?有哪些线程池?2.11sleep()和wait()有什么区别?2.12为什么线程没有运行态(只有runnable)2,13GC机制2.14双亲委派机制是什么?类加载过程是怎样的?2.15存储过程是什么,优点?2.16数据库索引2.17事务隔离级别?Mysql默认隔离级别是什么?2.18数据库最左匹配原则?Sql怎么优化2.19缓存雪崩与缓存穿透,解决方法?2.20Redis为什么是单线程?2.21缓存有哪几个实现方式?2.22请你谈谈消息队列?有哪些应用场景?2.23HTTP和HTTPs区别?2.24简述DNS过程2.25TcP三次握手具体过程
3反问环节自行提问即可
09-15-二面[面经]():
1自我介绍2我看你是做人工智能的,为什么不搞[算法]()?3你在[项目]()中遇到哪些问题,怎么解决?4你的[职业规划]()?5你对恒生有哪些了解?6请用两个词形容你自己?7为什么你选择恒生?8你对我们部门有什么了解?9最后反问
一面:(8月31号进行的一面。大概四十多分钟五十分钟吧)
1、自我介绍
2、你有没有体现你价值的东西,或者是惑你比较满意的[项目]()
3、介绍[项目]()
4、介绍一下服务器实现的流程,i/o模型
5、Epoll的建立流程,线程池
6、当你接收到数据的时候,用线程池去处理数据,他们之间的交互是怎么样的
7、Epoll的工作原理,各个函数的作用,从监听树到就绪[链表]()是由什么触发的呢,是怎么将就绪的事件放到就绪[链表]()的。
8、MD5加密会出现什么问题,如果我现在对它有一个更高的安全要求,可以怎么做。
9、中介者模式
10、map是放在进程里的?当用户量过大的时候会出现什么问题?那你这个问题可以怎么解决?
11、数组和[链表]()的区别
12、队列和栈两个数据结构的区别
13、树的广度优先遍历
14、[哈希表]()的实现原理、哈希冲突的解决
15、生成1-1000的数放到数组里,但是是随机的不能重复的不能有序。应该怎么做
16、TCP的三次握手、四次挥手,连接断开的时候会发送什么、[客户端]()和服务器分别处于什么状态
17、滑动窗口
18、在网络通信,发送端发送数据包的时候,是怎么把数据包传送给接收端的,也就是他是怎么找到接收端然后发送包的(具体的过程)
19、在网路中是如何根据ip和端口号找到对应的主机的。
20、什么是阻塞i/o、什么是非阻塞i/o。阻塞的什么?
21、当我们调用sleep的时候,他调用的线程会变成什么状态
22、当我们访问淘宝的时候,从浏览器到网络到对方的服务器中心,中间的具体实现过程。
23、做[项目]()的时候遇到的最大的挑战是什么?是怎么解决的?
整体面试感觉挺好的,面试官也没有咄咄逼人的状态。前面的问题主要是基于[项目]()展开的。问题相对来说都还是比较基础的,也会问一些比较底层是实现(比如epoll是怎么把就绪的I/O事件放到就绪[链表]()的),但是不多。
一面的时候问了数据库的知识,我说对数据库不是很熟悉。。。结果二面傻眼了
没有自我介绍,上来直接问的
1、线程间同步互斥有哪些方式
2、互斥锁有的是能跨进程使用的,有的是不能跨进程使用的,为什么有的能跨进程使用有的不能
3、条件变量用到了什么场景?用信号量是不是也可以?(可以,那你为什么用条件变量+互斥锁)
5、除了有序的数据结构还有其他的吗?
6、数据结构的字典,它的查找过程,是怎么根据key值找到value的?
7、map是线程安全的吗
8、假设一个数据结构不是线程安全的,我们要调用它进行读操作和写操作,可以选择读加锁和写加锁或者读写都加锁去保证它,那你觉得应该选哪个
9、当我们读加锁的时候,我们读的时候是不是也会被阻塞?
10、数据库的ACID,你理解的一致性是什么
11、隔离级别
12、隔离级别有哪些实现方式
14、MD5加密
15、生产者消费者是干什么的
16、你为什么要进行10个线程的创建和销毁
17、你觉得10个10个创建有什么不好的地方
18、假设一个场景,我去请求mysql或者[redis](),它报了一个socket超时的异常,有可能是哪的问题。
19、检测网络是否畅通用什么命令
20、TCP的三次握手过程,每一次[客户端]()和服务器的交互发送了什么
21、怎么保证缓存的数据和数据库的数据是一致的。
22、假设我写的时候先写数据库然后再写缓冲区,你觉得这个方法能不能保持一致性?考虑一下它的并发,有多个线程同时执行写操作?多个线程要对数据进行修改呢?
常见的有MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性;在信息传输过程中,散列函数不能单独实现信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密;
常见的有AES-CBC、DES、3DES、AES-GCM等,相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1;对称加密的优势是信息传输1对1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和N个客户端通信,需要维持N个密码记录,且缺少修改密码的机制;
即常见的RSA算法,还包括ECC、DH等算法,算法特点是,密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现1对多的通信,客户端也可以用来验证掌握私钥的服务器身份。非对称加密的特点是信息传输1对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。
主要区别主要体现在:
1.缓存处理:
在HTTP1.0中主要使用header里的lf-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entitytag,lf-Unmodified-Since,If-Match,lf-None-Match等更多可供选择的缓存头来控制缓存策略。2.带宽优化及网络连接的使用:
HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(PartialContent),这样就方便了开发者自由的选择以便于充分利用带宽和连接。3.错误通知的管理:
在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。4.Host头处理:
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homedWebServers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400BadRequest).5.长连接:
HTTP1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,其中长连接也就是对应在HTTP1.1中的Connection:keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。HTTP1.0和1.1现存的一些问题
HTTPS与HTTP的一些区别HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的TLS加密传输协议。HTTP和HTTPS使用的是完全不同的连接方式,用的默认端口也不一样,前者是80,后者是443.HTTPS的连接很简单,HTTPS协议是由TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
HTTP协议被认为不安全是因为传输过程容易被监听者勾线监听、伪造服务器,而HTTPS协议主要解决的便是网络传输的安全性问题。
首先我们假设不存在认证机构,任何人都可以制作证书,这带来的安全风险便是经典的“中间人攻击”问题。
“中间人攻击”的具体过程如下:
过程原理:
由于缺少对证书的验证,所以客户端虽然发起的是HTTPS请求,但客户端完全不知道自己的网络已被拦截,传输内容被中间人全部窃取。
HTTP2.0可以说是SPDY的升级版(其实原本也是基于SPDY设计的)。但是,HTTP2.0跟SPDY仍有不同的地方,主要是以下两点:
HTTP2.0消息头的压缩算法采用HPACK算法,而非SPDY采用的DEFLATE算法。HTTP2.0设计初期支持明文HTTP传输,而SPDY强制使用HTTPS,到后期两者都需要使用HTTPS。
HTTP3.0而QUIC是基于传输层UDP上的协议,可以定义成:HTTP3.0基于UDP的安全可靠的HTTP2.0协议。
QUIC协议针对基于TCP和TLS的HTTP2.0协议解决了下面的问题。
1.2多路复用丢包的线头阻塞问题QUIC保留了HTTP2.0多路复用的特性,在之前的多路复用过程中,同一个TCP连接上有多个stream,假如其中一个stream丢包,在重传前后的stream都会受到影响,而QUIC中一个连接上的多个stream之间没有依赖。所以当发生丢包时,只会影响当前的stream,也就避免了线头阻塞问题。
1.4流量控制通过流量控制可以限制客户端传输资料量的大小,有了流量控制后,接收端就可以只保留相对应大小的接收buffer,优化记忆体被占用的空间。但是如果存在一个流量极慢的stream,光一个stream就有可能估用掉接收端所有的资源。QUIC为了避免这个潜在的HOLBlocking,采用了连线层(connectionflowcontrol)和Stream层的(streamflowcontrol)流量控制,限制单一Stream可以占用的最大buffersize。
增强校验
有时用户上传/下载文件需要历时数小时,万一线路中断,不具备断点续传的HTTP/FTP服务器或下载软件就只能从头重传,比较好的HTTP/FTP服务器或下载软件具有断点续传能力,允许用户从上传/下载断线的地方继续传送,这样大大减少了用户的烦恼。
在Linux/Unix系统下,常用支持断点续传的FTP客户端软件是lftp。
HTTP1.1协议(RFC2616)开始支持获取文件的部分内容,这为并行下载以及断点续传提供了技术支持。它通过在Header里两个参数实现的,客户端发请求时对应的是Range,服务器端响应时对应的是Content-Range。
Range
用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式:
Range头部的格式有以下几种情况:
Content-Range
用于响应头中,在发出带Range的请求后,服务器会在Content-Range头部返回当前接受的范围和文件总大小。一般格式:
例如:
0-499是指当前发送的数据的范围,而22400则是文件的总大小。
而在响应完成后,返回的响应头内容也不同:
在实际场景中,会出现一种情况,即在终端发起续传请求时,URL对应的文件内容在服务器端已经发生变化,此时续传的数据肯定是错误的。如何解决这个问题了?显然此时需要有一个标识文件唯一性的方法。
Etag(EntityTags)主要为了解决Last-Modified无法解决的一些问题。
用于判断实体是否发生改变,如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。一般格式:
也就是说,If-Range可以使用Etag或者Last-Modified返回的值。当没有ETage却有Last-modified时,可以把Last-modified作为If-Range字段的值。
If-Range必须与Range配套使用。如果请求报文中没有Range,那么If-Range就会被忽略。如果服务器不支持If-Range,那么Range也会被忽略。
如果请求报文中的Etag与服务器目标内容的Etag相等,即没有发生变化,那么应答报文的状态码为206。如果服务器目标内容发生了变化,那么应答报文的状态码为200。
用于校验的其他HTTP头信息:If-Match/If-None-Match、If-Modified-Since/If-Unmodified-Since。
Etag由服务器端生成,客户端通过If-Range条件判断请求来验证资源是否修改。请求一个文件的流程如下:
第一次请求:
第二次请求(断点续传):
CURL实现检测:
这里主要讲解简单工厂模式,代理模式,适配器模式,单例模式4中设计模式.
1、简单工厂模式
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类集中了所有产品类的创建逻辑,如果产品量较大,会使得工厂类变的非常臃肿。
2、代理模式
代理模式:为其它对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
优点:
职责清晰。真实的角色只负责实现实际的业务逻辑,不用关心其它非本职责的事务,通过后期的代理完成具体的任务。这样代码会简洁清晰。
代理对象可以在客户端和目标对象之间起到中介的作用,这样就保护了目标对象。
扩展性好。
3、适配器模式
适配器模式可以将一个类的接口转换成客户端希望的另一个接口,使得原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。适配器类需要继承或依赖已有的类,实现想要的目标接口。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
4、单例模式
单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。实现单例模式必须注意一下几点:
单例类只能由一个实例化对象。
单例类必须自己提供一个实例化对象。
单例类必须提供一个可以访问唯一实例化对象的接口。
单例模式分为懒汉和饿汉两种实现方式。
堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)
栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。
堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。
栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。
静态变量放在方法区静态的对象还是放在堆。程序的可见度
堆对于整个应用程序都是共享、可见的。
栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。
操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。
说到对象的创建,首先让我们看看Java中提供的几种对象创建方式:
Header解释使用new关键字调用了构造函数使用Class的newInstance方法调用了构造函数使用Constructor类的newInstance方法调用了构造函数使用clone方法没有调用构造函数使用反序列化没有调用构造函数下面是对象创建的主要流程:
虚拟机遇到一条new指令时,先检查常量池是否已经加载相应的类,如果没有,必须先执行相应的类加载。类加载通过后,接下来分配内存。若Java堆中内存是绝对规整的,使用“指针碰撞“方式分配内存;如果不是规整的,就从空闲列表中分配,叫做”空闲列表“方式。划分内存时还需要考虑一个问题-并发,也有两种方式:CAS同步处理,或者本地线程分配缓冲(ThreadLocalAllocationBuffer,TLAB)。然后内存空间初始化操作,接着是做一些必要的对象设置(元信息、哈希码…),最后执行方法。
类加载完成后,接着会在Java堆中划分一块内存分配给对象。内存分配根据Java堆是否规整,有两种方式:
指针碰撞:如果Java堆的内存是规整,即所有用过的内存放在一边,而空闲的的放在另一边。分配内存时将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离,这样便完成分配内存工作。空闲列表:如果Java堆的内存不是规整的,则需要由虚拟机维护一个列表来记录那些内存是可用的,这样在分配的时候可以从列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。选择哪种分配方式是由Java堆是否规整来决定的,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
处理并发安全问题对象的创建在虚拟机中是一个非常频繁的行为,哪怕只是修改一个指针所指向的位置,在并发情况下也是不安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。解决这个问题有两种方案:
对分配内存空间的动作进行同步处理(采用CAS+失败重试来保障更新操作的原子性);把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(ThreadLocalAllocationBuffer,TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配。只有TLAB用完并分配新的TLAB时,才需要同步锁。通过-XX:+/-UserTLAB参数来设定虚拟机是否使用TLAB。
对象的访问定位Java程序需要通过JVM栈上的引用访问堆中的具体对象。对象的访问方式取决于JVM虚拟机的实现。目前主流的访问方式有句柄和直接指针两种方式。
指针:指向对象,代表一个对象在内存中的起始地址。
句柄:可以理解为指向指针的指针,维护着对象的指针。句柄不直接指向对象,而是指向对象的指针(句柄不发生变化,指向固定内存地址),再由对象的指针指向对象的真实内存地址。
句柄访问Java堆中划分出一块内存来作为句柄池,引用中存储对象的句柄地址,而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息,具体构造如下图所示:
优势:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改。
Serial收集器(复制算法):新生代单线程收集器,标记和清理都是单线程,优点是简单高效;ParNew收集器(复制算法):新生代收并行集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
SerialOld收集器(标记-整理算法):老年代单线程收集器,Serial收集器的老年代版本;
ParallelOld收集器(标记-整理算法):老年代并行收集器,吞吐量优先,ParallelScavenge收集器的老年代版本;
G1(GarbageFirst)收集器(标记-整理算法):Java堆并行收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1收集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年代),而前六种收集器回收的范围仅限于新生代或老年代。
MyISAM不支持聚集索引,InnoDB支持聚集索引。
MVCC(Multiversionconcurrencycontrol)就是同一份数据保留多版本的一种方式,进而实现并发控制。在查询的时候,通过readview和版本链找到对应版本的数据。
作用:提升并发性能。对于高并发场景,MVCC比行级锁更有效、开销更小。
MVCC实现原理如下:
MVCC的实现依赖于版本链,版本链是通过表的三个隐藏字段实现。
每条表记录大概是这样的:
使用事务更新行记录的时候,就会生成版本链,执行过程如下:
下面举个例子方便大家理解。
接下来了解下readview的概念。
readview可以理解成对数据在每个时刻的状态拍成“照片”记录下来。这样获取某时刻的数据时就还是原来的”照片“上的数据,是不会变的。
不同隔离级别创建readview的时机不同。
readview的记录筛选方式
前提:DATA_TRX_ID表示每个数据行的最新的事务ID;up_limit_id表示当前快照中的最先开始的事务;low_limit_id表示当前快照中的最慢开始的事务,即最后一个事务。
如果up_limit_id<=DATA_TRX_ID 总结:InnoDB的MVCC是通过readview和版本链实现的,版本链保存有历史版本记录,通过readview判断当前版本的数据是否可见,如果不可见,再从版本链中找到上一个版本,继续进行判断,直到找到一个可见的版本。 表记录有两种读取方式。 快照读情况下,InnoDB通过mvcc机制避免了幻读现象。而mvcc机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。 下面举个例子说明下: insertintouser`(user_name,user_password,user_mail,user_state)values('tyson','a','a',0);` updateusersetuser_name=`'a'`; 以上就是当前读出现的幻读现象。 那么MySQL如何实现避免幻读? next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。 Serializable隔离级别也可以避免幻读,会锁住整张表,并发性极低,一般不会使用。 MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是binlog(二进制日志)和redolog(重做日志)和undolog(回滚日志)。 binlog 二进制日志(binlog)是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库。 redolog 重做日志(redolog)是Innodb引擎级别,用来记录Innodb存储引擎的事务日志,不管事务是否提交都会记录下来,用于数据恢复。当数据库发生故障,InnoDB存储引擎会使用redolog恢复到发生故障前的时刻,以此来保证数据的完整性。将参数innodb_flush_log_at_tx_commit设置为1,那么在执行commit时会将redolog同步写到磁盘。 undolog 除了记录redolog外,当进行数据修改时还会记录undolog,undolog用于数据的撤回操作,它保留了记录修改前的内容。通过undolog可以实现事务回滚,并且可以根据undolog回溯到某个特定的版本的数据,实现MVCC。 【推荐】利用延迟关联或者子查询优化超多分页场景。 说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。 正例:先快速定位需要获取的id段,然后再关联: SELECTa.*FROM表1a,(selectidfrom表1where条件LIMIT100000,20)bwherea.id=b.id 数据切分可以分为两种方式:垂直划分和水平划分。 垂直划分 垂直划分数据库是根据业务进行划分,例如购物场景,可以将库中涉及商品、订单、用户的表分别划分出成一个库,通过降低单库的大小来提高性能,但这种方式并没有解决高数据量带来的性能损耗。同样的,分表的情况就是将一个大表根据业务功能拆分成一个个子表,例如商品基本信息和商品描述,商品基本信息一般会展示在商品列表,商品描述在商品详情页,可以将商品基本信息和商品描述拆分成两张表。 优点:行记录变小,数据页可以存放更多记录,在查询时减少I/O次数。 缺点: 水平划分 优点:单库(表)的数据量得以减少,提高性能;切分出的表结构相同,程序改动较少。 当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下: 查询语句的执行流程如下: 举个例子,查询语句如下: 举个例子,更新语句如下: updateusersetname=大彬whereid=1;先查询到id为1的记录,有缓存会使用缓存。拿到查询结果,将name更新为大彬,然后调用引擎接口,写入更新数据,innodb引擎将数据保存在内存中,同时记录redolog,此时redolog进入prepare状态。执行器收到通知后记录binlog,然后调用引擎接口,提交redolog为提交状态。更新完成。为什么记录完redolog,不直接提交,先进入prepare状态? 假设先写redolog直接提交,然后写binlog,写完redolog后,机器挂了,binlog日志没有被写入,那么机器重启后,这台机器会通过redolog恢复数据,但是这个时候binlog并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。 exists用于对外表记录做筛选。 exists会遍历外表,将外查询表的每一行,代入内查询进行判断。当exists里的条件语句能够返回记录行时,条件就为真,返回外表当前记录。反之如果exists里的条件语句不能返回记录行,条件为假,则外表当前记录被丢弃。 select``a.*``from``Aawhereexists(``select``1``from``Bb``where``a.id=b.id)in是先把后边的语句查出来放到临时表中,然后遍历临时表,将临时表的每一行,代入外查询去查找。 select``*``from``Awhereid``in``(``select``id``from``B)子查询的表大的时候,使用exists可以有效减少总的循环次数来提升速度;当外查询的表大的时候,使用IN可以有效减少对外查询表循环遍历来提升速度。 持久化: 通过配置文件中的save参数来定义快照的周期。 1、只有一个文件dump.rdb,方便持久化。2、容灾性好,一个文件可以保存到安全的磁盘。 3、性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。 使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能 4.相对于数据集大时,比AOF的启动效率更高。 所以这种方式更适合数据要求不严谨的时候)2、AOF(Append-onlyfile)持久化方式:是指所有的命令行记录以redis命令请求协议的格式完全持久化存储)保存为aof文件。 AOF:持久化 AOF持久化(即AppendOnlyFile持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。 当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。 有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份,并且RDB恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。 1、数据安全,aof持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次。2、通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。3、AOF机制的rewrite模式。AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)) 1、AOF文件比RDB文件大,且恢复速度慢。 2、数据集大的时候,比rdb启动效率低。 Redis的事务总是具有ACID中的一致性和隔离性(单线程),其他特性是不支持的。当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,事务也具有耐久性。 哨兵用于实现redis集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。 故障转移时,判断一个masternode是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。哨兵的核心知识 哨兵至少需要3个实例,来保证自己的健壮性。哨兵+redis主从的部署架构,是不保证数据零丢失的,只能保证redis集群的高可用性。对于哨兵+redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。 解决方案 布隆过滤器(推荐) 设置热点数据永远不过期。加互斥锁,互斥锁 服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题? 一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况 串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。 还有一种方式就是可能会暂时产生不一致的情况,但是发生的几率特别小,就是先更新数据库,然后再删除缓存。 Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。尽量避免在压力较大的主库上增加从库Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…,这样的结构也方便解决单点故障问题,实现Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。 一个客户端运行了新的命令,添加了新的数据。Redis检查内存使用情况,如果大于maxmemory的限制,则根据设定好的策略进行回收。一个新的命令被执行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。 LRU算法 以商品品牌为例实现基本的CRUD操作及通过PageHelper实现分页查询。 配置yaml {"sub":"admin","created":1489079981393,"exp":1489684781}CopytoclipboardErrorCopiedsignature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败