分享一下自己总结的7万多字java面试笔记和一些面试视频,简历啥的,已大厂上岸

分享一下自己总结的7万多字java面试笔记和一些面试视频,简历啥的,已大厂上岸

自己总结的面试简历资料:https://pan.quark.cn/s/8b602fe53b58

文章目录

SSM

SSM搭建的版本有很多,例如有一个版本可以这么搭建,两个核心配置文件web.xml,applicationContext.xml。1.前端控制器DispatcherServlet 2.过滤器CharacterEncodingFilter` ` ` `applicationContext.xml` `1.扫描包 <context:component-scan base-package="cn"/> 2.mvc驱动 <mvc:annotation-driven/>` `3.事务驱动 <tx:annotation-driven transaction-manager="txManager"/> 4.配置数据源` `5.SqlSessionFactoryBean 6.配置事务` `7.数据映射器 8``.视图解析器

spring============================================

Spring是一个开源的轻量级的Java开发框架。是一种简化应用程序的开发。
在spring出来之前,service层调用dao层都是用new的方式,在spring出来之后,service层和到dao层都会放在spring容器去管理,这是spring的第一种特性,我们称之为IOC,控制反转。
spring还有一种特性,我们称之为AOP,大白话,所谓“面向切面”,说白了就是专门的人干专门的事。在项目很多公有的或是要被重复被调用的模块可以被抽取出来,利用的就AOP的特性,例如日志模块。

spring 的优点?

1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦
2.可以使用容易提供的众多服务,如事务管理,消息服务等
3.容器提供单例模式支持
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
5.容器提供了众多的辅助类,能加快应用的开发
6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
7.spring属于低侵入式设计,代码的污染极低
8.独立于各种应用服务器
9.spring的DI机制降低了业务对象替换的复杂性
10.Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部

IoC和AOP的理解

IoC(控制反转)

就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理

​ IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。

aop(面向切面编程

面向切面编程(AOP)完善spring的依赖注入(DI),面向切面编程在spring中主要表现为两个方面
1.面向切面编程提供声明式事务管理
2.spring支持用户自定义的切面

面向切面编程(aop)是对面向对象编程(oop)的补充,
面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。

aop框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性

能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

Bean 的生命周期

1.Bean容器找到配置文件中Spring Bean的定义。
2.Bean容器利用Java Reflection API创建一个Bean的实例。
3.如果涉及到一些属性值,利用set()方法设置一些属性值。
4.如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字。
5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例。
6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory()方法,传入ClassLoader对象的实例。
7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。
8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执行postProcessBeforeInitialization()方法。
9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。
10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。
11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization()方法。
12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy()方法。
13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。

列举一些重要的Spring模块?

Spring Core:基础,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。

Spring Aspects:该模块为与AspectJ的集成提供支持。

Spring AOP:提供面向方面的编程实现。

Spring JDBC:Java数据库连接。

Spring JMS:Java消息服务。

Spring ORM:用于支持Hibernate等ORM工具。

Spring Web:为创建Web应用程序提供支持。

Spring Test:提供了对JUnit和TestNG测试的支持。

Spring框架中用到了哪些设计模式

1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。

2.代理设计模式:Spring AOP功能的实现。

3.单例设计模式:Spring中的bean默认都是单例的。

4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。

5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。

7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

@Component和@Bean的区别是什么

1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。

2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。

3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。

Spring事务管理的方式有几种?

1.编程式事务:在代码中硬编码(不推荐使用)。

2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。

Spring事务中的隔离级别有哪几种?

  • ISOLATION_DEFAULT:PlatfromTransactionManager 默认隔离级别,使用数据库默认的事务隔离级别。
  • ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。
  • ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。
  • ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。
  • ISOLATION_SERIALIZABLE:最严格的事务,序列化执行。

Spring事务中有哪几种事务传播行为?

在TransactionDefinition接口中定义了八个表示事务传播行为的常量。

支持当前事务的情况:

PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。

不支持当前事务的情况:

PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常

spring常用的注入方式有哪些?

1、xml中配置

2、注解

  • bean 的申明、注册

@Component //注册所有bean
@Controller //注册控制层的bean
@Service //注册服务层的bean
@Repository //注册dao层的bean

  • bean 的注入

@Autowired 作用于 构造方法、字段、方法,常用于成员变量字段之上。
@Autowired + @Qualifier 注入,指定 bean 的名称
@Resource JDK 自带注解注入,可以指定 bean 的名称和类型等

spring中的bean是线程安全的吗?

Spring 不保证 bean 的线程安全。
默认 spring 容器中的 bean 是单例的。当单例中存在竞态条件,即有线程安全问题。

springmvc===================================

spring mvc 是 spring web mvc,spring 框架的一部分,一个 mvc 设计模型的表现层框架。

Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一个模块,无需中间整合层来整合 ,它和Struts2一样都属于表现层的框架。在web模型中,MVC是一种很流行的框架,通过把Model,View,Controller分离,把较为复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

当用户发送请求到springmvc中的前端控制器中,通过映射器和适配器返回ModelAndView对象到客户端。这就是SpringMVC的基本原理。

SpringMVC的流程?

(1)用户发送请求至前端控制器DispatcherServlet; (2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle; (3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet; (4)DispatcherServlet 调用 HandlerAdapter处理器适配器; (5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器); (6)Handler执行完成返回ModelAndView; (7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet; (8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析; (9)ViewResolver解析后返回具体View; (10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中) (11)DispatcherServlet响应用户。

JSP 九大内置对象和四大作用域

名称 类型 含义 获取方式
request HttpSevletRequest 封装所有请求信息 方法参数
response HttpServletResponse 封装所有响应信息 方法参数
session HttpSession 封装所有会话信息 req.getSession()
application ServletContext 所有信息 getServletContext();
out PrintWriter 输出对象 response.getWriter()
exception Exception 异常对象 response.getWriter()
page Object 当前页面对象
pageContext PageContext 获取其他对象
config ServletConfig 配置信息

四大作用域

1.page 在当前页面不会重新实例化

2.request 在一次请求中同一个对象,下次请求重新实例化一个request对象。

3.session 一次会话中只有一个session对象,依赖于cookie,只要Cookie中传递的Jsessionid不变,Session就不会重新实例化(在不超过默认生效时间的情况下)
4.application:只有在tomcat启动项目时实例化,只有当关闭tomcat服务器时销毁application

spring mvc有哪些组件?

  • 前端控制器(DispatcherServlet)
  • 处理器映射器(HandlerMapping)
  • 处理器适配器(HandlerAdapter)
  • 拦截器(HandlerInterceptor)
  • 语言环境处理器(LocaleResolver)
  • 主题解析器(ThemeResolver)
  • 视图解析器(ViewResolver)
  • 文件上传处理器(MultipartResolver)
  • 异常处理器(HandlerExceptionResolver)
  • 数据转换(DataBinder)
  • 消息转换器(HttpMessageConverter)
  • 请求转视图翻译器(RequestToViewNameTranslator)
  • 页面跳转参数管理器(FlashMapManager)
  • 处理程序执行链(HandlerExecutionChain)

@RequestMapping的作用是什么?

@RequestMapping 是一个注解,用来标识 http 请求地址与 Controller 类的方法之间的映射。

可作用于类和方法上,方法匹配的完整是路径是 Controller 类上 @RequestMapping 注解的 value 值加上方法上的 @RequestMapping 注解的 value 值。

@Autowired的作用是什么?

@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。

@Autowired 与@Resource的区别

@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false

@Resource(这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。

Springmvc的优点:

如下:
(1)它是基于组件技术的。全部的应用对象,无论控制器和视图,还是业务对象之类的都是 java组件.并且和Spring提供的其他基础结构紧密集成
(2)不依赖于Servlet API(目标虽是如此,但是在实现的时候确实是依赖于Servlet的)
(3)可以任意使用各种视图技术,而不仅仅局限于JSP
(4) 支持各种请求资源的映射策略
(5)它应是易于扩展的

FactoryBean 与 BeanFactory 有什么区别?

  • BeanFactory 是 IoC 底层容器,提供了 bean 的管理
  • FactoryBean 是创建 Bean 的一种方式,帮助实现复杂的初始化逻辑

@Transactional 注解哪些情况下会失效?

1、@Transactional 作用在非 public 修饰的方法上

2、@Transactional 作用于接口,使用 CGLib 动态代理

3、@Transactional 注解属性 propagation 设置以下三种可能导致无法回滚

  • SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

4、同一类中加 @Transactional 方法被无 @Transactional 的方法调用,事务失效

5、@Transactional 方法内异常被捕获

6、默认 RuntimeException 和 Error 及子类抛出,会回滚;rollbackFor 指定的异常及子类发生才会回滚

7、数据库不支持事务,如 MySQL 使用 MyISAM 存储引擎

8、Spring 的配置文件中未配置事务注解生效

Spring MVC的异常处理 ?

答:可以将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添视图页面即可。

SpringMvc的核心入口类是什么,Struts1,Struts2的分别是什么:

答:SpringMvc的是DispatchServlet,Struts1的是ActionServlet,Struts2的是StrutsPrepareAndExecuteFilter。

SpringMvc的控制器是不是单例模式,如果是,有什么问题,怎么解决?

答:是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写字段。

SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代?

答:一般用@Conntroller注解,表示是表现层,不能用用别的注解代替。

@RequestMapping注解用在类上面有什么作用?

答:是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

怎么样把某个请求映射到特定的方法上面?

答:直接在方法上面加上注解@RequestMapping,并且在这个注解里面写上要拦截的路径。

如果在拦截请求中,我想拦截get方式提交的方法,怎么配置?

答:可以在@RequestMapping注解里面加上method=RequestMethod.GET。

怎么样在方法里面得到Request,或者Session?

答:直接在方法的形参中声明request,SpringMvc就自动把request对象传入。

如果想在拦截的方法里面得到从前台传入的参数,怎么得到?

答:直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。

如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?

答:直接在方法中声明这个对象,SpringMvc就自动会把属性赋值到这个对象里面。

SpringMvc中函数的返回值是什么?

答:返回值可以有很多类型,有String, ModelAndView,但一般用String比较好。

SpringMvc用什么对象从后台向前台传递数据的?

答:通过ModelMap对象,可以在这个对象里面用put方法,把对象加到里面,前台就可以通过el表达式拿到。

SpringMvc中有个类把视图和数据都合并的一起的,叫什么?

答:ModelAndView。

怎么样把ModelMap里面的数据放入Session里面?

答:可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。

当一个方法向AJAX返回特殊对象,譬如Object,List等,需要做什么处理?

答:要加上@ResponseBody注解。

项目中如何用 Spring 和 Spring MVC 框架的?

通过 web.xml 的配置、@Controller、@Service、@Repository,完成 http 请求到数据库的 crud 再到 view 层展示,整个调用链。其中还要配置对象转 json 的 Converter、登录拦截器、文件上传大小限制、数据源及连接池相关等等…

mybatis=======================================

  • 支持自定义 SQL、存储过程以及高级映射
  • 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
  • 通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

MyBatis是什么?有什么作用?

MyBatis(前身是iBatis)是一个支持普通SQL查询、存储过程以及高级映射的持久层框架,它消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索,并使用简单的XML或注解进行配置和原始映射,用以将接口和Java的POJO(Plain Old Java Object,普通Java对象)映射成数据库中的记录,使得Java开发人员可以使用面向对象的编程思想来操作数据库。

MyBatis 框架也被称之为 ORM(Object/Relational Mapping,即对象关系映射)框架。所谓的 ORM 就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述Java对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中。ORM框架的工作原理如下图所示。

img

从上图可以看出,使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象(Persisent Object,PO),而ORM框架则会通过映射关系将这些面向对象的操作转换成底层的SQL操作。

Mybaits 的优缺点

优点:

  • 消除 JDBC 中的重复代码
  • 可以在 XML 或注解中直接编写 SQL 语句,比较灵活,方便对 SQL 的优化与调整
  • SQL 写在 XML 中,与代码解耦,按照对应关系方便管理
  • XML 中提供了动态 SQL 的标签,方便根据条件拼接 SQL
  • 提供了 XML、注解与 Java 对象的映射机制
  • 与 Spring 集成比较方便

缺点:

  • 字段较多、关联表多时,编写 SQL 工作量较大
  • SQL 语句依赖了数据库特性,会导致程序的移植性较差,切换数据库困难

#{} 和 ${} 的区别

都是用于传值

MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ?,预编译 SQL

#是预编译占位符,在sql语句中用?代替,可以防止sql注入

$是用于sql语句的拼接,要判断参数类型,不能防止sql注入

#{} 比 ${} 安全,但还是提供了 ${} 这种动态替换参数的方式,是因为有些复杂的 SQL 使用场景通过预编译的方式比较麻烦,且在代码中完全可以做到控制非法参数,有些参数可能是一些常量或字段值。

MyBatis 中实体类的属性名与表中的字段名不一致怎么处理?

1、修改 SQL,给查询字段重命名,如 将 user_id 重命名为 userId

2、MyBatis 的 XML 映射文件中,使用 标签,定义数据库字段名与实体 bean 的属性字段名的映射关系

Mapper 接口如何与写 SQL 的 XML 文件进行绑定的?

  • Mapper 接口与 XML 文件的绑定是通过 XML 里 mapper 标签的 namespace 值与 Mapper 接口的 包路径.接口名 进行绑定
  • Mapper 接口的方法名与 XML 文件中的 sql、select、insert、update、delete 标签的 id 参数值进行绑定
  • 其中涉及到了 MappedStatement 的 id、SqlCommand 的 name 的值为 Mapper 接口的 包路径.接口名.方法名

MyBatis 如何批量插入?

方式一、打开批量插入的 SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
for (int i = 36; i <= 45; i++) {
    userMapper.insertUser(new User(i, "ConstXiong" + i));
}
sqlSession.commit();
sqlSession.close();

方式二、拼接批量插入的 insert SQL

//Java 代码
List<User> userList = new ArrayList<>();
for (int i = 46; i <= 55; i++) {
    userList.add(new User(i,"ConstXiong" + i));
}
userMapper.insertUserBatch(userList);

<!--Mapper xml 中配置-->
<insert id="insertUserBatch" parameterType="java.util.List">
    insert into user values
    <foreach collection="list" item="item" separator =",">
        (#{item.id}, #{item.name})
    </foreach>
</insert>

MyBatis 是如何与 Spring 集成的?

单纯使用 spring-context 和 spring-jdbc 集成 MyBatis,配置步骤:

  • 加载 spring-context、spring-jdbc、MyBatis、MyBatis-Spring 的 jar 包
  • spring 集成 MyBatis 的 xml 配置文件,配置 dataSource、sqlSessionFactory、Mapper 接口包扫描路径
  • Mapper 接口代理 bean 直接从 spring ioc 容器中获得使用即可

最核心的就是 spring 的配置文件,如下

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        ">

    <context:property-placeholder location="classpath:db.properties" ignore-unresolvable="true" />

    <bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
        <!-- 基本属性 url、user、password -->
        <property name="driver" value="${jdbc_driver}" />
        <property name="url" value="${jdbc_url}"/>
        <property name="username" value="${jdbc_username}"/>
        <property name="password" value="${jdbc_password}"/>
    </bean>

    <!-- spring 和 Mybatis整合 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:constxiong/mapper/*.xml" />
    </bean>

    <!-- DAO接口所在包,配置自动扫描 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="constxiong.mapper"/>
    </bean>

</beans>

核心配置就是 dataSource、SqlSessionFactoryBean、MapperScannerConfigurer

  • dataSource 是数据源

  • SqlSessionFactoryBean,配置数据源、可以加载解析 MyBatis 的配置文件、可以设置 Mapper xml 的文件路径与解析、SqlSessionFactory 对象的创建等

buildSqlSessionFactory() 方法中利用 MyBatis 的核心类解析 MyBatis 的配置文件、Mapper xml 文件,生成 Configuration 对象设置其中属性,创建 SqlSessionFactory 对象

MapperScannerConfigurer,设置 Mapper 接口的的包扫描路径,加载所有的 Mapper 接口生成 BeanDefinition,设置 BeanDefinition 的 beanClass 属性为 MapperFactoryBean,设置 sqlSessionFactory 和 sqlSessionTemplate 属性

springboot====================================

SpringBoot是Spring推出用于解决传统框架配置文件冗余,装配组件繁杂的基于Maven的解决方案,旨在快速搭建单个微服务。
版本号:2.1.6

SpringBoot基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。 [1] 
SpringBoot所具备的特征有:
(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;
(2)内嵌Tomcat或Jetty等Servlet容器;
(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;
(4)尽可能自动配置Spring容器;
(5)提供准备好的特性,如指标、健康检查和外部化配置;
(6)绝对没有代码生成,不需要XML配置。

SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。
	开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。
	约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

为什么要用 Spring Boot?

Spring Boot 优点非常多,如:

  • 独立运行
  • 简化配置
  • 自动配置
  • 无代码生成和XML配置
  • 应用监控
  • 上手容易

Spring Boot 的核心配置文件有哪几个?它们的区别是什么?

SpringBoot的核心配置文件有application和bootstarp配置文件。
2.他们的区别是什么?
application文件主要用于Springboot自动化配置文件。

bootstarp文件主要有以下几种用途:

使用Spring Cloud Config注册中心时 需要在bootStarp配置文件中添加链接到配置中心的配置属性来加载外部配置中心的配置信息。
一些固定的不能被覆盖的属性
一些加密/解密的场景
都有什么格式?
.properties 和 .yml
.yml采取的是缩进的格式 不支持@PeopertySource注解导入配置

**Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?**常用注解

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:

@SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。

@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

@ComponentScan:Spring组件扫描。

1、@SpringBootApplication

这个注解是Spring Boot最核心的注解,用在 Spring Boot的主类上

2、@EnableAutoConfiguration

允许 Spring Boot 自动配置注解,开启这个注解之后,Spring Boot 就能根据当前类路径下的包或者类来配置 Spring Bean。

3、@Configuration

用于定义配置类,指出该类是 Bean 配置的信息源,相当于传统的xml配置文件,一般加在主类上。

4、@ComponentScan

组件扫描。让spring Boot扫描到Configuration类并把它加入到程序上下文。

5、@Repository

用于标注数据访问组件,即DAO组件。

6、@Service

一般用于修饰service层的组件

7、@RestController

用于标注控制层组件(如struts中的action),表示这是个控制器bean,并且是将函数的返回值直 接填入HTTP响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的合集。

8、@ResponseBody

一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@responsebody后,会直接返回json数据。

9、@Component

泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

10、@Bean

相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。

11、@AutoWired

byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。

12、@Qualifier

当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用

13、@Resource(name=“name”,type=“type”)

没有括号内内容的话,默认byName。与@Autowired干类似的事。

14、@RequestMapping

RequestMapping是一个用来处理请求地址映射的注解;提供路由信息,负责URL到Controller中的具体函数的映射,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

15、@RequestParam

用在方法的参数前面。

16、@Profiles

Spring Profiles提供了一种隔离应用程序配置的方式,并让这些配置只能在特定的环境下生效。

17、@PathVariable

路径变量。参数与大括号里的名字一样要相同。

开启 Spring Boot 特性有哪几种方式?

1)继承spring-boot-starter-parent项目

2)导入spring-boot-dependencies项目依赖

运行 Spring Boot 有哪几种方式?

1)打包用命令或者放到容器中运行

2)用 Maven/ Gradle 插件运行

3)直接执行 main 方法运行

Spring Boot 自动配置原理是什么?

注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。

SpringBoot 有几种读取配置文件的方式

看配置文件 application.properties

user.username=CCCCXXX
user.password=123123123
user.age=18
user.salary=2000.00
  1. @Value读取

  2. @PropertySource读取 @PropertySource读取不支持yml文件配置*

  3. @Environment读取

    配置文件里面所有的配置都可以用Environment来获取

  4. @ConfigurationProperties读取

**这个注解就更屌了…**可以配合着别的注解一起使用

你如何理解 Spring Boot 配置加载顺序?

在 Spring Boot 里面,可以使用以下几种方式来加载配置。

1)properties文件;

2)YAML文件;

3)系统环境变量;

4)命令行参数;

Spring Boot 如何定义多套不同环境配置?

提供多套配置文件,如:

applcation.properties

application-dev.properties

application-test.properties

application-prod.properties

Spring Boot 可以兼容老 Spring 项目吗,如何做?

可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。

springCloud微服务架构====================

什么是微服务架构

微服务架构就是将单体的应用程序分成多个应用程序,这多个应用程序就成为微服务,每个微服务运行在自己的进程中,并使用轻量级的机制通信。这些服务围绕业务能力来划分,并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言,不同数据库,以保证最低限度的集中式管理。

Spring Cloud 是什么

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。

SpringCloud的优缺点

优点:

1.耦合度比较低。不会影响其他模块的开发。

2.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。

3.配置比较简单,基本用注解就能实现,不用使用过多的配置文件。

4.微服务跨平台的,可以用任何一种语言开发。

5.每个微服务可以有自己的独立的数据库也有用公共的数据库。

6.直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信。

缺点:

1.部署比较麻烦,给运维工程师带来一定的麻烦。

2.针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。

3.系统集成测试比较麻烦

4.性能的监控比较麻烦。【最好开发一个大屏监控系统】

总的来说优点大过于缺点,目前看来Spring Cloud是一套非常完善的分布式框架,目前很多企业开始用微服务、Spring Cloud的优势是显而易见的。因此对于想研究微服务架构的同学来说,学习Spring Cloud是一个不错的选择。

SpringBoot和SpringCloud的区别?

SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
为各个微服务之间提供,配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系
SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。

pringCloud由什么组成

这就有很多了,我讲几个开发中最重要的
Spring Cloud Eureka:服务注册与发现
Spring Cloud Zuul:服务网关
Spring Cloud Ribbon:客户端负载均衡
Spring Cloud Feign:声明性的Web服务客户端
Spring Cloud Hystrix:断路器
Spring Cloud Confifig:分布式统一配置管理

Spring Cloud 和dubbo区别?

(1)服务调用方式:dubbo是RPC springcloud Rest Api
(2)注册中心:dubbo 是zookeeper springcloud是eureka,也可以是zookeeper
(3)服务网关,dubbo本身没有实现,只能通过其他第三方技术整合,springcloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,springcloud支持断路器,与git完美集成配置文件支持版本控制,事物总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素。

Eureka

服务注册和发现是什么意思?Spring Cloud 如何实现?

什么是Eureka

Eureka作为SpringCloud的服务注册功能服务器,他是服务注册中心,系统中的其他服务使用Eureka的客户端将其连接到Eureka Service中,并且保持心跳,这样工作人员可以通过EurekaService来监控各个微服务是否运行正常。

Eureka怎么实现高可用

集群吧,注册多台 Eureka ,然后把 SpringCloud 服务互相注册,客户端从 Eureka 获取信息时,按照Eureka 的顺序来访问。

什么是Eureka的自我保护模式,

默认情况下,如果 Eureka Service 在一定时间内没有接收到某个微服务的心跳, Eureka Service 会进入自我保护模式,在该模式下Eureka Service 会保护服务注册表中的信息,不在删除注册表中的数据,当网络故障恢复后,Eureka Servic 节点会自动退出自我保护模式

DiscoveryClient的作用

可以从注册中心中根据服务别名获取注册的服务器信息。

Eureka和ZooKeeper都可以提供服务注册与发现的功能,请说说两个的区别

  1. ZooKeeper中的节点服务挂了就要选举 在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的, 选举就是改微服务做了集群,必须有一台主其他的都是从

  2. Eureka各个节点是平等关系,服务器挂了没关系,只要有一台Eureka就可以保证服务可用,数据都是最新的。 如果查询到的数据并不是最新的,就是因为Eureka的自我保护模式导致的

  3. Eureka本质上是一个工程,而ZooKeeper只是一个进程

  4. Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper 一样使得整个注册系统瘫痪

  5. ZooKeeper保证的是CP,Eureka保证的是AP

CAP: C:一致性>Consistency; 取舍:(强一致性、单调一致性、会话一致性、最终一致性、弱一致性) A:可用性>Availability; P:分区容错性>Partition tolerance;

Zuul

什么是网关?

网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。

网关的作用是什么

统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等

什么是Spring Cloud Zuul(服务网关)

网关与过滤器有什么区别

网关是对所有服务的请求进行分析过滤,过滤器是对单个服务而言。

常用网关框架有那些?

Nginx 、 Zuul 、 Gateway

Zuul与Nginx有什么区别?

Zuul 是 java 语言实现的,主要为 java 服务提供网关服务,尤其在微服务架构中可以更加灵活的对网关进行操作。Nginx 是使用 C 语言实现,性能高于 Zuul ,但是实现自定义操作需要熟悉 lua 语言,对程序员要求较高,可以使用Nginx 做 Zuul 集群。

既然Nginx可以实现网关?为什么还需要使用Zuul框架

Zuul 是 SpringCloud 集成的网关,使用 Java 语言编写,可以对 SpringCloud 架构提供更灵活的服务。

Zuul网关如何搭建集群

使用 Nginx 的 upstream 设置 Zuul 服务集群,通过 location 拦截请求并转发到 upstream ,默认使用轮询机制对Zuul 集群发送请求。

Ribbon

负载平衡的意义什么?

img

Ribbon是什么?

img

Nginx与Ribbon的区别

img

Ribbon底层实现原理

Ribbon 使用 discoveryClient 从注册中心读取目标服务信息,对同一接口请求进行计数,使用 % 取余算法获取目标服务集群索引,返回获取到的目标服务信息。

@LoadBalanced注解的作用

开启客户端负载均衡。

Hystrix

什么是断路器

img

什么是 Hystrix?

img

谈谈服务雪崩效应

img

在微服务中,如何保护服务?

img

服务雪崩效应产生的原因

因为 Tomcat 默认情况下只有一个线程池来维护客户端发送的所有的请求,这时候某一接口在某一时刻被大量访问就会占据tomcat 线程池中的所有线程,其他请求处于等待状态,无法连接到服务接口。

谈谈服务降级、熔断、服务隔离

img

整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。

1、服务降级的特征:
原因:整体负荷超出整体负载承受能力。
目的:保证重要或基本服务正常运行,非重要服务延迟使用或暂停使用
大小:降低服务粒度,要考虑整体模块粒度的大小,将粒度控制在合适的范围内
可控性:在服务粒度大小的基础上增加服务的可控性,后台服务开关的功能是一项必要配置(单机可配置文件,其他可领用数据库和缓存),可分为手动控制和自动控制。
次序:一般从外围延伸服务开始降级,需要有一定的配置项,重要性低的优先降级,比如可以分组设置等级1-10,当服务需要降级到某一个级别时,进行相关配置
2、降级的方式:

 (1)、延迟服务:比如发表了评论,重要服务,比如在文章中显示正常,但是延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行。
 (2)、在粒度范围内关闭服务(片段降级或服务功能降级):比如关闭相关文章的推荐,直接关闭推荐区
 (3)、页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
 (3)、页面跳转(页面降级):比如可以有相关文章推荐,但是更多的页面则直接跳转到某一个地址
 (4)写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
 (5)读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景;

服务降级底层是如何实现的?

Hystrix实现服务降级的功能是通过重写HystrixCommand中的getFallback()方法,当Hystrix的run方法或construct执行发生错误时转而执行getFallback()方法。

Feign

什么是Feign?

Feign 是一个声明web服务客户端,这使得编写web服务客户端更容易
他将我们需要调用的服务方法定义成抽象方法保存在本地就可以了,不需要自己构建Http请求了,直接调用接口就行了,不过要注意,调用方法要和本地抽象方法的签名完全一致。

SpringCloud有几种调用接口方式

Feign
RestTemplate

Ribbon和Feign调用服务的区别

img

Dubbo=======================================

介绍一下Dubbo?

Dubbo 是一个分布式、高性能、透明化的 RPC 服务框架,提供服务自动注册、自动发现等高效服务治理方案, 可以和 Spring 框架无缝集成

Dubbo有哪些核心组件?

  • Provider:服务的提供方
  • Consumer:调用远程服务的服务消费方
  • Registry:服务注册和发现的注册中心
  • Monitor:统计服务调用次数和调用时间的监控中心
  • Container:服务运行容器

Dubbo框架分了哪些层?

Dubbo 框架设计一共划分了 10 层:

  • 服务接口层(Service):该层是与实际业务逻辑相关的,根据服务提供方和服务消费方的业务设计对应的接口和实现
  • 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心
  • 服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton
  • 服务注册层(Registry):封装服务地址的注册与发现,以服务 URL 为中心
  • 集群层(Cluster):封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心
  • 监控层(Monitor):RPC 调用次数和调用时间监控
  • 远程调用层(Protocol):封将 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocol、Invoker、Exporter
  • 信息交换层(Exchange):封装请求响应模式,同步转异步,以 Request 和 Response 为中心
  • 网络传输层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心
  • 数据序列化层(Serialize):序列化的一些工具

Dubbo支持哪些序列化方式?

  • Hessian 序列化:是修改过的 hessian lite,默认启用
  • json 序列化:使用 FastJson 库
  • java 序列化:JDK 提供的序列化,性能不理想
  • dubbo 序列化:未成熟的高效 java 序列化实现,不建议在生产环境使用

Dubbo的主要作用?

  • 透明化的远程方法调用,像调用本地方法一样调用远程方法
  • 负载均衡及容错机制,负载分发请求到不同的服务提供者,解决单点故障
  • 服务自动注册与发现,动态服务注册与请求分发,能够平滑添加或删除服务提供者

Dubbo有哪些负载均衡策略?

Dubbo 实现了常见的集群策略,并提供扩展点予以自行实现。

  • Random LoadBalance:随机选取提供者策略,随机转发请求,可以加权
  • RoundRobin LoadBalance:轮循选取提供者策略,请求平均分布
  • LeastActive LoadBalance:最少活跃调用策略,可以让慢提供者接收更少的请求
  • ConstantHash LoadBalance:一致性 Hash 策略,相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者

缺省时为 Random LoadBalance

你觉得用 Dubbo 好还是 Spring Cloud 好?

扩展性的问题,没有好坏,只有适合不适合,不过我好像更倾向于使用 Dubbo, Spring Cloud 版本升级太快,组件更新替换太频繁,配置太繁琐,还有很多我觉得是没有 Dubbo 顺手的地方……

数据库==============================

mysql================================

事务的四大特性

  • 原子性(Atomicity)

事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

  • 一致性(Consistency)

事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。

  • 隔离性(Isolation)

指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

  • 持久性(Durability)

指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

事务有哪些隔离级别?

  • 读未提交(Read Uncommitted):是最低的事务隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。会出现脏读,幻读,不可重复读,所有并发问题都可能遇到。
  • 读已提交(Read Committed):保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。不会出现脏读现象,但是会出现幻读,不可重复读。
  • 可重复读(Repeatable Read):这种事务隔离级别可以防止脏读,不可重复读,但是可能会出现幻象读。它除了保证一个事务不能被另外一个事务读取未提交的数据之外还避免了不可重复读。
  • 串行化(Serializable):这是花费最高代价但最可靠的事务隔离级别。事务被处理为顺序执行。防止脏读、不可重复读、幻象读。

数据库如何优化

1优化sql语句
原则:
1.尽量根据主键查询
2.尽量使用单表查询/不要关联查询

3.查询时可以使用in但是绝对不要使用not in

2.创建索引 为搜索字段建索引
 create index 索引名称 on 表名称(列名称)
 	为什么使用数据索引能提高效率
    数据索引的存储是 有序的
    在有序的情况下, 通过索引查询一个数据是无需遍历索引记录的
    极端情况下,数据索引的查询效率为二分法查询效率,趋近于log2(N)

3.添加缓存
 例如:mybatis 一/二级缓存 该操作效率低
 主要:redis/memercache
 作用:可以有效的缓解数据库压力。

4.使用数据库的读写分离

5.定期将历史数据进行转储
 当前表/查询历史表
6.进行分库分表(最后策略,最有效的策略)
 数据库服务器数量和运维都要花费很多的时间和精力
 什么是表分区?
    表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成
    表分区与分表的区别?
    分表:指的是通过一定规则, 将一张表分解成多 张不同的表。比如将用户订单记录根据时间成多个表。
    分表与分区的区别在于:分区从逻辑上来讲只有一张表 ,而分表则是将一张表分解成多张表。

    表分区有什么好处?
    存储更多数据。分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。和单个磁盘或者文件系统相比,可以存储更多数据
    优化E询。在where语句中包含分区条件时,可以只扫描一个或多 个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。
    分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。
    避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问, ext3问价你系统的inode锁竞争等。
7.选择正确的存储引擎

MySql集群主从复制(主从同步)

原理

主(master) 从(slave)

  1. master将数据改变记录到二进制日志(binary log)中,即配置文件log-bin指定的文件(这些记录叫做二进制日志事件,binary log events)

  2. slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)

  3. slave重做中继日志中的事件,将改变反映它自己的数据(数据重演)

    \1. 主库和从库的版本要一致

    \2. 主库和从库的数据一致

    \3. 主库开启二进制日志,主库和从库的server_id都必须唯一

    在项目中,使用读写分离本质上就是,增加数据库服务器资源 + 网络带宽,来分摊对数据库的读写请求,从而提高了性能和可用性。主从复制实现读写分离最大的缺点就是从库同步到主库的数据存在延迟,网络不好的时候,延迟比较严重。

mysql默认的存储引擎是什么?

Mysql在V5.1之前默认存储引擎是MyISAM;在此之后默认存储引擎是InnoDB。
MyISAM不支持事务,InnoDB支持事务。
MySIAM不支持外键,InnoDB支持外键,
MySIAM支持全文索引,InnoDB不支持全文索引。

MyISAM与InnoDB的区别?

  • InnoDB 支持事务;MyISAM 不支持事务

  • InnoDB 支持行级锁;MyISAM 支持表级锁

  • InnoDB 支持 MVCC(多版本并发控制);MyISAM 不支持

  • InnoDB 支持外键,MyISAM 不支持

  • MySQL 5.6 以前的版本,InnoDB 不支持全文索引,MyISAM 支持;MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存储引擎均支持全文索引

  • InnoDB 不保存表的总行数,执行 select count(*) from table 时
    需要全表扫描;MyISAM 用一个变量保存表的总行数,查总行数速度很快

  • InnoDB 是聚集索引,数据文件是和索引绑在一起的,必须要有主键,
    通过主键索引效率很高。辅助索引需要两次查询,先查询到主键,再通过主键查询到数据。主键太大,其他索引也会很大;MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的

    总结

  • InnoDB 存储引擎提供了具有提交、回滚、崩溃恢复能力的事务安全,与 MyISAM 比 InnoDB 写的效率差一些,并且会占用更多的磁盘空间以保留数据和索引

  • MyISAM 不支持事务、也不支持外键,优势是访问的速度快。对事务的完整性没有要求、以 SELECT 和 INSERT 为主的应用可以使用这个存储引擎

Innodb引擎有什么特性?

  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应哈希索引(ahi)
  • 预读(read ahead)

数据库的三范式是什么?有什么作用?

  • 列不可分,确保表的每一列都是不可分割的原子数据项。作用:方便字段的维护、查询效率高、易于统计。
  • 属性字段完全依赖(完全依赖指不能存在仅依赖主键的部分属性)于主键。作用:保证每行数据都是按主键划分的独立数据。
  • 任何非主属性字段不依赖于其它非主属性字段。作用:减少表字段与数据存储,让相互依赖的非主键字段单独成为一张关系表,记录被依赖字段即可。

Mysql驱动程序是什么?

  • Mysql 提供给 Java 编程语言的驱动程序就是这样 mysql-connector-java-5.1.18.jar 包
  • 针对不同的数据库版本,驱动程序包版本也不同
  • 不同的编程语言,驱动程序的包形式也是不一样的
  • 驱动程序主要帮助编程语言与 MySQL 服务端进行通信,如果连接、关闭、传输指令与数据等

如何连接MySQL服务端、关闭连接?

  • 连接:使用指令 mysql -u -p -h -P (-u:指定用户名 -p:指定密码 -h:主机 -P:端口) 连接 MySQL 服务端
  • 关闭:使用指令 exit 或 quit

左连接、右连接、内连接和全外连接的区别

  • 左连接(left join):返回包括左表中的所有记录和右表中连接字段相等的记录。
  • 右连接(right join):返回包括右表中的所有记录和左表中连接字段相等的记录。
  • 内连接(inner join):只返回两个表中连接字段相等的记录。
  • 全外连接(full join):返回左右表中连接字段相等的记录和剩余所有记录。

什么是表分区?

表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。

表分区与分表的区别

分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。

分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。

表分区有什么好处?

1、存储更多数据。分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。和单个磁盘或者文件系统相比,可以存储更多数据

2、优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。

3、分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。

4、避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。

分区表的限制因素

  1. 一个表最多只能有1024个分区
  2. MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。
  3. 如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
  4. 分区表中无法使用外键约束
  5. MySQL的分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区而不对表分区,也不能只对表的一部分数据分区。

如何判断当前MySQL是否支持分区?

命令:show variables like ‘%partition%’ 运行结果:

have_partintioning 的值为YES,表示支持分区。

MySQL支持的分区类型有哪些?

  1. RANGE分区: 这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区
  2. LIST分区: 这种模式允许系统通过预定义的列表的值来对数据进行分割。按照List中的值分区,与RANGE的区别是,range分区的区间范围值是连续的。
  3. HASH分区 :这中模式允许通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。
  4. KEY分区 :上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。

如何实现分库分表?怎么配置?

分库分表的实现方案,一般分为两种

1、增加一个中间层,中间层实现 MySQL 客户端协议,可以做到应用程序无感知地与中间层交互。由于是基于协议层的代理,可以做到支持多语言,但需要多启动一个进程、SQL 的解析也耗费大量性能、由于协议绑定仅支持单个种类的数据库库。

2、在代码层面增加一个路由程序,控制对数据库与表的读写。路由程序写在项目里,与编程语言绑定、连接数高、但相对轻量(比如 Java 仅需要引入 SharingShpere 组件中 Sharding-JDBC 的 jar 即可)、支持任意数据库。

索引

Mysql索引使用的数据结构主要有BTree索引哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

Mysql的BTree索引使用的是B数中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。

MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。

InnoDB: 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,在走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。

什么是索引?什么场景使用?

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息

使用索引目的是加快检索表中数据

使用场景:

  • 中到大数据量表适合使用索引
  • 小数据量表,大部分情况全表扫描效率更高
  • 特大数据量表,建立和使用索引的代价会随之增大,适合使用分区或分库

索引如何创建与删除?

  • 创建单个字段索引的语法:CREATE INDEX 索引名 on 表名(字段名)
  • 创建联合索引的语法:CREATE INDEX 索引名 on 表名(字段名1,字段名2)
  • 索引命名格式一般可以这样:idx_表名_字段名。注意有长度限制
  • 删除索引:DROP INDEX 索引名 ON 表名

索引的种类有哪些?

  • 普通索引:最基本的索引,没有任何约束限制。列值可以取空值或重复值。创建使用关键字INDEX或KEY;
  • 唯一索引:和普通索引类似,但是具有唯一性约束,可以有 null;创建使用关键字UNIQUE;
  • 主键索引:特殊的唯一索引,主键索引是系统自动创建的主键索引,并且是唯一的。与唯一索引区别是;列值不能为空
  • 组合索引:多列值组成一个索引,用于组合搜索,效率大于索引合并
  • 聚簇索引:就是数据存储的物理存储顺序,非聚簇索引就是索引顺序与数据的物理顺序无关。一个表只能有一个聚簇索引。目前只有InoDB和solidDB支持。
  • 全文索引:对文本的内容进行分词、搜索
  • 覆盖索引:查询列要被所建的索引覆盖,不必读取数据行

MySQL创建和使用索引的注意事项?

  • 适合创建索引的列是出现在 WHERE 或 ON 子句中的列,而不是出现在 SELECT 关键字后的列
  • 索引列的基数越大,数据区分度越高,索引的效果越好
  • 对字符串列进行索引,可制定一个前缀长度,节省索引空间
  • 避免创建过多的索引,索引会额外占用磁盘空间,降低写操作效率
  • 主键尽可能选择较短的数据类型,可减少索引的磁盘占用,提高查询效率
  • 联合索引遵循前缀原则
  • LIKE 查询,%在前不到索引,可考虑使用 ElasticSearch、Lucene 等搜索引擎
  • MySQL 在数据量较小的情况可能会不使用索引,因为全表扫描比使用索引速度更快
  • 关键词 or 前面的条件中的列有索引,后面的没有,所有列的索引都不会被用到
  • 列类型是字符串,查询时一定要给值加引号,否则索引失效
  • 联合索引要遵从最左前缀原则,否则不会用到索引

说一些索引失效的情况

  • 如果条件中有 or,即使其中有部分条件是索引字段,也不会使用索引
  • 复合索引,查询条件不使用索引前面的字段,后续字段也将无法使用索引
  • 以 % 开头的 like 查询
  • 索引列的数据类型存在隐形转换
  • where 子句里对索引列有数学运算
  • where 子句里对索引列使用函数
  • MySQL 引擎估算使用全表扫描要比使用索引快,则不使用索引

索引对性能有哪些影响?

优点:

  • 减少数据库服务器需要扫描的数据量
  • 帮助数据库服务器避免排序和临时表
  • 将随机 I/O 变顺序I/O
  • 提高查询速度
  • 唯一索引,能保证数据的唯一性

缺点:

  • 索引的创建和维护耗时随着数据量的增加而增加
  • 对表中数据进行增删改时,索引也要动态维护,降低了数据的维护速度
  • 增大磁盘占用

列值为NULL时,查询是否会用到索引?

MySQL 中存在 NULL 值的列也是走索引的

计划对列进行索引,应尽量避免把它设置为可空,因为这会让 MySQL 难以优化引用了可空列的查询,同时增加了引擎的复杂度

缓存

my.cnf加入以下配置,重启Mysql开启查询缓存

query_cache_type=1



query_cache_size=600000

Mysql执行以下命令也可以开启查询缓存

set global  query_cache_type=1;



set global  query_cache_size=600000;

如上,开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、Mysql库中的系统表,其查询结果也不会被缓存。

缓存建立之后,Mysql的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。

缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。 因此,开启缓存查询要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十MB比较合适。此外,还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存:

select sql_no_cache count(*) from usr;

为什么使用数据索引能提高效率

  1. 数据索引的存储是有序的
  2. 在有序的情况下,通过索引查询一个数据是无需遍历索引记录的
  3. 极端情况下,数据索引的查询效率为二分法查询效率,趋近于 log2(N)

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。

  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

    表级锁和行级锁对比:

  • 表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。

  • 行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

    InnoDB存储引擎的锁的算法有三种:

  • Record lock:单个行记录上的锁

  • Gap lock:间隙锁,锁定一个范围,不包括记录本身

  • Next-key lock:record+gap 锁定一个范围,包含记录本身

行级锁定的优点:

1、当在许多线程中访问不同的行时只存在少量锁定冲突。

2、回滚时只有少量的更改

3、可以长时间锁定单一的行。

行级锁定的缺点:

  1. 比页级或表级锁定占用更多的内存。
  2. 当在表的大部分中使用时,比页级或表级锁定速度慢,因为你必须获取更多的锁。
  3. 如果你在大部分数据上经常进行GROUP BY操作或者必须经常扫描整个表,比其它锁定明显慢很多。
  4. 用高级别锁定,通过支持不同的类型锁定,你也可以很容易地调节应用程序,因为其锁成本小于行级锁定。

MySQL的乐观锁和悲观锁?

乐观锁:每次去获取数据的时候都认为别人不会修改,不会上锁,但是在提交修改的时候会判断一下在此期间别人有没有修改这个数据。
悲观锁:每次去获取数据的时候都认为别人会修改,每次都会上锁,阻止其他线程获取数据,直到这个锁释放。

MySQL 的乐观锁需要自己实现。一般在表里面添加一个 version 字段,每次修改成功值加 1;每次其他字段值的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就可以返回失败也可以进行重试。

MySQL 的悲观锁,以 Innodb 存储引擎为例,将 MySQL 设置为非 autoCommit 模式

begin;
select * from table where id = 1 for update;
insert ...
update ...
commit;

当上面语句未 commit,id = 1 的数据是被锁定的,即其他事务中的查到这条语句会被阻塞,直到事务提交。

数据的锁定还涉及到索引的不同可能是行锁、表锁的问题。

MySQL中如何避免死锁?

  • 尽量以相同的顺序来访问索引记录和表
  • 业务上能够接受幻读和不可重复读,考虑降低锁的级别到 Read committed,降低死锁发生的概率
  • 添加合理的索引,走索引避免为每一行加锁,降低死锁的概率
  • 在事务中一次锁定所需要的所有资源,如 MyISAM 引擎的表锁
  • 避免大事务,尽量将大事务拆成多个小事务来处理
  • 尽量避免同时并发对同一表进行读写操作,特别是执行加锁且操作数据量较大的语句
  • 设置锁等待超时参数

MySQL 单表上亿,怎么优化分页查询?

1、表容量的问题

首先,MySQL 不管怎么优化也是很难支持单表一亿数据量带查询条件的分页查询,需要提前考虑分表分库。单表设计以 200-500 万为宜;优化的好,单表数据到一两千万,性能也还行。出现那么单表那么大的数据量,已经是设计问题了。

2、总页数的问题

页面不需要显示总页数,仅显示附近的页码,这样可以避免单表总行数的查询。

需要显示总页数,这种情况就比较难处理一些。首先 MySQL 的 MyISAM 引擎把一个表的总行数记录在磁盘中,查询 count(*) 可以直接返回;InnoDB 引擎是一行行读出来累加计数,大数据量时性能堪忧,大几秒甚至几十秒都有可能(我相信你一定遇到过)。所以 MyISAM 的总行数查询速度是比 InnoDB 快的,但这个快也仅限于不带 where 条件的。MyISAM 还有一个硬伤,不支持事务。

3、具体的 SQL 优化

新增表记录业务表的总数,也是无法彻底解决带查询条件的总行数查询慢的问题。这里只能借助具体的 SQL 优化。

不带条件 + 自增 id 字段连续

这种理想情况就不讨论了,通过 pageNo 和 pageSize 算出 id 的起始与结束值

where id >= ? and id <?
where id between
where id >= ? limit 10

就可以直接搞定了。

带查询条件 + 主键 id 不连续

这种就是我们最需要解决的情况。使用 limit 分页,有个查询耗时与起始记录的位置成正比的问题,所以不能直接使用。

可以这样根据主键进行关联查询

select * from table t1
join (select id from table where condition limit 10) t2
on t1.id = t2.id 
order by t1.id asc

其中 condition 是包含索引的查询条件,使用 id 字段进行具体信息的关联回查。当然查询条件 condition 中索引是否生效对性能影响也很大。

索引没有生效的一些情况:

  • 组合索引的「最左前缀」原则
  • or 的使用可能导致索引未生效,可使用 union all 替代
  • like 查询以 % 开头
  • 对 null 值判断
  • 使用 != 或 <> 操作符
  • 索引列上使用计算、函数

4、其他解法

  • 继续优化数据库配置
  • 提升数据库服务器硬件性能
  • 引入大数据组件
  • 引入大型商业数据库或者非关系型数据库解决大表问题

MySQL有哪些常用函数?

数值型函数

  • ABS:计算绝对值 SQRT:计算二次方根 MOD:计算余数 CEIL、CEILING:返回不小于参数的最小整数,即向上取整
    • FLOOR:向下取整,返回值转化为一个 BIGINT RAND:生成一个 0~1 之间的随机数
  • ROUND:四舍五入
  • SIGN:返回参数的符号
  • POW、POWER:参数次方的值

字符串函数

  • LENGTH:返回字符串的字节长度
  • CONCAT:合并字符串,返回结果为连接参数产生的字符串,参数可以使一个或多个
  • INSERT:替换字符串
  • LOWER:将字符串中的字母转换为小写
  • UPPER:将字符串中的字母转换为大写
  • LEFT:从左侧字截取符串,返回字符串左边的若干个字符
  • RIGHT:从右侧字截取符串,返回字符串右边的若干个字符
  • TRIM:删除字符串左右两侧的空格
  • REPLACE:字符串替换,返回替换后的新字符串
  • SUBSTRING:截取字符串,返回从指定位置开始的指定长度的字符换
  • REVERSE:字符串反转,返回与原始字符串顺序相反的字符串

聚合函数

  • MAX:查询指定列的最大值
  • MIN:查询指定列的最小值
  • COUNT:统计查询结果的行数
  • SUM:求和,返回指定列的总和
  • AVG:求平均值,返回指定列数据的平均值

流程控制函数

  • IF:判断是否为 true
  • IFNULL:判断是否为空
  • CASE:分支判断

与Oracle相比,Mysql有什么优势?

  • Mysql 是开源软件、无需付费
  • 操作简单、部署方便,用户可以根据应用的需求去定制数据库
  • Mysql 的引擎是插件式

MySQL如何进行慢SQL优化?

思路:

  • 通过慢查询日志去寻找哪些 SQL 执行效率低
  • 使用 explain 获取低效率 SQL 的执行计划
  • 结合 SQL 与执行计划,进行分析与优化

引起 SQL 查询很慢的原因与解决办法:

1、没有索引。解决办法:

  • 根据 where 和 order by 使用比较频繁的字段创建索引,提高查询效率
  • 索引不宜过多,单表最好不要超过 6 个。索引过多会导致占用存储空间变大;insert、update 变慢
  • 删除未使用的索引

2、索引未生效。解决办法:

  • 避免在 where 子句中对字段进行 null 值判断,创建表默认值是 NULL。尽量使用 NOT NULL,或使用特殊值,如 0、-1
  • 避免在 where 子句中使用 != 或 <> 操作符, MySQL 只有对以下操作符才使用索引:<、<=、=、>、>=、BETWEEN、IN、非 % 开头的 LIKE
  • 避免在 where 子句中使用 or 来连接条件,可以使用 UNION 进行连接
  • 能用 union all 就不用 union,union 过滤重复数据要耗费更多的 CPU 资源
  • 避免部分 like 查询,如 ‘%ConstXiong%’
  • 避免在索引列上使用计算、函数
  • in 和 not in 慎用,能用 between 不要用 in
  • select 子句中避免使用 *

3、单表数据量太大。解决办法:

  • 分页查询(在索引上完成排序分页操作、借助主键进行关联)
  • 单表数据过大,进行分库分表
  • 考虑使用非关系型数据库提高查询效率
  • 全文索引场景较多,考虑使用 ElasticSearch、solr

提升性能的一些技巧:

  • 尽量使用数字型字段
  • 只需要一行数据时使用 limit 1
  • 索引尽量选择较小的列
  • 不需要的数据在 GROUP BY 之前过滤掉
  • 大部分时候 exists、not exists 比 in、not in 效率(除了子查询是小表的情况使用 in 效率比 exists 高)
  • 不确定长度的字符串字段使用 varchar/nvarchar,如使用 char/nchar 定长存储会带来空间浪费
  • 不要使用 select *,去除不需要的字段查询
  • 避免一次性查询过大的数据量
  • 使用表别名,减少多表关联解析时间
  • 多表 join 最好不超过 5 个,视图嵌套最好不超过 2 个
  • or 条件查询可以拆分成 UNION 多个查询
  • count(1) 比 count(*) 有效
  • 判断是否存在数据使用 exists 而非 count,count 用来获取数据行数

如何避免sql注入?

  • 严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
  • 校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
  • 对进入数据库的特殊字符进行转义处理,或编码转换
  • 预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
  • 发布前,利用工具进行 SQL 注入检测
  • 报错信息不要包含 SQL 信息输出到 Web 页面

什么是XSS攻击,如何避免?

XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。

  • web 页面中可由用户输入的地方,如果对输入的数据转义、过滤处理
  • 后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库)
  • 前端对 html 标签属性、css 属性赋值的地方进行校验

什么是CSRF攻击,如何避免?

CSRF:Cross Site Request Forgery(跨站点请求伪造)。
CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

避免方法:

  • CSRF 漏洞进行检测的工具,如 CSRFTester、CSRF Request Builder…
  • 验证 HTTP Referer 字段
  • 添加并验证 token
  • 添加自定义 http 请求头
  • 敏感操作添加验证码
  • 使用 post 请求

Oracle========================================

Oracle中字符串链接符是什么?

Oracle中使用 || 这个符号连接字符串

如 ‘Const’ || ‘Xiong’

Oracle中有哪几种文件?

数据文件(一般后缀为.dbf或者.ora),日志文件(后缀名.log),控制文件(后缀名为.ctl)*

Oracle优化

个人理解,数据库性能最关键的因素在于IO,因为操作内存是快速的,但是读写磁盘是速度很慢的,优化数据库最关键的问题在于减少磁盘的IO,就个人理解应该分为物理的和逻辑的优化, 物理的是指oracle产品本身的一些优化,逻辑优化是指应用程序级别的优化
物理优化的一些原则:
1). Oracle的运行环境(网络,硬件等)
2). 使用合适的优化器
3). 合理配置oracle实例参数
4). 建立合适的索引(减少IO)
5). 将索引数据和表数据分开在不同的表空间上(降低IO冲突)
6). 建立表分区,将数据分别存储在不同的分区上(以空间换取时间,减少IO)
逻辑上优化:
1). 可以对表进行逻辑分割,如中国移动用户表,可以根据手机尾数分成10个表,这样对性能会有一定的作用
2). Sql语句使用占位符语句,并且开发时候必须按照规定编写sql语句(如全部大写,全部小写等)oracle解析语句后会放置到共享池中
如: select * from Emp where name=? 这个语句只会在共享池中有一条,而如果是字符串的话,那就根据不同名字存在不同的语句,所以占位符效率较好
3). 数据库不仅仅是一个存储数据的地方,同样是一个编程的地方,一些耗时的操作,可以通过存储过程等在用户较少的情况下执行,从而错开系统使用的高峰时间,提高数据库性能
4). 尽量不使用*号,如select * from Emp,因为要转化为具体的列名是要查数据字典,比较耗时
5). 选择有效的表名
对于多表连接查询,可能oracle的优化器并不会优化到这个程度, oracle 中多表查询是根据FROM字句从右到左的数据进行的,那么最好右边的表(也就是基础表)选择数据较少的表,这样排序更快速,如果有link表(多对多中间表),那么将link表放最右边作为基础表,在默认情况下oracle会自动优化,但是如果配置了优化器的情况下,可能不会自动优化,所以平时最好能按照这个方式编写sql
6). Where字句 规则
Oracle 中Where字句时从右往左处理的,表之间的连接写在其他条件之前,能过滤掉非常多的数据的条件,放在where的末尾, 另外!=符号比较的列将不使用索引,列经过了计算(如变大写等)不会使用索引(需要建立起函数), is null、is not null等优化器不会使用索引
7). 使用Exits Not Exits 替代 In Not in
8). 合理使用事务,合理设置事务隔离性
数据库的数据操作比较消耗数据库资源的,尽量使用批量处理,以降低事务操作次数

Oracle有哪几种索引?

  • 单列索引与复合索引

单列索引是基于单列所创建的索引,复合索引是基于两列或者多列所创建的索引

  • 唯一索引与非唯一索引

唯一索引是索引列值不能重复的索引,非唯一索引是索引列可以重复的索引。都允许取 NULL 值,默认 Oracle 创建的索引是不唯一索引

  • B 树索引

B 树索引是按 B 树算法组织并存放索引数据的,B 树索引主要依赖其组织并存放索引数据的算法来实现快速检索功能

  • 位图索引

它采用位图偏移方式来与表的行 ROWID 号对应,通过位图索引中的映射函数完成位到行的 ROWID 的转换

主要用于节省空间,减少oracle对数据块的访问

采用位图索引一般是重复值太多、只有几个枚举值的表字段

  • 函数索引

Oracle 对包含列的函数或表达式创建的索引

哪些因素影响oracle查询性能?

  • 硬件:处理器速度,内存大小,磁盘读写速度,网络传输速度等
  • 索引:是否建立了索引,索引是否合理
  • 碎片:表碎片和索引碎片,生产库长时间运营,碎片可能导致查询使用错误的执行计划,导致查询速度变慢
  • initial 参数:表或索引的 initial 参数配置不同,导致数据扩展区大小不一,也可能导致查询速度降低
  • 慢 SQL:编写的 SQL 执行效率低,查询速度慢
  • 负载:数据库负载过大

Oracle中排序对性能的影响?

order by 只有满足如下情况才会使用索引:

  • order by中的列必须包含相同的索引并且索引顺序和排序顺序一致
  • 不能有 null 值的列

所以排序的性能并不高,尽量避免 order by

Oracle中字符串用什么符号链接?

Oracle中使用 || 这个符号连接字符串 如 ‘abc’ || ‘d’

Oracle有哪些备份方式?

备份就是把数据库复制到转储设备的过程

从物理与逻辑的角度:

  • 物理备份:对数据库操作系统的物理文件(数据文件、控制文件、日志文件)的备份。物理备份又可以分为脱机备份(冷备份)和联机备份(热备份),前者是在关闭数据库的时候进行的,后者是以归档日志的方式对运行的数据库进行备份
  • 逻辑备份:对数据库逻辑组件(如表和存储过程等数据库对象)的备份。逻辑备份的手段很多,如 EXP、EXPDP、第三方工具

从数据库的备份角度:

  • 完全备份:每次对数据库进行完整备份
  • 增量备份:在上次完全备份或增量备份后被修改的文件才会被备份
  • 差异备份:备份自从上次完全备份之后被修改过的文件

冷备和热备的优缺点?

冷备发生在数据库已经正常关闭的情况下,将数据库文件拷贝到其他位置
热备是在数据库运行的情况下,采用归档方式备份数据

冷备的优缺点:

  • 只需拷贝文件,非常快速

  • 拷贝即可,容易归档

  • 文件拷贝回去,即可恢复到某个时间点上

  • 能与归档方法相结合
    冷备份的不足:

  • 只能提供到数据库文件备份的时间点的恢复

  • 在冷备过程中,数据库必须是关闭状态,不能工作

  • 不能按表或按用户恢复

热备的优缺点:

  • 可在表空间或数据文件级备份

  • 备份时数据库可用

  • 可达到秒级恢复到某时间点

  • 可对几乎所有数据库实体作恢复

  • 数据完整性与一致性好
    热备份的不足:

  • 维护较复杂

  • 设备要求高,网络环境稳定性要求高

  • 若热备份不成功,所得结果不可用

Oracle怎么分页?

Oracle 使用 rownum 进行分页

select col1,col2 from 
  ( select rownum r,col1,col2 from tablename where rownum <= 20 ) 
where r > 10 

怎样创建一个视图,视图的好处, 视图可以控制权限吗?

create view 视图名 as select 列名 [别名] … from 表 [unio [all] select … ] ]
好处:
\1. 可以简单的将视图理解为sql查询语句,视图最大的好处是不占系统空间
\2. 一些安全性很高的系统,不会公布系统的表结构,可能会使用视图将一些敏感信息过虑或者重命名后公布结构
\3. 简化查询
可以控制权限的,在使用的时候需要将视图的使用权限grant给用户

rowid, rownum的定义

\1. rowid和rownum都是虚列
\2. rowid是物理地址,用于定位oracle中具体数据的物理存储位置
\3. rownum则是sql的输出结果排序,从下面的例子可以看出其中的区别。

oracle中存储过程,游标和函数的区别

游标类似指针,游标可以执行多个不相关的操作.如果希望当产生了结果集后,对结果集中的数据进行多 种不相关的数据操作
函数可以理解函数是存储过程的一种; 函数可以没有参数,但是一定需要一个返回值,存储过程可以没有参数,不需要返回值;两者都可以通过out参数返回值, 如果需要返回多个参数则建议使用存储过程;在sql数据操纵语句中只能调用函数而不能调用存储过程

Oracle怎样实现每天备份一次?

通过操作系统的定时任务调用脚本导出数据库

windows:

在 任务计划程序 里创建基本任务,设置备份周期,执行 bat 脚本,脚本参考:
cd d:\oracle_back
del oracle.dmp
expdp username/password@orcl directory=DIR_EXP dumpfile=oracle.dmp

linux:

通过 crontab 制作定时任务,执行 shell 脚本,脚本参考:
cd /back/oracle_back
rm oracle.dmp
expdp username/password@orcl directory=DIR_EXP dumpfile=oracle.dmp

Oracle分区有哪些作用?

  • Oracle的分区分为:列表分区、范围分区、散列分区、复合分区
  • 增强可用性:表的一个分区由于系统故障不能使用,其他好的分区仍可以使用
  • 减少故障修复时间:如果系统故障只影响表的一部份分区,只需修复部分分区,比修复整个表花的时间少
  • 维护轻松:独产管理公区比管理单个大表轻松
  • 均衡 I/O:把表的不同分区分配到不同的磁盘,平衡 I/O
  • 改善性能:对大表的查询、增加、修改等操作可以分解到表的不同分区来并行执行,加快执行速度
  • 在使用层是感觉不到分区的存在

Oracle数据库如何迁移?

  • 使用 imp/exp 将老库中的数据导入到新的库中。可以跨平台使用,但停机时间长
  • 如果是存储迁移直接将存储设备挂到新机器上,在新机器上启动数据库。这种方式操作简单,但要求新老库版本一致
  • 使用 rman,适合跨文件系统的迁移
  • 使用 dataguard 迁移
  • 借助工具,如 pl/sql

oracle临时表有几种。

临时表和普通表的主要区别有哪些,使用临时表的主要原因是什么?

在Oracle中,可以创建以下两种临时表:
a。会话特有的临时表
CREATE GLOBAL TEMPORARY ( )
ON COMMIT PRESERVE ROWS;

b。事务特有的临时表
CREATE GLOBAL TEMPORARY ( )
ON COMMIT DELETE ROWS;
CREATE GLOBAL TEMPORARY TABLE MyTempTable
所建的临时表虽然是存在的,但是你试一下insert 一条记录然后用别的连接登上去select,记录是空的,明白了吧。
下面两句话再贴一下:
–ON COMMIT DELETE ROWS 说明临时表是事务指定,每次提交后ORACLE将截断表(删除全部行)
–ON COMMIT PRESERVE ROWS 说明临时表是会话指定,当中断会话时ORACLE将截断表。

Oracle存储文件类型的字段?

  • clob:可变长度的字符型数据,文本型数据类型
  • nclob:可变字符类型的数据,存储的是 Unicode 字符集的字符数据
  • blob:可变长度的二进制数据
  • Bfile:存储在数据库外的操作系统文件,变二进制数据,不参与数据库事务操作

oracle数据库中有多行相同数据,只留一行怎么实现?

distinct 函数查询出来 插入另一张表 最简单了

select distinct col1,col2,col3 from table

Redis=========================================

介绍一下Redis

Redis 是一款使用 C 语言编写的高性能 key-value 数据库,开源免费,遵守 BSD 协议。

特点:

  • 性能极高,能到 100000 次/s 读写速度
  • 支持数据的持久化,对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上
  • 丰富的数据类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)
  • 原子性:Redis 的所有操作都是原子性的,多个操作通过 MULTI 和 EXEC 指令支持事务
  • 丰富的特性:key 过期、publish/subscribe、notify
  • 支持数据的备份,快速的主从复制
  • 节点集群,很容易将数据分布到多个Redis实例中

Redis支持哪些数据类型?

  • string:字符串 hash:哈希 list:列表 set:集合 sorted set:有序集合

redis受到攻击怎么办?

1.主从配置

2.持久化储存redis,不以root账户启动
3,。增加redis密码验证(设置复杂密码)
4.不允许key方式登录
5.以非root权限启动redis
6.禁止公网开放redis端口,
7.检查authorized_keys是否非法

Redis 缓存穿透、击穿、雪崩现象及解决方案

缓存穿透

1、缓存空结果
如果系统发现 Redis 及 DB 中都不存在该资源,就缓存空结果一段时间。需要注意哈,这次的失效时间不能设置的太长,否则数据的实效性会产生很大的问题。

2、用户合法性校验
对用户的请求合法性进行校验,拦截恶意重复请求。

3、布隆过滤器
看到这个名词不要慌。简单来说布隆过滤器的用途就是帮助你判断某个值是否存在。举个例子来看下:假设我们现在有一个长度为 9 的 bit 数组,该数组的每个位置上只能保存 1 或者 0,1 标识该位置被占用,0 标识该位置未被使用。

对于 key1,我们借助三个 Hash 函数分别对其哈希运算。
再将得到的这三个哈希值对 9 求模。
最后将这三个模值落入到 bit 数组上。
key2、key3 按照同样的方式再处理一遍。

缓存击穿

关键词:定点打击
试想如果所有请求对着一个 key 照死里搞,这是不是就是一种定点打击呢?

怎么理解呢?举个极端的例子:比如某某明星爆出一个惊天狠料,海量吃瓜群众同时访问微博去查看该八卦新闻,而微博 Redis 集群中数据在此刻正好过期了,那么无数的请求则直接打到了微博系统的物理 DB 上,DB 瞬间挂了。

解决方案:
1、热点数据永远不过期
比如我们可以将某个 key 的缓存时间设置为 25 小时,然后后台有个 JOB 每隔 24 小时就去批量刷新一下热点数据。就可以解决这个问题了。

2、使用互斥锁
容易影响吞吐量,大部分项目设置热点 key 永不过期就妥妥的了。

缓存雪崩

关键词:Redis 崩了,没有数据了
这里的 Redis 崩了指的并不是 Redis 集群宕机了。而是说在某个时刻 Redis 集群中的热点 key 都失效了。如果集群中的热点 key 在某一时刻同时失效了的话,试想海量的请求都将直接打到 DB 上,DB 可能在瞬间就被打爆了。

解决方案:

1、Redis 失效时间加上随机数
Redis 失效时间加上随机数,是一种比较取巧的解决方案。在一定程度上减轻了 DB 的瞬时压力,但是这种方案也在一定程度上增加了维护的成本。

2、Redis 永不过期
实现方案在上文中简单提过了。

Redis 缓存穿透、击穿、雪崩的区别?

  • 缓存穿透—穿过(绕过) Redis 和 DB 来搞你
  • 缓存击穿—定点打击来搞你
  • 缓存雪崩—热点 key 在某一个时刻同时失效

Redis有哪些优缺点?

优点:

  • 性能极高,能到 100000 次/s 读写速度
  • 支持数据的持久化,对数据的更新采用Copy-on-write技术,可以异步地保存到磁盘上
  • 丰富的数据类型,String(字符串)、List(列表)、Hash(字典)、Set(集合)、Sorted Set(有序集合)
  • 原子性:Redis 的所有操作都是原子性的,多个操作通过 MULTI 和 EXEC 指令支持事务
  • 丰富的特性:key 过期、publish/subscribe、notify
  • 支持数据的备份,快速的主从复制
  • 节点集群,很容易将数据分布到多个Redis实例中

缺点:

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
  • 适合的场景主要局限在较小数据量的高性能操作和运算上

Redis的哨兵原理

每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).
若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel)。

哨兵有三个定时监控任务完成对各节点的发现和监控:

任务1,每个哨兵节点每10秒会向主节点和从节点发送info命令获取最拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到

**任务2,**每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的;

**任务3,**每隔1秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据

主观下线和客观下线:

**主观下线:**刚知道哨兵节点每隔1秒对主节点和从节点、其它哨兵节点发送ping做心跳检测,当这些心跳检测时间超过down-after-milliseconds时,哨兵节点则认为该节点错误或下线,这叫主观下线;这可能会存在错误的判断。

**客观下线:**当主观下线的节点是主节点时,此时该哨兵3节点会通过指令sentinel is-masterdown-by-addr寻求其它哨兵节点对主节点的判断,当超过quorum(法定人数)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了,大部分哨兵节点都同意下线操作,也就说是客观下线

领导者哨兵选举流程:

**a,**每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;

**b,**当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;

**c,**如果哨兵3发现自己在选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举…………

心跳检测

在命令传播阶段,从服务器默认以每秒一次的频率,向主服务器发送命令:

REPLCONF ACK <replication_offset> //replication_offset是从服务器当前的复制偏移量。

心跳检测的作用:检测主服务器的网络连接状态;辅助实现min-slaves选项;检测命令丢失。

检测主从服务器的网络连接状态

通过向主服务器发送INFO replication命令,可以列出从服务器列表,可以看出从最后一次向主发送命令距离现在过了多少秒。

redis主从复制

Redis主从复制可以根据是否是全量分为全量同步和增量同步。

全量复制

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
  1)Slave 连接Master ,发送SYNC命令;
  2)Master 接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件 , 同时将这之后新的写命令记入缓冲区;
  3)Master BGSAVE执行完后,向所有Slave 发送快照文件, 并继续记录写命令;
  4)Slave 收到快照文件后丢弃所有旧数据,载入收到的快照;
  5)Master 快照发送完毕后开始向Slave 发送缓冲区中的写命令;
  6)Slave 完成对快照的载入,开始接收命令请求,并执行来自Master 缓冲区的写命令;
  完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。

增量复制

增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

Redis主从同步策略

主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

Redis持久化机制有哪些?各有什么优缺点?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

Redis 提供两种持久化机制: RDB 和 AOF

RDB(**RDBRedis DataBase:*)

指用数据集快照的方式半持久化模式,记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,可恢复数据

优点:

  • 只有一个文件 dump.rdb,恢复操作简单,容灾性好
  • 性能较高,fork 子进程进行写操作,主进程继续处理命令
  • 大数据集比 AOF 的恢复效率高

缺点:

  • 数据安全性低,RDB 是每间隔一段时间进行持久化,若期间 redis 发生故障,可能会发生数据丢失

AOF(AOFAppend-only file)

指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储,保存为 aof 文件

优点:

  • 数据安全,aof 持久化可以配置 appendfsync 属性为 always,记录每个命令操作到 aof 文件中一次;通过 append 模式写文件,即使中途服务器宕机,也可以通过 redis-check-aof 工具解决数据一致性问题
  • AOF 机制的 rewrite 模式,AOF 文件没被 rewrite 之前可以进行处理,如删除文件中的 flushall 命令

缺点:

  • AOF 的持久化文件比 RDB 大,恢复速度慢

Redis使用过程中的注意事项?

  • 主库压力很大,可以考虑读写分离
  • Master 最好不要做持久化工作,如 RDB 内存快照和 AOF 日志文件。(Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程,文件较大时会间断性暂停服务;AOF 文件过大会影响 Master 重启的恢复速度)
  • 如果数据比较重要,使用 AOF 方式备份数据,设置合理的备份频率
  • 保证主从复制的速度和网络连接的稳定性,主从机器最好在同一内网
  • 官方推荐,使用 sentinel 集群配合多个主从节点集群,解决单点故障问题实现高可用

Redis过期键的删除策略有哪些?

  • 定时删除:在设置键的过期时间的同时,创建一个定时器,达到过期时间,执行键的删除操作
  • 惰性删除:不主动删除过期键,从键空间中获取键时,都检查取得的键是否过期,过期则删除;没过期则返回
  • 定期删除:每隔一段时间对数据库进行一次检查,删除里面的过期键。删除多少过期键、检查多少个数据库,由算法决定。

为什么Redis所有数据放到内存中?

  • Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘,所以 Redis 具有高速读写和数据持久化的特征
  • 如果程序直接与磁盘交互,磁盘 IO 速度会严重影响 Redis 的性能
  • 内存的硬件成本降低,使得 Redis 更受欢迎

说说Redis的同步机制?

Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步

同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致

  • 从节点向主节点发送 SYNC 指令
  • 收到 SYNC 指令,主节点执行 BGSAVE 指令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写指令
  • 主节点 BGSAVE 指令执行后,会将生成的 RDB 文件发送给从节点
  • 从节点接收、载入 RDB 文件,将数据库状态更新至主节点执行 BGSAVE 指令时的数据库状态
  • 从节点加载完 RDB 文件,通知主节点将记录在缓冲区里面的所有写指令发送给从节点,从节点执行这些写指令,将数据库状态更新至主节点当前数据库状态

说说Redis集群?

  • 主从同步/复制:解决读写分离的问题。分为主库 master、从库 slave。一般主库可以写数据,从库只读自动同步主库更新的数据。集群情况下,有节点宕机会导致请求不可用;主机宕机可能会导致数据不一致;从机重启同步数据需要考虑主机的 io 压力。生产环境建议使用下面两种方法
  • Redis Sentinel,哨兵机制,解决主从节点高可用的问题。监控主从服务器运行状态;检测到 master 宕机时,会发布消息进行选举,自动将 slave 提升为 master
  • Redis Cluster,分布式解决方案,解决单点故障与扩展性以及哨兵机制中每台 Redis 服务器都存储相同的数据浪费内存的问题。实现了 Redis 的分布式存储,也就是每台 Redis 节点上存储不同的内容

说说遇到的Redis集群方案不可用的情况?

  • 集群主库半数宕机(根据 failover 原理,fail 掉一个主需要一半以上主都投票通过才可以)
  • 集群某一节点的主从全数宕机

Redis如何设置密码?

配置文件,修改 requirepass 属性,重启有效

指令设置密码为 123456,无需重启

config set requirepass 123456

设置验证密码为 654321,登录完之后没有通过密码认证还是无法访问 Redis

auth 654321

Redis 集群会有写操作丢失吗?

以下情况可能导致写操作丢失:

  • 过期 key 被清理
  • 最大内存不足,导致 Redis 自动清理部分 key 以节省空间
  • 主库故障后自动重启,从库自动同步
  • 单独的主备方案,网络不稳定触发哨兵的自动切换主从节点,切换期间会有数据丢失

Redis如何做内存优化?

  • 缩减键值对象:满足业务要求下 key 越短越好;value 值进行适当压缩
  • 共享对象池:即 Redis 内部维护[0-9999]的整数对象池,开发中在满足需求的前提下,尽量使用整数对象以节省内存
  • 尽可能使用散列表(hashes)
  • 编码优化,控制编码类型
  • 控制 key 的数量

Redis集群之间是如何复制?

2.8 版以前,Redis 通过同步(sync)和指令传播(command propagate)两个操作完成同步

  • 同步(sync):将从节点的数据库状态更新至与主节点的数据库状态一致
  • 指令传播(command propagate):主节点数据被修改,会主动向从节点发送执行的写指令,从节点执行之后,两个节点数据状态又保持一致

2.8 版开始新增 PSYNC 指令,PSYNC 具有两种模式:

  • 完整重同步(full resynchronization),与 SYNC 过程基本一致
  • 部分重同步(partial resynchronization),借助复制偏移量、复制积压缓冲区、服务器运行 ID ,完成主从节点断开连接后,从节点重连主节点后,条件允许,主节点将连接断开期间执行的写指令发送给从节点,从节点接收并执行写指令,将数据库更新至主节点当前状态

Redis如何选择数据库?

SELECT index

切换到指定的数据库,数据库索引号 index 用数字值指定,0 作为起始索引值

连接建立后,如果不 select,默认对 db 0 操作

说一说你对Redis的事务的理解?

Redis事务的特性:

  • 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 没有隔离级别,事务提交前结果不可见,事务提交执行后可见
  • 不保证原子性,Redis 同一个事务中有命令执行失败,其后的命令仍然会被执行,不会回滚

事务三阶段:

  • 开启:MULTI 指令开启一个事务
  • 入队:将多个命令入队到事务中,这些命令不会立即执行,而是放到等待执行的事务队列
  • 执行:由 EXEC 指令触发事务执行

相关指令:

  • multi,标记一个事务块的开始,返回 ok
  • exec,执行所有事务块内,事务块内所有命令执行的先后顺序的返回值,操作被,返回空值 nil
  • discard,取消事务,放弃执行事务块内的所有命令,返回 ok
  • watch,监视 key 在事务执行之前是否被其他指令改动,若已修改则事务内的指令取消执行,返回 ok
  • unwatch,取消 watch 命令对 key 的监视,返回 ok

Redis如何设置过期时间?

redis.expire(key, expiration)

setex name2 10 wangwu 设置一个键值对,时间为10秒,过期后自动删除

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

这个问题主要考察了以下几点内容:

1.Redis的内存淘汰策略。
2.Redis的最大内存设置。

思路:首先计算出20w数据所需的内存空间,设置最大内存,然后选择合适的内存淘汰策略。

内存淘汰策略

(1)volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
(2)volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
(3)volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
(4)volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
(5)allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
(6)allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
(7)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
(8) no-enviction(驱逐):禁止驱逐数据,这也是默认策略。意思是当内存不足以容纳新入数据时,新写入操作就会报错,请求可以继续进行,线上任务也不能持续进行,采用no-enviction策略可以保证数据不被丢失。

Redis事务支持隔离性吗

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。

Redis事务保证原子性吗,支持回滚吗

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

nginx==========================================

什么是Nginx?

Nginx是一个 轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发,现在中国使用nginx网站用户有很多,例如:新浪、网易、 腾讯等。

为什么要用Nginx?

跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,

而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。

使用Nginx的话还能:

节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
稳定性高:宕机的概率非常小
接收用户请求是异步的
为什么Nginx性能这么高?
因为他的事件处理机制:异步非阻塞事件处理机制:运用了epoll模型,提供了一个队列,排队解决

Nginx怎么处理请求的?

nginx接收一个请求后,首先由listen和server_name指令匹配server模块,再匹配server模块里的location,location就是实际地址
server { # 第一个Server区块开始,表示一个独立的虚拟主机站点
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / { # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
} # 第一个location区块结果

什么是正向代理和反向代理?

正向代理就是一个人发送一个请求直接就到达了目标的服务器
反方代理就是请求统一被Nginx接收,nginx反向代理服务器接收到之后,按照一定的规 则分发给了后端的业务处理服务器进行处理了
使用“反向代理服务器的优点是什么?
反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。

Nginx的优缺点?

优点:

占内存小,可实现高并发连接,处理响应快
可实现http服务器、虚拟主机、方向代理、负载均衡
Nginx配置简单
可以不暴露正式的服务器IP地址
缺点:
动态处理差:nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,现在一般前端用nginx作为反向代理抗住压力,

Nginx应用场景?

http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。

如何用Nginx解决前端跨域问题?

使用Nginx转发请求。把跨域的接口写成调本域的接口,然后将这些接口转发到真正的请求地址。
Nginx虚拟主机怎么配置?
1、基于域名的虚拟主机,通过域名来区分虚拟主机——应用:外部网站

2、基于端口的虚拟主机,通过端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台

3、基于ip的虚拟主机。

限流怎么做的?

  • Nginx限流就是限制用户请求速度,防止服务器受不了

  • 限流有3种

    1. 正常限制访问频率(正常流量)

      限制一个用户发送的请求,我Nginx多久接收一个请求。

      Nginx中使用ngx_http_limit_req_module模块来限制的访问频率,限制的原理实质是基于漏桶算法原理来实现的。在nginx.conf配置文件中可以使用limit_req_zone命令及limit_req命令限制单个IP的请求处理频率。

    2. 突发限制访问频率(突发流量)

      限制一个用户发送的请求,我Nginx多久接收一个。

      上面的配置一定程度可以限制访问频率,但是也存在着一个问题:如果突发流量超出请求被拒绝处理,无法处理活动时候的突发流量,这时候应该如何进一步处理呢?Nginx提供burst参数结合nodelay参数可以解决流量突发的问题,可以设置能处理的超过设置的请求数外能额外处理的请求数。

    3. 限制并发连接数

      Nginx中的ngx_http_limit_conn_module模块提供了限制并发连接数的功能,可以使用limit_conn_zone指令以及limit_conn执行进行配置。

  • Nginx的限流都是基于漏桶流算法

漏桶算法

漏桶算法是网络世界中流量整形或速率限制时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。也就是我们刚才所讲的情况。漏桶算法提供的机制实际上就是刚才的案例:突发流量会进入到一个漏桶,漏桶会按照我们定义的速率依次处理请求,如果水流过大也就是突发流量过大就会直接溢出,则多余的请求会被拒绝。所以漏桶算法能控制数据的传输速率。

Nginx负载均衡的算法怎么实现的?策略有哪些?

为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。

Nginx负载均衡实现的策略有以下五种:

1 轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。

upstream backserver {  
	server 192.168.0.12; 
	server 192.168.0.13;  
} 

2 权重 weight
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。

upstream backserver { 
 server 192.168.0.12 weight=2; 
 server 192.168.0.13 weight=8; 
} 

权重越高,在被访问的概率越大,如上例,分别是20%,80%。

3 ip_hash( IP绑定)
每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题
upstream backserver {
ip_hash;
server 192.168.0.12:88;
server 192.168.0.13:80;
4 fair(第三方插件)
必须安装upstream_fair模块。

对比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。

upstream backserver { 
 server server1; 
 server server2; 
 fair; 

哪个服务器的响应速度快,就将请求分配到那个服务器上。
5、url_hash(第三方插件)
必须安装Nginx的hash软件包

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。

upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}

Linux=========================================

linux如何添加新系统用户?

  • 以 root 身份登录 linux 系统,进入终端
  • 增加一个新用户,useradd 用户名
  • 设置密码,passwd 用户名

什么是bash别名?

相当于自定义 shell 指令
如:ll 指令可以查看文件的详细信息,ll 就是一个被定义好的别名,能够大大的简化指令

1.通过 alias 命令可以查看命令别名
[root]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
2.定义新的别
[root]#alias rmall = 'rm -rf'
3.取消别名
[root]# unalias rmall

linux指令-ls

list 的缩写,通过 ls 命令不仅可以查看 linux 文件夹包含的文件,而且可以查看文件权限(包括目录、文件夹、文件权限)、查看目录信息等等。

删除目录 rmdir

改变 linux 系统文件或目录的访问权限 chmod

该命令有两种用法:一种是包含字母和操作符表达式的文字设定法;另一种是包含数字的数字设定法
每一文件或目录的访问权限都有三组,每组用三位代号表示:

  • 文件属主的读、写和执行权限
  • 与属主同组的用户的读、写和执行权限
  • 系统中其他用户的读、写和执行权限
常用参数:
-c 当发生改变时,报告处理信息
-R 处理指定目录以及其子目录下所有文件
权限范围:
u:目录或者文件的当前的用户
g:目录或者文件的当前的群组
o:除了目录或者文件的当前用户或群组之外的用户或者群组
a:所有的用户及群组
权限代号:
r:读权限,用数字4表示
w:写权限,用数字2表示
x:执行权限,用数字1表示
-:删除权限,用数字0表示
s:特殊权限

查看文件内容有哪些命令可以使用?

vi 文件名 #编辑方式查看,可修改
cat 文件名 #显示全部文件内容
more 文件名 #分页显示文件内容
less 文件名 #与 more 相似,更好的是可以往前翻页
tail 文件名 #仅查看尾部,还可以指定行数
head 文件名 #仅查看头部,还可以指定行数

复制文件用哪个命令?如果需要连同文件夹一块复制呢?如果需要有提示功能呢?

cp cp -r ????

删除文件用哪个命令?如果需要连目录及目录下文件一块删除呢?删除空文件夹用什么命令?

rm rm -r rmdir

Linux 下命令有哪几种可使用的通配符?分别代表什么含义?

“?”可替代单个字符。

“*”可替代任意多个字符。

方括号“[charset]”可替代 charset 集中的任何单个字符,如[a-z],[abABC]

用什么命令对一个文件的内容进行统计?(行号、单词数、字节数)

wc 命令 - c 统计字节数 - l 统计行数 - w 统计字数。

搜索文件用什么命令? 格式是怎么样的?

find <指定目录> <指定条件> <指定动作>

whereis 加参数与文件名

locate 只加文件名

find 直接搜索磁盘,较慢。

find / -name “string*”

使用什么命令查看用过的命令列表?

history

docker========================================

什么是docker?什么是docker镜像?

docker是一个容器化平台,它以容器的形式将您的应用程序及其所有依赖项打包在一起,以确保您的应用程序在任何环境中无缝运行。
是docker容器的源代码,用于创建容器。使用build命令创建镜像。

什么是docker容器?docker容器有几种状态?docker容器内部机制?容器与主机之间的数据拷贝?启动容器并挂在目录?

(1)docker容器:docker容器包括应用程序及其所有依赖项,作为操作系统的独立进程运行。
(2)docker容器4种状态:运行+已暂停+重新启动+已退出
(3)docker容器内部机制:每个容器都在自己的命名空间中运行,但使用与所有其他容器完全相同的内核。发生隔离是因为内核知道分配给进程的命名空间,并且在API调用期间确保进程只能访问其自己的命名空间中的资源。
【操作系统的一个功能是允许将全局资源(如网络和磁盘)共享到进程。如果将这些全局资源包装在命名空间中,以使它们仅对在同一命名空间中运行的那些进程可见】
(4)主机copy到容器:docker cp /www 96f7f14e99ab:/www/ 容器copy到主机:docker cp 96f7f14e99ab:/www /tmp/
(5)启动nginx容器(随机端口映射),并挂载本地文件目录到容器html的命令:
docker run -d -P --name nginx2 -v /home/nginx:/usr/share/nginx/html nginx
docker run

Docker容器有几种状态

四种状态:运行、已暂停、重新启动、已退出。

docker常用命令

docker pull 拉取或者更新指定镜像
docker push 将镜像推送至远程仓库
docker rm 删除容器
docker rmi 删除镜像
docker images 列出所有镜像
docker ps 列出所有容器

JVM===========================================

JVM内存结构

JVM内存结构主要有三大块:堆内存、方法区和栈

堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。

JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)

内存划分

Java虚拟机按照运行时内存使用区域划分如图:

img

img

一、程序计数器(Program Counter Register)

程序计数器就是记录当前线程执行程序的位置,改变计数器的值来确定执行的下一条指令,比如循环、分支、方法跳转、异常处理,线程恢复都是依赖程序计数器来完成。

Java虚拟机多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线程切换能恢复到正确的位置,每条线程都需要一个独立的程序计数器,所以它是线程私有的。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

二、Java虚拟机栈(VM Stack)

Java虚拟机栈是线程私有,生命周期与线程相同。创建线程的时候就会创建一个java虚拟机栈。

虚拟机执行java程序的时候,每个方法都会创建一个栈帧,栈帧存放在java虚拟机栈中,通过压栈出栈的方式进行方法调用。

栈帧又分为一下几个区域:局部变量表、操作数栈、动态连接、方法出口等。

平时我们所说的变量存在栈中,这句话说的不太严谨,应该说局部变量存放在java虚拟机栈的局部变量表中。

Java的8中基本类型的局部变量的值存放在虚拟机栈的局部变量表中,如果是引用型的变量,则只存储对象的引用地址。

注意:

当用户请求web服务器,每个请求开启一个线程负责用户的响应计算(每个线程分配一个虚拟机栈空间),如果并发量大时,可能会导致内存溢出(OutOfMemoneyError),可以适当的把每个虚拟机栈的大小适当调小一点,减少内存的使用量来提高系统的并发量。

当栈空间调小以后,又会引发方法调用深度的的问题。因为,每个方法都会生成一个栈帧,如果方法调用深度很深就意味着,栈里面存放大量的栈帧,可能导致栈内存溢出(StackOverFlowError)。

三、本地方法栈(Native Method Stack)

本地方法栈 为虚拟机使用到本地方法服务(native)。本地方法栈为线程私有,功能和虚拟机栈非常类似。线程在调用本地方法时,来存储本地方法的局部变量表,本地方法的操作数栈等等信息。

本地方法:是非java语言实现的方法,例如,java调用C语言,来操作某些硬件信息。

四、堆(Heap):

堆是被所有线程共享的区域,实在虚拟机启动时创建的。堆里面存放的都是对象的实例(new 出来的对象都存在堆中)。

我们平常所说的垃圾回收,主要回收的就是堆区。为了提升垃圾回收的性能,又把堆分成两块区新生代(young)年老代(old),更细一点划分新生代又可划分为Eden区和2个Survivor区(From Survivor和To Survivor)。

如下图结构:

img

**Eden:**新创建的对象存放在Eden区

**From Survivor和To Survivor:**保存新生代gc后还存活的对象。(使用复制算法,导致有一个Survivor空间浪费)Hotspot虚拟机新生代Eden和Survivor的大小比值为4:1,因为有两个Survivor,所以Eden:From Survivor:To Survivor比值为8:1:1。

**老年代:**对象存活时间比较长(经过多次新生代的垃圾收集,默认是15次)的对象则进入老年的。当堆中分配的对象实例过多,且大部分对象都在使用,就会报内存溢出异常(OutOfMemoneyError)。

五、方法区

方法区是被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)

永久代也会垃圾回收,主要针对常量池回收,类型卸载(比如反射生成大量的临时使用的Class等信息)。

常量池用于存放编译期生成的各种字节码和符号引用,常量池具有一定的动态性,里面可以存放编译期生成的常量;运行期间的常量也可以添加进入常量池中,比如string的intern()方法。

当方法区满时,无法在分配空间,就会抛出内存溢出的异常(OutOfMemoneyError)。

Java8中已经没有方法区了,取而代之的是元空间(Metaspace)。

六:直接内存

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。

JDK1.4加的NIO中,ByteBuffer有个方法是allocateDirect(int capacity) ,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

GC垃圾回收

:主要就是为了提高内存利用率,只回收“托管堆中的内存资源”,没有引用的对象。不回收“栈”上的资源(比如值类型)。

一个跟踪过程,它传递性地跟踪指向当前使用的对象的所有指针,以便找到可以引用的所有对象,然后重新使用在此跟踪过程中未找到的任何堆内存。公共语言运行库垃圾回收器还压缩使用中的内存,以缩小堆所需要的工作空间。
Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()

GC垃圾回收机制(

引入“代”的概念,

①总共有三代,0~2代。

②每次新建对象都在第0代中。

③每代有固定大小。

这个叫“自动回收”,肯定有小朋友会问,那是不是可以手动回收。答案是肯定的,比如析构函数就可以达到这个目的。

将引用可分为强引用、软引用、弱引用、虚引用。

强引用:如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError,使程序异常终止;
软引用:如果内存空间够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存,只要垃圾回收器没有回收它,该对象就可以被程序使用,软引用可用来实现内存敏感的高速缓存。
弱引用:比软引用更弱一些,弱引用关联的对象只能生存到下一次垃圾回收发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
虚引用:在任何时候都可能被垃圾回收器回收,主要用于跟踪对象被垃圾回收器回收的活动

弱引用

人就是这样,得不到的永远在骚动,得到的有恃无恐。当失去了,才追悔莫及。假如对象要被回收了,但是我又想再次用这个对象,该怎么办?这个时候就出来了这个–“弱引用”。

为什么需要弱对象呢?因为,有一些数据创建起来很容易,但是却需要很多内存。

例如:有一个程序需要去访问用户硬盘上的所有文件夹和文件名;你可以在程序第一次需要这个数据时访问用户磁盘生成一次数据,数据生成之后你就可以访问内存中的数据来得到用户文件数据,而不是每次都去读磁盘获得数据,这样做可以提升程序的性能。

你可以这么理解,你跟你女朋友闹分手,微信电话什么的全部删除了,但是还留着QQ,以后想要联系,可以通过这个QQ来联系,而此时的这个QQ,就相当于是弱引用了。至于为什么分手了还要联系,这个原因也就是为什么要有弱引用的原因了~

软引用、弱引用都在引用对象被垃圾回收之后,JVM才把引用加入到与之关联的引用队列中;而虚引用对象在垃圾回收之前,JVM把引用加入到阈值关联的引用队列中。虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象时,若发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。这样就可以通过判断引用队列中是否已经加入了虚引用来了解被引用对象是否将要被垃圾回收器回收

GC 优化

  • 将进入老年代的对象数量降到最低
  • 减少Full GC的执行时间
  • 优化JVM参数——堆和栈的大小,设置垃圾收集器的模式

新生代和老年代的区别(阿里面试官的题目):

所谓的新生代和老年代是针对于分代收集算法来定义的,新生代又分为Eden和Survivor两个区。加上老年代就这三个区。数据会首先分配到Eden区 当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。),当Eden没有足够空间的时候就会 触发jvm发起一次Minor GC。如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。如果老年代满了就执行:Full GC 因为不经常执行,因此采用了 Mark-Compact算法清理

新生代

新生代存放一些新生的对象实例。

新生代内又划分了3个区域,分别是:

Eden
SurvivorFrom
SurvivorTo
一般情况占据的位置是8:1:1
当新生代的Eden区区域不够的时候,就会发生minor GC。

每当进行一次minor GC时,区域间进行数据交换,Eden区和SurvivorFrom区域会把存活的对象放进SurvivorTo区域。

在新生代之中一般采用复制算法进行GC。

老年代

老年代存放比较稳定存活的对象。
对于老年代有Major GC的垃圾回收机制。

有两个触发条件:

一个是当新生代发生minor GC之后,仍然不够位置存放新生对象时,借用老年代空间不足时,会发生major GC
另一个是当申请一个大的连续空间(如大数组)给较大对象时,也会触发Major GC进行垃圾回收。

永久代

永久代在方法区内,存放Class和Meta的信息。

在GC里面回收永久代的信息有两个

一是废弃变量
一个是无用的类
废弃变量比较好处理,只要常量池中没有String对象引用,也没有其他对象引用,就会回收。

无用的类的话就有比较多的判断标准。

一是Java堆中没有这个类的实例对象
二是这个类的类加载器以及被回收
三是该类对应的Class对象没有被其他方法引用,不能通过反射访问该类的方法

元数据

在Java8中完全移除了永久代这个概念,新创建了元数据这个概念。

原本永久代存放的Meta的数据不再存在于方法区内,而是依赖于Native Memory(本地内存)里,而String常量池和类的静态变量转移到Java堆里,因此Java堆内存有所变动。

默认情况下,元数据只受本地内存限制(操作系统的虚拟内存),可以定义新参数MaxMetaspaceSize来限制元数据的大小,如果不限制元空间就会根据需要动态调整。在这里插入图片描述

JDK,JRE,JVM区别

Jdk中包括了Jre,Jre中包括了JVM

jdk
Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm的一些工具 Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。

jre
Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库 Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。

jvm
Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

说一下堆栈的区别?

\1. 栈内存存储的是局部变量而堆内存存储的是实体;

\2. 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;

\3. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

队列和栈是什么?有什么区别?

  • 队列和栈都是被用来预存储数据的。
  • 队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
  • 栈和队列很相似,但它运行对元素进行后进先出进行检索。

说一下类加载的执行过程?

类加载分为以下 5 个步骤:

  1. 加载:根据查找路径找到相应的 class 文件然后导入;
  2. 检查:检查加载的 class 文件的正确性;
  3. 准备:给类中的静态变量分配内存空间;
  4. 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  5. 初始化:对静态变量和静态代码块执行初始化工作

什么是乐观锁和悲观锁?

悲观锁

Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有共享变量的锁,都采用独占的方式来访问这些变量。独占锁其实就是一种悲观锁,所以可以说synchronized是悲观锁。

乐观锁

乐观锁( Optimistic Locking)其实是一种思想。相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

Zookeeper=====================================

zookeeper 是什么?

zookeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 google chubby 的开源实现,是 hadoop 和 hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

zookeeper 都有哪些功能?

  • 集群管理:监控节点存活状态、运行请求等。
  • 主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 zookeeper 可以协助完成这个过程。
  • 分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。
  • 命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。配置中心

zookeeper 有几种部署模式?

zookeeper 有三种部署模式:

  • 单机部署:一台集群上运行;
  • 集群部署:多台集群运行;
  • 伪集群部署:一台集群启动多个 zookeeper 实例运行。

zookeeper 怎么保证主从节点的状态同步?

zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 zab 协议。 zab 协议有两种模式,分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,zab 就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。

集群中为什么要有主节点?

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点。

集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?

可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

说一下 zookeeper 的通知机制?

客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。

集合===========================================

集合有两个父接口,一个collection,一个Map;
而collection有两个子接口,一个List,一个Set;
List有两个常见的实现类 ArrayList,LinkedList;
Set有两个常见的实现类 HashSet,TreeSet;
Map有两个常见的实现类 HashMap,HashTable。

ArrayList和Vector的联系和区别

相同点:

  • 底层都使用数组实现
  • 功能相同,实现增删改查等操作的方法相似
  • 长度可变的数组结构

不同点:

  • Vector是早期JDK版本提供,ArrayList是新版本替代Vector的
  • Vector 的方法都是同步的,线程安全;ArrayList 非线程安全,但性能比Vector好
  • 默认初始化容量都是10,Vector 扩容默认会翻倍,可指定扩容的大小;ArrayList只增加 50%

Collection和Collections有什么区别?

Collection 是JDK中集合层次结构中的最根本的接口。定义了集合类的基本方法。

Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法,不能实例化,Collection 集合框架的工具类。

List、Set、Map 之间的区别是什么?

  • List:有序集合,元素可重复
  • Set:不重复集合,LinkedHashSet按照插入排序,SortedSet可排序,HashSet无序
  • Map:键值对集合,存储键、值和之间的映射;Key无序,唯一;value 不要求有序,允许重复

HashMap和Hashtable 有什么区别?

  • 线程安全性不同。HashMap 线程不安全;Hashtable 中的方法是 synchronized 的。
  • key、value 是否允许 null。HashMap 的 key 和 value 都是可以是 null,key 只允许一个 null;Hashtable 的 key 和 value 都不可为 null。
  • 迭代器不同。HashMap 的 Iterator 是 fail-fast 迭代器;Hashtable 还使用了 enumerator 迭代器。
  • hash的计算方式不同。HashMap 计算了 hash值;Hashtable 使用了 key 的 hashCode方法。
  • 默认初始大小和扩容方式不同。HashMap 默认初始大小 16,容量必须是 2 的整数次幂,扩容时将容量变为原来的2倍;Hashtable 默认初始大小 11,扩容时将容量变为原来的 2 倍加 1。
  • 是否有 contains 方法。HashMap 没有 contains 方法;Hashtable 包含 contains 方法,类似于 containsValue。
  • 父类不同。HashMap 继承自 AbstractMap;Hashtable 继承自 Dictionary。

如何决定使用HashMap还是TreeMap?

  • HashMap基于散列桶(数组和链表)实现;TreeMap基于红黑树实现。
  • HashMap不支持排序;TreeMap默认是按照Key值升序排序的,可指定排序的比较器,主要用于存入元素时对元素进行自动排序。
  • HashMap大多数情况下有更好的性能,尤其是读数据。在没有排序要求的情况下,使用HashMap。都是非线程安全。

HashMap原理

1,无序的。

2,可以接受null键值和值

3,非同步的,线程不安全的

1,hashmap是我们几乎每天用到的集合类,它以键值对的形式存在。
2,在jdk1.7中:底层是数组加链表,1.8中采用的是数组加链表加红黑树,红黑树的引入是为了提高查询效率
3,1.7中hash碰撞采用头插法,头插法会形成循环链表(?),1.8尾插法
4,hash算法1.8进行了简化
5,最好传入一个二的次幂的初始化容量, put时,会初始化数组,容量为大于等于初始化容量的最近的二次幂,比如初始化容量为6,他初始化就是8。
6,key的hash值 与 上容量-1,计算出所在位置
7,扩容的问题:加载因子0.75,达到 容量 *0.75后就会扩容,两倍扩容
8,树化,数组容量达到64,链表长度大于等于8,后才会进行树化,链表长度小于6就会解除树化

简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

​ 以键值对(key-value)的形式存储元素

通过 hashcode() 和 equals() 方法定位键值对存储的准确位置

  1. 首先通过 hashcode() 获得key映射的散列值(也叫 hash 值),通过 hash 值确定键值对存储在哪个链表。不过,不同的 key 生成的 hash 值可能是一样的。
  2. 如果 hash 值定位到的存储空间存在其它的键值对,那就通过 equals() 比较 key ,如果一样,就更新它的 value,如果不一样,就添加在链表后面
  3. 通俗的说,hashcode() 就相当于字典目录,确定好某个字所在的页数,equals()相当于页数上存放的位置

在场景应用上,hashcode() 和 equals() 是重点修改的部分

手动实现 HashMap

// 键值对存储
package HashMap;

public class Entry {
    
    public Object key;
    public Object value;
    
    public Entry(Object key, Object value){
        this.key = key;
        this.value = value;
    }
    
    @Override
    public String toString() {
        return "[key=" + key + ", value=" + value + "]";
    }
}
// put,get接口
package HashMap;

public interface IHashMap {
    public void put(Object key, Object object);
    public Object get(Object key);
}
package HashMap;

import java.util.LinkedList;

public class MyHashMap implements IHashMap {

    LinkedList<Entry>[] values = new LinkedList[2000];

    @Override
    public void put(Object key, Object value) {
        // 获得 hash 值
        int hashcode = hashcode(key);
        // 根据 hash 值确定存储在哪个链表位置
        LinkedList<Entry> list = values[hashcode];
        if (null == list) {
            list = new LinkedList<>();
            values[hashcode] = list;
        }
        boolean found = false;
        for (Entry entry : list) {
            // 通过比较确定存储的链表位置
            if (key.equals(entry.key)) {
                entry.value = value;
                found = true;
                break;
            }
        }
        if (!found) {
            Entry entry = new Entry(key, value);
            list.add(entry);
        }
    }

    @Override
    public Object get(Object key) {
        // 获得 hash 值
        int hashcode = hashcode(key);
        // 根据 hash 值确定存储在哪个链表位置
        LinkedList<Entry> list = values[hashcode];
        if (null == list)
            return null;
        Object result = null;
         // 通过比较确定存储的链表位置,并取出相应的 value
        for (Entry entry : list) {
            if (entry.key.equals(key)) {
                result = entry.value;
            }
        }
        return result;
    }
    
    // 自定义 hash 值,使其落在 0~1999 之间
    private int hashcode(Object obj) {
        String str = obj.toString();
        if (0 == str.length())
            return 0;

        int hashcode = 0;
        char[] cs = str.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            hashcode += cs[i];
        }
        hashcode *= 23;
        // 取绝对值
        Math.abs(hashcode);
        // 取值落在 0 - 1999 之间
        hashcode %= 2000;
        return hashcode;
    }
    
    public static void main(String[] args) {
        MyHashMap map =new MyHashMap();

        map.put("test", "坦克");
        map.put("test", "物理");
        map.put("t", "坦克2");
        System.out.println(map.get("test"));
        

   }

}

img

运行结果

场景应用

  1. 有一个 people 类,HashMap 的 key 想通过 name 和 age 判断 people 是否相等,而不是通过 people 对象的存储地址

实现方法:根据 HashMap 原理,需同时重写 equals() 和 hashCode()。很容易犯的错误是,只重写 equals(),而忘记重写 hashCode()

package HashMap;

import java.util.HashMap;
import java.util.Set;

class People {
    private String name;
    private int age;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        return name.hashCode() * 37 + age;
    }

    @Override
    public boolean equals(Object obj) {
        return this.name.equals(((People) obj).name) && this.age == ((People) obj).age;
    }

    public static void main(String[] args) {
        People p1 = new People("Jack", 12);
        HashMap<People, Integer> hashMap = new HashMap<People, Integer>();
        hashMap.put(p1, 1);

        System.out.println(hashMap.get(new People("Jack", 12)));
    }
}

img

运行结果

  • ArrayList 基于动态数组实现的非线程安全的集合;LinkedList 基于双向链表实现的非线程安全的集合。
  • 扩容问题:ArrayList 使用数组实现,无参构造函数默认初始化长度为 10,数组扩容是会将原数组中的元素重新拷贝到新数组中,长度为原来的 1.5 倍(扩容代价高);LinkedList 不存在扩容问题,新增元素放到集合尾部,修改相应的指针节点即可。
  • LinkedList 比 ArrayList 更占内存,因为 LinkedList 为每一个节点存储了两个引用节点,一个指向前一个元素,一个指向下一个元素。
  • 对于随机 index 访问的 get 和 set 方法,一般 ArrayList 的速度要优于 LinkedList。因为 ArrayList 直接通过数组下标直接找到元素;LinkedList 要移动指针遍历每个元素直到找到为止。
  • 新增和删除元素,一般 LinkedList 的速度要优于 ArrayList。因为 ArrayList 在新增和删除元素时,可能扩容和复制数组;LinkedList 实例化对象需要时间外,只需要修改节点指针即可。
  • LinkedList 集合不支持高效的随机访问(RandomAccess)
  • ArrayList 的空间浪费主要体现在在list列表的结尾预留一定的容量空间;LinkedList 的空间花费则体现在它的每一个元素都需要消耗存储指针节点对象的空间。

都是非线程安全,允许存放 null

Array和ArrayList有何区别?

Array 即数组 定义一个 Array 时,必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同。

ArrayList 是动态数组,长度动态可变,会自动扩容。不使用泛型的时候,可以添加不同类型元素。

如何实现数组和List之间的转换?

数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法

List 转数组,使用 List 的 toArray 方法。无参 toArray 方法返回 Object 数组,传入初始化长度的数组对象,返回该对象数组

哪些集合类是线程安全的?

  • Vector
  • Stack
  • Hashtable
  • java.util.concurrent 包下所有的集合类 ArrayBlockingQueue、ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentLinkedDeque…

Iterator怎么使用?有什么特点?

  • java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
  • next() 方法获得集合中的下一个元素
  • hasNext() 检查集合中是否还有元素
  • remove() 方法将迭代器新返回的元素删除
  • forEachRemaining(Consumer<? super E> action) 方法,遍历所有元素

多线程线程池====================================

什么是线程?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,可以使用多线程对进行运算提速。

比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒

什么是线程安全和线程不安全?

通俗的说:加锁的就是是线程安全的,不加锁的就是是线程不安全的

线程安全

线程安全: 就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问,直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染

一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。

线程不安全

线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

什么是多线程?优缺点?

什么是多线程?

多线程:是指从软件或者硬件上实现多个线程的并发技术。

多线程的好处:

  1. 使用多线程可以把程序中占据时间长的任务放到后台去处理,如图片、视屏的下载
  2. 发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好

多线程的缺点:

  1. 大量的线程降低代码的可读性;
  2. 更多的线程需要更多的内存空间
  3. 当多个线程对同一个资源出现争夺时候要注意线程安全的问题。

什么是多线程的上下文切换?

即使是单核CPU也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程时同时执行的,时间片一般是几十毫秒(ms)

上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态

  • 从任务保存到再加载的过程就是一次上下文切换

如何创建、启动 Java 线程?

一、重写 Thread 类的 run() 方法。

表现形式有两种:1)new Thread 对象匿名重写 run() 方法

2)继承 Thread 对象,重写 run() 方法

二、实现 Runnable 接口,重写 run() 方法。

表现形式有两种:1)new Runnable 对象,匿名重写 run() 方法

2)实现 Runnable 接口,重写 run() 方法

三、实现 Callable 接口,使用 FutureTask 类创建线程

四、使用线程池创建、启动线程

package constxiong.concurrency.a006;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池的方式启动线程
 * @author ConstXiong
 */
public class TestCreateThreadByThreadPool {

    public static void main(String[] args) {
        // 使用工具类 Executors 创建单线程线程池
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        //提交执行任务
        singleThreadExecutor.submit(() -> {System.out.println("单线程线程池执行任务");});
        //关闭线程池
        singleThreadExecutor.shutdown();
    }
}

什么是线程池?

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

一个线程池包括以下四个基本组成部分:

1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

常见线程池

①newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)
可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程

线程池常用参数

corePoolSize:核心线程数量,会一直存在,除非allowCoreThreadTimeOut设置为true
maximumPoolSize:线程池允许的最大线程池数量
keepAliveTime:线程数量超过corePoolSize,空闲线程的最大超时时间
unit:超时时间的单位
workQueue:工作队列,保存未执行的Runnable 任务
threadFactory:创建线程的工厂类
handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

为什么要使用线程池?

因为 Java 中创建一个线程,需要调用操作系统内核的 API,操作系统要为线程分配一系列的资源,成本很高,所以线程是一个重量级的对象,应该避免频繁创建和销毁。
使用线程池就能很好地避免频繁创建和销毁。

如何在两个线程之间共享数据

1,如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做。

2,如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计4个线程。其中两个线程每次对j增加1,另外两个线程对j每次减1,银行存取款

java基础=======================================

==和equals的区别是什么?

  • == 是关系运算符,equals() 是方法,结果都返回布尔值
  • Object 的 == 和 equals() 比较的都是地址,作用相同

== 作用:

  • 基本类型,比较值是否相等
  • 引用类型,比较内存地址值是否相等
  • 不能比较没有父子关系的两个对象

equals()方法的作用:

  • JDK 中的类一般已经重写了 equals(),比较的是内容
  • 自定义类如果没有重写 equals(),将调用父类(默认 Object 类)的 equals() 方法,Object 的 equals() 比较使用了 this == obj
  • 可以按照需求逻辑,重写对象的 equals() 方法(重写 equals 方法,一般须重写 hashCode 方法)

hashCode()相同,equals()也一定为true吗?

首先,答案肯定是不一定。同时反过来 equals() 为true,hashCode() 也不一定相同。

  • 类的 hashCode() 方法和 equals() 方法都可以重写,返回的值完全在于自己定义。
  • hashCode() 返回该对象的哈希码值;equals() 返回两个对象是否相等。

关于 hashCode() 和 equals() 是方法是有一些 常规协定:

1、两个对象用 equals() 比较返回true,那么两个对象的hashCode()方法必须返回相同的结果。

2、两个对象用 equals() 比较返回false,不要求hashCode()方法也一定返回不同的值,但是最好返回不同值,以提高哈希表性能。

3、重写 equals() 方法,必须重写 hashCode() 方法,以保证 equals() 方法相等时两个对象 hashcode() 返回相同的值。

final finally finalize()区别

  • final 表示最终的、不可改变的。用于修饰类、方法和变量。final 修饰的类不能被继承;final 方法也同样只能使用,不能重写,但能够重载;final 修饰的成员变量必须在声明时给定初值或者在构造方法内设置初始值,只能读取,不可修改;final 修饰的局部变量必须在声明时给定初值;final 修饰的变量是非基本类型,对象的引用地址不能变,但对象的属性值可以改变
  • finally 异常处理的一部分,它只能用在 try/catch 语句中,表示希望 finally 语句块中的代码最后一定被执行(存在一些情况导致 finally 语句块不会被执行,如 jvm 结束)
  • finalize() 是在 java.lang.Object 里定义的,Object 的 finalize() 方法什么都不做,对象被回收时 finalize() 方法会被调用。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作,在垃圾收集器删除对象之前被调用的。一般情况下,此方法由JVM调用。特殊情况下,可重写 finalize() 方法,当对象被回收的时候释放一些资源,须调用 super.finalize() 。

如何将字符串反转?

  • 使用 StringBuilder 或 StringBuffer 的 reverse 方法,本质都调用了它们的父类 AbstractStringBuilder 的 reverse 方法实现。(JDK1.8)
  • 不考虑字符串中的字符是否是 Unicode 编码,自己实现。
  • 递归

String类的常用方法有哪些?

String 类的常用方法:

  • equals:字符串是否相同
  • equalsIgnoreCase:忽略大小写后字符串是否相同
  • compareTo:根据字符串中每个字符的Unicode编码进行比较
  • compareToIgnoreCase:根据字符串中每个字符的Unicode编码进行忽略大小写比较
  • indexOf:目标字符或字符串在源字符串中位置下标
  • lastIndexOf:目标字符或字符串在源字符串中最后一次出现的位置下标
  • valueOf:其他类型转字符串
  • charAt:获取指定下标位置的字符
  • codePointAt:指定下标的字符的Unicode编码
  • concat:追加字符串到当前字符串
  • isEmpty:字符串长度是否为0
  • contains:是否包含目标字符串
  • startsWith:是否以目标字符串开头
  • endsWith:是否以目标字符串结束
  • format:格式化字符串
  • getBytes:获取字符串的字节数组
  • getChars:获取字符串的指定长度字符数组
  • toCharArray:获取字符串的字符数组
  • join:以某字符串,连接某字符串数组
  • length:字符串字符数
  • matches:字符串是否匹配正则表达式
  • replace:字符串替换
  • replaceAll:带正则字符串替换
  • replaceFirst:替换第一个出现的目标字符串
  • split:以某正则表达式分割字符串
  • substring:截取字符串
  • toLowerCase:字符串转小写
  • toUpperCase:字符串转大写
  • trim:去字符串首尾空格

普通类和抽象类有哪些区别?

  • 抽象类不能被实例化
  • 抽象类可以有抽象方法,抽象方法只需申明,无需实现
  • 含有抽象方法的类必须申明为抽象类
  • 抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
  • 抽象方法不能被声明为静态
  • 抽象方法不能用 private 修饰
  • 抽象方法不能用 final 修饰

**重写:**在子类中将父类的成员方法的名称保留,重新编写成员方法的实现内容,更改方法的访问权限,修改返回类型的为父类返回类型的子类。
一些规则:
重写发生在子类继承父类
参数列表必须完全与被重写方法的相同
重写父类方法时,修改方法的权限只能从小范围到大范围
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的子类(JDK1.5 及更早版本返回类型要一样,JDK1.7 及更高版本可以不同)
访问权限不能比父类中被重写的方法的访问权限更低。如:父类的方法被声明为 public,那么子类中重写该方法不能声明为 protected
重写方法不能抛出新的检查异常和比被重写方法申明更宽泛的异常(即只能抛出父类方法抛出异常的子类)
声明为 final 的方法不能被重写
声明为 static 的方法不能被重写
声明为 private 的方法不能被重写

**重载:**一个类中允许同时存在一个以上的同名方法,这些方法的参数个数或者类型不同

重载条件:

  • 方法名相同
  • 参数类型不同 或 参数个数不同 或 参数顺序不同

规则:
被重载的方法参数列表(个数或类型)不一样
被重载的方法可以修改返回类型
被重载的方法可以修改访问修饰符
被重载的方法可以修改异常抛出
方法能够在同一个类中或者在一个子类中被重载
无法以返回值类型作为重载函数的区分标准

方法重载和重写是什么?有什么区别?

  • 作用范围:重写的作用范围是父类和子类之间;重载是发生在一个类里面
  • 参数列表:重载必须不同;重写不能修改
  • 返回类型:重载可修改;重写方法返回相同类型或子类
  • 抛出异常:重载可修改;重写可减少或删除,一定不能抛出新的或者更广的异常
  • 访问权限:重载可修改;重写一定不能做更严格的限制

内存泄漏和内存溢出的区别

  • 内存溢出(out of memory):指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory。
  • 内存泄露(memory leak):指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光。
  • memory leak 最终会导致 out of memory。

什么是多态?如何实现?有什么好处?

多态:
同一个接口,使用不同的实例而执行不同操作。同一个行为具有多个不同表现形式或形态的能力。

实现多态有三个条件:

  • 继承
  • 子类重写父类的方法
  • 父类引用变量指向子类对象

用Lambda表达式实现Runnable:

@Test
public void test1(){
    //使用Java8之前
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("使用Lambda表达式之前,需要这么写!");
        }
    }).start();
    //使用Lambda表达式
    new Thread(() -> System.out.println("使用Lambda表达式,只需要这一句话!")).start();

String s=“a”+“b”+“c”+“d”;创建了几个对象?

1个

Java 编译器对字符串常量直接相加的表达式进行优化,不等到运行期去进行加法运算,在编译时就去掉了加号,直接将其编译成一个这些常量相连的结果。

所以 “a”+“b”+“c”+“d” 相当于直接定义一个 “abcd” 的字符串。

String s = new String(“xyz”);创建几个String对象?

两个或一个

  • 第一次调用 new String(“xyz”); 时,会在堆内存中创建一个字符串对象,同时在字符串常量池中创建一个对象 “xyz”
  • 第二次调用 new String(“xyz”); 时,只会在堆内存中创建一个字符串对象,指向之前在字符串常量池中创建的 “xyz”

序列化

**序列化:**把对象转化为可传输的字节序列过程称为序列化。

**反序列化:**把字节序列还原为对象的过程称为反序列化。

为什么要序列化?

如果光看定义我想你很难一下子理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么?

其实序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组。

因为我们单方面的只把对象转成字节数组还不行,因为没有规则的字节数组我们是没办法把对象的本来面目还原回来的,所以我们必须在把对象转成字节数组的时候就制定一种规则**(序列化),那么我们从IO流里面读出数据的时候再以这种规则把对象还原回来(反序列化)。**

如果我们要把一栋房子从一个地方运输到另一个地方去,序列化就是我把房子拆成一个个的砖块放到车子里,然后留下一张房子原来结构的图纸,反序列化就是我们把房子运输到了目的地以后,根据图纸把一块块砖头还原成房子原来面目的过程

什么情况下需要序列化?

通过上面我想你已经知道了凡是需要进行“跨平台存储”和”网络传输”的数据,都需要进行序列化。

本质上存储和网络传输 都需要经过 把一个对象状态保存成一种跨平台识别的字节格式,然后其他的平台才可以通过字节信息解析还原对象信息。


序列化的方式

序列化只是一种拆装组装对象的规则,那么这种规则肯定也可能有多种多样,比如现在常见的序列化方式有:

JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protostuff、FST(不支持跨语言)


序列化技术选型的几个关键点

序列化协议各有千秋,不能简单的说一种序列化协议是最好的,只能从你的当时环境下去选择最适合你们的序列化协议,如果你要为你的公司项目进行序列化技术的选型,那么主要从以下几个因素。

协议是否支持跨平台

如果你们公司有好多种语言进行混合开发,那么就肯定不适合用有语言局限性的序列化协议,要不然你JDK序列化出来的格式,其他语言并没法支持。

序列化的速度

如果序列化的频率非常高,那么选择序列化速度快的协议会为你的系统性能提升不少。

序列化出来的大小

如果频繁的在网络中传输的数据那就需要数据越小越好,小的数据传输快,也不占带宽,也能整体提升系统的性能。


Java 是如何实现序列化的?

前面主要介绍了一下什么是序列化,那么下面主要讲下JAVA是如何进行序列化的,以及序列化的过程中需要注意的一些问题

  • java 实现序列化很简单,只需要实现Serializable 接口即可。

JAVA序列化中常见的问题

  • 问题一:static 属性不能被序列化

原因:序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

  • 问题二:Transient 属性不会被序列化