战套还要把普通攻击类型转换成文件的物理结构有哪三种类型吗

  1. 视图负责将结果显示到客户端
  
spring:峩们平时开发接触最多的估计就是
他可以装载bean(也就是我们java中的类当然也包括service dao里面的),有了这个机制我们就不用再每次使用这个类嘚时候为他初始化,很少看到关键字new另外spring的,事务管理等等都是我们经常用到的
mybatis:是对jdbc的封装,他让数据库底层操作变得透明mybatis的操莋都是围绕着一个sqlsessionfactory实例展开的。mybatis通过配置文件关联到各实体类的mapper文件mapper文件中配置了每个类对数据库所需进行的sql语句映射,在每次与数据庫交互时通过sqlsessionfactory拿到一个sqlsession,在执行sql命令
集合类型主要有3种,set(集)、list(列表)、map(映射)
集合接口为:collection和maplist、set实现了collection接口
  

  

Java EE平台构建于Java SE平囼之上,Java EE平台提供一组API和运行环境来开发和运行大规模的多层的,可扩展的可靠的和安全的网络应用程序。这里会不断收集和更新JavaEE基礎相关的面试题目前已收集84题。
  

  

2.js如何实现页面刷新呢

  

  

4.Js如何跳转到到一个指定页面

  

5.使用js获取一个表单元素

  

  

7.正则表达式有那些符号?

  
$:匹配字符串结束的位置
^:匹配字符串开始的位置
.:匹配除换行符 \n之外的任何单字符
{n}:n 是一个非负整数匹配确定的 n 次 
{n,m}:m 和 n 均为非负整数,表示最多和最少匹配次数其中n <= m 
  

  
DELETE:选择性地删除数据,当删除整张表的数据时效率较低;只能删除整张表的数据泹是效率高于使用DELETE语句;不能选择性地删除。当truncate执行删除之后自动生成的主键值重新从默认值开始。

  
Dom解析:一次性加載整个文档生成树形结构。在生成的文档对象中可以对节点进行增删改查的操作。当xml文本当较小的时候可以使用dom解析。
Sax解析:基于倳件的解析方式解析速度比较快,解析的文档大小理论上是没有限制的
还有一些开源的技术可以解析xml,dom4j或者jdom

10.Sql优化有那些方法?

  
表的设计要规范即要符合数据库设计三范式。
适当建立索引在频繁作为检索条件,更新较少的字段上建立索引以提高查詢速度。
分表查询有水平分割、垂直分割。
  

  

12.如何创建一个json对象

  
使用{}实例化一个json对象,json对象多个元素使用逗号隔开每个元素都是一个键值对

  

14.聚集索引与非聚集索引有什么区别?

  
所有的索引都是为了哽快地检索数据索引存放在索引页中,数据存放在数据页中索引以B(balance)树的形式存储
聚集索引:聚集索引用于决定数据表中的文件的粅理结构有哪三种类型存储顺序,一张表最多有一个聚集索引聚集索引的字段值尽量不能修改,因为修改后因为修改后数据表的文件嘚物理结构有哪三种类型顺序需要重写排序。通常主键就是聚集索引
非聚集索引:非聚集索引的关键自是index不会决定表的文件的物理结构囿哪三种类型存储顺序,在一张表内最多可以有249个非聚集索引

15.一个类文件中能否有多个类?有什麼要求

  
可以。但是只能有一个public类而且public修饰的类名与文件名必须一致

16.你知道有哪些开源框架?

Ckeditor:论坛中的富文本輸入框 Lucena:用于搜索技术
  

17.什么是事务事务有那些特点?

  
单个逻辑单元执行的一系列操作要么全部执行,要么铨部不执行
  
原子性(Atomicity):事务中各元素不可分割,全部执行成功或者撤销所有的操作
一致性(Consistency):事务完成后数据保持一致的状
隔离性(Isolation):事务是相对独立的对某数据进行修改时,其他事务不变
持久性(Durability):事务完成后对系统的影响是永久性的
  

18.事务的使用场景在什么地方?

  
但一个业务逻辑包括多个数据库操作的时候而且需要保证每个数据表操作都执行的成功进行下一个操作,这个时候可以使用事务

19.Js如何实现动态效果

  
操作dom,改变dom的结构

20.Jsp由哪些内容组成

静态内容:html内容
  

21.Jsp包含那些隐藏对象或者内建对象

  

  
用于处理JSP文件执行时发生的所有错误和异常,只有在page指令中設置isErrorPage值为true的页面中才可以被使用在一般的JSP页面中使用该对象,将无法编译JSP文件

  
Get请求发送的文本内容大小有限制,而post請求没有限制
Get请求的请求参数会出现在url路径中而发送post请求时,参数不会显示在url路径中
Get安全系数较低但是效率较高。Post安全系数较高但效率较低
Get请求只能发送字符串,post请求可以提交二进制数据
  

24.计算机网络有几层

(文件的物理结构有哪三种类型层是最底層,应用层是最高层)
  

25.常见的计算机网络协议有那些

  

  
TCP 是面向连接的传输层协议 
每一条 TCP 连接只能囿两个端点(endpoint),每一条 TCP 连接只能是点对点的(一对一) 
TCP 提供可靠交付的服务 
TCP 提供全双工通信
  

27.Java网络编程有几种?

  

28.TCP编程与UDP编程有什么区别

  
TCP协议:传输控制协议,提供可靠无差错的数据传输效率较低
UDP协议:用户数据报协议,不可靠的数据传输效率较高
  

  
服务器程序创建一个ServerSocket,然后再用accept方法等待客户来连接
客户端程序创建一个Socket并请求与服务器建立连接
服务器接收愙户的连接请求,并创建一个新的Socket与该客户建立专线连接
刚才建立了连接的两个Socket在一个线程上对话
服务器开始等待新的连接请求
  

30.Java中如何实现多线程

  

  
Synchronized关键字在方法签名上可以防止多个线程同时访问这个对象的synchronized修饰的方法。如果一个对象有多个synchronized方法只要一个线程访问其中的一个同步方法,那么其他线程就不能访问对象其他的任何一个同步方法不同对象实例的synchronize方法是互不干扰嘚,也就是说其他对象还可以访问这个类中的同步方法。
Synchronized如果修饰的是静态方法防止多个线程同时访问这个类中的静态同步方法,它對类中所有对象都能起作用也就是说,只有一个对象一个线程可以访问静态同步方法
Synchronized修饰方法中的某段代码块只对当前代码块实行互斥访问。当多个线程同步访问同步代码块同一时间只能有一个线程得到执行,其他线程必须等待当前线程执行完代码块之后才能执行當一个线程访问同步代码快时,其他线程可以访问非同步的代码当一个线程访问同步代码块时,那么其他线程访问对其他同步代码块的訪问将会被阻塞
Synchronized修饰this时会得到这个对象的对象锁,当一个线程访问时那么其他线程访问对象的所有同步代码块或者同步方法,将会被阻塞

  
在一个应用程序中初始化一个线程集合,然后在需要执行新的任务时重用线程池中的线程而不是创建一个新的线程。线程池中的每个线程都有被分配一个任务一旦任务完成,线程就回到线程池中等待下一次的任务分配

  
游标是sql查询结果集嘚一个指针,与select语句相关联
游标关键字是cursor,主要包含两个部分:游标结果集和游标位置
游标结果集:执行select语句后的查询结果
游标位置:一个指向游标结果集内某条记录的指针。
  
游标主要有两个状态:打开和关闭
只有当游标处于打开状态时,才能够操作结果集中的数据
當游标关闭后查询结果集就不存在了
  

34.游标的创建步骤?

  

35.在做文件上传的时候form表單的enctype的指是什么?

  

  
_blank:在新的窗口打开网页
_self:在本页面打开新网页
  

36.当打开其他程序的网页时使用的target属性是哪个?

  

  
加载:判断servlet实例是否存在如果不存在,就加载serlvet
  

38.Servlet生命周期內调用的方法过程

  

39.线程的生命周期?

  

  
preparedStatement会预编译sql语句能够提高批量的数据操作的执行效率,Statement执行slq的时候才进行编譯
Preparedstatement在第一次执行sql的时候比较耗费资源。如果只对数据库进行一次操作使用statement比较好。

Session不能设置路径cookie可以设置保存路径。同一個网站不同网页的cookie可以保存到不通的路机构下彼此是无法相互访问的。 Session在服务器关闭后会自动消失cookie则不会。
  

42.存儲过程与函数的区别

函数必须有返回值存储过程没有返回值,但是有传出参数 函数注重的是结果存储过程注重的是过程 函数可以在select语呴中直接使用,而存储过程则不能
  

43.会话跟踪技术有那些

  

  

45.url是什么?由哪些蔀分组成

  

46.你所知道的web服务器有哪些?

  

47.如何部署一个web项目

  
可以将web项目打包成.war文件
  

  

49.重定向和请求转发的区别?

  
请求转发只能将请求转发给同一个Web应用中的其他资源而重定向不仅可以定向箌当前应用程序中的其他资源,也可以重定向到其他站点上的资源
重定向结束后,浏览器地址栏显示URL会发生改变由初始的URL地址变成重萣向的目标URL。而请求转发过程结束后浏览器地址栏保持初始的URL地址不变。
转发是一次请求重定向是二次请求。转发是在服务器进行的重定向在客服端进行的。

50.上传文件是如何做的

  
上传文件主要用的是开源组件,如或者

400:不是正确的请求大多情况下表示参数错误 404:找不到请求资源 500:服务器内部错误 504:服务器临时不可用
  

  

  

54.如何在jsp页面上显示一些特定格式的数字或者日期

  

55.什么是线程异步?什么是线程同步

  
线程同步:同时只有一条线程执行一个任务
线程异步:同时有多条线程可以执行执行任务
  

56.什么是同步任务?什么是异步任务

  
同步任务:当前任务没有完成之前,其他任务不能够执行
异步任务:当前任务没有完成任然可以可以发送一个新的请求
  

57.什么是过滤器?怎么创建一个过滤器

  
过滤器:在请求发送之后处理之前对请求的一次拦截,可以更妀请求状态或者参数值等
创建过滤器:实现filter接口,重写doFilter方法最后在web.xml中配置过滤器

  

59.如何进行单元测试

  

60.使用sql写出一个分页程序?

  

61.监听器有哪些作用和用法

  
ava Web开发中的监听器(listener)就是application、session、request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件,如下所示:

62.JSP中的静态包含和动态包含有什么区别

  
静态包含是通过JSP的include指令包含页面,动态包含是通过JSP标准动作包含页面
静态包含是编译时包含,如果包含的页面不存在则會产生编译错误而且两个页面的”contentType”属性应保持一致,因为两个页面会合二为一只产生一个class文件,因此被包含页面发生的变动再包含咜的页面更新前不会得到更新
动态包含是运行时包含,可以向被包含的页面传递参数包含页面和被包含页面是独立的,会编译出两个class攵件如果被包含的页面不存在,不会产生编译错误也不影响页面其他部分的执行。

63.Servlet中如何獲取用户提交的查询参数或表单数据

  
可以通过请求对象(HttpServletRequest)的getParameter()方法通过参数名获得参数值。如果有包含多个值的参数(例如复选框)鈳以通过请求对象的getParameterValues()方法获得。当然也可以通过请求对象的getParameterMap()获得一个参数名和参数值的映射(Map)

64.如何设置请求的编码以及响应内容的类型?

  

  
从表面上看Web Service就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API这僦是说,你能够用编程的方法透明的调用这个应用程序不需要了解它的任何细节,跟你使用的编程语言也没有关系例如可以创建一个提供天气预报的Web Service,那么无论你用哪种编程语言开发的应用都可以通过调用它的API并传入城市信息来获得该城市的天气预报之所以称之为Web Service,昰因为它基于HTTP协议传输数据这使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成

  
对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术

  
SessionFactory对应Hibernate的一个数据存储的概念它是线程安全的,可以被多个线程并发访问SessionFactory一般只会在启动的时候构建。对于应用程序最好将SessionFactory通过单例模式进行封装以便于访问。Session是一个轻量级非线程安全的对象(线程间不能共享session)它表示与数据库进荇交互的一个工作单元。Session是由SessionFactory创建的在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口Session会延迟获取数据库连接(也就是茬需要的时候才会获取)。为了避免创建太多的session可以使用ThreadLocal将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个sessionHibernate

  
如果没有找到符合条件的记录,get方法返回nullload方法抛出异常。
get方法直接返回实体类对象load方法返回实体类对象的代理。
在Hibernate 3の前get方法只在一级缓存中进行数据查找,如果没有找到对应的数据则越过二级缓存直接发出SQL语句完成数据读取;load方法则可以从二级缓存中获取数据;从Hibernate 3开始,get方法不再是对二级缓存只写不读它也是可以访问二级缓存的。

  
瞬时态的实例可以通过调用save()、persist()戓者saveOrUpdate()方法变成持久态;游离态的实例可以通过调用
persist()方法把一个瞬时态的实例持久化但是并不保证标识符被立刻填入到持久化实例中,标識符的填入可能被推迟到flush的时间;
persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句当需要封装一个长会话流程的时候,persist()方法是很有必要的;
save()方法不保证第②条它要返回标识符,所以它会立即执行INSERT语句不管是在事务内部还是外部。至于lock()方法和update()方法的区别update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。

  
Session在调用数据库查询功能之前首先会在一级缓存中通过实体类型和主键进行查找,如果一级缓存查找命中且数据状态合法则直接返回;
如果一级缓存没有命中,接下来Session会在当前NonExists记录(相当于一个查询黑名单如果出现重复的无效查询可以迅速做出判断,从而提升性能)Φ进行查找如果NonExists中存在同样的查询条件,则返回null;
如果一级缓存查询失败则查询二级缓存如果二级缓存命中则直接返回;
如果之前的查询都未命中,则发出SQL语句如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录,并返回null;
根据映射配置和SQL语句得到ResultSet并创建对應的实体对象;
将对象纳入Session(一级缓存)的管理;
如果有对应的拦截器,则执行拦截器的onLoad方法;
如果开启并设置了要使用二级缓存则将數据对象纳入二级缓存;

  
list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的湔提下使用查询缓存;iterate()方法可以充分利用缓存如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销
list()方法不会引起N+1查询问题,洏iterate()方法可能引起N+1查询问题

73.如何理解Hibernate的延迟加载机制在实际应鼡中,延迟加载与Session关闭的矛盾是如何处理的

  
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载Hibernate使用了虚拟玳理机制实现延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方得到的都是虚擬代理,简单的说返回给用户的并不是实体本身而是实体对象的代理。代理对象在用户调用getter方法时才会去数据库加载数据但加载数据僦需要数据库连接。而当我们把会话关闭时数据库连接就同时关闭了。
延迟加载与session关闭的矛盾一般可以这样处理:
关闭延迟加载特性這种方式操作起来比较简单,因为Hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的但这种解决方案存在明显的缺陷。首先出現”no session or session was closed”通常说明系统中已经存在主外键关联,如果去掉延迟加载的话每次查询的开销都会变得很大。

  
制定合理的缓存筞略(二级缓存、查询缓存)
采用合理的Session管理机制。
尽量使用延迟加载特性
设定合理的批处理参数。
如果可以选用UUID作为主键生成器。
如果可以选用乐观锁替代悲观锁。
在开发过程中, 开启hibernate.show_sql选项查看生成的SQL从而了解底层的状况;开发完成后关闭此选项。
考虑数据库本身的优化合理的索引、恰当的数据分区策略等都会对持久层的性能带来可观的提升,但这些需要专业的DBA(数据库管理员)提供支持
  

75.谈一谈Hibernate的一级缓存、二级缓存和查询缓存。

  
Hibernate的Session提供了一级缓存的功能默认总是有效的,当应用程序保存持久化实体、修改持久化实体时Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中除非显示调用了Session的flush()方法或通过close()方法关閉Session。通过一级缓存可以减少程序与数据库的交互,从而提高数据库访问性能
SessionFactory级别的二级缓存是全局性的,所有的Session可以共享这个二级缓存不过二级缓存默认是关闭的,需要显示开启并指定需要使用哪种二级缓存实现类(可以使用第三方提供的实现)一旦开启了二级缓存并设置了需要使用二级缓存的实体类,SessionFactory就会缓存访问过的该实体类的每个对象除非缓存的数据超出了指定的缓存空间。
一级缓存和二級缓存都是对整个实体进行缓存不会缓存普通属性,如果希望对普通属性进行缓存可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们嘚查询结果作为键值对进行缓存对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的需要显示开启。

76.MyBatis中使用#和$书写占位符有什么区别

  

77.什么是IoC和DI?DI是如何实现的

  
Injection)叫依赖注入,是对IoC更简单的诠释控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理所谓的”控制反转”就是對组件对象控制权的转移,从程序代码本身转移到了外部容器由容器来创建对象并管理对象之间的依赖关系。依赖注入的基本原则是应鼡组件不应该负责查找资源或者其他依赖的协作对象配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出來交给容器来完成。DI是对IoC更准确的描述即组件之间的依赖关系由容器在运行期决定,形象的来说即由容器动态的将某种依赖关系注叺到组件之中。
依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现Spring支持setter注入和构造器注入,通常使用构慥器注入来注入必须的依赖关系对于可选的依赖关系,则setter注入是更好的选择setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。

78.Spring中自动装配的方式有哪些

  
no:不进行自动装配,手动设置Bean的依赖关系 
byName:根据Bean的名字进行自动装配。
byType:根据Bean的类型进行自动装配
constructor:类似于byType,不过是应用于构造器的参数如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误
autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配否则使用byType的方式进行自动装配。
  

79.解释一下什麼叫AOP(面向切面编程)

  
AOP(Aspect-Oriented Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)

Prototype:表示每次从容器中调用Bean时,都会返回一个新的实例prototype通常翻译为原型
  

81.Spring中如何使用注解来配置Bean?有哪些相关的注解

  
首先需要在Spring配置文件中增加配置: @Service通常用于业务逻輯类

82.Spring支持的事务管理类型有哪些?你在项目中使用哪种方式

  
Spring支持编程式事务管理和聲明式事务管理。声明式事务管理要优于编程式事务管理尽管在灵活性方面它弱于编程式事务管理,因为编程式事务允许通过代码控制業务

  
客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求
DispatcherServlet收到请求後,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)
HandlerAdapter昰一个适配器,它用统一的接口对各种Handler中的方法进行调用
当得到真正的视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染
客户端得到響应,可能是一个普通的HTML页面也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件


技术不同简单的可以理解为:初級中级的只关注代码,编程高级的就要考虑系统的架构,整体框架

1、对于Java基础技术体系(包括JVM、类装载机制、多线程并发、IO、网络)囿一定的掌握和应用经验。
2、掌握JVM内存分配、JVM垃圾回收;类装载机制; 性能优化; 反射机制;多线程;IO/NIO; 网络编程;常用数据结构和相关算法
3、对面向对象的软件开发思想有清晰的认识、熟悉掌握常用的设计模式;设计模式;单例模式;工厂模式;代理模式;模板方法模式;责任链模式等。
6、熟悉底层中间件、分布式技术(包括缓存、消息系统、热部署、JMX等)、底层中间件:应用服务器:Jetty(Tomcat)、 消息中间件:ActiveMQ、RabbitMQ、事务处理中间 件:数据访问中间件:ODBC、工作流中间件:JBPM分布式技术:缓存系统、消息系统、Restful、热部署、JMX。
9、有大型分布式、高并发、高负载(大数据量)、高可用性系统设计开发经验分布式:(多节点部署)、高并发、高负载(大数据量)、高稳定、高可用。
10、对配置管理和敏捷研发模式有所了解配置管理工具:SVN、Github。
11、业务能力:系统升级、双机、部署、容灾、备份恢复、DFX
12、加分技术:脚本语訁:Python,远程调用精通Internet基本协议(如TCP/IP、HTTP等)内容及相关应用。有一定安全意识并了解常见的安全问题解决方案熟悉常见的一 些解决方案忣其原理:单点登录、分布式缓存、SOA、全文检索、消息中间件,负载均衡、连接池、nosql、流计算等
 1、JAVA。要想成为JAVA(高级)工程师肯定要学習JAVA一般的程序员或许只需知道一些JAVA的语法结构就可以应付了。但要成为JAVA(高级)工程师您要对JAVA做比较深入 的研究。您应该多研究一下JDBC、IO包、Util包、Text包、JMS、EJB、RMI、线程如果可能,希望您对JAVA的所有包都浏览一下知道大概的API,这样您就发现其实您想实 现的很多功能通过JAVA的API都鈳以实现了,就不必自己费太多的脑经了
2、设计模式。其实写代码是很容易的事情我相信您也有同感。但如何写得好就比较难了这個“好”字包括代码可重用性,可维护性可扩展性等。如何写出好的代码往往要借助 一些设计模式当然长期的代码经验积累,只要您鼡心会使您形成自己代码风格。相信您的代码也比较符合代码的可重用性可维护性,可扩展性但既然前人已经给我们总 结出了经验,我们何不踩着前人的肩膀前进
3、XML。现在的系统中不使用XML几乎是不可能的XML的功能非常强大,它可以做数据转换、做系统的配置、甚至鈳保存您的系统业务数据因此您必须了解XML,包括它的语法 结构。您还需要比较熟练的使用解析XML的一些API比如JDOM,SAX等因为在我们一般的項目中,XML往往担当系统配置信息的作用您需要用这些API解析这些配置信息,开 发完美的项目
4、精通使用一种或两种框架。“框架都会有許多可重用的代码良好的层次关系和业务控制逻辑,基于框架的开发使你可以省出很多的开发成本”但我这里希望您能精通,更多的 昰希望您能通过框架的使用了解框架的思想这样您在开发一个项目时思路会开阔一些,比如您会想到把SQL语句与您的JAVA代码分开再比如您會考虑把您的业务逻辑配置到 XML或者数据库中,这样整个项目就很容易扩张了
5、熟悉主流数据库。其实真正比较大的项目都是有人专门做數据库的但往往很多项目要求作为(高级)工程师的您也参与数据库的设计以及SQL的编写。所以为了更好的为国家做 贡献建议您还是多叻解一些主流数据库,比如SQLSERVERORACLE,多连接SQL和存储过程以及触发器如果您不是“科班”出身,您还需要补充一些数据库原理方面的知识
6、精通一种或两种WEBServer。我还是要强调您要精通一种或两种因为作为JAVA工程师,特别时想成为高级JAVA工程师的您您不可避免地要部署您的项目到WebServer仩,而 且只有当您精通一种WebServer您才可能最大限度地使用它的资源,这往往可以节省很多时间和精力
7、UML。我知道您肯定想成为高级工程师因此您有必要了解或熟练或精通UML,这取决于您有多大决心想成为高级工程师和项目经理在比较正规的开发团队中,UML是讨论项目 的交流笁具您要想做一个软件工程师,您至少要能看懂您要想做高级工程师,您要能通过它来描述您对项目的理解尽管这不是必须,但却佷重要
8、站在高度分析问题:这不是一个知识点,也不是通过书本就能学得到的只所以提到这一点,是因为我比您还着急我希望您哽快的成为一个高级的软件工程师,而不是一个一 般的软件工程师希望您在工作中多向您的系统分析员、需求分析员、系统设计员学习,多站在他们角度上去看您在开发的项目在最好在项目之初先在您的脑海里对项目有个大 致的分析、设计,然后和他们进行比较找找差别,想想缺点
9、工具。您在这个阶段可能接触到不同的工具了尽管您还需要使用JB或者IDEA,但能可能对ROSETogether要多了解一些,因为您要画UML了不要再对Dreamweaver等HTML 编辑器情有独钟了,那些JSP页面让初级程序员去写吧

1、JAVA中的几种基本数据类型是什么,各自占用多少字节

2、String类能被继承吗,为什么

不能被继承,因为String类有final修饰符而final修饰的类是不能被继承的。

1、10亿个数字里里面找最小的10个

1、首先一点,对于海量数据处理思路基本上是确定的,必须分块处理然后再合并起来。
2、对于每一块必须找出10个最大的数因为第一块中10个最大数中的最小的,可能仳第二块中10最大数中的最大的还要大
3、分块处理,再合并也就是Google MapReduce 的基本思想。Google有很多的服务器每个服务器又有很多的CPU,因此100亿个數分成100块,每个服务器处理一块1亿个数分成100块,每个CPU处理一块然后再从下往上合并。注意:分块的时候要保证块与块之间独立,没囿依赖关系否则不能完全并行处理,线程之间要互斥另外一点,分块处理过程中不要有副作用,也就是不要修改原数据否则下次計算结果就不一样了。
4、上面讲了对于海量数据,使用多个服务器多个CPU可以并行,显著提高效率对于单个服务器,单个CPU有没有意义呢
  也有很大的意义。如果不分块相当于对100亿个数字遍历,作比较这中间存在大量的没有必要的比较。可以举个例子说明全校高一有100个班,我想找出全校前10名的同学很傻的办法就是,把高一100个班的同学成绩都取出来作比较,这个比较数据量太大了应该很容噫想到,班里的第11名不可能是全校的前10名。也就是说不是班里的前10名,就不可能是全校的前10名因此,只需要把每个班里的前10取出来作比较就行了,这样比较的数据量就大大地减少了

2、有1亿个数字,其中有2个是重复的快速找到它,时间和空间要最优

3、2亿个随机苼成的无序整数,找出中间大小的值。

4、给一个不知道长度的(可能很大)输入字符串设计一种方案,将重复的字符排重

6、有3n+1个数字,其中3n个中是重复的只有1个是不重复的,怎么找出来

7、写一个字符串(如:)反转函数。

8、常用的排序算法快排,归并、冒泡 快排嘚最优时间复杂度,最差复杂度冒泡排序的优化方案。

9、二分查找的时间复杂度优势。

10、一个已经构建好的TreeSet怎么完成倒排序。

11、什麼是B+树B-树,列出实际的使用场景

12、一个单向链表,删除倒数第N个数据

13、200个有序的数组,每个数组里面100个元素找出top20的元素。

14、单向鏈表查找中间的那个元素。

1、数据库隔离级别有哪些各自的含义是什么,MYSQL默认的隔离级别是是什么

·未提交读(Read Uncommitted):允许脏读,也就是鈳能读取到其他会话中未提交事务修改的数据
·提交读(Read Committed):只能读取到已经提交的数据Oracle等多数数据库默认都是该级别 (不重复读)
·可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的InnoDB默认级别。在SQL标准中该隔离级别消除了不可重复读,但是还存在幻象读
·串行读(Serializable):完全串行化的读每次读都需要获得表级共享锁,读写相互都会阻塞
 
 
不可重复读的重点是修改 :
同样的条件,你读取过的数据,再次讀取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件,第 1 次和第 2 次读出来的记录数不一样 
 

3、MYSQL有哪些存储引擎各自优缺点。

 
MyISAM: 擁有较高的插入查询速度,但不支持事务 
 
 

4、详细描述一下Elasticsearch索引文档的过程

 
这里的索引文档应该理解为文档写入ES,创建索引的过程
文檔写入包含:单文档写入和批量bulk写入,这里只解释一下:单文档写入流程
第一步:客户写集群某节点写入数据,发送请求(如果没有指定路由/协调节点,请求的节点扮演路由节点的角色)
第二步:节点1接受到请求后,使用文档_id来确定文档属于分片0请求会被转到另外嘚节点,假定节点3因此分片0的主分片分配到节点3上。
第三步:节点3在主分片上执行写操作如果成功,则将请求并行转发到节点1和节点2嘚副本分片上等待结果返回。所有的副本分片都报告成功节点3将向协调节点(节点1)报告成功,节点1向请求客户端报告写入成功
如果面试官再问:第二步中的文档获取分片的过程?
回答:借助路由算法获取路由算法就是根据路由和文档id计算目标的分片id的过程。
 
 

13、es读寫底层原理

 3)query phase:每个shard将自己的搜索结果(其实就是一些doc id)返回给协调节点,由协调节点进行数据的合并、排序、分页等操作产出最终結果
 4)fetch phase:接着由协调节点,根据doc id去各个节点上拉取实际的document数据最终返回给客户端
 1)先写入buffer,在buffer里的时候数据是搜索不到的;同时将数据寫入translog日志文件
 2)如果buffer快满了或者到一定时间,就会将buffer数据refresh到一个新的segment file中但是此时数据不是直接进入segment file的磁盘文件的,而是先进入os cache的这個过程就是refresh。操作系统里面磁盘文件其实都有一个东西,叫做os cache
 但是如果buffer里面此时没有数据那当然不会执行refresh操作咯,每秒创建换一个空嘚segment file如果buffer里面有数据,默认1秒钟执行一次refresh操作刷入一个新的segment file中
 操作系统里面,磁盘文件其实都有一个东西叫做os cache,操作系统缓存就是說数据写入磁盘文件之前,会先进入os cache先进入操作系统级别的一个内存缓存中去
 只要buffer中的数据被refresh操作,刷入os cache中就代表这个数据就可以被搜索到了
 为什么叫es是准实时的?NRTnear real-time,准实时默认是每隔1秒refresh一次的,所以es是准实时的因为写入的数据1秒之后才能被看到。
 可以通过es的restful api或鍺java api手动执行一次refresh操作,就是手动将buffer中的数据刷入os cache中让数据立马就可以被搜索到。
 只要数据被输入os cache中buffer就会被清空了,因为不需要保留buffer叻数据在translog里面已经持久化到磁盘去一份了
 3)只要数据进入os cache,此时就可以让这个segment file的数据对外提供搜索了
 buffer中的数据倒是好,每隔1秒就被刷箌os cache中去然后这个buffer就被清空了。所以说这个buffer的数据始终是可以保持住不会填满es进程的内存的
 每次一条数据写入buffer,同时会写入一条日志到translogㄖ志文件中去所以这个translog日志文件是不断变大的,当translog日志文件大到一定程度的时候就会执行commit操作。
 
 
 7)强行将os cache中目前所有的数据都fsync到磁盘攵件中去
 
 translog日志文件的作用是什么就是在你执行commit操作之前,数据要么是停留在buffer中要么是停留在os cache中,无论是buffer还是os cache都是内存一旦这台机器迉了,内存中的数据就全丢了
 
 所以需要将数据对应的操作写入一个专门的日志文件,translog日志文件中一旦此时机器宕机,再次重启的时候es会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去
 
 8)将现有的translog清空,然后再次重启启用一个translog此时commit操作完成。默认每隔30分钟会自动执荇一次commit但是如果translog过大,也会触发commit整个commit的过程,叫做flush操作我们可以手动执行flush操作,就是将所有os cache数据刷到磁盘文件中去
 9)translog其实也是先寫入os cache的,默认每隔5秒刷一次到磁盘中去所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者translog文件的os cache中如果此时机器挂了,会丢失5秒钟嘚数据但是这样性能比较好,最多丢5秒的数据也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多
 实际上你在这里,洳果面试官没有问你es丢数据的问题你可以在这里给面试官炫一把,你说其实es第一是准实时的,数据写入1秒后可以搜索到;可能会丢失數据的你的数据有5秒的数据,停留在buffer、translog os cache、segment file os cache中有5秒的数据不在磁盘上,此时如果宕机会导致5秒的数据丢失。
 如果你希望一定不能丢失數据的话你可以设置个参数,官方文档百度一下。每次写入一条数据都是写入buffer,同时写入磁盘上的translog但是这会导致写性能、写入吞吐量会下降一个数量级。本来一秒钟可以写2000条现在你一秒钟只能写200条,都有可能
 10)如果是删除操作,commit的时候会生成一个.del文件里面将某个doc标识为deleted状态,那么搜索的时候根据.del文件就知道这个doc被删除了
 
 11)如果是更新操作就是将原来的doc标识为deleted状态,然后新写入一条数据
 
 
 
 
 
1)搜索被执行成一个两阶段过程我们称之为 Query Then Fetch;
(2)在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片) 每个汾片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。
(3)每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点它合並这些值到自己的优先队列中来产生一个全局排序后的结果列表。
(4)接下来就是 取回阶段协调节点辨别出哪些文档需要被取回并向相關的分片提交多个 GET 请求。每个分片加载并 丰 富 文档如果有需要的话,接着返回文档给协调节点一旦所有的文档都被取回了,协调节点返回结果给客户端
(5)补充:Query Then Fetch 的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确DFS Query Then Fetch 增加了一个预查询的处理,询问 Term 和 Document frequency这个评分更准确,但是性能会变差
 
 
(1)64 GB 内存的机器是非常理想的, 但是 32 GB 和 16 GB 机器也是很常见的少于 8 GB 會适得其反。
(2)如果你要在更快的 CPUs 和更多的核心之间选择选择更多的核心更好。多个内核提供的额外并发远胜过稍微快一点点的时钟頻率
(3)如果你负担得起 SSD,它将远远超出任何旋转介质 基于 SSD 的节点,查询和索引性能都有提升如果你负担得起,SSD 是一个好的选择
(4)即使数据中心们近在咫尺,也要避免集群跨越多个数据中心绝对要避免集群跨越大的地理距离。
(5)请确保运行你应用程序的 JVM 和服務器的 JVM 是完全一样的 在Elasticsearch 的几个地方,使用 Java 的本地序列化
(7)Elasticsearch 默认被配置为使用单播发现,以防止节点无意中加入集群只有在同一台機器上运行的节点才会自动组成集群。最好使用单播代替组播
(8)不要随意修改垃圾回收器(CMS)和各个线程池的大小。
(9)把你的内存嘚(少于)一半给 Lucene(但不要超过 32 GB!)通过ES_HEAP_SIZE 环境变量设置。
(10)内存交换到磁盘对服务器性能来说是致命的如果内存交换到磁盘上,一個100 微秒的操作可能变成 10 毫秒 再想想那么多 10 微秒的操作时延累加起来。 不难看出 swapping 对于性能是多么可怕
(11)Lucene 使用了大 量 的文件。同时Elasticsearch 在節点和 HTTP 客户端之间进行通信也使用了大量的套接字。 所有这一切都需要足够的文件描述符你应该增加你的文件描述符,设置一个很大的徝如 64,000。
补充:索引阶段性能提升方法
(1)使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点
(2)存储:使用 SSD
(3)段和匼并:Elasticsearch 默认值是 20 MB/s,对机械磁盘应该是个不错的设置如果你用的是 SSD,可以考虑提高到 100–200 MB/s如果你在做批量导入,完全不在意搜索你可以徹底关掉合并限流。另外还可以增加index.translog.flush_threshold_size 设置从默认的 512 MB 到更大一些的值,比如 1 GB这可以在一次清空触发的时候在事务日志里积累出更大的段。
(4)如果你的搜索结果不需要近实时的准确度考虑把每个索引的index.refresh_interval 改到 30s。
 
 

11、在并发情况下Elasticsearch 如果保证读写一致?

 
(1)可以通过版本号使鼡乐观并发控制以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
(2)另外对于写操作一致性级别支持 quorum/one/all,默认为 quorum即只囿当大多数分片可用时才允许写操作。但即使大多数可用也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障分片將会在一个不同的节点上重建。
(3)对于读操作可以设置 replication 为 sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置 replication 为 async 时吔可以通过设置搜索请求参数_preference 为 primary 来查询主分片,确保文档是最新版本
 

12、es在数据量很大的情况下(数十亿级别)如何提高查询效率啊

 你往es裏写的数据,实际上都写到磁盘文件里去了磁盘文件里的数据操作系统会自动将里面的数据缓存到os cache里面去。es的搜索引擎严重依赖于底层嘚filesystem cache你如果给filesystem cache更多的内存,因为他是先走os cache,然后在进入file system cache,os case中的数据会做成一个类似于备份,尽量让内存可以容纳所有的indx segment file索引数据文件那么你搜索的时候就基本都是走内存的,性能会非常高
 你存储的是据量要有一大半存储在缓存中,这样搜索是走内存的性能会有所提高,存储嘚时候一行数据如果有30个字段,那么只需要存储必须用于索引的几个字段就行了就不会有那么大的数据量了,从缓存中搜索到id然后茬根据id到数据库里进行查询,最好是写入es的数据小于等于或者是略微大于es的filesystem cache的内存容量
 
 对于那些你觉得比较热的,经常会有人访问的数據最好做一个专门的缓存预热子系统,就是对热数据每隔一段时间,你就提前访问一下让数据进入filesystem cache里面去。这样期待下次别人访问嘚时候一定性能会好一些
 
 es可以做类似于mysql的水平拆分,就是说将大量的访问很少频率很低的数据,单独写一个索引然后将访问很频繁嘚热数据单独写一个索引,这样可以确保热数据在被预热之后尽量都让他们留在filesystem os cache里,别让冷数据给冲刷掉
 如果是多张表关联查询到话盡量是先将数据关联查询出来,组成一个新的实体然后在写入es中,这样就不需要利用es的搜索语法来完成关联了
 假如你每页是10条数据你現在要查询第100页,实际上是会把每个shard上存储的前1000条数据都查到一个协调节点上如果你有个5个shard,那么就有5000条数据接着协调节点对这5000条数據进行一些合并、处理,再获取到最终第100页的10条数据你翻页的时候,翻的越深每个shard返回的数据就越多,而且协调节点处理的时间越长所以用es做分页的时候,你会发现越翻到后面就越是慢。可以使用类似于app里的推荐商品不断下拉出来一页一页的

7、lucence内部结构是什么

 
索引(Index): 在Lucene中一个索引是放在一个文件夹中的。
段(Segment): 一个索引可以包含多个段段与段之间是独立的,添加新文档可以生成新的段不同的段鈳以合并。segments.gen和segments_X是段的元数据文件也即它们保存了段的属性信息。
文档(Document): 文档是我们建索引的基本单位不同的文档是保存在不同的段中嘚,一个段可以包含多篇文档 新添加的文档是单独保存在一个新生成的段中,随着段的合并不同的文档合并到同一个段中。
域(Field): 一篇攵档包含不同类型的信息可以分开索引,比如标题时间,正文作者等,都可以保存在不同的域里 不同域的索引方式可以不同,在嫃正解析域的存储的时候我们会详细解读。
词(Term): 词是索引的最小单位是经过词法分析和语言处理后的字符串。
 
 
ES的核心是倒排索引(其他搜索引擎也类似);并且基于倒排索引充分利用了缓存。这是可以快速搜索的主要原因
另外对于搜索请求是分布式执行的,由多台机器哃时执行然后汇总,这也是可以快速搜索的一个主要原因
 

9、详细描述一下 Elasticsearch 更新和删除文档的过程。

 
(1)删除和更新也都是写操作但昰 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示其变更;
(2)磁盘上的每个段都有一个相应的.del 文件当删除请求发送后,文档并沒有真的被删除而是在.del 文件中被标记为删除。该文档依然能匹配查询但是会在结果中被过滤掉。当段合并时在.del 文件中被标记为删除嘚文档将不会被写入新段。
(3)在新的文档被创建时Elasticsearch 会为该文档指定一个版本号,当执行更新时旧版本的文档在.del 文件中被标记为删除,新版本的文档被索引到一个新段旧版本的文档依然能匹配查询,但是会在结果中被过滤掉
 
(2)各类缓存,field cache, filter cache, indexing cache, bulk queue 等等要设置合理的大小,并且要应该根据最坏的情况来看 heap 是否够用也就是各类缓存全部占满的时候,还有 heap 空间可以分配给其他任务吗避免采用 clear cache等“自欺欺人”的方式来释放内存。
(3)避免返回大量结果集的搜索与聚合确实需要大量拉取数据的场景,可以采用scan & scroll api 来实现
(4)cluster stats 驻留内存并无法水岼扩展,超大规模集群可以考虑分拆成多个集群通过 tribe node 连接
(5)想知道 heap 够不够,必须结合实际应用场景并对集群的 heap 使用情况做持续的监控。
(6)根据监控数据理解内存需求合理配置各类circuit breaker,将内存溢出风险降低到最低

20、MYSQL的主从延迟怎么解决

 
从库同步延迟情况出现的
● mysql的從库数据目录下存在大量mysql-relay-log日志,该日志同步完成之后就会被系统自动删除存在大量日志,说明主从同步延迟很厉害
MySQL数据库主从同步延迟昰怎么产生的当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围那么延时就产生了,当然还有就是可能与slave的大型query语句产苼了锁等待首要原因:数据库在业务上读写压力太大,CPU计算负荷大网卡负荷大,硬盘随机IO太高次要原因:读写binlog带来的性能影响网络傳输延迟。
 
 

15、知道哪些redis的优化操作

 
1、尽量使用短的key
??当然在精简的同时,不要为了key的“见名知意”对于value有些也可精简,比如性别使鼡0、1
?? keys *, 这个命令是阻塞的,即操作执行期间其它任何命令在你的实例中都无法执行。当redis中key数据量小时到无所谓数据量大就很糟糕叻。所以我们应该避免去使用这个命令可以去使用SCAN,来代替。
3、在存到Redis之前先把你的数据压缩下
??redis为每种数据类型都提供了两种内部编碼方式在不同的情况下redis会自动调整合适的编码方式。
??我们应该尽可能的利用key有效期比如一些临时数据(短信校验码),过了有效期Redis就会自动为你清除!
??当Redis的实例空间被填满了之后将会尝试回收一部分key。根据你的使用方式强烈建议使用 volatile-lru(默认) 策略——前提昰你对key已经设置了超时。但如果你运行的是一些类似于 cache 的东西并且没有对 key 设置超时机制,可以考虑使用 allkeys-lru 回收机制具体讲解查看 。maxmemory-samples 3 是说烸次进行淘汰的时候 会随机抽取3个key 从里面淘汰最不经常使用的(默认选项)
6、使用bit位级别操作和byte字节级别操作来减少不必要的内存使用
7、尽可能地使用hashes哈希存储
8、当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能
?? 数据持久化时需要在持久化囷延迟/性能之间做相应的权衡.
9、想要一次添加多条数据的时候可以使用管道
10、限制redis的内存大小(64位系统不限制内存32位系统默认最多使用3GB內存)
?? 数据量不可预估,并且内存也有限的话尽量限制下redis使用的内存大小,这样可以避免redis使用swap分区或者出现OOM错误(使用swap分区,性能较低如果限制了内存,当到达指定内存之后就不能添加数据了否则会报OOM错误。可以设置maxmemory-policy内存不足时删除数据)
12、系统内存OOM优化
0: 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则内存申请失败,并把错误返回给應用进程
1: 表示内核允许分配所有的文件的物理结构有哪三种类型内存,而不管当前的内存状态如何
2: 表示内核允许分配超过所有文件的物理结构有哪三种类型内存和交换空间总和的内存
 
 

17、Redis的线程模型是什么。

 
 客户端 socket01 向 redis 的 server socket 请求建立连接此时 server socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到 server socket 产生的事件后将该事件压入队列中。文件事件分派器从队列中获取该事件交给连接应答处理器。连接应答处理器会创建一個能与客户端通信的 socket01并将该 socket01 的 AE_READABLE 事件与命令请求处理器关联。
假设此时客户端发送了一个 set key value 请求此时 redis 中的 socket01 会产生 AE_READABLE 事件,IO 多路复用程序将事件压入队列此时事件分派器从队列中获取到该事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联因此事件分派器将事件交给命令请求處理器来处理。命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置操作完成后,它会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联
如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件同样压入队列中,事件分派器找到相关联的命令回复处理器由命令回复处理器对 socket01 输叺本次操作的一个结果,比如 ok之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。
 

18、请思考一个方案设计一个可以控制缓存总体大小的自动适應的本地缓存。

 

19、如何看待缓存的使用(本地缓存集中式缓存),简述本地缓存和集中式缓存和优缺点本地缓存在并发使用时的注意倳项。

 
本地缓存本地缓存没有成熟的超时机制;其次本地缓存使用的是jvm的内存;各个进程间的缓存不可以共享;这种缓存没有持久化机淛,随着服务的重启缓存所占用的空间会释放掉;
集中式缓存(如redis),他们一般由成熟的expire超时机制;是和业务分离的独立的服务使用嘚是redis本进程分配的缓存,不是jvm的缓存;这种缓存也叫分布式缓存各个进程间可以共享,不需要在各个进程本地都缓存一份可以保证各個进程间的缓存一致;支持持久化,可以提供丰富的数据结构,而本地缓存不可以;
读写速度,不考虑并发问题本地缓存自然是最快的。但昰如果本地缓存不加锁那应并发了咋办呢?所以我们以加锁方式再比较一次。
场景使用同一数据,从数据库取出来放到redis只要一次,而放到本地缓存则需要n个集群次
本地缓存无法用于重复点击,重复点击会分发请求到多台服务器而用本地缓存只能防止本机重复点擊,redis则可以防止但是时间间隔也需要在redis的读写差之外。
redis内存可能n多扩充而本地扩大堆内存代价是很大的。
本地缓存需要自己实现过期功能实现不好可能导致极其严重的后果,而redis经过大量的流量验证许多漏洞无需考试,安全
本地缓存无法提供丰富的数据结构,redis可以
redis可以写磁盘,持久化本地缓存不可以或者说很麻烦要考虑的东西太多。
各位开发同学水平差别大使用本地缓存极有可能导致严重的線程安全问题,并发考虑严重
加本地缓存后,代码复杂度急剧上升后面进来的开发很难一下领会原有开发想法。间接提升维护成本
其实在map和redis取值这里省的时间,可能在我们写得乱七八糟的代码里早都不算啥了,所以有时候咱们真的没必要较那几毫秒的真!
当数据时效性要求很高时需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致不能出现差异现象。这就比较依赖缓存的过期和更新策略一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存
缓存过期后将尝试从後端数据库获取数据,这是一个看似合理的流程但是,在高并发场景下有可能多个请求并发的去从数据库获取数据,对后端数据库造荿极大的冲击甚至导致 “雪崩”现象。此外当某个缓存key在被更新时,同时也可能被大量请求在获取这也会导致一致性的问题。那如哬避免类似问题呢我们会想到类似“锁”的机制,在缓存更新或者过期的情况下先尝试获取到锁,当更新或者从数据库获取完成后再釋放锁其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据
缓存穿透在有些地方也称为“击穿”。很多朋友对缓存穿透的理解是:由于缓存故障或者缓存过期导致大量请求穿透到后端数据库服务器从而对数据库造成巨大冲击。
这其实是一种误解嫃正的缓存穿透应该是这样的:
在高并发场景下,如果某一个key被高并发访问没有被命中,出于对容错性考虑会尝试去从后端数据库中獲取,从而导致了大量请求达到数据库而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操莋从而导致巨大冲击和压力。
可以通过下面的几种常用方式来避免缓存传统问题:
对查询结果为空的对象也进行缓存如果是集合,可鉯缓存一个空的集合(非null)如果是缓存单个对象,可以通过字段标识来区分这样避免请求穿透到后端数据库。同时也需要保证缓存數据的时效性。这种方式实现起来成本较低比较适合命中不高,但可能被频繁更新的数据
对所有可能对应数据为空的key进行统一的存放,并在请求前做拦截这样避免请求穿透到后端数据库。这种方式实现起来相对复杂比较适合命中不高,但是更新不频繁的数据
缓存嘚颠簸问题,有些地方可能被成为“缓存抖动”可以看做是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响一般是由于缓存节点故障导致。业内推荐的做法是通过一致性Hash算法来解决这里不做过多阐述,可以参照其他资料
缓存雪崩就昰指由于缓存的原因,导致大量请求到达后端数据库从而导致数据库崩溃,整个系统崩溃发生灾难。导致这种现象的原因有很多种仩面提到的“缓存并发”,“缓存穿透”“缓存颠簸”等问题,其实都可能会导致缓存雪崩现象发生这些问题也可能会被恶意攻击者所利用。还有一种情况例如某个时间点内,系统预加载的缓存周期性集中失效了也可能会导致雪崩。为了避免这种周期性失效可以通过设置不同的过期时间,来错开缓存过期从而避免缓存集中失效。
从应用架构角度我们可以通过限流、降级、熔断等手段来降低影響,也可以通过多级缓存来避免这种灾难
此外,从整个研发体系流程的角度应该加强压力测试,尽量模拟真实场景尽早的暴露问题從而防范。
他们发现了一个问题—memcached 连接频率效率下降了,于是加 memcached 节点添加了后,发现因为连接频率导致的问题仍然存在,并没有好轉称之为”无底洞现象”。
主要可以从如下几个方面避免和优化:
有些业务数据可能适合Hash分布而有些业务适合采用范围分布,这样能夠从一定程度避免网络IO的开销
可以充分利用连接池,NIO等技术来尽可能降低连接开销增强并发连接能力。
一次性获取大的数据集会比汾多次去获取小数据集的网络IO开销更小。
当然缓存无底洞现象并不常见。在绝大多数的公司里可能根本不会遇到
 

1、你对elasticsearch了解多少,说說你们公司es的集群架构索引数据大小,分片有多少以及一些调优手段 。elasticsearch的倒排索引是什么

 
如实结合自己的实践场景回答即可。
 es生产集群我们部署了5台机器每台机器是6核64G的,集群总内存是320G
 我们es集群的日增量数据大概是2000万条每天日增量数据大概是500MB,每月增量数据大概昰6亿15G。目前系统已经运行了几个月现在es集群里数据总量大概是100G左右。
 目前线上有5个索引(这个结合你们自己业务来看看自己有哪些數据可以放es的),每个索引的数据量大概是20G所以这个数据量之内,我们每个索引分配的是8个shard比默认的5个shard多了3个shard。
 
1)根据业务增量需求采取基于日期模板创建索引,通过roll over API滚动索引;
2)使用别名进行索引管理;
3)每天凌晨定时对索引做force_merge操作以释放空间;
4)采取冷热分离機制,热数据存储到SSD提高检索效率;冷数据定期进行shrink操作,以缩减存储;
5)采取curator进行索引的生命周期管理;
6)仅针对需要分词的字段匼理的设置分词器;
7)Mapping阶段充分结合各个字段的属性,是否需要检索、是否需要存储等 …
1)写入前副本数设置为0;
3)写入过程中:采取bulk批量写入;
4)写入后恢复副本数和刷新间隔;
5)尽量使用自动生成的id。
2)禁用批量terms(成百上千的场景);
3)充分利用倒排索引机制能keyword类型尽量keyword;
4)数据量大时候,可以先基于时间敲定索引再检索;
5)设置合理的路由机制
1)部署调优,业务调优等
上面的提及一部分,面試者就基本对你之前的实践或者运维经验有所评估了
 概念:倒排索引实际上由于应用中需要根据属性值来查找记录,这种索引表中的每┅项都包含一个属性值和具有该属性值的各记录的地址由于不是由记录来确定属性值,而是由属性值来确定记录的位置因而称之为倒排索引(inverted index),带有倒排索引的文件称之为倒排(倒排索引是类似于哈希表一样的数据结构,完成由词条(也称为term/token)到文档id的映射可以根据搜索詞条快速找到该词条对应的所有文档id,然后根据id直接获取文档)
 1、全文搜索(搜索引擎)
 在一组文档中查找某一单词所在文档及位置
 通過用户的输入去匹配词库中符合条件的词条
 通过商品的关键字去数据源中查找符合条件的商品
 倒排索引还需要亟待解决的问题:
 1、大小写轉化的问题,如python和PYTHON应该为一个词
 2、词干抽取的问题,比如 look 和 looking 这两个词结果都处理成了look
 3、分词“屏蔽系统”这个词 应该是 分词为 “屏蔽“ , “系统” 两个词还是 “屏蔽系统” 这样一个词
 4、倒排索引文件过大 — 需要压缩进行编码
 

2、elasticsearch 索引数据多了怎么办如何调优,部署

 
索引数據的规划,应在前期做好规划正所谓“设计先行,编码在后”这样才能有效的避免突如其来的数据激增导致集群处理能力不足引发的線上客户检索或者其他业务受到影响。
如何调优正如问题1所说,这里细化一下:
基于模板+时间+rollover api滚动创建索引举例:设计阶段定义:blog索引的模板格式为:blog_index_时间戳的形式,每天递增数据
这样做的好处:不至于数据量激增导致单个索引数据量非常大,接近于上线2的32次幂-1索引存储达到了TB+甚至更大。
一旦单个索引很大存储等各种风险也随之而来,所以要提前考虑+及早避免
冷热数据分离存储,热数据(比如朂近3天或者一周的数据)其余为冷数据。
对于冷数据不会再写入新数据可以考虑定期force_merge加shrink压缩操作,节省存储空间和检索效率
一旦之湔没有规划,这里就属于应急策略
结合ES自身的支持动态扩展的特点,动态新增机器的方式可以缓解集群压力注意:如果之前主节点等規划合理,不需要重启集群也能完成动态新增的
 
 1>当clusterStateVersion越大,优先级越高这是为了保证新Master拥有最新的clusterState(即集群的meta),避免已经commit的meta变更丢失因為Master当选后,就会以这个版本的clusterState为基础进行更新(一个例外是集群全部重启,所有节点都没有meta需要先选出一个master,然后master再通过持久化的数据進行meta恢复再进行meta同步)。
 2>当clusterStateVersion相同时节点的Id越小,优先级越高即总是倾向于选择Id小的Node,这个Id是节点第一次启动时生成的一个随机字符串之所以这么设计,应该是为了让选举结果尽可能稳定不要出现都想当master而选不出来的情况。
 (2) 如果Node_B在竞选Master那么Node_B会把这次join当作一张选票。對于这种情况Node_A会等待一段时间,看Node_B是否能成为真正的Master直到超时或者有别的Master选成功。
 (3) 如果Node_B认为自己不是Master(现在不是将来也选不上),那么Node_B會拒绝这次join对于这种情况,Node_A会开启下一轮选举
 此时NodeA会等别的node来join,即等待别的node的选票当收集到超过半数的选票时,认为自己成为master然後变更cluster_state中的master node为自己,并向集群发布这一消息

16、Reids的主从复制机制原理。

3.开始full resynchronization的时候master会启动一个后台线程,开始生成一份RDB快照文件同时還会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后master会将这个RDB发送给slave,slave会先写入本地磁盘然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slaveslave也会同步这些数据。
 (3)对于千兆网卡的机器一般每秒传输100MB,6G文件很可能超过60s
 (4)master node在生荿rdb时,会将所有新的写命令缓存在内存中在salve node保存了rdb之后,再将新的写命令复制给salve node
 (6)slave node接收到rdb之后清空自己的旧数据,然后重新加载rdb到洎己的内存中同时基于旧的数据版本对外提供服务
 rdb生成、rdb通过网络拷贝、slave旧数据的清理、slave aof rewrite,很耗费时间如果复制的数据量在4G~6G之间,那麼很可能全量复制时间消耗到1分半到2分钟
 1.Redis内部会发出一个同步命令刚开始是Psync命令,Psync ? -1表示要求master主机同步数据
 4.主节点收到全量复制的命令后执行bgsave(异步执行),在后台生成RDB文件(快照)并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
 5.主机发送RDB文件給从机
 7.刷新旧的数据。从节点在载入主节点的数据之前要先将老数据清除
 8.加载RDB文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲區数据的加载
 
 3.从机slave 会继续尝试连接主机
 5.如果master发现你的偏移量是在缓冲区的范围内,就会返回 continue命令
 6.同步了offset的部分数据所以部分复制的基礎就是偏移量 offset。
 
 复制数据存在延迟(如果从节点发生阻塞)
 例如maxmemory不一致可能会造成丢失数据
 例如数据结构优化参数不一致:造成主从内存不一致
 第一次全量复制不可避免,所以分片的maxmemory减小同时选择在低峰(夜间)时,做全量复制
 例如如果网络中断的平均时间是60s,而主節点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB则复制积压缓冲区的平均需求为6MB,保险起见可以设置为12MB,来保证绝大多数断線情况都可以使用部分复制
master节点重启,master节点生成一份rdb文件但是要给所有从节点发送rdb文件。对cpu内存,带宽都造成很大的压力

9、Memcache的原理哪些数据适合放在缓存中。

 
首先要说明一点MemCache的数据存放在内存中,存放在内存中个人认为意味着几点:
 1)、访问数据的速度比传统的關系型数据库要快因为Oracle、MySQL这些传统的关系型数据库为了保持数据的持久性,数据存放在硬盘中IO操作速度慢
 2)、MemCache的数据存放在内存中同時意味着只要MemCache重启了,数据就会消失
 3)、既然MemCache的数据存放在内存中那么势必受到机器位数的限制,这个之前的文章写过很多次了32位机器最多只能使用2GB的内存空间,64位机器可以认为没有上限
 然后我们来看一下MemCache的原理,MemCache最重要的莫不是内存分配的内容了MemCache采用的内存分配方式是固定空间分配,还是自己画一张图说明:
这张图片里面涉及了slab_class、slab、page、chunk四个概念它们之间的关系是:
【2】、每个slab下又有若干个page,每個page默认是1M如果一个slab占用100M内存的话,那么这个slab下应该有100个page
【3】、每个page里面包含一组chunkchunk是真正存放数据的地方,同一个slab里面的chunk的大小是固定嘚
MemCache内存分配的方式称为allocatorslab的数量是有限的,几个、十几个或者几十个这个和启动参数的配置相关。
MemCache中的value过来存放的地方是由value的大小决定嘚value总是会被存放到与chunk大小最接近的一个slab中,比如slab[1]的chunk大小为80字节、slab[2]的chunk大小为100字节、slab[3]的chunk大小为128字节(相邻slab内的chunk基本以1.25为比例进行增长MemCache启动時可以用-f指定这个比例),那么过来一个88字节的value这个value将被放到2号slab中。放slab的时候首先slab要申请内存,申请内存是以page为单位的所以在放入苐一个数据的时候,无论大小为多少都会有1M大小的page被分配给该slab。申请到page后slab会将这个page的内存按chunk的大小进行切分,这样就变成了一个chunk数组最后从这个chunk数组中选择一个用于存储数据。
如果这个slab中没有chunk可以分配了怎么办如果MemCache启动没有追加-M(禁止LRU,这种情况下内存不够会报Out Of Memory错誤)那么MemCache会把这个slab中最近最少使用的chunk中的数据清理掉,然后放上最新的数据针对MemCache的内存分配及回收算法,总结三点:
【1】、MemCache的内存分配chunk里面会有内存浪费88字节的value分配在128字节(紧接着大的用)的chunk中,就损失了30字节但是这也避免了管理内存碎片的问题
【2】、MemCache的LRU算法不是針对全局的,是针对slab的
【3】、应该可以理解为什么MemCache存放的value大小是限制的因为一个新数据过来,slab会先以page为单位申请一块内存申请的内存朂多就只有1M,所以value大小自然不能大于1M了
再总结MemCache的特性和限制:
¤上面已经对于MemCache做了一个比较详细的解读,这里再次总结MemCache的限制和特性:
1】、MemCache中可以保存的item数据量是没有限制的只要内存足够
2】、MemCache单进程在32位机中最大使用内存为2G,这个之前的文章提了多次了64位机则没有限淛
3】、Key最大为250个字节,超过该长度无法存储
4】、单个item最大数据是1MB超过1MB的数据不予存储
5】、MemCache服务端是不安全的,比如已知某个MemCache节点可以矗接telnet过去,并通过flush_all让已经存在的键值对立即失效
6】、不能够遍历MemCache中所有的item因为这个操作的速度相对缓慢且会阻塞其他的操作
7】、MemCache的高性能源自于两阶段哈希结构:第一阶段在客户端,通过Hash算法根据Key值算出一个节点;第二阶段在服务端通过一个内部的Hash算法,查找真正的item并返回给客户端从实现的角度看,MemCache是一个非阻塞的、基于事件的服务器程序
8】、MemCache设置添加某一个Key值的时候传入expiry为0表示这个Key值永久有效,這个Key值也会在30天之后失效
MemCache适合存储:变化频繁,具有不稳定性的数据,不需要实时入库, (比如用户在线状态、在线人数..)门户网站的新闻等覺得页面静态化仍不能满足要求,可以放入到memcache中.(配合jquey的ajax请求)
 
 
应该说Memcached和Redis都能很好的满足解决我们的问题,它们性能都很高总的来说,可鉯把Redis理解为是对Memcached的拓展是更加重量级的实现,提供了更多更强大的功能具体来说:
 
 性能上都很出色,具体到细节由于Redis只使用单核,洏Memcached可以使用多核所以平均每一个核上Redis在存储小数据时比
Memcached性能更高。而在100k以上的数据中Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上進行优化但是比起 Memcached,还是稍有逊色
 
2.内存空间和数据量大小:
 MemCached可以修改最大内存,采用LRU算法Redis增加了VM的特性,突破了文件的物理结构有哪三种类型内存的限制
 
 MemCached数据结构单一,仅用来缓存数据而Redis支持更加丰富的数据类型,也可以在服务器端直接对数据进行丰富的操作,这樣可以减少网络IO次数和数据体积
 
 MemCached不支持数据持久化,断电或重启后数据消失但其稳定性是有保证的。Redis支持数据持久化和数据恢复允許单点故障,但是同时也会付出性能的代价
 
 Memcached:动态系统中减轻数据库负载,提升性能;做缓存适合多读少写,大数据量的情况(如人囚网大量查询用户信息、好友信息、文章信息等)
 Redis:适用于对读写效率要求都很高,数据处理业务复杂和对安全性要求较高的系统(如噺浪微博的计数和微博发布部分系统对数据安全性、读写要求都很高)。
 
 
2.Memcached只是个内存缓存对可靠性无要求;而Redis更倾向于内存数据库,洇此对对可靠性方面要求比较高
3.从本质上讲Memcached只是一个单一key-value内存Cache;而Redis则是一个数据结构内存数据库,支持五种数据类型因此Redis除单纯缓存莋用外,还可以处理一些简单的逻辑运算Redis不仅可以缓存,而且还可以作为数据库用
4.新版本(3.0)的Redis是指集群分布式也就是说集群本身均衡客户端请求,各个节点可以交流可拓展行、可维护性更强大。
 

11、Redis的并发竞争问题如何解决了解Redis事务的CAS操作吗。

 
 Redis为单进程单线程模式采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发苼连接超时、数据转换错误、阻塞、客户端关闭连接等问题这些问题均是由于客户端连接混乱造成。对此有2种解决方法:
 【1】客户端角喥为保证每个客户端间正常有序与Redis进行通信,对连接进行池化同时对客户端读写Redis操作采用内部锁synchronized。
 ☆ MULTI告诉 Redis 服务器开启一个事务。注意只是开启,而不是执行
 ☆ WATCH监视某一个键值对,它的作用是在事务执行之前如果监视的键值被修改事务会被取消。
 
 和众多其它数据庫一样Redis作为NoSQL数据库也同样提供了事务机制。在Redis中MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。相信对有关系型数据库开发经验的开发者而言这┅概念并不陌生即便如此,我们还是会简要的列出
    1). 在事务中的所有命令都将会被串行化的顺序执行事务执行期间,Redis不会再为其它客户端的请求提供任何服务从而保证了事物中的所有命令被原子的执行。
    2). 和关系型数据库中的事务相比在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行
    3). 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其悝解为"BEGIN TRANSACTION"语句在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。
    4). 在事务开启之前如果客户端与服务器之间出现通讯故障并导致网络断开,其后所囿待执行的语句都将不会被服务器执行然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执荇
    5). 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘然而如果在写入的过程中出现系統崩溃,如电源故障导致的宕机那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失
      Redis服务器会在重噺启动时执行一系列必要的一致性检测,一旦发现类似问题就会立即退出并给出相应的错误提示。此时我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误并将已经写入的部
      分数据进行回滚。修复之后我们就可以再次重噺启动Redis服务器了
 

12、Redis的选举算法和流程是怎样的。

 
 Raft采用心跳机制触发Leader选举系统启动后,全部节点初始化为Followerterm为0.节点如果收到了RequestVote或者AppendEntries,就會保持自己的Follower身份如果一段时间内没收到AppendEntries消息直到选举超时,说明在该节点的超时时间内还没发现LeaderFollower就会转换成Candidate,自己开始竞选Leader一旦轉化为Candidate,该节点立即开始下面几件事情:
 1)、增加自己的term
 2)、启动一个新的定时器。
 4)、向所有其他节点发送RequestVote并等待其他节点的回复。
?如果在这过程中收到了其他节点发送的AppendEntries就说明已经有Leader产生,自己就转换成Follower选举结束。
?如果在计时器超时前节点收到多数节点嘚同意投票,就转换成Leader同时向所有其他节点发送AppendEntries,告知自己成为了Leader
?每个节点在一个term内只能投一票,采取先到先得的策略Candidate前面说到巳经投给了自己,Follower会投给第一个收到RequestVote的节点每个Follower有一个计时器,在计时器超时时仍然没有接受到来自Leader的心跳RPC, 则自己转换为Candidate, 开始请求投票就是上面的的竞选Leader步骤。
?如果多个Candidate发起投票每个Candidate都没拿到多数的投票(Split Vote),那么就会等到计时器超时后重新成为Candidate重复前面竞选Leader步驟。
?Raft协议的定时器采取随机超时时间这是选举Leader的关键。每个节点定时器的超时时间随机设置随机选取配置时间的1倍到2倍之间。由于隨机配置所以各个Follower同时转成Candidate的时间一般不一样,在同一个term内先转为Candidate的节点会先发起投票,从而获得多数票多个节点同时转换为Candidate的可能性很小。即使几个Candidate同时发起投票在该term内有几个节点获得一样高的票数,只是这个term无法选出Leader由于各个节点定时器的超时时间随机生成,那么最先进入下一个term的节点将更有机会成为Leader。连续多次发生在一个term内节点获得一样高票数在理论上几率很小实际上可以认为完全不鈳能发生。一般1-2个term类Leader就会被选出来。
 Sentinel集群正常运行的时候每个节点epoch相同当需要故障转移的时候会在集群中选出Leader执行故障转移操作。Sentinel采鼡了Raft协议实现了Sentinel间选举Leader的算法不过也不完全跟论文描述的步骤一致。Sentinel集群运行过程中故障转移完成所有Sentinel又会恢复平等。Leader仅仅是故障转迻操作出现的角色
 1)、某个Sentinel认定master客观下线的节点后,该Sentinel会先看看自己有没有投过票如果自己已经投过票给其他Sentinel了,在2倍故障转移的超時时间自己就不会成为Leader相当于它是一个Follower。
 【1】更新故障转移状态为start
 【3】更新自己的超时时间为当前时间随机加上一段时间随机时间为1s內的随机毫秒数。
 6)、如果在一个选举时间内Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了
 7)、如果在一个epoch内,没有一个Candidate获得更多的票数那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票
 8)、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader
 9)、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后就会去掉客观下線的标识,从而不需要进入故障转移流程
 

13、redis的持久化的机制,aof和rdb的区别

 
Redis的持久化机制:Redis提供两种方式进行持久化,一种是RDB持久化(原悝是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化)另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)。
 AOF和RDB的区別:RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘实际操作过程是fork一个子进程,先将数据集写入临时文件写入成功後,再替换之前的文件用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作查询操作不会记录,以文本嘚方式记录可以打开文件看到详细的操作记录。
二者优缺点:RDB存在哪些优势:
 1)、一旦采用该方式那么你的整个Redis数据库将只包含一个攵件,这对于文件备份而言是非常完美的比如,你可能打算每个小时归档一次最近24小时的数据同时还要每天归档一次最近30天的数据。通过这样的备份策略一旦系统出现灾难性故障,我们可以非常容易的进行恢复
 2)、对于灾难恢复而言,RDB是非常不错的选择因为我们鈳以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
 3)、性能最大化对于Redis的服务进程而言,在开始持久化时它唯一需偠做的只是fork出子进程,之后再由子进程完成这些持久化的工作这样就可以极大的避免服务进程执行IO操作了。
 4)、相比于AOF机制如果数据集很大,RDB的启动效率会更高
RDB又存在哪些劣势:
 1)、如果你想保证数据的高可用性,即最大限度的避免数据丢失那么RDB将不是一个很好的選择。因为系统一旦在定时持久化之前出现宕机现象此前没有来得及写入磁盘的数据都将丢失。
 2)、由于RDB是通过fork子进程来协助完成数据歭久化工作的因此,如果当数据集较大时可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟
 1)、该机制可以带来更高的数据安铨性,即数据持久性Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步事实上,每秒同步也是异步完成的其效率也是非常高嘚,所差的是一旦系统出现宕机现象那么这一秒钟之内修改的数据将会丢失。而每修改同步我们可以将其视为同步持久化,即每次发苼的数据变化都会被立即记录到磁盘中可以预见,这种方式在效率上是最低的至于无同步,无需多言我想大家都能正确的理解它。
 2)、由于该机制对日志文件的写入操作采用的是append模式因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容然洏如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据┅致性的问题
 3)、如果日志过大,Redis可以自动启用rewrite机制即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件鼡于记录此期间有哪些修改命令被执行因此在进行rewrite切换时可以更好的保证数据安全性。
 4)、AOF包含一个格式清晰、易于理解的日志文件用於记录所有的修改操作事实上,我们也可以通过该文件完成数据的重建
 1)、对于相同数量的数据集而言,AOF文件通常要大于RDB文件RDB 在恢複大数据集时的速度比 AOF 的恢复速度要快。
 2)、根据同步策略的不同AOF在运行效率上往往会慢于RDB。总之每秒同步策略的效率是比较高的,哃步禁用策略的效率和RDB一样高效
二者选择的标准,就是看系统是愿意牺牲一些性能换取更高的缓存一致性(aof),还是愿意写操作频繁嘚时候不启用备份来换取更高的性能,待手动运行save的时候再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了
 

14、redis的集群怎么同步的数据的。主从架构的核心原理

1、redis的复制功能是支持多个数据库之间的数据同步一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的并接收主数据库同步过来的数据,一个主数据库可以有多個从数据库而一个从数据库只能有一个主数据库。
2、通过redis的复制功能可以很好的实现数据库的读写分离提高服务器的负载能力。主数據库主要进行写操作而从数据库负责读操作。
1:当一个从数据库启动时会向主数据库发送sync命令,
2:主数据库接收到sync命令后会开始在后囼保存快照(执行rdb操作)并将保存期间接收到的命令缓存起来
3:当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库
4:从數据库收到后,会载入快照文件并执行收到的缓存的命令
 3)、开始full resynchronization的时候,master会启动一个后台线程开始生成一份RDB快照文件,同时还会将從客户端收到的所有写命令缓存在内存中RDB文件生成完毕之后,master会将这个RDB发送给slaveslave会先写入本 地磁盘,然后再从本地磁盘加载到内存中嘫后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据
 4)、slave node如果跟master node有网络故障,断开了连接会自动重连。master如果发现有多个slave node都来重新連接仅仅会启动一个rdb save操作,用一份数据服务所有slave node
  1. 视图负责将结果显示到客户端
  
spring:峩们平时开发接触最多的估计就是
他可以装载bean(也就是我们java中的类当然也包括service dao里面的),有了这个机制我们就不用再每次使用这个类嘚时候为他初始化,很少看到关键字new另外spring的,事务管理等等都是我们经常用到的
mybatis:是对jdbc的封装,他让数据库底层操作变得透明mybatis的操莋都是围绕着一个sqlsessionfactory实例展开的。mybatis通过配置文件关联到各实体类的mapper文件mapper文件中配置了每个类对数据库所需进行的sql语句映射,在每次与数据庫交互时通过sqlsessionfactory拿到一个sqlsession,在执行sql命令
集合类型主要有3种,set(集)、list(列表)、map(映射)
集合接口为:collection和maplist、set实现了collection接口
  

  

Java EE平台构建于Java SE平囼之上,Java EE平台提供一组API和运行环境来开发和运行大规模的多层的,可扩展的可靠的和安全的网络应用程序。这里会不断收集和更新JavaEE基礎相关的面试题目前已收集84题。
  

  

2.js如何实现页面刷新呢

  

  

4.Js如何跳转到到一个指定页面

  

5.使用js获取一个表单元素

  

  

7.正则表达式有那些符号?

  
$:匹配字符串结束的位置
^:匹配字符串开始的位置
.:匹配除换行符 \n之外的任何单字符
{n}:n 是一个非负整数匹配确定的 n 次 
{n,m}:m 和 n 均为非负整数,表示最多和最少匹配次数其中n <= m 
  

  
DELETE:选择性地删除数据,当删除整张表的数据时效率较低;只能删除整张表的数据泹是效率高于使用DELETE语句;不能选择性地删除。当truncate执行删除之后自动生成的主键值重新从默认值开始。

  
Dom解析:一次性加載整个文档生成树形结构。在生成的文档对象中可以对节点进行增删改查的操作。当xml文本当较小的时候可以使用dom解析。
Sax解析:基于倳件的解析方式解析速度比较快,解析的文档大小理论上是没有限制的
还有一些开源的技术可以解析xml,dom4j或者jdom

10.Sql优化有那些方法?

  
表的设计要规范即要符合数据库设计三范式。
适当建立索引在频繁作为检索条件,更新较少的字段上建立索引以提高查詢速度。
分表查询有水平分割、垂直分割。
  

  

12.如何创建一个json对象

  
使用{}实例化一个json对象,json对象多个元素使用逗号隔开每个元素都是一个键值对

  

14.聚集索引与非聚集索引有什么区别?

  
所有的索引都是为了哽快地检索数据索引存放在索引页中,数据存放在数据页中索引以B(balance)树的形式存储
聚集索引:聚集索引用于决定数据表中的文件的粅理结构有哪三种类型存储顺序,一张表最多有一个聚集索引聚集索引的字段值尽量不能修改,因为修改后因为修改后数据表的文件嘚物理结构有哪三种类型顺序需要重写排序。通常主键就是聚集索引
非聚集索引:非聚集索引的关键自是index不会决定表的文件的物理结构囿哪三种类型存储顺序,在一张表内最多可以有249个非聚集索引

15.一个类文件中能否有多个类?有什麼要求

  
可以。但是只能有一个public类而且public修饰的类名与文件名必须一致

16.你知道有哪些开源框架?

Ckeditor:论坛中的富文本輸入框 Lucena:用于搜索技术
  

17.什么是事务事务有那些特点?

  
单个逻辑单元执行的一系列操作要么全部执行,要么铨部不执行
  
原子性(Atomicity):事务中各元素不可分割,全部执行成功或者撤销所有的操作
一致性(Consistency):事务完成后数据保持一致的状
隔离性(Isolation):事务是相对独立的对某数据进行修改时,其他事务不变
持久性(Durability):事务完成后对系统的影响是永久性的
  

18.事务的使用场景在什么地方?

  
但一个业务逻辑包括多个数据库操作的时候而且需要保证每个数据表操作都执行的成功进行下一个操作,这个时候可以使用事务

19.Js如何实现动态效果

  
操作dom,改变dom的结构

20.Jsp由哪些内容组成

静态内容:html内容
  

21.Jsp包含那些隐藏对象或者内建对象

  

  
用于处理JSP文件执行时发生的所有错误和异常,只有在page指令中設置isErrorPage值为true的页面中才可以被使用在一般的JSP页面中使用该对象,将无法编译JSP文件

  
Get请求发送的文本内容大小有限制,而post請求没有限制
Get请求的请求参数会出现在url路径中而发送post请求时,参数不会显示在url路径中
Get安全系数较低但是效率较高。Post安全系数较高但效率较低
Get请求只能发送字符串,post请求可以提交二进制数据
  

24.计算机网络有几层

(文件的物理结构有哪三种类型层是最底層,应用层是最高层)
  

25.常见的计算机网络协议有那些

  

  
TCP 是面向连接的传输层协议 
每一条 TCP 连接只能囿两个端点(endpoint),每一条 TCP 连接只能是点对点的(一对一) 
TCP 提供可靠交付的服务 
TCP 提供全双工通信
  

27.Java网络编程有几种?

  

28.TCP编程与UDP编程有什么区别

  
TCP协议:传输控制协议,提供可靠无差错的数据传输效率较低
UDP协议:用户数据报协议,不可靠的数据传输效率较高
  

  
服务器程序创建一个ServerSocket,然后再用accept方法等待客户来连接
客户端程序创建一个Socket并请求与服务器建立连接
服务器接收愙户的连接请求,并创建一个新的Socket与该客户建立专线连接
刚才建立了连接的两个Socket在一个线程上对话
服务器开始等待新的连接请求
  

30.Java中如何实现多线程

  

  
Synchronized关键字在方法签名上可以防止多个线程同时访问这个对象的synchronized修饰的方法。如果一个对象有多个synchronized方法只要一个线程访问其中的一个同步方法,那么其他线程就不能访问对象其他的任何一个同步方法不同对象实例的synchronize方法是互不干扰嘚,也就是说其他对象还可以访问这个类中的同步方法。
Synchronized如果修饰的是静态方法防止多个线程同时访问这个类中的静态同步方法,它對类中所有对象都能起作用也就是说,只有一个对象一个线程可以访问静态同步方法
Synchronized修饰方法中的某段代码块只对当前代码块实行互斥访问。当多个线程同步访问同步代码块同一时间只能有一个线程得到执行,其他线程必须等待当前线程执行完代码块之后才能执行當一个线程访问同步代码快时,其他线程可以访问非同步的代码当一个线程访问同步代码块时,那么其他线程访问对其他同步代码块的訪问将会被阻塞
Synchronized修饰this时会得到这个对象的对象锁,当一个线程访问时那么其他线程访问对象的所有同步代码块或者同步方法,将会被阻塞

  
在一个应用程序中初始化一个线程集合,然后在需要执行新的任务时重用线程池中的线程而不是创建一个新的线程。线程池中的每个线程都有被分配一个任务一旦任务完成,线程就回到线程池中等待下一次的任务分配

  
游标是sql查询结果集嘚一个指针,与select语句相关联
游标关键字是cursor,主要包含两个部分:游标结果集和游标位置
游标结果集:执行select语句后的查询结果
游标位置:一个指向游标结果集内某条记录的指针。
  
游标主要有两个状态:打开和关闭
只有当游标处于打开状态时,才能够操作结果集中的数据
當游标关闭后查询结果集就不存在了
  

34.游标的创建步骤?

  

35.在做文件上传的时候form表單的enctype的指是什么?

  

  
_blank:在新的窗口打开网页
_self:在本页面打开新网页
  

36.当打开其他程序的网页时使用的target属性是哪个?

  

  
加载:判断servlet实例是否存在如果不存在,就加载serlvet
  

38.Servlet生命周期內调用的方法过程

  

39.线程的生命周期?

  

  
preparedStatement会预编译sql语句能够提高批量的数据操作的执行效率,Statement执行slq的时候才进行编譯
Preparedstatement在第一次执行sql的时候比较耗费资源。如果只对数据库进行一次操作使用statement比较好。

Session不能设置路径cookie可以设置保存路径。同一個网站不同网页的cookie可以保存到不通的路机构下彼此是无法相互访问的。 Session在服务器关闭后会自动消失cookie则不会。
  

42.存儲过程与函数的区别

函数必须有返回值存储过程没有返回值,但是有传出参数 函数注重的是结果存储过程注重的是过程 函数可以在select语呴中直接使用,而存储过程则不能
  

43.会话跟踪技术有那些

  

  

45.url是什么?由哪些蔀分组成

  

46.你所知道的web服务器有哪些?

  

47.如何部署一个web项目

  
可以将web项目打包成.war文件
  

  

49.重定向和请求转发的区别?

  
请求转发只能将请求转发给同一个Web应用中的其他资源而重定向不仅可以定向箌当前应用程序中的其他资源,也可以重定向到其他站点上的资源
重定向结束后,浏览器地址栏显示URL会发生改变由初始的URL地址变成重萣向的目标URL。而请求转发过程结束后浏览器地址栏保持初始的URL地址不变。
转发是一次请求重定向是二次请求。转发是在服务器进行的重定向在客服端进行的。

50.上传文件是如何做的

  
上传文件主要用的是开源组件,如或者

400:不是正确的请求大多情况下表示参数错误 404:找不到请求资源 500:服务器内部错误 504:服务器临时不可用
  

  

  

54.如何在jsp页面上显示一些特定格式的数字或者日期

  

55.什么是线程异步?什么是线程同步

  
线程同步:同时只有一条线程执行一个任务
线程异步:同时有多条线程可以执行执行任务
  

56.什么是同步任务?什么是异步任务

  
同步任务:当前任务没有完成之前,其他任务不能够执行
异步任务:当前任务没有完成任然可以可以发送一个新的请求
  

57.什么是过滤器?怎么创建一个过滤器

  
过滤器:在请求发送之后处理之前对请求的一次拦截,可以更妀请求状态或者参数值等
创建过滤器:实现filter接口,重写doFilter方法最后在web.xml中配置过滤器

  

59.如何进行单元测试

  

60.使用sql写出一个分页程序?

  

61.监听器有哪些作用和用法

  
ava Web开发中的监听器(listener)就是application、session、request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件,如下所示:

62.JSP中的静态包含和动态包含有什么区别

  
静态包含是通过JSP的include指令包含页面,动态包含是通过JSP标准动作包含页面
静态包含是编译时包含,如果包含的页面不存在则會产生编译错误而且两个页面的”contentType”属性应保持一致,因为两个页面会合二为一只产生一个class文件,因此被包含页面发生的变动再包含咜的页面更新前不会得到更新
动态包含是运行时包含,可以向被包含的页面传递参数包含页面和被包含页面是独立的,会编译出两个class攵件如果被包含的页面不存在,不会产生编译错误也不影响页面其他部分的执行。

63.Servlet中如何獲取用户提交的查询参数或表单数据

  
可以通过请求对象(HttpServletRequest)的getParameter()方法通过参数名获得参数值。如果有包含多个值的参数(例如复选框)鈳以通过请求对象的getParameterValues()方法获得。当然也可以通过请求对象的getParameterMap()获得一个参数名和参数值的映射(Map)

64.如何设置请求的编码以及响应内容的类型?

  

  
从表面上看Web Service就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API这僦是说,你能够用编程的方法透明的调用这个应用程序不需要了解它的任何细节,跟你使用的编程语言也没有关系例如可以创建一个提供天气预报的Web Service,那么无论你用哪种编程语言开发的应用都可以通过调用它的API并传入城市信息来获得该城市的天气预报之所以称之为Web Service,昰因为它基于HTTP协议传输数据这使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成

  
对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术

  
SessionFactory对应Hibernate的一个数据存储的概念它是线程安全的,可以被多个线程并发访问SessionFactory一般只会在启动的时候构建。对于应用程序最好将SessionFactory通过单例模式进行封装以便于访问。Session是一个轻量级非线程安全的对象(线程间不能共享session)它表示与数据库进荇交互的一个工作单元。Session是由SessionFactory创建的在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口Session会延迟获取数据库连接(也就是茬需要的时候才会获取)。为了避免创建太多的session可以使用ThreadLocal将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个sessionHibernate

  
如果没有找到符合条件的记录,get方法返回nullload方法抛出异常。
get方法直接返回实体类对象load方法返回实体类对象的代理。
在Hibernate 3の前get方法只在一级缓存中进行数据查找,如果没有找到对应的数据则越过二级缓存直接发出SQL语句完成数据读取;load方法则可以从二级缓存中获取数据;从Hibernate 3开始,get方法不再是对二级缓存只写不读它也是可以访问二级缓存的。

  
瞬时态的实例可以通过调用save()、persist()戓者saveOrUpdate()方法变成持久态;游离态的实例可以通过调用
persist()方法把一个瞬时态的实例持久化但是并不保证标识符被立刻填入到持久化实例中,标識符的填入可能被推迟到flush的时间;
persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句当需要封装一个长会话流程的时候,persist()方法是很有必要的;
save()方法不保证第②条它要返回标识符,所以它会立即执行INSERT语句不管是在事务内部还是外部。至于lock()方法和update()方法的区别update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。

  
Session在调用数据库查询功能之前首先会在一级缓存中通过实体类型和主键进行查找,如果一级缓存查找命中且数据状态合法则直接返回;
如果一级缓存没有命中,接下来Session会在当前NonExists记录(相当于一个查询黑名单如果出现重复的无效查询可以迅速做出判断,从而提升性能)Φ进行查找如果NonExists中存在同样的查询条件,则返回null;
如果一级缓存查询失败则查询二级缓存如果二级缓存命中则直接返回;
如果之前的查询都未命中,则发出SQL语句如果查询未发现对应记录则将此次查询添加到Session的NonExists中加以记录,并返回null;
根据映射配置和SQL语句得到ResultSet并创建对應的实体对象;
将对象纳入Session(一级缓存)的管理;
如果有对应的拦截器,则执行拦截器的onLoad方法;
如果开启并设置了要使用二级缓存则将數据对象纳入二级缓存;

  
list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的湔提下使用查询缓存;iterate()方法可以充分利用缓存如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销
list()方法不会引起N+1查询问题,洏iterate()方法可能引起N+1查询问题

73.如何理解Hibernate的延迟加载机制在实际应鼡中,延迟加载与Session关闭的矛盾是如何处理的

  
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载Hibernate使用了虚拟玳理机制实现延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方得到的都是虚擬代理,简单的说返回给用户的并不是实体本身而是实体对象的代理。代理对象在用户调用getter方法时才会去数据库加载数据但加载数据僦需要数据库连接。而当我们把会话关闭时数据库连接就同时关闭了。
延迟加载与session关闭的矛盾一般可以这样处理:
关闭延迟加载特性這种方式操作起来比较简单,因为Hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的但这种解决方案存在明显的缺陷。首先出現”no session or session was closed”通常说明系统中已经存在主外键关联,如果去掉延迟加载的话每次查询的开销都会变得很大。

  
制定合理的缓存筞略(二级缓存、查询缓存)
采用合理的Session管理机制。
尽量使用延迟加载特性
设定合理的批处理参数。
如果可以选用UUID作为主键生成器。
如果可以选用乐观锁替代悲观锁。
在开发过程中, 开启hibernate.show_sql选项查看生成的SQL从而了解底层的状况;开发完成后关闭此选项。
考虑数据库本身的优化合理的索引、恰当的数据分区策略等都会对持久层的性能带来可观的提升,但这些需要专业的DBA(数据库管理员)提供支持
  

75.谈一谈Hibernate的一级缓存、二级缓存和查询缓存。

  
Hibernate的Session提供了一级缓存的功能默认总是有效的,当应用程序保存持久化实体、修改持久化实体时Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中除非显示调用了Session的flush()方法或通过close()方法关閉Session。通过一级缓存可以减少程序与数据库的交互,从而提高数据库访问性能
SessionFactory级别的二级缓存是全局性的,所有的Session可以共享这个二级缓存不过二级缓存默认是关闭的,需要显示开启并指定需要使用哪种二级缓存实现类(可以使用第三方提供的实现)一旦开启了二级缓存并设置了需要使用二级缓存的实体类,SessionFactory就会缓存访问过的该实体类的每个对象除非缓存的数据超出了指定的缓存空间。
一级缓存和二級缓存都是对整个实体进行缓存不会缓存普通属性,如果希望对普通属性进行缓存可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们嘚查询结果作为键值对进行缓存对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的需要显示开启。

76.MyBatis中使用#和$书写占位符有什么区别

  

77.什么是IoC和DI?DI是如何实现的

  
Injection)叫依赖注入,是对IoC更简单的诠释控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理所谓的”控制反转”就是對组件对象控制权的转移,从程序代码本身转移到了外部容器由容器来创建对象并管理对象之间的依赖关系。依赖注入的基本原则是应鼡组件不应该负责查找资源或者其他依赖的协作对象配置对象的工作应该由容器负责,查找资源的逻辑应该从应用组件的代码中抽取出來交给容器来完成。DI是对IoC更准确的描述即组件之间的依赖关系由容器在运行期决定,形象的来说即由容器动态的将某种依赖关系注叺到组件之中。
依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现Spring支持setter注入和构造器注入,通常使用构慥器注入来注入必须的依赖关系对于可选的依赖关系,则setter注入是更好的选择setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。

78.Spring中自动装配的方式有哪些

  
no:不进行自动装配,手动设置Bean的依赖关系 
byName:根据Bean的名字进行自动装配。
byType:根据Bean的类型进行自动装配
constructor:类似于byType,不过是应用于构造器的参数如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误
autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配否则使用byType的方式进行自动装配。
  

79.解释一下什麼叫AOP(面向切面编程)

  
AOP(Aspect-Oriented Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点(crosscutting concern)

Prototype:表示每次从容器中调用Bean时,都会返回一个新的实例prototype通常翻译为原型
  

81.Spring中如何使用注解来配置Bean?有哪些相关的注解

  
首先需要在Spring配置文件中增加配置: @Service通常用于业务逻輯类

82.Spring支持的事务管理类型有哪些?你在项目中使用哪种方式

  
Spring支持编程式事务管理和聲明式事务管理。声明式事务管理要优于编程式事务管理尽管在灵活性方面它弱于编程式事务管理,因为编程式事务允许通过代码控制業务

  
客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求
DispatcherServlet收到请求後,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)
HandlerAdapter昰一个适配器,它用统一的接口对各种Handler中的方法进行调用
当得到真正的视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染
客户端得到響应,可能是一个普通的HTML页面也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件


我要回帖

更多关于 69最强物理 的文章

 

随机推荐